diff --git a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/api/IBigBlueButtonInGW.java b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/api/IBigBlueButtonInGW.java
index 8c9811d1c022a58dac2bb773c983187ea306e0a2..957eca1d1f00b9e4f1e49fdfbec3fe878c013549 100755
--- a/akka-bbb-apps/src/main/java/org/bigbluebutton/core/api/IBigBlueButtonInGW.java
+++ b/akka-bbb-apps/src/main/java/org/bigbluebutton/core/api/IBigBlueButtonInGW.java
@@ -114,7 +114,7 @@ public interface IBigBlueButtonInGW {
 	void editCaptionHistory(String meetingID, String userID, Integer startIndex, Integer endIndex, String locale, String text);
 
 	// DeskShare
-	void deskShareStarted(String conferenceName, String callerId, String callerIdName);
+	void deskShareStarted(String confId, String callerId, String callerIdName);
 	void deskShareStopped(String conferenceName, String callerId, String callerIdName);
 	void deskShareRTMPBroadcastStarted(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp);
 	void deskShareRTMPBroadcastStopped(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp);
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala
index a3986b2452d41ff6bc00411c659314d6498b5ac3..9fd760f88e270994dae062df04fadfe1e7892dd2 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonActor.scala
@@ -37,11 +37,6 @@ class BigBlueButtonActor(val system: ActorSystem,
     case msg: UserMutedInVoiceConfMessage => handleUserMutedInVoiceConfMessage(msg)
     case msg: UserTalkingInVoiceConfMessage => handleUserTalkingInVoiceConfMessage(msg)
     case msg: VoiceConfRecordingStartedMessage => handleVoiceConfRecordingStartedMessage(msg)
-    case msg: DeskShareStartedRequest => handleDeskShareStartedRequest(msg)
-    case msg: DeskShareStoppedRequest => handleDeskShareStoppedRequest(msg)
-    case msg: DeskShareRTMPBroadcastStartedRequest => handleDeskShareRTMPBroadcastStartedRequest(msg)
-    case msg: DeskShareRTMPBroadcastStoppedRequest => handleDeskShareRTMPBroadcastStoppedRequest(msg)
-    case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
     case _ => // do nothing
   }
 
@@ -151,6 +146,7 @@ class BigBlueButtonActor(val system: ActorSystem,
         /** Subscribe to meeting and voice events. **/
         eventBus.subscribe(m.actorRef, m.mProps.meetingID)
         eventBus.subscribe(m.actorRef, m.mProps.voiceBridge)
+        eventBus.subscribe(m.actorRef, m.mProps.deskshareBridge)
 
         meetings += m.mProps.meetingID -> m
         outGW.send(new MeetingCreated(m.mProps.meetingID, m.mProps.externalMeetingID, m.mProps.recorded, m.mProps.meetingName,
@@ -206,52 +202,5 @@ class BigBlueButtonActor(val system: ActorSystem,
     outGW.send(new GetAllMeetingsReply(resultArray))
   }
 
-  private def handleDeskShareStartedRequest(msg: DeskShareStartedRequest) {
-    log.info("handleDeskShareStartedRequest: msg.conferenceName=" + msg.conferenceName)
-    findMeetingWithVoiceConfId(msg.conferenceName) foreach { m =>
-      {
-        //        println(msg.conferenceName + " (in for each) handleDeskShareStartedRequest BBBActor   ")
-        m.actorRef ! msg
-      }
-    }
-  }
-
-  private def handleDeskShareStoppedRequest(msg: DeskShareStoppedRequest) {
-    log.info("handleDeskShareStoppedRequest msg.conferenceName=" + msg.conferenceName)
-    findMeetingWithVoiceConfId(msg.conferenceName) foreach { m =>
-      {
-        //        println(msg.conferenceName + " (in for each) handleDeskShareStoppedRequest BBBActor   ")
-        m.actorRef ! msg
-      }
-    }
-  }
-
-  private def handleDeskShareRTMPBroadcastStartedRequest(msg: DeskShareRTMPBroadcastStartedRequest) {
-    log.info("handleDeskShareRTMPBroadcastStartedRequest msg.conferenceName=" + msg.conferenceName)
-    findMeetingWithVoiceConfId(msg.conferenceName) foreach { m =>
-      {
-        //        println(msg.conferenceName + " (in for each) handleDeskShareRTMPBroadcastStartedRequest BBBActor   ")
-        m.actorRef ! msg
-      }
-    }
-  }
-
-  private def handleDeskShareRTMPBroadcastStoppedRequest(msg: DeskShareRTMPBroadcastStoppedRequest) {
-    log.info("handleDeskShareRTMPBroadcastStoppedRequest msg.conferenceName=" + msg.conferenceName)
-    findMeetingWithVoiceConfId(msg.conferenceName) foreach { m =>
-      {
-        //        println(msg.conferenceName + " (in for each) handleDeskShareRTMPBroadcastStoppedRequest BBBActor   ")
-        m.actorRef ! msg
-      }
-    }
-  }
-
-  private def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
-    val m = meetings.values.find(m => {
-      m.mProps.meetingID == msg.conferenceName
-    })
-    m foreach { mActor => mActor.actorRef ! msg }
-  }
-
 }
 
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala
index 211ce68b0207f335e4ce2fbf16ea0c0a6469432d..508011e47fea43eda6fed683a05c162a5915688d 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/BigBlueButtonInGW.scala
@@ -60,6 +60,7 @@ class BigBlueButtonInGW(
           msg.payload.name,
           msg.payload.record,
           msg.payload.voiceConfId,
+          msg.payload.voiceConfId + "-DESKSHARE", // WebRTC Desktop conference id
           msg.payload.durationInMinutes,
           msg.payload.autoStartRecording,
           msg.payload.allowStartStopRecording,
@@ -498,8 +499,11 @@ class BigBlueButtonInGW(
    * Message Interface for DeskShare
    * *****************************************************************
    */
-  def deskShareStarted(meetingId: String, callerId: String, callerIdName: String) {
-    eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareStartedRequest(meetingId, callerId, callerIdName)))
+  def deskShareStarted(confId: String, callerId: String, callerIdName: String) {
+    println("____BigBlueButtonInGW::deskShareStarted " + confId + callerId + "    " +
+      callerIdName)
+    eventBus.publish(BigBlueButtonEvent(confId, new DeskShareStartedRequest(confId, callerId,
+      callerIdName)))
   }
 
   def deskShareStopped(meetingId: String, callerId: String, callerIdName: String) {
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala
index ced4ccc8d1da8414c814940fa6c0127a0e21b21f..8d5e313e3fbaeaf4a1e72234820d7d90e65d649b 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala
@@ -163,4 +163,80 @@ class LiveMeeting(val mProps: MeetingProperties,
   def permissionsEqual(other: Permissions): Boolean = {
     meetingModel.permissionsEqual(other)
   }
+
+  // WebRTC Desktop Sharing
+
+  def handleDeskShareStartedRequest(msg: DeskShareStartedRequest): Unit = {
+    log.info("handleDeskShareStartedRequest: dsStarted=" + meetingModel.getDeskShareStarted())
+
+    if (!meetingModel.getDeskShareStarted()) {
+      val timestamp = System.currentTimeMillis().toString
+      val streamPath = "rtmp://" + mProps.red5DeskShareIP + "/" + mProps.red5DeskShareApp +
+        "/" + mProps.meetingID + "/" + mProps.meetingID + "-" + timestamp
+      log.info("handleDeskShareStartedRequest: streamPath=" + streamPath)
+
+      // Tell FreeSwitch to broadcast to RTMP
+      outGW.send(new DeskShareStartRTMPBroadcast(msg.conferenceName, streamPath))
+
+      meetingModel.setDeskShareStarted(true)
+    }
+  }
+
+  def handleDeskShareStoppedRequest(msg: DeskShareStoppedRequest): Unit = {
+    log.info("handleDeskShareStoppedRequest: dsStarted=" + meetingModel.getDeskShareStarted() +
+      " URL:" + meetingModel.getRTMPBroadcastingUrl())
+
+    // Tell FreeSwitch to stop broadcasting to RTMP
+    outGW.send(new DeskShareStopRTMPBroadcast(msg.conferenceName, meetingModel.getRTMPBroadcastingUrl()))
+
+    meetingModel.setDeskShareStarted(false)
+  }
+
+  def handleDeskShareRTMPBroadcastStartedRequest(msg: DeskShareRTMPBroadcastStartedRequest): Unit = {
+    log.info("handleDeskShareRTMPBroadcastStartedRequest: isBroadcastingRTMP=" + meetingModel.isBroadcastingRTMP() +
+      " URL:" + meetingModel.getRTMPBroadcastingUrl())
+
+    // only valid if not broadcasting yet
+    if (!meetingModel.isBroadcastingRTMP()) {
+      meetingModel.setRTMPBroadcastingUrl(msg.streamname)
+      meetingModel.broadcastingRTMPStarted()
+      meetingModel.setDesktopShareVideoWidth(msg.videoWidth)
+      meetingModel.setDesktopShareVideoHeight(msg.videoHeight)
+      log.info("START broadcast ALLOWED when isBroadcastingRTMP=false")
+
+      // Notify viewers in the meeting that there's an rtmp stream to view
+      outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, msg.streamname, msg.videoWidth, msg.videoHeight, true))
+    } else {
+      log.info("START broadcast NOT ALLOWED when isBroadcastingRTMP=true")
+    }
+  }
+
+  def handleDeskShareRTMPBroadcastStoppedRequest(msg: DeskShareRTMPBroadcastStoppedRequest): Unit = {
+    log.info("handleDeskShareRTMPBroadcastStoppedRequest: isBroadcastingRTMP=" + meetingModel
+      .isBroadcastingRTMP() + " URL:" + meetingModel.getRTMPBroadcastingUrl())
+
+    // only valid if currently broadcasting
+    if (meetingModel.isBroadcastingRTMP()) {
+      log.info("STOP broadcast ALLOWED when isBroadcastingRTMP=true")
+      meetingModel.broadcastingRTMPStopped()
+
+      // notify viewers that RTMP broadcast stopped
+      outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, meetingModel.getRTMPBroadcastingUrl(),
+        msg.videoWidth, msg.videoHeight, false))
+    } else {
+      log.info("STOP broadcast NOT ALLOWED when isBroadcastingRTMP=false")
+    }
+  }
+
+  def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
+
+    log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting="
+      + meetingModel.isBroadcastingRTMP() + " URL:" + meetingModel.getRTMPBroadcastingUrl())
+    if (meetingModel.isBroadcastingRTMP()) {
+      // if the meeting has an ongoing WebRTC Deskshare session, send a notification
+      outGW.send(new DeskShareNotifyASingleViewer(mProps.meetingID, msg.requesterID, meetingModel.getRTMPBroadcastingUrl(),
+        meetingModel.getDesktopShareVideoWidth(), meetingModel.getDesktopShareVideoHeight(), true))
+    }
+  }
+
 }
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingActor.scala
index dfb42b5bd57a777afa0134e8ee213874c60dcaad..72d17227ea32d9700456ccb0fe16db0d605d77ff 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingActor.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingActor.scala
@@ -178,77 +178,13 @@ class MeetingActor(val mProps: MeetingProperties,
     case msg: UpdateCaptionOwnerRequest => liveMeeting.handleUpdateCaptionOwnerRequest(msg)
     case msg: EditCaptionHistoryRequest => liveMeeting.handleEditCaptionHistoryRequest(msg)
 
-    case _ => // do nothing
-  }
-
-  // Broadcast video stream,
-  private def handleDeskShareStartedRequest(msg: DeskShareStartedRequest) {
-    log.info("handleDeskShareStartedRequest: dsStarted=" + meetingModel.getDeskShareStarted())
-
-    if (!meetingModel.getDeskShareStarted()) {
-      val timestamp = System.currentTimeMillis().toString()
-      val streamPath = "rtmp://" + mProps.red5DeskShareIP + "/" + mProps.red5DeskShareApp +
-        "/" + mProps.meetingID + "/" + mProps.meetingID + "-" + timestamp
-      log.info("handleDeskShareStartedRequest: streamPath=" + streamPath)
-
-      // Tell FreeSwitch to broadcast to RTMP
-      outGW.send(new DeskShareStartRTMPBroadcast(msg.conferenceName, streamPath))
-
-      meetingModel.setDeskShareStarted(true)
-    }
-  }
-
-  private def handleDeskShareStoppedRequest(msg: DeskShareStoppedRequest) {
-    log.info("handleDeskShareStoppedRequest: dsStarted=" + meetingModel.getDeskShareStarted())
-
-    // Tell FreeSwitch to stop broadcasting to RTMP
-    outGW.send(new DeskShareStopRTMPBroadcast(msg.conferenceName, meetingModel.getRTMPBroadcastingUrl()))
-
-    meetingModel.setDeskShareStarted(false)
-  }
+    case msg: DeskShareStartedRequest => liveMeeting.handleDeskShareStartedRequest(msg)
+    case msg: DeskShareStoppedRequest => liveMeeting.handleDeskShareStoppedRequest(msg)
+    case msg: DeskShareRTMPBroadcastStartedRequest => liveMeeting.handleDeskShareRTMPBroadcastStartedRequest(msg)
+    case msg: DeskShareRTMPBroadcastStoppedRequest => liveMeeting.handleDeskShareRTMPBroadcastStoppedRequest(msg)
+    case msg: DeskShareGetDeskShareInfoRequest => liveMeeting.handleDeskShareGetDeskShareInfoRequest(msg)
 
-  private def handleDeskShareRTMPBroadcastStartedRequest(msg: DeskShareRTMPBroadcastStartedRequest) {
-    log.info("handleDeskShareRTMPBroadcastStartedRequest: isBroadcastingRTMP=" + meetingModel.isBroadcastingRTMP())
-
-    // only valid if not broadcasting yet
-    if (!meetingModel.isBroadcastingRTMP()) {
-      meetingModel.setRTMPBroadcastingUrl(msg.streamname)
-      meetingModel.broadcastingRTMPStarted()
-      meetingModel.setDesktopShareVideoWidth(msg.videoWidth)
-      meetingModel.setDesktopShareVideoHeight(msg.videoHeight)
-      log.info("START broadcast ALLOWED when isBroadcastingRTMP=false")
-
-      // Notify viewers in the meeting that there's an rtmp stream to view
-      outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, msg.streamname, msg.videoWidth, msg.videoHeight, true))
-    } else {
-      log.info("START broadcast NOT ALLOWED when isBroadcastingRTMP=true")
-    }
-  }
-
-  private def handleDeskShareRTMPBroadcastStoppedRequest(msg: DeskShareRTMPBroadcastStoppedRequest) {
-    log.info("handleDeskShareRTMPBroadcastStoppedRequest: isBroadcastingRTMP=" + meetingModel.isBroadcastingRTMP())
-
-    // only valid if currently broadcasting
-    if (meetingModel.isBroadcastingRTMP()) {
-      log.info("STOP broadcast ALLOWED when isBroadcastingRTMP=true")
-      meetingModel.broadcastingRTMPStopped()
-
-      // notify viewers that RTMP broadcast stopped
-      outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, meetingModel.getRTMPBroadcastingUrl(),
-        msg.videoWidth, msg.videoHeight, false))
-    } else {
-      log.info("STOP broadcast NOT ALLOWED when isBroadcastingRTMP=false")
-    }
-  }
-
-  private def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
-
-    log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting=" + meetingModel.isBroadcastingRTMP())
-    if (meetingModel.isBroadcastingRTMP()) {
-      // if the meeting has an ongoing WebRTC Deskshare session, send a notification
-      outGW.send(new DeskShareNotifyASingleViewer(mProps.meetingID, msg.requesterID, meetingModel.getRTMPBroadcastingUrl(),
-        meetingModel.getDesktopShareVideoWidth(), meetingModel.getDesktopShareVideoHeight(), true))
-    }
+    case _ => // do nothing
   }
 
 }
diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingModel.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingModel.scala
index 41bc576fe5ecc8cba6c7d87cce3c5cf007587ce7..241bde0f856baf53663f9f00187ccadfa4a319dc 100755
--- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingModel.scala
+++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/MeetingModel.scala
@@ -5,9 +5,9 @@ import java.util.concurrent.TimeUnit
 
 case object StopMeetingActor
 case class MeetingProperties(meetingID: String, externalMeetingID: String, meetingName: String, recorded: Boolean,
-  voiceBridge: String, duration: Int, autoStartRecording: Boolean, allowStartStopRecording: Boolean,
-  moderatorPass: String, viewerPass: String, createTime: Long, createDate: String,
-  red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean)
+  voiceBridge: String, deskshareBridge: String, duration: Int, autoStartRecording: Boolean,
+  allowStartStopRecording: Boolean, moderatorPass: String, viewerPass: String, createTime: Long,
+  createDate: String, red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean)
 
 case class MeetingExtensionProp(maxExtensions: Int = 2, numExtensions: Int = 0, extendByMinutes: Int = 20,
   sendNotice: Boolean = true, sent15MinNotice: Boolean = false,
diff --git a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala
index ef901d3464b95c0f1cb0a9ff9a5de33294e860d4..97bf4c31003f97f1260c8d961393c0006bf1a98f 100755
--- a/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala
+++ b/akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala
@@ -7,6 +7,7 @@ trait AppsTestFixtures {
   val meetingName = "test meeting"
   val record = false
   val voiceConfId = "85115"
+  val deskshareConfId = "85115-DESKSHARE"
   val durationInMinutes = 10
   val autoStartRecording = false
   val allowStartStopRecording = false
@@ -18,7 +19,7 @@ trait AppsTestFixtures {
 
   val mProps = new MeetingProperties(meetingId, externalMeetingId,
     meetingName, record,
-    voiceConfId,
+    voiceConfId, deskshareConfId,
     durationInMinutes,
     autoStartRecording, allowStartStopRecording,
     moderatorPassword, viewerPassword,
diff --git a/akka-bbb-fsesl/fs_conf_files/conf/dialplan/default.xml b/akka-bbb-fsesl/fs_conf_files/conf/dialplan/default.xml
index e79b78e64f0e6ee0802978cdd3e23854a4e5902a..73219f1b2f06e7f96ec60f2e288fef90d62460c0 100644
--- a/akka-bbb-fsesl/fs_conf_files/conf/dialplan/default.xml
+++ b/akka-bbb-fsesl/fs_conf_files/conf/dialplan/default.xml
@@ -13,20 +13,12 @@
 <include>
     <context name="default">
     <extension name="public_extensions">
-        <condition field="destination_number" expression="^\d{5}$">
+        <condition field="destination_number" expression="^\d{5}-DESKSHARE$">
             <action application="log" data="INFO AAAAAA transferring to $1 XML public!!"/>
             <action application="transfer" data="${destination_number} XML public"/>
         </condition>
     </extension>
 
-
-    <extension name="public_extensions">
-        <condition field="destination_number" expression="^(\d{5})(-screen)$">
-            <action application="log" data="INFO BB  $1  $2   BBBB transferring to $1 XML public!!"/>
-            <action application="transfer" data="$1 XML public"/>
-        </condition>
-    </extension>
-
     <!-- other extensions -->
 
   </context>
diff --git a/akka-bbb-fsesl/fs_conf_files/conf/dialplan/public.xml b/akka-bbb-fsesl/fs_conf_files/conf/dialplan/public.xml
index eb61209ed1e0285926592998fe531b49875a3770..fce3a678b1edf27b17a363caf718eb3b2e123320 100644
--- a/akka-bbb-fsesl/fs_conf_files/conf/dialplan/public.xml
+++ b/akka-bbb-fsesl/fs_conf_files/conf/dialplan/public.xml
@@ -16,10 +16,10 @@
         <!-- other extensions -->
 
         <extension name="public_extensions">
-            <condition field="destination_number" expression="^\d{5}$">
-                <action application="log" data="INFO ************ redirecting ${destination_number} to ${destination_number}-DESKSHARE@video-mcu-stereo ***********" />
+            <condition field="destination_number" expression="^\d{5}-DESKSHARE$">
+                <action application="log" data="INFO ************WEBRTC DESKSHARE EXT***********" />
                 <action application="answer"/>
-                <action application="conference" data="${destination_number}-DESKSHARE@video-mcu-stereo"/>
+                <action application="conference" data="${destination_number}@video-mcu-stereo"/>
             </condition>
         </extension>
 
diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ESLEventListener.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ESLEventListener.java
index feac5d62a08c53f9019342d7e8e28a2bd76a81bc..a8c45063fd4ff9252d37f2cdc8a79fa73825c79f 100755
--- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ESLEventListener.java
+++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ESLEventListener.java
@@ -28,8 +28,6 @@ public class ESLEventListener implements IEslEventListener {
     private static final String STOP_RECORDING_EVENT = "stop-recording";
 
     private static final String DESKSHARE_CONFERENCE_NAME_SUFFIX = "-DESKSHARE";
-    private static final String DESKSHARE_CALLER_NAME_SUFFIX = " (Screen)";
-    private static final String DESKSHARE_CALLER_ID_SUFFIX = " (screen)";
 
     private final ConferenceEventListener conferenceEventListener;
     
@@ -68,53 +66,53 @@ public class ESLEventListener implements IEslEventListener {
 
         String voiceUserId = callerIdName;
 
-        System.out.println("User joined voice conference, user=[" + callerIdName + "], conf=[" + confName + "]");
-
         Matcher gapMatcher = GLOBAL_AUDION_PATTERN.matcher(callerIdName);
         if (gapMatcher.matches()) {
             System.out.println("Ignoring GLOBAL AUDIO USER [" + callerIdName + "]");
             return;
         }
         
-        // Deskstop sharing conferences have their name in the form ddddd-DESKSHARE
-        // Deskstop sharing conferences have the user with the desktop video displayed in this way:
-        // username (Screen) and usernum (screen)
-        if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX) &&
-                callerId.endsWith(DESKSHARE_CALLER_ID_SUFFIX) &&
-                callerIdName.endsWith(DESKSHARE_CALLER_NAME_SUFFIX)) {
+        // (WebRTC) Deskstop sharing conferences' name is of the form ddddd-DESKSHARE
+        // Voice conferences' name is of the form ddddd
+        if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX)) {
+            System.out.println("User joined deskshare conference, user=[" + callerIdName + "], " +
+                    "conf=[" + confName + "] callerId=[" + callerId + "]");
             DeskShareStartedEvent dsStart = new DeskShareStartedEvent(confName, callerId, callerIdName);
             conferenceEventListener.handleConferenceEvent(dsStart);
-        }
+        } else {
+            Matcher matcher = CALLERNAME_PATTERN.matcher(callerIdName);
+            if (matcher.matches()) {
+                voiceUserId = matcher.group(1).trim();
+                callerIdName = matcher.group(2).trim();
+            }
 
-        Matcher matcher = CALLERNAME_PATTERN.matcher(callerIdName);
-        if (matcher.matches()) {
-            voiceUserId = matcher.group(1).trim();
-            callerIdName = matcher.group(2).trim();
-        }
+            System.out.println("User joined voice conference, user=[" + callerIdName + "], conf=[" +
+                    confName + "] callerId=[" + callerId + "]");
 
-        VoiceUserJoinedEvent pj = new VoiceUserJoinedEvent(voiceUserId, memberId.toString(), confName, callerId, callerIdName, muted, speaking, "");
-        conferenceEventListener.handleConferenceEvent(pj);
+            VoiceUserJoinedEvent pj = new VoiceUserJoinedEvent(voiceUserId, memberId.toString(), confName, callerId, callerIdName, muted, speaking, "");
+            conferenceEventListener.handleConferenceEvent(pj);
+        }
     }
 
     @Override
     public void conferenceEventLeave(String uniqueId, String confName, int confSize, EslEvent event) {      
         Integer memberId = this.getMemberIdFromEvent(event);
-        System.out.println("User left voice conference, user=[" + memberId.toString() + "], conf=[" + confName + "]");
         String callerId = this.getCallerIdFromEvent(event);
         String callerIdName = this.getCallerIdNameFromEvent(event);
 
-        // Deskstop sharing conferences have their name in the form ddddd-DESKSHARE
-        // Deskstop sharing conferences have the user with the desktop video displayed in this way:
-        // username (Screen) and usernum (screen)
-        if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX) &&
-                callerId.endsWith(DESKSHARE_CALLER_ID_SUFFIX) &&
-                callerIdName.endsWith(DESKSHARE_CALLER_NAME_SUFFIX)) {
+        // (WebRTC) Deskstop sharing conferences' name is of the form ddddd-DESKSHARE
+        // Voice conferences' name is of the form ddddd
+        if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX)) {
+            System.out.println("User left deskshare conference, user=[" + memberId.toString() +
+                    "], " + "conf=[" + confName + "]");
             DeskShareEndedEvent dsEnd = new DeskShareEndedEvent(confName, callerId, callerIdName);
             conferenceEventListener.handleConferenceEvent(dsEnd);
+        } else {
+            System.out.println("User left voice conference, user=[" + memberId.toString() + "], " +
+                    "conf=[" + confName + "]");
+            VoiceUserLeftEvent pl = new VoiceUserLeftEvent(memberId.toString(), confName);
+            conferenceEventListener.handleConferenceEvent(pl);
         }
-
-        VoiceUserLeftEvent pl = new VoiceUserLeftEvent(memberId.toString(), confName);
-        conferenceEventListener.handleConferenceEvent(pl);
     }
 
     @Override
@@ -231,7 +229,8 @@ public class ESLEventListener implements IEslEventListener {
     
     @Override
     public void eventReceived(EslEvent event) {
-        System.out.println("ESL Event Listener received event=[" + event.getEventName() + "]");
+        System.out.println("ESL Event Listener received event=[" + event.getEventName() + "]" +
+                event.getEventHeaders().toString());
 //        if (event.getEventName().equals(FreeswitchHeartbeatMonitor.EVENT_HEARTBEAT)) {
 ////           setChanged();
 //           notifyObservers(event);
diff --git a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala
index 51db6186dc061217ff66f8a80b84e9263e9cc644..fc0375dc84f7b2aa6023d6fb48dd80b5dc1b4dbf 100755
--- a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala
+++ b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala
@@ -14,8 +14,7 @@ import org.bigbluebutton.common.messages.DeskShareRTMPBroadcastStoppedEventMessa
 
 class VoiceConferenceService(sender: RedisPublisher) extends IVoiceConferenceService {
 
-  val FROM_VOICE_CONF_SYSTEM_CHAN = "bigbluebutton:from-voice-conf:system";
-  private final val DESKSHARE_CONFERENCE_NAME_SUFFIX = "-DESKSHARE"
+  val FROM_VOICE_CONF_SYSTEM_CHAN = "bigbluebutton:from-voice-conf:system"
 
   def voiceConfRecordingStarted(voiceConfId: String, recordStream: String, recording: java.lang.Boolean, timestamp: String) {
     val msg = new VoiceConfRecordingStartedMessage(voiceConfId, recordStream, recording, timestamp)
@@ -24,13 +23,14 @@ class VoiceConferenceService(sender: RedisPublisher) extends IVoiceConferenceSer
 
   def userJoinedVoiceConf(voiceConfId: String, voiceUserId: String, userId: String, callerIdName: String,
     callerIdNum: String, muted: java.lang.Boolean, talking: java.lang.Boolean, avatarURL: String) {
-    //    println("******** FreeswitchConferenceService received voiceUserJoined vui=[" + userId + "] wui=[" + webUserId + "]")
+    println("******** FreeswitchConferenceService received voiceUserJoined vui=[" +
+      userId + "] wui=[" + voiceUserId + "]")
     val msg = new UserJoinedVoiceConfMessage(voiceConfId, voiceUserId, userId, callerIdName, callerIdNum, muted, talking, avatarURL)
     sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
   }
 
   def userLeftVoiceConf(voiceConfId: String, voiceUserId: String) {
-    //    println("******** FreeswitchConferenceService received voiceUserLeft vui=[" + userId + "] conference=[" + conference + "]")
+    println("******** FreeswitchConferenceService received voiceUserLeft vui=[" + voiceUserId + "] conference=[" + voiceConfId + "]")
     val msg = new UserLeftVoiceConfMessage(voiceConfId, voiceUserId)
     sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
   }
@@ -52,30 +52,26 @@ class VoiceConferenceService(sender: RedisPublisher) extends IVoiceConferenceSer
   }
 
   def deskShareStarted(voiceConfId: String, callerIdNum: String, callerIdName: String) {
-    val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "")
-    println("******** FreeswitchConferenceService send deskShareStarted to BBB " + trimmedVoiceConfId)
-    val msg = new DeskShareStartedEventMessage(trimmedVoiceConfId, callerIdNum, callerIdName)
+    println("******** FreeswitchConferenceService send deskShareStarted to BBB " + voiceConfId)
+    val msg = new DeskShareStartedEventMessage(voiceConfId, callerIdNum, callerIdName)
     sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
   }
 
   def deskShareEnded(voiceConfId: String, callerIdNum: String, callerIdName: String) {
-    val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "")
-    println("******** FreeswitchConferenceService send deskShareStopped to BBB " + trimmedVoiceConfId)
-    val msg = new DeskShareStoppedEventMessage(trimmedVoiceConfId, callerIdNum, callerIdName)
+    println("******** FreeswitchConferenceService send deskShareStopped to BBB " + voiceConfId)
+    val msg = new DeskShareStoppedEventMessage(voiceConfId, callerIdNum, callerIdName)
     sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
   }
 
   def deskShareRTMPBroadcastStarted(voiceConfId: String, streamname: String, vw: java.lang.Integer, vh: java.lang.Integer, timestamp: String) {
-    val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "")
-    println("******** FreeswitchConferenceService send deskShareRTMPBroadcastStarted to BBB " + trimmedVoiceConfId)
-    val msg = new DeskShareRTMPBroadcastStartedEventMessage(trimmedVoiceConfId, streamname, vw, vh, timestamp)
+    println("******** FreeswitchConferenceService send deskShareRTMPBroadcastStarted to BBB " + voiceConfId)
+    val msg = new DeskShareRTMPBroadcastStartedEventMessage(voiceConfId, streamname, vw, vh, timestamp)
     sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
   }
 
   def deskShareRTMPBroadcastStopped(voiceConfId: String, streamname: String, vw: java.lang.Integer, vh: java.lang.Integer, timestamp: String) {
-    val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "")
-    println("******** FreeswitchConferenceService send deskShareRTMPBroadcastStopped to BBB " + trimmedVoiceConfId)
-    val msg = new DeskShareRTMPBroadcastStoppedEventMessage(trimmedVoiceConfId, streamname, vw, vh, timestamp)
+    println("******** FreeswitchConferenceService send deskShareRTMPBroadcastStopped to BBB " + voiceConfId)
+    val msg = new DeskShareRTMPBroadcastStoppedEventMessage(voiceConfId, streamname, vw, vh, timestamp)
     sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
   }
 
diff --git a/bigbluebutton-client/resources/config.xml.template b/bigbluebutton-client/resources/config.xml.template
index 093d326a93d5c438ed29845d70313b479a8bd075..d7e52643f3ac64fc61e2197b919dd05c88f92ae7 100755
--- a/bigbluebutton-client/resources/config.xml.template
+++ b/bigbluebutton-client/resources/config.xml.template
@@ -42,13 +42,15 @@
 			baseTabIndex="301"
 		/>
 
-		<module name="ScreenshareModule" 
-			url="http://HOST/client/ScreenshareModule.swf?v=VERSION" 
+		<module name="ScreenshareModule"
+			url="http://HOST/client/ScreenshareModule.swf?v=VERSION"
 			uri="rtmp://HOST/screenshare"
 			showButton="true"
 			autoStart="false"
 			autoFullScreen="false"
 			baseTabIndex="201"
+			useWebRTCIfAvailable="false"
+			chromeExtensionKey=""
 			help="http://HOST/client/help/screenshare-help.html"
 		/>
     
diff --git a/bigbluebutton-client/resources/prod/BigBlueButton.html b/bigbluebutton-client/resources/prod/BigBlueButton.html
index c2bdad7cbfbebe38dac37cd4513e8781934c6370..c107caf3ff28a411137a8a8bcb421ef8c72775fe 100755
--- a/bigbluebutton-client/resources/prod/BigBlueButton.html
+++ b/bigbluebutton-client/resources/prod/BigBlueButton.html
@@ -105,7 +105,6 @@
     <script src="lib/jquery.verto.js" language="javascript"></script>
     <script src="lib/Screen-Capturing.js" language="javascript"></script>
     <script src="lib/verto_extension.js" language="javascript"></script>
-    <script src="lib/verto_extension_share.js" language="javascript"></script>
 
     <script src="lib/bbb_deskshare.js?v=VERSION" language="javascript"></script>
     <script src="lib/bbb_api_bridge.js?v=VERSION" language="javascript"></script>
diff --git a/bigbluebutton-client/resources/prod/lib/Screen-Capturing.js b/bigbluebutton-client/resources/prod/lib/Screen-Capturing.js
index 42526d4b9dbe98c927d7f6c2c52e2e7fe4e602ea..7761a70ecbfd1692687fa2e2c8633bdcce9fe440 100755
--- a/bigbluebutton-client/resources/prod/lib/Screen-Capturing.js
+++ b/bigbluebutton-client/resources/prod/lib/Screen-Capturing.js
@@ -92,8 +92,6 @@ var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
 var isChrome = !!window.chrome && !isOpera;
 
 function getChromeExtensionStatus(extensionid, callback) {
-    callback = normalizeCallback(callback);
-
     if (isFirefox) return callback('not-chrome');
 
     if (chromeMediaSource == 'desktop') return callback('installed-enabled');
diff --git a/bigbluebutton-client/resources/prod/lib/jquery.FSRTC.js b/bigbluebutton-client/resources/prod/lib/jquery.FSRTC.js
index 13fa548a6d5d336e7aaa358c1da4f54f201b46d3..8c6b625a8e9cf02b3a0ccb3815bd1fb20bbab5b5 100755
--- a/bigbluebutton-client/resources/prod/lib/jquery.FSRTC.js
+++ b/bigbluebutton-client/resources/prod/lib/jquery.FSRTC.js
@@ -103,7 +103,7 @@
 
 	if (moz) {
             this.constraints = {
-		offerToReceiveAudio: true,
+		offerToReceiveAudio: this.options.useSpeak === "none" ? false : true,
 		offerToReceiveVideo: this.options.useVideo ? true : false,
             };
 	} else {
@@ -111,7 +111,7 @@
 		optional: [{
 		    'DtlsSrtpKeyAgreement': 'true'
 		}],mandatory: {
-		    OfferToReceiveAudio: true,
+		    OfferToReceiveAudio: this.options.useSpeak === "none" ? false : true,
 		    OfferToReceiveVideo: this.options.useVideo ? true : false,
 		}
             };
@@ -342,8 +342,8 @@
             if(typeof self.options.localVideoStream.stop == 'function') {
 	        self.options.localVideoStream.stop();
             } else {
-		if (self.localVideoStream.active){
-                    var tracks = self.localVideoStream.getTracks();
+		if (self.options.localVideoStream.active){
+                    var tracks = self.options.localVideoStream.getTracks();
                     console.error(tracks);
 		    tracks.forEach(function(track, index){
 			console.log(track);
@@ -513,7 +513,7 @@
 	    audio = false;
 	} else {
 	    audio = {
-		mandatory: obj.options.audioParams,
+		mandatory: {},
 		optional: []
 	    };
 
@@ -521,6 +521,15 @@
 		audio.optional = [{sourceId: obj.options.useMic}]
 	    }
 
+	    if (obj.options.audioParams) {
+		for (var key in obj.options.audioParams) {
+		    var con = {};
+		    con[key] = obj.options.audioParams[key];
+		    audio.optional.push(con);
+		}
+	    }
+
+
 	}
 
 	if (obj.options.useVideo && obj.options.localVideo) {
@@ -558,8 +567,9 @@
 		video.optional.push({sourceId: obj.options.useCamera});
 	    }
 
-	    if (bestFrameRate && !window.moz) {
-		 video.optional.push({minFrameRate: bestFrameRate});
+	    if (bestFrameRate) {
+		video.optional.push({minFrameRate: bestFrameRate});
+		video.optional.push({maxFrameRate: bestFrameRate});
 	    }
 
 	} else {
@@ -603,15 +613,7 @@
                 onICEComplete: function() {
                     return onICEComplete(self);
                 },
-                onRemoteStream: screen ? function(stream) {
-                  // Added by Dan Perrone (perroned)
-                  // https://github.com/perroned
-                  // Date: January 13, 2016
-                  // Commit: 279f40a4c280bba11052adc621fddbb3135ccb6d
-
-                  verto_afterStreamPublish();} :
-                  // ----------------------------------------------------
-                  function(stream) {
+                onRemoteStream: screen ? function(stream) {} : function(stream) {
                     return onRemoteStream(self, stream);
                 },
                 onOfferSDP: function(sdp) {
@@ -633,58 +635,12 @@
         function onError(e) {
             onStreamError(self, e);
         }
+
 	var mediaParams = getMediaParams(self);
 
 	console.log("Audio constraints", mediaParams.audio);
 	console.log("Video constraints", mediaParams.video);
 
-  // Added by Dan Perrone (perroned)
-  // https://github.com/perroned
-  // Date: January 13, 2016
-  // Commit: 279f40a4c280bba11052adc621fddbb3135ccb6d
-
-    // watchOnly, listenOnly, joinAudio
-    // modify the gUM calls based on additional types of calls I added
-    if (window.watchOnly && !window.listenOnly && !window.joinAudio) {
-        var stream = null;
-        if (typeof webkitMediaStream !== 'undefined') {
-            // Google Chrome
-            stream = new webkitMediaStream;
-        } else {
-            // Firefox
-            audioContext = new window.AudioContext;
-            stream = audioContext.createMediaStreamDestination().stream;
-        }
-        onSuccess(stream);
-        return;
-    }
-    if (window.listenOnly && !window.watchOnly && !window.joinAudio) {
-        var stream = null;
-        if (typeof webkitMediaStream !== 'undefined') {
-            // Google Chrome
-            stream = new webkitMediaStream;
-        } else {
-            // Firefox
-            audioContext = new window.AudioContext;
-            stream = audioContext.createMediaStreamDestination().stream;
-        }
-        onSuccess(stream);
-        return;
-    }
-    if (window.joinAudio && !window.watchOnly && !window.listenOnly) {
-        getUserMedia({
-            constraints: {
-                audio: mediaParams.audio,
-                video: mediaParams.video
-            },
-            video: mediaParams.useVideo,
-            onsuccess: onSuccess,
-            onerror: onError
-        });
-        return;
-    }
-    // ---------------------------------------------------
-
 	if (mediaParams.audio || mediaParams.video) {
 
             getUserMedia({
@@ -1078,41 +1034,13 @@
         var n = navigator,
         media;
         n.getMedia = n.webkitGetUserMedia || n.mozGetUserMedia;
-
-        // Added by Dan Perrone (perroned)
-        // https://github.com/perroned
-        // Date: February 17, 2016
-        // Commit: 05488dda5d9d1048e286b0bdee27515d217b15a5
-
-        var constraints = {};
-        var errorCallback = null;
-        if (window.firefoxDesksharePresent) {
-            window.firefoxDesksharePresent = false;
-            constraints = {
-                audio: false,
-                video: {
-                    mediaSource: 'window',
-                    mozMediaSource: 'window'
-                }
-            };
-        } else {
-            constraints = options.constraints || {
-                audio: true,
-                video: video_constraints
-            };
-        }
-        // if a callback was added use it, otherwise use default error handler
-        if (typeof window.firefoxDesksharePresentErrorCallback === "function") {
-          errorCallback = window.firefoxDesksharePresentErrorCallback;
-        } else {
-          errorCallback = options.onerror ||
-          function(e) {
-              console.error(e);
-          }
-        }
-
-        n.getMedia(constraints, streaming, function() {
-          errorCallback({'status': 'Failed to getUserMedia on Firefox', 'errorcode': 2000});
+        n.getMedia(options.constraints || {
+            audio: true,
+            video: video_constraints
+        },
+        streaming, options.onerror ||
+        function(e) {
+            console.error(e);
         });
 
         function streaming(stream) {
diff --git a/bigbluebutton-client/resources/prod/lib/jquery.jsonrpcclient.js b/bigbluebutton-client/resources/prod/lib/jquery.jsonrpcclient.js
index 6f4f07c72b6398f679ee82ff71a7367eb173e7c1..267eef8c8c9f825cd2edc0ffdec8c7c8afe2f691 100755
--- a/bigbluebutton-client/resources/prod/lib/jquery.jsonrpcclient.js
+++ b/bigbluebutton-client/resources/prod/lib/jquery.jsonrpcclient.js
@@ -106,7 +106,7 @@
 	    for (i = 0; i < loops; i++) {
 		socket.send("#SPB " + data);
 	    }
-	    
+
 	    if (rem) {
 		socket.send("#SPB " + data);
 	    }
@@ -418,12 +418,12 @@
 
 		var up_kps = (((this.speedBytes * 8) / (this.up_dur / 1000)) / 1024).toFixed(0);
 		var down_kps = (((this.speedBytes * 8) / (this.down_dur / 1000)) / 1024).toFixed(0);
-		
+
 		console.info("Speed Test: Up: " + up_kps + " Down: " + down_kps);
 		this.speedCB(event, { upDur: this.up_dur, downDur: this.down_dur, upKPS: up_kps, downKPS: down_kps });
 		this.speedCB = null;
 	    }
-	    
+
 	    return;
 	}
 
diff --git a/bigbluebutton-client/resources/prod/lib/jquery.verto.js b/bigbluebutton-client/resources/prod/lib/jquery.verto.js
index 0fd9fb846a6717bbab9be1e38b03711706645656..ffb66a80dd3f3476738a757a70eae4659e5d1c6c 100755
--- a/bigbluebutton-client/resources/prod/lib/jquery.verto.js
+++ b/bigbluebutton-client/resources/prod/lib/jquery.verto.js
@@ -132,8 +132,13 @@
             }
         });
 
+        var tag = verto.options.tag;
+        if (typeof(tag) === "function") {
+          tag = tag();
+        }
+
         if (verto.options.ringFile && verto.options.tag) {
-            verto.ringer = $("#" + verto.options.tag);
+            verto.ringer = $("#" + tag);
         }
 
         verto.rpcClient.call('login', {});
@@ -473,7 +478,7 @@
 
         if (data.params.callID) {
             var dialog = verto.dialogs[data.params.callID];
-	    
+
 	    if (data.method === "verto.attach" && dialog) {
 		delete dialog.verto.dialogs[dialog.callID];
 		dialog.rtc.stop();
@@ -1308,11 +1313,15 @@
             this.modCommand("vid-write-png", null, file);
         };
 
-        $.verto.conf.prototype.setVideoLayout = function(layout) {
+        $.verto.conf.prototype.setVideoLayout = function(layout, canvasID) {
             if (!this.params.hasVid) {
                 throw 'Conference has no video';
             }
-            this.modCommand("vid-layout", null, layout);
+	    if (canvasID) {
+		this.modCommand("vid-layout", null, [layout, canvasID]);
+	    } else {
+		this.modCommand("vid-layout", null, layout);
+	    }
         };
 
         $.verto.conf.prototype.kick = function(memberID) {
@@ -1382,7 +1391,7 @@
                 }
             });
         };
-            
+
     }
 
     $.verto.modfuncs = {};
@@ -1405,7 +1414,7 @@
         confMan.verto = verto;
         confMan.serno = CONFMAN_SERNO++;
 	confMan.canvasCount = confMan.params.laData.canvasCount;
-	
+
         function genMainMod(jq) {
             var play_id = "play_" + confMan.serno;
             var stop_id = "stop_" + confMan.serno;
@@ -1424,7 +1433,7 @@
 
             jq.html(html);
 
-	    $.verto.modfuncs.change_video_layout = function(id, canvas_id) {	    
+	    $.verto.modfuncs.change_video_layout = function(id, canvas_id) {
 		var val = $("#" + id + " option:selected").text();
 		if (val !== "none") {
                     confMan.modCommand("vid-layout", null, [val, canvas_id]);
@@ -1435,11 +1444,11 @@
 		for (var j = 0; j < confMan.canvasCount; j++) {
 		    var vlayout_id = "confman_vid_layout_" + j + "_" + confMan.serno;
 		    var vlselect_id = "confman_vl_select_" + j + "_" + confMan.serno;
-		
+
 
 		    var vlhtml =  "<div id='" + vlayout_id + "'><br>" +
-			"<b>Video Layout Canvas " + (j+1) + 
-			"</b> <select onChange='$.verto.modfuncs.change_video_layout(\"" + vlayout_id + "\", \"" + j + "\")' id='" + vlselect_id + "'></select> " +
+			"<b>Video Layout Canvas " + (j+1) +
+			"</b> <select onChange='$.verto.modfuncs.change_video_layout(\"" + vlayout_id + "\", \"" + (j+1) + "\")' id='" + vlselect_id + "'></select> " +
 			"<br><br></div>";
 		    jq.append(vlhtml);
 		}
@@ -1490,7 +1499,7 @@
             var layer_set_id = "layer_set_" + x;
             var layer_next_id = "layer_next_" + x;
             var layer_prev_id = "layer_prev_" + x;
-	    
+
             var tmute_id = "tmute_" + x;
             var tvmute_id = "tvmute_" + x;
             var vbanner_id = "vbanner_" + x;
@@ -1502,7 +1511,7 @@
             var volup_id = "vol_in_up" + x;
             var voldn_id = "vol_in_dn" + x;
             var transfer_id = "transfer" + x;
-	    
+
 
             var html = "<div id='" + box_id + "'>";
 
@@ -1515,7 +1524,7 @@
                 "<button class='ctlbtn' id='" + voldn_id + "'>Vol -</button>" +
                 "<button class='ctlbtn' id='" + volup_id + "'>Vol +</button>" +
                 "<button class='ctlbtn' id='" + transfer_id + "'>Transfer</button>";
-		
+
 	    if (confMan.params.hasVid) {
 		html += "<br><br><b>Video Controls</b><hr noshade>";
 
@@ -1530,14 +1539,14 @@
 			"<button class='ctlbtn' id='" + canvas_in_set_id + "'>Set Input Canvas</button>" +
 			"<button class='ctlbtn' id='" + canvas_in_prev_id + "'>Prev Input Canvas</button>" +
 			"<button class='ctlbtn' id='" + canvas_in_next_id + "'>Next Input Canvas</button>" +
-			
+
 		    "<br>" +
-			
+
 		    "<button class='ctlbtn' id='" + canvas_out_set_id + "'>Set Watching Canvas</button>" +
 			"<button class='ctlbtn' id='" + canvas_out_prev_id + "'>Prev Watching Canvas</button>" +
 			"<button class='ctlbtn' id='" + canvas_out_next_id + "'>Next Watching Canvas</button>";
 		}
-		
+
 		html += "<br>" +
 
                 "<button class='ctlbtn' id='" + layer_set_id + "'>Set Layer</button>" +
@@ -1620,7 +1629,7 @@
             $("#" + canvas_out_prev_id).click(function() {
                 confMan.modCommand("vid-watching-canvas", x, "prev");
             });
-	    
+
             $("#" + tmute_id).click(function() {
                 confMan.modCommand("tmute", x);
             });
@@ -1697,14 +1706,14 @@
 			for (var j = 0; j < confMan.canvasCount; j++) {
 			    var vlselect_id = "#confman_vl_select_" + j + "_" + confMan.serno;
 			    var vlayout_id = "#confman_vid_layout_" + j + "_" + confMan.serno;
-			    
+
 			    var x = 0;
 			    var options;
-			    
+
 			    $(vlselect_id).selectmenu({});
 			    $(vlselect_id).selectmenu("enable");
 			    $(vlselect_id).empty();
-			    
+
 			    $(vlselect_id).append(new Option("Choose a Layout", "none"));
 
 			    if (e.data.responseData) {
@@ -1713,15 +1722,15 @@
 				for (var i in e.data.responseData) {
 				    rdata.push(e.data.responseData[i].name);
 				}
-				
+
 				options = rdata.sort(function(a, b) {
 				    var ga = a.substring(0, 6) == "group:" ? true : false;
 				    var gb = b.substring(0, 6) == "group:" ? true : false;
-				    
+
 				    if ((ga || gb) && ga != gb) {
 					return ga ? -1 : 1;
 				    }
-				    
+
 				    return ( ( a == b ) ? 0 : ( ( a > b ) ? 1 : -1 ) );
 				});
 
@@ -1881,6 +1890,11 @@
     $.verto.dialog = function(direction, verto, params) {
         var dialog = this;
 
+        var tag = verto.options.tag;
+        if (typeof(tag) === "function") {
+            tag = tag();
+        }
+
         dialog.params = $.extend({
             useVideo: verto.options.useVideo,
             useStereo: verto.options.useStereo,
@@ -1888,12 +1902,12 @@
 	    useCamera: verto.options.deviceParams.useCamera,
 	    useMic: verto.options.deviceParams.useMic,
 	    useSpeak: verto.options.deviceParams.useSpeak,
-            tag: verto.options.tag,
+            tag: tag,
             localTag: verto.options.localTag,
             login: verto.options.login,
 	    videoParams: verto.options.videoParams
         }, params);
-	
+
         dialog.verto = verto;
         dialog.direction = direction;
         dialog.lastState = null;
@@ -1905,13 +1919,13 @@
 	dialog.useCamera = dialog.params.useCamera;
 	dialog.useMic = dialog.params.useMic;
 	dialog.useSpeak = dialog.params.useSpeak;
-	
+
         if (dialog.params.callID) {
             dialog.callID = dialog.params.callID;
         } else {
             dialog.callID = dialog.params.callID = generateGUID();
         }
-	
+
         if (dialog.params.tag) {
             dialog.audioStream = document.getElementById(dialog.params.tag);
 
@@ -1973,7 +1987,7 @@
                     });
 		} else {
                     dialog.setState($.verto.enum.state.requesting);
-		    
+
                     dialog.sendMethod("verto.invite", {
 			sdp: rtc.mediaData.SDP
                     });
@@ -2070,7 +2084,7 @@
     }
 
 
-    // Attach audio output device to video element using device/sink ID.                                                                                           
+    // Attach audio output device to video element using device/sink ID.
     function find_name(id) {
 	for (var i in $.verto.audioOutDevices) {
 	    var source = $.verto.audioOutDevices[i];
@@ -2154,7 +2168,7 @@
 	    var speaker = dialog.useSpeak;
 	    console.info("Using Speaker: ", speaker);
 
-	    if (speaker && speaker !== "any") {
+	    if (speaker && speaker !== "any" && speaker !== "none") {
 		setTimeout(function() {
 		    dialog.setAudioPlaybackDevice(speaker);
 		}, 500);
@@ -2181,6 +2195,11 @@
             dialog.setState($.verto.enum.state.destroy);
             break;
         case $.verto.enum.state.destroy:
+
+            if (typeof(dialog.verto.options.tag) === "function") {
+              $('#' + dialog.params.tag).remove();
+            }
+
             delete dialog.verto.dialogs[dialog.callID];
 	    if (dialog.params.screenShare) {
 		dialog.rtc.stopPeer();
@@ -2314,7 +2333,7 @@
     };
 
     $.verto.dialog.prototype.getMute = function() {
-	var dialog = this; 
+	var dialog = this;
 	return dialog.rtc.getMute();
     };
 
@@ -2324,7 +2343,7 @@
     };
 
     $.verto.dialog.prototype.getVideoMute = function() {
-	var dialog = this; 
+	var dialog = this;
 	return dialog.rtc.getVideoMute();
     };
 
@@ -2411,7 +2430,7 @@
 
     $.verto.dialog.prototype.answer = function(params) {
         var dialog = this;
-	
+
         if (!dialog.answered) {
 	    if (!params) {
 		params = {};
@@ -2438,7 +2457,7 @@
 		    dialog.useSpeak = params.useSpeak;
 		}
             }
-	    
+
             dialog.rtc.createAnswer(params);
             dialog.answered = true;
         }
@@ -2605,7 +2624,7 @@
     $.verto.enum = Object.freeze($.verto.enum);
 
     $.verto.saved = [];
-    
+
     $.verto.unloadJobs = [];
 
     $(window).bind('beforeunload', function() {
@@ -2630,7 +2649,7 @@
 
     var checkDevices = function(runtime) {
 	console.info("enumerating devices");
-	var aud_in = [], aud_out = [], vid = [];	
+	var aud_in = [], aud_out = [], vid = [];
 
 	if ((!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) && MediaStreamTrack.getSources) {
 	    MediaStreamTrack.getSources(function (media_sources) {
@@ -2642,17 +2661,17 @@
 			aud_in.push(media_sources[i]);
 		    }
 		}
-		
+
 		$.verto.videoDevices = vid;
 		$.verto.audioInDevices = aud_in;
-		
+
 		console.info("Audio Devices", $.verto.audioInDevices);
 		console.info("Video Devices", $.verto.videoDevices);
 		runtime(true);
 	    });
 	} else {
 	    /* of course it's a totally different API CALL with different element names for the same exact thing */
-	    
+
 	    if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
 		console.log("enumerateDevices() not supported.");
 		return;
@@ -2667,7 +2686,7 @@
 
 			console.log(device.kind + ": " + device.label +
 				    " id = " + device.deviceId);
-			
+
 			if (device.kind === "videoinput") {
 			    vid.push({id: device.deviceId, kind: "video", label: device.label});
 			} else if (device.kind === "audioinput") {
@@ -2676,17 +2695,17 @@
 			    aud_out.push({id: device.deviceId, kind: "audio_out", label: device.label});
 			}
 		    });
-		    
+
 
 		    $.verto.videoDevices = vid;
 		    $.verto.audioInDevices = aud_in;
 		    $.verto.audioOutDevices = aud_out;
-		    
+
 		    console.info("Audio IN Devices", $.verto.audioInDevices);
 		    console.info("Audio Out Devices", $.verto.audioOutDevices);
 		    console.info("Video Devices", $.verto.videoDevices);
 		    runtime(true);
-		    
+
 		})
 		.catch(function(err) {
 		    console.log(" Device Enumeration ERROR: " + err.name + ": " + err.message);
diff --git a/bigbluebutton-client/resources/prod/lib/verto_extension.js b/bigbluebutton-client/resources/prod/lib/verto_extension.js
index 19037f9938efb03151cd58ac089d333dfca2434c..3bed5ec621d9fb7cec2240b60418abb6c87a4cfb 100755
--- a/bigbluebutton-client/resources/prod/lib/verto_extension.js
+++ b/bigbluebutton-client/resources/prod/lib/verto_extension.js
@@ -1,14 +1,76 @@
-var callback = function(message){console.log(message);}; // holds the user's callback for a global scope
-callbacks = {};
-var callICEConnected = false;
-var callPurposefullyEnded = false; // used to determine whether the user ended the call or the call was ended from somewhere else outside
-var callTimeout = null; // function that will run if there is no call established
-var toDisplayDisconnectCallback = true; // if a call is dropped only display the error the first time
-var wasCallSuccessful = false; // when the websocket connection is closed this determines whether a call was ever successfully established
-webcamStream = "webcamStream";
-window[webcamStream] = null;
-verto = null;
-videoTag = null;
+Verto = function (
+  tag,
+  voiceBridge,
+  conferenceUsername,
+  userCallback,
+  onFail = null,
+  chromeExtension = null) {
+
+  voiceBridge += "-DESKSHARE";
+  this.cur_call = null;
+  this.share_call = null;
+  this.vertoHandle;
+
+  this.vid_width = 1920;
+  this.vid_height = 1080;
+
+  this.local_vid_width = 320;
+  this.local_vid_height = 180;
+  this.outgoingBandwidth;
+  this.incomingBandwidth;
+  this.sessid = null;
+
+  this.renderTag = 'remote-media';
+
+  this.destination_number = voiceBridge;
+  this.caller_id_name = conferenceUsername;
+  this.caller_id_number = conferenceIdNumber;
+
+  this.vertoPort = "8082";
+  this.hostName = window.location.hostname;
+  this.socketUrl = 'wss://' + this.hostName + ':' + this.vertoPort;
+  this.login = "bbbuser";
+  this.password = "secret";
+  this.minWidth = '640';
+  this.minHeight = '480';
+  this.maxWidth = '1920';
+  this.maxHeight = '1080';
+
+  this.useVideo = false;
+  this.useCamera = false;
+  this.useMic = false;
+
+  this.callWasSuccessful = false;
+
+  this.iceServers = null;
+
+  this.userCallback = userCallback;
+
+  if (chromeExtension != null) {
+    this.chromeExtension = chromeExtension;
+  }
+
+  if (onFail != null) {
+    this.onFail = Verto.normalizeCallback(onFail);
+  } else {
+    var _this = this;
+    this.onFail = function () {
+      _this.logError('Default error handler');
+    };
+  }
+};
+
+Verto.prototype.logger = function (obj) {
+  console.log(obj);
+};
+
+Verto.prototype.logError = function (obj) {
+  console.error(obj);
+};
+
+Verto.prototype.setRenderTag = function (tag) {
+  this.renderTag = tag;
+};
 
 // receives either a string variable holding the name of an actionscript
 // registered callback, or a javascript function object.
@@ -16,388 +78,432 @@ videoTag = null;
 // or if it is an actionscript string it will return a javascript Function
 // that when invokved will invoke the actionscript registered callback
 // and passes along parameters
-function normalizeCallback(callback) {
-	if (typeof callback == "function") {
-		return callback;
-	} else {
-		return function(args) {
-			document.getElementById("BigBlueButton")[callback](args);
-		};
-	}
-}
-
-// save a copy of the hangup function registered for the verto object
-var oldHangup = $.verto.prototype.hangup;
-// overwrite the verto hangup handler with my own handler
-$.verto.prototype.hangup = function(callID, userCallback) {
-	console.log("call state callbacks - bye");
-	if (userCallback) {
-		callback = userCallback;
-	}
-	callActive = false;
-
-	if (cur_call) {
-		console.log('call ended ' + cur_call.audioStream.currentTime); // the duration of the call
-		if (callPurposefullyEnded === true) { // the user ended the call themself
-			callback({'status':'ended'});
-		} else {
-			callback({'status':'failed', 'errorcode': 1005}); // Call ended unexpectedly
-		}
-		clearTimeout(callTimeout);
-		cur_call = null;
-	} else {
-		console.log('bye event already received');
-	}
-	// call the original hangup procedure
-	return oldHangup.apply(this, arguments);
-}
-
-// main entry point to making a verto call
-callIntoConference_verto = function(voiceBridge, conferenceUsername, conferenceIdNumber, userCallback, videoTag, options, vertoServerCredentials) {
-	window.videoTag = videoTag;
-	// stores the user's callback in the global scope
-	if (userCallback) {
-		callback = userCallback;
-	}
-	if(!isLoggedIntoVerto()) { // start the verto log in procedure
-		// runs when a web socket is disconnected
-		callbacks.onWSClose = function(v, success) {
-			if(wasCallSuccessful) { // a call was established through the websocket
-				if(toDisplayDisconnectCallback) { // will only display the error the first time
-					// the connection was dropped in an already established call
-					console.log("websocket disconnected");
-					callback({'status':'failed', 'errorcode': 1001}); // WebSocket disconnected
-					toDisplayDisconnectCallback = false;
-				}
-			} else {
-				// this callback was triggered and a call was never successfully established
-				console.log("websocket connection could not be established");
-				callback({'status':'failed', 'errorcode': 1002}); // Could not make a WebSocket connection
-			}
-		}
-		// runs when the websocket is successfully created
-		callbacks.onWSLogin = function(v, success) {
-			cur_call = null;
-			ringing = false;
-			console.log("Inside onWSLogin");
-
-			if (success) {
-				console.log("starting call");
-				toDisplayDisconnectCallback = true; // yes, display an error if the socket closes
-				wasCallSuccessful = true; // yes, a call was successfully established through the websocket
-				webrtc_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, callback, options);
-			} else {
-				callback({'status':'failed', 'errorcode': '10XX'}); // eror logging verto into freeswitch
-			}
-		}
-		// set up verto
-		// $.verto.init({}, init(videoTag));
-		init(videoTag, vertoServerCredentials);
-	} else {
-		console.log("already logged into verto, going straight to making a call");
-		webrtc_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, callback, options);
-	}
-}
-
-checkSupport = function(callback) {
-	if(!isWebRTCAvailable_verto()) {
-		callback({'status': 'failed', 'errorcode': 1003}); // Browser version not supported
-	}
-
-	if (!navigator.getUserMedia) {
-		navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
-	}
-
-	if (!navigator.getUserMedia){
-		callback({'status': 'failed', 'errorcode': '10XX'}); // getUserMedia not supported in this browser
-	}
-}
-
-configStuns = function(callbacks, callback, videoTag, vertoServerCredentials) {
-	console.log("Fetching STUN/TURN server info for Verto initialization");
-	var stunsConfig = {};
-	$.ajax({
-		dataType: 'json',
-		url: '/bigbluebutton/api/stuns/'
-	}).done(function(data) {
-		console.log("ajax request done");
-		console.log(data);
-		if(data['response'] && data.response.returncode == "FAILED") {
-			console.error(data.response.message);
-			callback({'status':'failed', 'errorcode': data.response.message});
-			return;
-		}
-		stunsConfig['stunServers'] = ( data['stunServers'] ? data['stunServers'].map(function(data) {
-			return {'url': data['url']};
-		}) : [] );
-		stunsConfig['turnServers'] = ( data['turnServers'] ? data['turnServers'].map(function(data) {
-			return {
-				'urls': data['url'],
-				'username': data['username'],
-				'credential': data['password']
-			};
-		}) : [] );
-		stunsConfig = stunsConfig['stunServers'].concat(stunsConfig['turnServers']);
-		console.log("success got stun data, making verto");
-		makeVerto(callbacks, stunsConfig, videoTag, vertoServerCredentials);
-	}).fail(function(data, textStatus, errorThrown) {
-		// BBBLog.error("Could not fetch stun/turn servers", {error: textStatus, user: callerIdName, voiceBridge: conferenceVoiceBridge});
-		callback({'status':'failed', 'errorcode': 1009});
-		return;
-	});
-}
-
-docall_verto = function(extension, conferenceUsername, conferenceIdNumber, callbacks, options) {
-	console.log(extension + ", " + conferenceUsername + ", " + conferenceIdNumber);
-
-	if (cur_call) { // only allow for one call
-		console.log("Quitting: Call already in progress");
-		return;
-	}
-	// determine the resolution the user chose for webcam video
-	outgoingBandwidth = "default";
-	incomingBandwidth = "default";
-	var useVideo = useCamera = useMic = false;
-	// debugger;
-	if(options.watchOnly) {
-		window.watchOnly = true;
-		window.listenOnly = false;
-		window.joinAudio = false;
-		useVideo = true;
-		useCamera = false;
-		useMic = false;
-	} else if(options.listenOnly) {
-		window.listenOnly = true;
-		window.watchOnly = false;
-		window.joinAudio = false;
-		useVideo = false;
-		useCamera = false;
-		useMic = false;
-	} else if(options.joinAudio) {
-		window.joinAudio = true;
-		window.watchOnly = false;
-		window.listenOnly = false;
-		useVideo = false;
-		useCamera = false;
-		useMic = true;
-	}
-
-	cur_call = verto.newCall({
-		destination_number: extension,
-		caller_id_name: conferenceUsername,
-		caller_id_number: conferenceIdNumber,
-		outgoingBandwidth: outgoingBandwidth,
-		incomingBandwidth: incomingBandwidth,
-		useStereo: true,
-		useVideo: useVideo,
-		useCamera: useCamera,
-		useMic: useMic,
-		dedEnc: false,
-		mirrorInput: false
-	});
-
-	if (callbacks != null) { // add user supplied callbacks to the current call
-		cur_call.rtc.options.callbacks = $.extend(cur_call.rtc.options.callbacks, callbacks);
-	}
-}
-
-// check if logged into verto by seeing if there is a ready websocket connection
-function isLoggedIntoVerto() {
-	return (verto != null ? (ref = verto.rpcClient) != null ? ref.socketReady() : void 0 : void 0);
-}
-
-// overwrite and substitute my own init function
-init = function(videoTag, vertoServerCredentials) {
-	videoTag = window.videoTag;
-	cur_call = null;
-	share_call = null;
-	incomingBandwidth = "default";
-	configStuns(callbacks, callback, videoTag, vertoServerCredentials);
-}
+Verto.normalizeCallback = function (callback) {
+  if (typeof callback == 'function') {
+    return callback;
+  } else {
+    return function (args) {
+      document.getElementById('BigBlueButton')[callback](args);
+    };
+  }
+};
+
+Verto.prototype.onWSLogin = function (v, success) {
+  this.cur_call = null;
+  if (success) {
+    this.callWasSuccessful = true;
+    this.mediaCallback();
+    return;
+  } else {
+    // error logging verto into freeswitch
+    this.logError({ status: 'failed', errorcode: '10XX' });
+    this.callWasSuccessful = false;
+    this.onFail();
+    return;
+  }
+};
+
+Verto.prototype.registerCallbacks = function () {
+  var callbacks = {
+    onMessage: function () {},
+
+    onDialogState: function (d) {},
+
+    onWSLogin: this.onWSLogin.bind(this),
+
+    onWSClose: function (v, success) {
+      cur_call = null;
+      if (this.callWasSuccessful) {
+        // the connection was dropped in an already established call
+        this.logError('websocket disconnected');
+
+        // WebSocket disconnected
+        this.logError({ status:  'failed', errorcode: 1001 });
+        toDisplayDisconnectCallback = false;
+      } else {
+        // this callback was triggered and a call was never successfully established
+        this.logError('websocket connection could not be established');
+
+        // Could not make a WebSocket connection
+        this.logError({ status:  'failed', errorcode: 1002 });
+        this.onFail();
+        return;
+      }
+    }.bind(this),
+  };
+  this.callbacks = callbacks;
+};
+
+Verto.prototype.hold = function () {
+  this.cur_call.toggleHold();
+};
+
+Verto.prototype.hangup = function () {
+  if (this.cur_call) {
+    // the duration of the call
+    this.logger('call ended ' + this.cur_call.audioStream.currentTime);
+    this.cur_call.hangup();
+    this.cur_call = null;
+  }
+
+  if (this.share_call) {
+    // the duration of the call
+    this.logger('call ended ' + this.share_call.audioStream.currentTime);
+    this.share_call.hangup();
+    this.share_call = null;
+  }
+
+  // the user ended the call themself
+  // if (callPurposefullyEnded === true) {
+  if (true) {
+    this.logger({ status: 'ended' });
+  } else {
+    // Call ended unexpectedly
+    this.logError({ status: 'failed', errorcode: 1005 });
+  }
+};
+
+Verto.prototype.mute = function () {
+  this.cur_call.dtmf('0');
+};
+
+Verto.prototype.localmute = function () {
+  // var muted = cur_call.setMute('toggle');
+  // if (muted) {
+  //   display('Talking to: ' + cur_call.cidString() + ' [LOCALLY MUTED]');
+  // } else {
+  //   display('Talking to: ' + cur_call.cidString());
+  // }
+};
+
+Verto.prototype.localvidmute = function () {
+  // var muted = cur_call.setVideoMute('toggle');
+  // if (muted) {
+  //   display('Talking to: ' + cur_call.cidString() + ' [VIDEO LOCALLY MUTED]');
+  // } else {
+  //   display('Talking to: ' + cur_call.cidString());
+  // }
+};
+
+Verto.prototype.vmute = function () {
+  this.cur_call.dtmf('*0');
+};
+
+Verto.prototype.setWatchVideo = function (tag) {
+  this.mediaCallback = this.docall;
+  this.useVideo = true;
+  this.useCamera = 'none';
+  this.useMic = 'none';
+  this.create(tag);
+};
+
+Verto.prototype.setListenOnly = function (tag) {
+  this.mediaCallback = this.docall;
+  this.useVideo = false;
+  this.useCamera = 'none';
+  this.useMic = 'none';
+  this.create(tag);
+};
+
+Verto.prototype.setMicrophone = function (tag) {
+  this.mediaCallback = this.docall;
+  this.useVideo = false;
+  this.useCamera = 'none';
+  this.useMic = 'any';
+  this.create(tag);
+};
+
+Verto.prototype.setScreenShare = function (tag) {
+  this.mediaCallback = this.makeShare;
+  this.create(tag);
+};
+
+Verto.prototype.create = function (tag) {
+  this.setRenderTag(tag);
+  this.registerCallbacks();
+  this.configStuns(this.init);
+};
+
+Verto.prototype.docall = function () {
+  if (this.cur_call) {
+    this.logger('Quitting: Call already in progress');
+    return;
+  }
+
+  this.cur_call = window.vertoHandle.newCall({
+    destination_number: this.destination_number,
+    caller_id_name: this.caller_id_name,
+    caller_id_number: this.caller_id_number,
+    outgoingBandwidth: this.outgoingBandwidth,
+    incomingBandwidth: this.incomingBandwidth,
+    useVideo: this.useVideo,
+    useStereo: true,
+    useCamera: this.useCamera,
+    useMic: this.useMic,
+    useSpeak: 'any',
+    dedEnc: true,
+    tag: this.renderTag,
+  });
+  this.logger(this.cur_call);
+};
+
+Verto.prototype.makeShare = function () {
+  if (this.share_call) {
+    this.logError('Quitting: Call already in progress');
+    return;
+  }
+
+  var screenInfo = null;
+  if (!!navigator.mozGetUserMedia) {
+    screenInfo = {
+      video: {
+        mozMediaSource: 'window',
+        mediaSource: 'window',
+      },
+    };
+    this.doShare(screenInfo.video);
+  } else if (!!window.chrome) {
+    var _this = this;
+    if (!_this.chromeExtension) {
+      _this.logError({
+        status:  'failed',
+        message: 'Missing Chrome Extension key',
+      });
+      _this.onFail();
+      return;
+    }
+
+    getChromeExtensionStatus(this.chromeExtension, function (status) {
+      if (status != 'installed-enabled') {
+        _this.logError('No chrome Extension');
+        _this.onFail();
+        return -1;
+      }
+
+      // bring up Chrome screen picker
+      getScreenConstraints(function (error, screenConstraints) {
+        if (error) {
+          _this.onFail();
+          return _this.logError(error);
+        }
+
+        screenInfo =  screenConstraints.mandatory;
+
+        _this.logger(screenInfo);
+        _this.doShare(screenInfo);
+      });
+    });
+  }
+};
+
+Verto.prototype.doShare = function (screenConstraints) {
+  this.share_call = window.vertoHandle.newCall({
+    destination_number: this.destination_number,
+    caller_id_name: this.caller_id_name,
+    caller_id_number: this.caller_id_number,
+    outgoingBandwidth: this.outgoingBandwidth,
+    incomingBandwidth: this.incomingBandwidth,
+    videoParams: screenConstraints,
+    useVideo: true,
+    screenShare: true,
+    dedEnc: true,
+    mirrorInput: false,
+    tag: this.renderTag,
+  });
+};
+
+Verto.prototype.init = function () {
+  this.cur_call = null;
+
+  if (!window.vertoHandle) {
+    window.vertoHandle = new $.verto({
+      login: this.login,
+      passwd: this.password,
+      socketUrl: this.socketUrl,
+      tag: this.renderTag,
+      ringFile: 'sounds/bell_ring2.wav',
+      sessid: this.sessid,
+      videoParams: {
+        minWidth: this.vid_width,
+        minHeight: this.vid_height,
+        maxWidth: this.vid_width,
+        maxHeight: this.vid_height,
+        minFrameRate: 15,
+        vertoBestFrameRate: 30,
+      },
+
+      deviceParams: {
+        useCamera: false,
+        useMic: false,
+        useSpeak: 'none',
+      },
+
+      audioParams: {
+        googAutoGainControl: false,
+        googNoiseSuppression: false,
+        googHighpassFilter: false,
+      },
+
+      iceServers: this.iceServers,
+    }, this.callbacks);
+  } else {
+    this.mediaCallback();
+    return;
+  }
+};
+
+Verto.prototype.configStuns = function (callback) {
+  this.logger('Fetching STUN/TURN server info for Verto initialization');
+  var _this = this;
+  var stunsConfig = {};
+  $.ajax({
+    dataType: 'json',
+    url: '/bigbluebutton/api/stuns/',
+  }).done(function (data) {
+    _this.logger('ajax request done');
+    _this.logger(data);
+    if (data.response && data.response.returncode == 'FAILED') {
+      _this.logError(data.response.message, { error: true });
+      _this.logError({ status: 'failed', errorcode: data.response.message });
+      return;
+    }
+
+    stunsConfig.stunServers = (data.stunServers ? data.stunServers.map(function (data) {
+      return { url: data.url };
+    }) : []);
+
+    stunsConfig.turnServers = (data.turnServers ? data.turnServers.map(function (data) {
+      return {
+        urls: data.url,
+        username: data.username,
+        credential: data.password,
+      };
+    }) : []);
+
+    stunsConfig = stunsConfig.stunServers.concat(stunsConfig.turnServers);
+    _this.logger('success got stun data, making verto');
+    _this.iceServers = stunsConfig;
+    callback.apply(_this);
+  }).fail(function (data, textStatus, errorThrown) {
+    _this.logError({ status: 'failed', errorcode: 1009 });
+    _this.onFail();
+    return;
+  });
+};
 
 // checks whether Google Chrome or Firefox have the WebRTCPeerConnection object
-function isWebRTCAvailable_verto() {
-	return (typeof window.webkitRTCPeerConnection !== 'undefined' || typeof window.mozRTCPeerConnection !== 'undefined');
-}
-
-// exit point for conference
-function leaveWebRTCVoiceConference_verto() {
-	console.log("Leaving the voice conference");
-	webrtc_hangup_verto();
-}
-
-function make_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, userCallback, server, recall, options) {
-	if (userCallback) {
-		callback = userCallback;
-	}
-	callPurposefullyEnded = false;
-
-	// after 15 seconds if a call hasn't been established display error, hangup and logout of verto
-	callTimeout = setTimeout(function() {
-		console.log('Ten seconds without updates sending timeout code');
-		callback({'status':'failed', 'errorcode': 1006}); // Failure on call
-		if (verto != null) {
-			verto.hangup();
-			verto.logout();
-			verto = null;
-		}
-		cur_call = null;
-	}, 10000*1.5);
-
-	var myRTCCallbacks = {
-		onError: function(vertoErrorObject, errorMessage) {
-			console.error("custom callback: onError");
-			console.error(vertoErrorObject);
-			console.error("ERROR:");
-			console.error(errorMessage);
-			if(errorMessage.name === "PermissionDeniedError") { // user denied access to media peripherals
-				console.error("User denied permission/access to hardware");
-				console.error("getUserMedia: failure - ", errorMessage);
-				callback({'status': 'mediafail', 'cause': errorMessage});
-			}
-			cur_call.hangup({cause: "Device or Permission Error"});
-			clearTimeout(callTimeout);
-		},
-		onICEComplete: function(self, candidate) { // ICE candidate negotiation is complete
-			console.log("custom callback: onICEComplete");
-			console.log('Received ICE status changed to completed');
-			if (callICEConnected === false) {
-				callICEConnected = true;
-				if (callActive === true) {
-					callback({'status':'started'});
-				}
-				clearTimeout(callTimeout);
-			}
-		},
-		onStream: function(rtc, stream) { // call has been established
-			console.log("getUserMicMedia: success");
-			callback({'status':'mediasuccess'});
-			console.log("custom callback: stream started");
-			callActive = true;
-			console.log('BigBlueButton call accepted');
-
-			if (callICEConnected === true) {
-				callback({'status':'started'});
-			} else {
-				callback({'status':'waitingforice'});
-			}
-			clearTimeout(callTimeout);
-		}
-	};
-
-	if(isLoggedIntoVerto()) {
-		console.log("Verto is logged into FreeSWITCH, socket is available, making call");
-		callICEConnected = false;
-
-		docall_verto(voiceBridge, conferenceUsername, conferenceIdNumber, myRTCCallbacks, options);
-
-		if(recall === false) {
-			console.log('call connecting');
-			callback({'status': 'connecting'});
-		} else {
-			console.log('call connecting again');
-		}
-
-		callback({'status':'mediarequest'});
-	} else {
-		console.error("Verto is NOT logged into FreeSWITCH, socket is NOT available, abandoning call request");
-	}
-}
-
-function makeVerto(callbacks, stunsConfig, videoTag, vertoServerCredentials) {
-	var vertoPort = vertoServerCredentials.vertoPort;
-	var hostName = vertoServerCredentials.hostName;
-	var socketUrl = "wss://" + hostName + ":" + vertoPort;
-	var login = vertoServerCredentials.login;
-	var password = vertoServerCredentials.password;
-	var minWidth = "640";
-	var minHeight = "480";
-	var maxWidth = "1920";
-	var maxHeight = "1080";
-
-	console.log("stuns info is");
-	console.log(stunsConfig);
-	// debugger;
-	verto = new $.verto({
-		login: login,
-		passwd: password,
-		socketUrl: socketUrl,
-		tag: videoTag,
-		ringFile: "sounds/bell_ring2.wav",
-		loginParams: {foo: true, bar: "yes"},
-		useVideo: false,
-		useCamera: false,
-		iceServers: stunsConfig, // use user supplied stun configuration
-		// iceServers: true, // use stun, use default verto configuration
-	}, callbacks);
-}
-
-// sets verto to begin using the resolution that the user selected
-my_check_vid_res = function() {
-	var selectedVideoConstraints = getChosenWebcamResolution();
-	my_real_size(selectedVideoConstraints);
-
-	if (verto) {
-		verto.videoParams({
-			"minWidth": selectedVideoConstraints.constraints.minWidth,
-			"minHeight": selectedVideoConstraints.constraints.minHeight,
-			"maxWidth": selectedVideoConstraints.constraints.maxWidth,
-			"maxHeight": selectedVideoConstraints.constraints.maxHeight,
-			"minFrameRate": selectedVideoConstraints.constraints.minFrameRate,
-			"vertoBestFrameRate": selectedVideoConstraints.constraints.vertoBestFrameRate
-		});
-	}
-}
-
-my_real_size = function(selectedVideoConstraints) {
-	$("#" + window.videoTag).height("100%");
-	$("#" + window.videoTag).width("100%");
-}
-
-var RTCPeerConnectionCallbacks = {
-	iceFailed: function(e) {
-		console.log('received ice negotiation failed');
-		callback({'status':'failed', 'errorcode': 1007}); // Failure on call
-		//
-		// TODO unless I do this, the call only lasts for a few seconds.
-		// When I comment out the lines below, it works fine indefinitely
-		// Anton Georgiev Dec 10 2015
-		//
-		//cur_call = null;
-		//verto.hangup();
-		//verto = null;
-		//clearTimeout(callTimeout);
-	}
-};
-this.RTCPeerConnectionCallbacks = RTCPeerConnectionCallbacks;
-
-window.verto_afterStreamPublish = function() {}
-
-function webrtc_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, userCallback, options) {
-	if (userCallback) {
-		callback = userCallback;
-	}
-	console.log("webrtc_call\n"+voiceBridge + ", " + conferenceUsername + ", " + conferenceIdNumber + ", " + callback);
-
-	if(!isWebRTCAvailable()) {
-		callback({'status': 'failed', 'errorcode': 1003}); // Browser version not supported
-		return;
-	}
-
-	var server = window.document.location.hostname;
-	console.log("user " + conferenceUsername + " calling to " +	voiceBridge);
-	if (isLoggedIntoVerto()) {
-		make_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, callback, "", false, options);
-	}
-}
-
-function webrtc_hangup_verto(userCallback) {
-	if (userCallback) {
-		callback = userCallback;
-	}
-	callPurposefullyEnded = true;
-	console.log("Hanging up current session");
-	if(verto) {
-		verto.hangup(false, callback);
-	}
-}
+Verto.prototype.isWebRTCAvailable = function () {
+  return (typeof window.webkitRTCPeerConnection !== 'undefined' ||
+    typeof window.mozRTCPeerConnection !== 'undefined');
+};
+
+this.VertoManager = function () {
+  this.vertoAudio = null;
+  this.vertoVideo = null;
+  this.vertoScreenShare = null;
+  window.vertoHandle = null;
+};
+
+Verto.prototype.logout = function () {
+  this.exitAudio();
+  this.exitVideo();
+  this.exitScreenShare();
+  window.vertoHandle.logout();
+};
+
+VertoManager.prototype.exitAudio = function () {
+  if (this.vertoAudio != null) {
+    console.log('Hanging up vertoAudio');
+    this.vertoAudio.hangup();
+    this.vertoAudio = null;
+  }
+};
+
+VertoManager.prototype.exitVideo = function () {
+  if (this.vertoVideo != null) {
+    console.log('Hanging up vertoVideo');
+    this.vertoVideo.hangup();
+    this.vertoVideo = null;
+  }
+};
+
+VertoManager.prototype.exitScreenShare = function () {
+  if (this.vertoScreenShare != null) {
+    console.log('Hanging up vertoScreenShare');
+    this.vertoScreenShare.hangup();
+    this.vertoScreenShare = null;
+  }
+};
+
+VertoManager.prototype.joinListenOnly = function (tag) {
+  this.exitAudio();
+  var obj = Object.create(Verto.prototype);
+  Verto.apply(obj, arguments);
+  this.vertoAudio = obj;
+  this.vertoAudio.setListenOnly(tag);
+};
+
+VertoManager.prototype.joinMicrophone = function (tag) {
+  this.exitAudio();
+  var obj = Object.create(Verto.prototype);
+  Verto.apply(obj, arguments);
+  this.vertoAudio = obj;
+  this.vertoAudio.setMicrophone(tag);
+};
+
+VertoManager.prototype.joinWatchVideo = function (tag) {
+  this.exitVideo();
+  var obj = Object.create(Verto.prototype);
+  Verto.apply(obj, arguments);
+  this.vertoVideo = obj;
+  this.vertoVideo.setWatchVideo(tag);
+};
+
+VertoManager.prototype.shareScreen = function (tag) {
+  this.exitScreenShare();
+  var obj = Object.create(Verto.prototype);
+  Verto.apply(obj, arguments);
+  this.vertoScreenShare = obj;
+  this.vertoScreenShare.setScreenShare(tag);
+};
+
+window.vertoInitialize = function () {
+  if (window.vertoManager == null || window.vertoManager == undefined) {
+    window.vertoManager = new VertoManager();
+  }
+};
+
+window.vertoExitAudio = function () {
+  window.vertoInitialize();
+  window.vertoManager.exitAudio();
+};
+
+window.vertoExitScreenShare = function () {
+  window.vertoInitialize();
+  window.vertoManager.exitScreenShare();
+};
+
+window.vertoJoinListenOnly = function () {
+  window.vertoInitialize();
+  window.vertoManager.joinListenOnly.apply(window.vertoManager, arguments);
+};
+
+window.vertoJoinMicrophone = function () {
+  window.vertoInitialize();
+  window.vertoManager.joinMicrophone.apply(window.vertoManager, arguments);
+};
+
+window.vertoWatchVideo = function () {
+  window.vertoInitialize();
+  window.vertoManager.joinWatchVideo.apply(window.vertoManager, arguments);
+};
+
+window.vertoShareScreen = function () {
+  window.vertoInitialize();
+  window.vertoManager.shareScreen.apply(window.vertoManager, arguments);
+};
+
+window.vertoExtensionGetChromeExtensionStatus = function (extensionid, callback) {
+  callback = Verto.normalizeCallback(callback);
+  getChromeExtensionStatus(extensionid, callback);
+};
diff --git a/bigbluebutton-client/resources/prod/lib/verto_extension_share.js b/bigbluebutton-client/resources/prod/lib/verto_extension_share.js
deleted file mode 100755
index 79a5606cb98dc72476615d0f5471bee4a48de01e..0000000000000000000000000000000000000000
--- a/bigbluebutton-client/resources/prod/lib/verto_extension_share.js
+++ /dev/null
@@ -1,214 +0,0 @@
-var deskshareStream = "deskshareStream";
-window[deskshareStream] = null;
-this.share_call = null;
-
-// final entry point for sharing from Chrome.
-// already have the resolution and constraints chosen
-var configDeskshareFromChrome = function(videoTag, callbacks, extensionId, resolutionConstruction) {
-	// do initial check for extension
-	getChromeExtensionStatus(extensionId, function(status) {
-		if (status != "installed-enabled") {
-			callbacks.onError({'status': 'failed', 'errorcode': 2001});
-			console.error("No chrome Extension");
-			return -1;
-		}
-
-		// bring up Chrome screen picker
-		getScreenConstraints(function(error, screen_constraints) {
-			if(error) {
-				callbacks.onError({'status': 'failed', 'errorcode': 2021});
-				return console.error(error);
-			}
-
-			screen_constraints = resolutionConstruction(screen_constraints);
-			window.firefoxDesksharePresent = false;
-			doCall(screen_constraints, videoTag, callbacks);
-		});
-	});
-};
-
-// entry point for Chrome HTML5 sharing
-// connects with html5 client libraries to retrieve a selected resolution
-var configDeskshareFromChromeHTML5 = function(videoTag, callbacks, extensionId) {
-	var resolutionConstruction = function(screen_constraints) {
-		console.log("modifying video quality");
-		var selectedDeskshareResolution = getChosenDeskshareResolution(); // this is the video profile the user chose
-		my_real_size(selectedDeskshareResolution);
-		var selectedDeskshareConstraints = getDeskshareConstraintsFromResolution(selectedDeskshareResolution, screen_constraints); // convert to a valid constraints object
-		console.log(selectedDeskshareConstraints);
-		return selectedDeskshareConstraints.video.mandatory;
-	};
-	configDeskshareFromChrome(videoTag, callbacks, extensionId, resolutionConstruction);
-};
-
-// entry point when desksharing using Google Chrome via the flash Client
-// currently uses a default preset resolution in place of a resolution picker
-// prepares the constraints and passes off to generic Chrome handler
-var configDeskshareFromChromeFlash = function(videoTag, callbacks, extensionId) {
-	var resolutionConstruction = function(screen_constraints) {
-		// BigBlueButton low
-		var getDeskshareConstraints = function(constraints) {
-			return {
-				"audio": false,
-				"video": {
-					"mandatory": {
-						"maxWidth": 1920,
-						"maxHeight": 1080,
-						"chromeMediaSource": constraints.mandatory.chromeMediaSource,
-						"chromeMediaSourceId": constraints.mandatory.chromeMediaSourceId,
-						"minFrameRate": 12,
-					},
-					"optional": []
-				}
-			};
-		};
-
-		console.log("not modifying video quality");
-		var selectedDeskshareConstraints = getDeskshareConstraints(screen_constraints); // convert to a valid constraints object
-		console.log(selectedDeskshareConstraints);
-		return selectedDeskshareConstraints.video.mandatory;
-	};
-	configDeskshareFromChrome(videoTag, callbacks, extensionId, resolutionConstruction);
-};
-
-// final entry point for Firefox sharing
-var configDeskshareFromFirefox = function(screen_constraints, videoTag, callbacks) {
-	// bypass all the default gUM calls inside jquery.FSRTC.js to use my own
-	window.firefoxDesksharePresent = true;
-	// the gUM args to invoke the Firefox screen picker
-	var screen_constraints = {
-		video: {
-			"mozMediaSource": 'window',
-			"mediaSource": 'window',
-		}
-	};
-	// register the callback to the window namespace to be available in jquery.FSRTC.js
-	window.firefoxDesksharePresentErrorCallback = callbacks.onError;
-	doCall(screen_constraints, videoTag, callbacks);
-};
-
-var configDeskshareFromFirefoxFlash = function(screen_constraints, videoTag, callbacks) {
-	console.log("deskshare from firefox flash");
-	configDeskshareFromFirefox(screen_constraints, videoTag, callbacks);
-};
-
-var configDeskshareFromFirefoxHTML5 = function(screen_constraints, videoTag, callbacks) {
-	console.log("deskshare from firefox html5");
-	configDeskshareFromFirefox(screen_constraints, videoTag, callbacks);
-};
-
-function endScreenshare(loggingCallback, onSuccess) {
-	console.log("endScreenshare");
-	if (share_call) {
-		console.log("a screenshare call is active. Hanging up");
-		share_call.hangup();
-		share_call = null;
-		normalizeCallback(onSuccess)();
-	} else {
-		console.log("a screenshare call is NOT active. Doing nothing");
-	}
-	normalizeCallback(loggingCallback)({'status':'success', 'message': 'screenshare ended'});
-}
-
-function startScreenshare(loggingCallback, videoTag, vertoServerCredentials, extensionId, modifyResolution, onSuccess, onFail) {
-	onSuccess = normalizeCallback(onSuccess);
-	onFail = normalizeCallback(onFail);
-	loggingCallback = normalizeCallback(loggingCallback);
-	console.log("startScreenshare");
-	if(!isLoggedIntoVerto()) { // start the verto log in procedure
-		// runs when the websocket is successfully created
-		callbacks.onWSLogin = function(v, success) {
-			startScreenshareAfterLogin(loggingCallback, videoTag, extensionId, modifyResolution, onSuccess, onFail);
-			loggingCallback({'status':'success', 'message': 'screenshare started'});
-			console.log("logged in. starting screenshare");
-		}
-		// set up verto
-		init(window.videoTag, vertoServerCredentials);
-	} else {
-		console.log("already logged into verto, going straight to making a call");
-		startScreenshareAfterLogin(loggingCallback, videoTag, extensionId, modifyResolution, onSuccess, onFail);
-		loggingCallback({'status':'success', 'message': 'screenshare started'});
-	}
-}
-
-function startScreenshareAfterLogin(loggingCallback, videoTag, extensionId, modifyResolution, onSuccess, onFail) {
-	if (share_call) {
-		return;
-	}
-
-	outgoingBandwidth = incomingBandwidth = "default";
-	var sharedev = "screen";
-
-	if (sharedev !== "screen") {
-		console.log("Attempting Screen Capture with non-screen device....");
-
-		BBB.getMyUserInfo(function (retData){
-			share_call = verto.newCall({
-				destination_number: retData.voiceBridge + "-screen",
-				caller_id_name: retData.myUsername + " (Screen)",
-				caller_id_number: retData.myUserID + " (screen)",
-				outgoingBandwidth: outgoingBandwidth,
-				incomingBandwidth: incomingBandwidth,
-				useCamera: sharedev,
-				useVideo: true,
-				screenShare: true,
-				dedEnc: false,
-				mirrorInput: false
-			});
-		});
-		return;
-	}
-
-	var callbacks = {
-		onError: normalizeCallback(onFail),
-		onICEComplete: function(self, candidate) { // ICE candidate negotiation is complete
-			console.log("custom callback: onICEComplete");
-			normalizeCallback(onSuccess)(candidate);
-		}
-	};
-
-	// determine if Firefox or Chrome
-	// for now the only difference is that html5 has a resolution dialog
-	if (!!navigator.mozGetUserMedia) {
-		if (modifyResolution) {
-			configDeskshareFromFirefoxHTML5(null, videoTag, callbacks);
-		} else {
-			configDeskshareFromFirefoxFlash(null, videoTag, callbacks);
-		}
-	} else if (!!window.chrome) {
-		if (modifyResolution) {
-			configDeskshareFromChromeHTML5(videoTag, callbacks, extensionId);
-		} else {
-			configDeskshareFromChromeFlash(videoTag, callbacks, extensionId);
-		}
-	}
-}
-
-function doCall(screen_constraints, videoTag, callbacks) {
-	console.log("\n\n\nhere are the screen_constraints\n\n\n");
-	console.log(screen_constraints);
-	window.listenOnly = false;
-	window.watchOnly = false;
-	window.joinAudio = true;
-
-	BBB.getMyUserInfo(function (retData){
-		var callParams = {
-			destination_number: retData.voiceBridge + "-screen",
-			caller_id_name: retData.myUsername + " (Screen)",
-			caller_id_number: retData.myUserID + " (screen)",
-			outgoingBandwidth: outgoingBandwidth,
-			incomingBandwidth: incomingBandwidth,
-			videoParams: screen_constraints,
-			useVideo: true,
-			screenShare: true,
-			dedEnc: true,
-			mirrorInput: true,
-		};
-
-		if (videoTag != null) {
-			callParams.tag = videoTag;
-		}
-		share_call = verto.newCall(callParams);
-		share_call.rtc.options.callbacks = $.extend(share_call.rtc.options.callbacks, callbacks);
-	});
-}
diff --git a/bigbluebutton-client/src/ScreenshareModule.mxml b/bigbluebutton-client/src/ScreenshareModule.mxml
index 606745dfc9749cdf14e4bce53c1dcdc2f60ba8fa..1c91614083b7955092abc567c98aae87f5de4866 100755
--- a/bigbluebutton-client/src/ScreenshareModule.mxml
+++ b/bigbluebutton-client/src/ScreenshareModule.mxml
@@ -20,13 +20,14 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 
 -->
 
-<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" 
-           xmlns:maps="org.bigbluebutton.modules.deskShare.maps.*"
+<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
+
            implements="org.bigbluebutton.common.IBigBlueButtonModule"
            creationComplete="onCreationComplete()" xmlns:maps1="org.bigbluebutton.modules.screenshare.maps.*">
-  
+
   <maps1:ScreenshareEventMap id="deskshareGlobalEventMap" />
-  
+  <maps1:WebRTCDeskshareEventMap id="webRTCDeskshareEventMap" />
+
   <mx:Script>
     <![CDATA[
       import com.asfusion.mate.events.Dispatcher;
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/managers/WebRTCDeskshareManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/managers/WebRTCDeskshareManager.as
index 3a90cbb1f3f17ccb59fbabf8ab675d21530b0b67..d2bd98188e01edd6773c892b1ccf2a74712f7f26 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/managers/WebRTCDeskshareManager.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/managers/WebRTCDeskshareManager.as
@@ -109,23 +109,26 @@ package org.bigbluebutton.modules.deskshare.managers
 		private function startWebRTCDeskshare():void {
 			LOGGER.debug("DeskshareManager::startWebRTCDeskshare");
 
-			var result:String;
 			if (ExternalInterface.available) {
-				var loggingCallback:Function = function(args:Object):void {LOGGER.debug(args); JSLog.warn("loggingCallback", args)};
-				ExternalInterface.addCallback("loggingCallback", loggingCallback);
 				var videoTag:String = "localVertoVideo";
-				var modifyResolution:Boolean = false;
-				// register these callbacks
-				var onSuccess:Function = function():void { LOGGER.debug("onSuccess"); JSLog.warn("onSuccess - as", {})};
-				ExternalInterface.addCallback("onSuccess", onSuccess);
 				var onFail:Function = function(args:Object):void {
 					JSLog.warn("onFail - as", args);
 					JSLog.warn("WebRTCDeskshareManager::startWebRTCDeskshare - falling back to java", {});
 					globalDispatcher.dispatchEvent(new UseJavaModeCommand())
 				};
 				ExternalInterface.addCallback("onFail", onFail);
-				JSLog.warn("calling startScreenshare", {});
-				result = ExternalInterface.call("startScreenshare", "loggingCallback", videoTag, vertoServerCredentials, chromeExtensionKey, modifyResolution, "onSuccess", "onFail");
+
+				ExternalInterface.call(
+					'vertoShareScreen',
+					videoTag,
+					'3500',
+					'FreeSWITCH USers - abc',
+					'1008',
+					null,
+					vertoServerCredentials,
+					chromeExtensionKey,
+					onFail
+				);
 			}
 		}
 
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/DeskshareToolbarEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/DeskshareToolbarEvent.as
new file mode 100755
index 0000000000000000000000000000000000000000..66449c8a4a42e94d273cc1043e62b8683edd1d61
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/DeskshareToolbarEvent.as
@@ -0,0 +1,32 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 3.0 of the License, or (at your option) any later
+* version.
+*
+* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+*
+*/
+package org.bigbluebutton.modules.screenshare.events
+{
+  import flash.events.Event;
+
+  public class DeskshareToolbarEvent extends Event
+  {
+    public static const STOP:String = "deactivate toolbar status";
+
+    public function DeskshareToolbarEvent(type:String)
+    {
+      super(type, true, false);
+    }
+  }
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/UseJavaModeCommand.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/UseJavaModeCommand.as
new file mode 100755
index 0000000000000000000000000000000000000000..081a3f11c6c4fc1ebaf37be2297d491f3be5f465
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/UseJavaModeCommand.as
@@ -0,0 +1,14 @@
+package org.bigbluebutton.modules.screenshare.events
+{
+  import flash.events.Event;
+
+  public class UseJavaModeCommand extends Event
+  {
+    public static const USE_JAVA_MODE:String = "use Java to join deskshare event";
+
+    public function UseJavaModeCommand()
+    {
+      super(USE_JAVA_MODE, true, false);
+    }
+  }
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/WebRTCShareWindowEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/WebRTCShareWindowEvent.as
new file mode 100755
index 0000000000000000000000000000000000000000..33119e200a0c389c47fe1e4b016b18d8357a5329
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/WebRTCShareWindowEvent.as
@@ -0,0 +1,33 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 3.0 of the License, or (at your option) any later
+* version.
+*
+* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+*
+*/
+package org.bigbluebutton.modules.screenshare.events
+{
+	import flash.events.Event;
+
+	public class WebRTCShareWindowEvent extends Event
+	{
+		public static const CLOSE:String = "WebRTC Deskshare Share Window Close Event";
+
+		public function WebRTCShareWindowEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
+		{
+			super(type, bubbles, cancelable);
+		}
+
+	}
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/WebRTCStreamEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/WebRTCStreamEvent.as
new file mode 100755
index 0000000000000000000000000000000000000000..17301cec7e4854ad4ba20aff4d27a4ca84086a7f
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/WebRTCStreamEvent.as
@@ -0,0 +1,37 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 3.0 of the License, or (at your option) any later
+* version.
+*
+* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+*
+*/
+package org.bigbluebutton.modules.screenshare.events
+{
+	import flash.events.Event;
+
+	public class WebRTCStreamEvent extends Event
+	{
+		public static const START:String = "WebRTC Deskshare Stream Started Event";
+		public static const STOP:String = "WebRTC Deskshare Stream Stopped Event";
+
+		public var videoWidth:Number = 0;
+		public var videoHeight:Number = 0;
+
+		public function WebRTCStreamEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
+		{
+			super(type, bubbles, cancelable);
+		}
+
+	}
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/WebRTCViewStreamEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/WebRTCViewStreamEvent.as
new file mode 100755
index 0000000000000000000000000000000000000000..bccef0ad9decd7e093d50507301a9040afc72bb8
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/WebRTCViewStreamEvent.as
@@ -0,0 +1,40 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 3.0 of the License, or (at your option) any later
+* version.
+*
+* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+*
+*/
+package org.bigbluebutton.modules.screenshare.events
+{
+	import flash.events.Event;
+	import org.bigbluebutton.main.api.JSLog;
+
+	public class WebRTCViewStreamEvent extends Event
+	{
+		public static const START:String = "WebRTC Start Viewing Stream Event";
+		public static const STOP:String = "WebRTC Stop Viewing Stream Event";
+
+		public var videoWidth:Number = 0;
+		public var videoHeight:Number = 0;
+		public var rtmp:String = null;
+
+		public function WebRTCViewStreamEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
+		{
+			super(type, bubbles, cancelable);
+			JSLog.warn("creating a WebRTCViewStreamEvent event " + type, null);
+		}
+
+	}
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/WebRTCViewWindowEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/WebRTCViewWindowEvent.as
new file mode 100755
index 0000000000000000000000000000000000000000..ff9ffa75e091f68857f591ec3bf51283ca3ee119
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/events/WebRTCViewWindowEvent.as
@@ -0,0 +1,33 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 3.0 of the License, or (at your option) any later
+* version.
+*
+* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+*
+*/
+package org.bigbluebutton.modules.screenshare.events
+{
+	import flash.events.Event;
+
+	public class WebRTCViewWindowEvent extends Event
+	{
+		public static const CLOSE:String = "WebRTC Deskshare View Window Close Event";
+
+		public function WebRTCViewWindowEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
+		{
+			super(type, bubbles, cancelable);
+		}
+
+	}
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/ScreenshareManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/ScreenshareManager.as
index 02ebfd6a3a1e82ecc2265cde1fcc284f64ac5fce..15e2d629ff37a929c5bdb47b603a609eb7d29bc5 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/ScreenshareManager.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/ScreenshareManager.as
@@ -32,6 +32,9 @@ package org.bigbluebutton.modules.screenshare.managers {
     import org.bigbluebutton.modules.screenshare.model.ScreenshareModel;
     import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions;
     import org.bigbluebutton.modules.screenshare.services.ScreenshareService;
+    import org.bigbluebutton.modules.screenshare.events.UseJavaModeCommand;
+    import org.bigbluebutton.modules.screenshare.utils.BrowserCheck;
+    import org.bigbluebutton.main.api.JSLog;
     
     public class ScreenshareManager {
         private static const LOGGER:ILogger = getClassLogger(ScreenshareManager);
@@ -43,6 +46,7 @@ package org.bigbluebutton.modules.screenshare.managers {
         private var service:ScreenshareService;
         private var globalDispatcher:Dispatcher;
         private var sharing:Boolean = false;
+        private var usingJava:Boolean = true;
         
         public function ScreenshareManager() {
             service = new ScreenshareService();
@@ -73,7 +77,7 @@ package org.bigbluebutton.modules.screenshare.managers {
             LOGGER.debug("handle Connection Success Event");
             service.checkIfPresenterIsSharingScreen();
         }
-               
+        
         public function handleStreamStartedEvent(event:StreamStartedEvent):void {
             ScreenshareModel.getInstance().streamId = event.streamId;
             ScreenshareModel.getInstance().width = event.width;
@@ -98,8 +102,8 @@ package org.bigbluebutton.modules.screenshare.managers {
             ScreenshareModel.getInstance().url = event.url;
             
             if (UsersUtil.amIPresenter()) {
-                //				var dispatcher:Dispatcher = new Dispatcher();
-                //				dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));        
+                //        var dispatcher:Dispatcher = new Dispatcher();
+                //        dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));        
             } else {
                 handleStreamStartEvent(ScreenshareModel.getInstance().streamId, event.width, event.height);
                 
@@ -108,8 +112,7 @@ package org.bigbluebutton.modules.screenshare.managers {
             var dispatcher:Dispatcher = new Dispatcher();
             dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));
         }
-        
-        
+
         private function initDeskshare():void {
             sharing = false;
             var option:ScreenshareOptions = new ScreenshareOptions();
@@ -141,10 +144,16 @@ package org.bigbluebutton.modules.screenshare.managers {
             toolbarButtonManager.startedSharing();
             var option:ScreenshareOptions = new ScreenshareOptions();
             option.parseOptions();
-            var autoStart:Boolean = false; // harcode for now
-            publishWindowManager.startSharing(module.getCaptureServerUri(), module.getRoom(), autoStart, option.autoFullScreen);
-            sharing = true;
-            service.requestStartSharing();
+
+            if (option.useWebRTCIfAvailable && !BrowserCheck.isWebRTCSupported()) {
+              usingJava = true;
+              var autoStart:Boolean = false; // harcode for now
+              publishWindowManager.startSharing(module.getCaptureServerUri(), module.getRoom(), autoStart, option.autoFullScreen);
+              sharing = true;
+              service.requestStartSharing();
+            } else {
+              usingJava = false;
+            }
         }
         
         public function handleRequestPauseSharingEvent():void {
@@ -191,8 +200,19 @@ package org.bigbluebutton.modules.screenshare.managers {
         
         private function handleStreamStartEvent(streamId:String, videoWidth:Number, videoHeight:Number):void {
             LOGGER.debug("Received start vieweing command");
+            if (!usingJava) { return; }
             viewWindowManager.startViewing(streamId, videoWidth, videoHeight);
         }
-    
+
+        public function handleUseJavaModeCommand():void {
+          JSLog.warn("ScreenshareManager::handleUseJavaModeCommand", {});
+          usingJava = true;
+          handleStartSharingEvent(true);
+        }
+
+        public function handleDeskshareToolbarStopEvent():void {
+          JSLog.warn("ScreenshareManager::handleDeskshareToolbarStopEvent", {});
+          toolbarButtonManager.stopedSharing();
+        }
     }
 }
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/WebRTCDeskshareManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/WebRTCDeskshareManager.as
new file mode 100755
index 0000000000000000000000000000000000000000..f183f04db7a625757463070166b86edd0dbef0ef
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/WebRTCDeskshareManager.as
@@ -0,0 +1,298 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 3.0 of the License, or (at your option) any later
+* version.
+*
+* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+package org.bigbluebutton.modules.screenshare.managers
+{
+	import com.asfusion.mate.events.Dispatcher;
+
+	import flash.external.ExternalInterface;
+
+	import org.as3commons.logging.api.ILogger;
+	import org.as3commons.logging.api.getClassLogger;
+	import org.bigbluebutton.core.UsersUtil;
+	import org.bigbluebutton.core.managers.UserManager;
+	import org.bigbluebutton.main.events.MadePresenterEvent;
+	import org.bigbluebutton.modules.screenshare.events.UseJavaModeCommand;
+	import org.bigbluebutton.modules.screenshare.events.WebRTCViewStreamEvent;
+	import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions;
+	import org.bigbluebutton.modules.screenshare.events.ShareWindowEvent;
+	import org.bigbluebutton.modules.screenshare.services.WebRTCDeskshareService;
+	import org.bigbluebutton.modules.screenshare.utils.BrowserCheck;
+	import org.bigbluebutton.modules.screenshare.events.DeskshareToolbarEvent;
+	import org.bigbluebutton.main.api.JSLog;
+
+	public class WebRTCDeskshareManager {
+		private static const LOGGER:ILogger = getClassLogger(WebRTCDeskshareManager);
+
+		private var publishWindowManager:WebRTCPublishWindowManager;
+		private var viewWindowManager:WebRTCViewerWindowManager;
+		private var toolbarButtonManager:ToolbarButtonManager;
+		private var module:ScreenshareModule;
+		private var service:WebRTCDeskshareService;
+		private var globalDispatcher:Dispatcher;
+		private var sharing:Boolean = false;
+		private var usingWebRTC:Boolean = false;
+		private var chromeExtensionKey:String = null;
+
+		public function WebRTCDeskshareManager() {
+			JSLog.warn("WebRTCDeskshareManager::WebRTCDeskshareManager", {});
+			service = new WebRTCDeskshareService();
+			globalDispatcher = new Dispatcher();
+			publishWindowManager = new WebRTCPublishWindowManager(service);
+			viewWindowManager = new WebRTCViewerWindowManager(service);
+		}
+
+		public function handleStartModuleEvent(module:ScreenshareModule):void {
+			LOGGER.debug("WebRTC Screenshare Module starting");
+			JSLog.warn("WebRTCDeskshareManager::handleStartModuleEvent", {});
+			this.module = module;
+			service.handleStartModuleEvent(module);
+
+			if (UsersUtil.amIPresenter()) {
+				initDeskshare();
+			}
+		}
+
+		public function handleStopModuleEvent():void {
+			LOGGER.debug("WebRTC Deskshare Module stopping");
+
+			publishWindowManager.stopSharing();
+			viewWindowManager.stopViewing();
+			service.disconnect();
+		}
+
+		/*presenter stopped their program stream*/
+		public function handleStreamStoppedEvent():void {
+			LOGGER.debug("WebRTCDeskshareManager::handleStreamStoppedEvent Sending deskshare stopped command");
+			JSLog.warn("WebRTCDeskshareManager::handleStreamStoppedEvent", {});
+			stopWebRTCDeskshare();
+		}
+
+		/*viewer being told there is no more stream*/
+		public function handleStreamStopEvent(args:Object):void {
+			LOGGER.debug("WebRTCDeskshareManager::handleStreamStopEvent");
+			JSLog.warn("WebRTCDeskshareManager::handleStreamStopEvent", {});
+			viewWindowManager.handleViewWindowCloseEvent();
+		}
+
+		public function handleRequestStopSharingEvent():void {
+			JSLog.warn("WebRTCDeskshareManager::handleRequestStopSharingEvent", {});
+			/* stopping WebRTC deskshare. Alert DeskshareManager to reset toolbar */
+			globalDispatcher.dispatchEvent(new DeskshareToolbarEvent(DeskshareToolbarEvent.STOP));
+			stopWebRTCDeskshare();
+		}
+
+		private function stopWebRTCDeskshare():void {
+			LOGGER.debug("WebRTCDeskshareManager::stopWebRTCDeskshare");
+			JSLog.warn("WebRTCDeskshareManager::stopWebRTCDeskshare", {});
+			viewWindowManager.stopViewing();
+
+			globalDispatcher.dispatchEvent(new ShareWindowEvent(ShareWindowEvent.CLOSE));
+
+			if (ExternalInterface.available) {
+				ExternalInterface.call("vertoExitScreenShare");
+			}
+		}
+
+		private function startWebRTCDeskshare():void {
+			LOGGER.debug("WebRTCDeskshareManager::startWebRTCDeskshare");
+			JSLog.warn("WebRTCDeskshareManager::startWebRTCDeskshare", {});
+
+			if (ExternalInterface.available) {
+				var videoTag:String = "localVertoVideo";
+				var onFail:Function = function(args:Object):void {
+					JSLog.warn("onFail - as", args);
+					JSLog.warn("WebRTCDeskshareManager::startWebRTCDeskshare - falling back to java", {});
+					globalDispatcher.dispatchEvent(new UseJavaModeCommand())
+				};
+				ExternalInterface.addCallback("onFail", onFail);
+
+				var voiceBridge:String = UserManager.getInstance().getConference().voiceBridge;
+				var myName:String = 'FreeSWITCH Users - ';
+				myName += UserManager.getInstance().getConference().getMyName();
+
+				ExternalInterface.call(
+					'vertoShareScreen',
+					videoTag,
+					voiceBridge,
+					myName,
+					null,
+					"onFail",
+					chromeExtensionKey
+				);
+			}
+		}
+
+		private function initDeskshare():void {
+			JSLog.warn("WebRTCDeskshareManager::initDeskshare", {});
+			sharing = false;
+			var options:ScreenshareOptions = new ScreenshareOptions();
+			options.parseOptions();
+			if (options.chromeExtensionKey) {
+				chromeExtensionKey = options.chromeExtensionKey;
+			}
+			if (options.autoStart) {
+				handleStartSharingEvent(true);
+			}
+		}
+
+		public function handleMadePresenterEvent(e:MadePresenterEvent):void {
+			LOGGER.debug("Got MadePresenterEvent ");
+			initDeskshare();
+		}
+
+		public function handleMadeViewerEvent(e:MadePresenterEvent):void{
+			LOGGER.debug("Got MadeViewerEvent ");
+			if (sharing) {
+				publishWindowManager.stopSharing();
+				stopWebRTCDeskshare();
+			}
+			sharing = false;
+		}
+
+		private function canIUseVertoOnThisBrowser(onFailure:Function, onSuccess:Function):void {
+			LOGGER.debug("DeskshareManager::canIUseVertoOnThisBrowser");
+			JSLog.warn("WebRTCDeskshareManager::canIUseVertoOnThisBrowser", {});
+			var options:ScreenshareOptions = new ScreenshareOptions();
+			options.parseOptions();
+
+			if (options.useWebRTCIfAvailable && BrowserCheck.isWebRTCSupported()) {
+				JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent WebRTC Supported", {});
+				if (BrowserCheck.isFirefox()) {
+					onSuccess("Firefox, lets try");
+				} else {
+					if (chromeExtensionKey != null) {
+
+						JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent chrome extension key exists - ", chromeExtensionKey);
+						if (ExternalInterface.available) {
+
+							var success:Function = function(status:String):void {
+								ExternalInterface.addCallback("gCETCallback", null);
+								JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent inside onSuccess", {});
+								if (status == "installed-enabled") {
+									JSLog.warn("Chrome Extension exists", {});
+									onSuccess("worked");
+								} else {
+									onFailure("No Chrome Extension");
+								}
+							};
+							ExternalInterface.addCallback("gCETCallback", success);
+							ExternalInterface.call("vertoExtensionGetChromeExtensionStatus", chromeExtensionKey, "gCETCallback");
+						}
+					} else {
+						onFailure("No chromeExtensionKey in config.xml");
+						return;
+					}
+				}
+			} else {
+				onFailure("Web browser doesn't support WebRTC");
+				return;
+			}
+		}
+
+		/*handle start sharing event*/
+		public function handleStartSharingEvent(autoStart:Boolean):void {
+			LOGGER.debug("WebRTCDeskshareManager::handleStartSharingEvent");
+			JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent", {});
+
+			var onFailure:Function = function(message:String):void {
+				JSLog.warn(message, {});
+				usingWebRTC = false;
+				// send out event to fallback to Java
+				JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent - falling back to java", {});
+				globalDispatcher.dispatchEvent(new UseJavaModeCommand());
+				return;
+			};
+
+			var onSuccess:Function = function(message:String):void {
+				JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent onSuccess", {});
+				JSLog.warn(message, {});
+				usingWebRTC = true;
+				startWebRTCDeskshare();
+			};
+
+			canIUseVertoOnThisBrowser(onFailure, onSuccess);
+		}
+
+		public function handleShareWindowCloseEvent():void {
+			publishWindowManager.handleShareWindowCloseEvent();
+			sharing = false;
+			stopWebRTCDeskshare();
+		}
+
+		public function handleViewWindowCloseEvent():void {
+			LOGGER.debug("Received stop viewing command");
+			JSLog.warn("WebRTCDeskshareManager::handleViewWindowCloseEvent", {});
+			viewWindowManager.handleViewWindowCloseEvent();
+		}
+
+		public function handleStreamStartEvent(e:WebRTCViewStreamEvent):void{
+			JSLog.warn("WebRTCDeskshareManager::handleStreamStartEvent rtmp=", e.rtmp);
+			// if (!usingWebRTC) { return; } //TODO this was causing issues
+			if (sharing) return; //TODO must uncomment this for the non-webrtc desktop share
+			var isPresenter:Boolean = UserManager.getInstance().getConference().amIPresenter;
+			JSLog.warn("WebRTCDeskshareManager::handleStreamStartEvent isPresenter=", isPresenter);
+			LOGGER.debug("Received start viewing command when isPresenter==[{0}]",[isPresenter]);
+
+			if(isPresenter) {
+				publishWindowManager.startViewing(e.rtmp, e.videoWidth, e.videoHeight);
+			} else {
+				viewWindowManager.startViewing(e.rtmp, e.videoWidth, e.videoHeight);
+			}
+
+			 sharing = true; //TODO must uncomment this for the non-webrtc desktop share
+		}
+
+		public function handleUseJavaModeCommand():void {
+			JSLog.warn("WebRTCDeskshareManager::handleUseJavaModeCommand", {});
+			usingWebRTC = false;
+		}
+
+		public function handleRequestStartSharingEvent():void {
+			JSLog.warn("WebRTCDeskshareManager::handleRequestStartSharingEvent", {});
+			initDeskshare();
+			handleStartSharingEvent(true);
+		}
+
+		public function handleStreamStartedEvent(event: WebRTCViewStreamEvent):void {
+			if (UsersUtil.amIPresenter()) {
+			} else {
+				/*handleStreamStartEvent(ScreenshareModel.getInstance().streamId, event.width, event.height);*/
+				handleStreamStartEvent(null);
+			}
+
+			var dispatcher:Dispatcher = new Dispatcher();
+			dispatcher.dispatchEvent(new WebRTCViewStreamEvent(WebRTCViewStreamEvent.START));
+		}
+		
+		/*public function handleIsSharingScreenEvent(event: IsSharingScreenEvent):void {*/
+		public function handleIsSharingScreenEvent():void {
+			if (UsersUtil.amIPresenter()) {
+			} else {
+				/*handleStreamStartEvent(ScreenshareModel.getInstance().streamId, event.width, event.height);*/
+				handleStreamStartEvent(null);
+			}
+
+			var dispatcher:Dispatcher = new Dispatcher();
+			dispatcher.dispatchEvent(new WebRTCViewStreamEvent(WebRTCViewStreamEvent.START));
+		}
+
+
+	}
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/WebRTCPublishWindowManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/WebRTCPublishWindowManager.as
new file mode 100644
index 0000000000000000000000000000000000000000..f4f966979b594c7c7f749bb1f873c11688ccf06b
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/WebRTCPublishWindowManager.as
@@ -0,0 +1,86 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 3.0 of the License, or (at your option) any later
+* version.
+*
+* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+package org.bigbluebutton.modules.screenshare.managers
+{
+	import com.asfusion.mate.events.Dispatcher;
+
+	import flash.events.TimerEvent;
+	import flash.utils.Timer;
+
+	import org.as3commons.logging.api.ILogger;
+	import org.as3commons.logging.api.getClassLogger;
+	import org.bigbluebutton.common.IBbbModuleWindow;
+	import org.bigbluebutton.common.events.CloseWindowEvent;
+	import org.bigbluebutton.common.events.OpenWindowEvent;
+	import org.bigbluebutton.modules.screenshare.services.WebRTCDeskshareService;
+	import org.bigbluebutton.modules.screenshare.view.components.WebRTCDesktopPublishWindow;
+
+	public class WebRTCPublishWindowManager {
+		private static const LOGGER:ILogger = getClassLogger(PublishWindowManager);
+
+		private var shareWindow:WebRTCDesktopPublishWindow;
+		private var globalDispatcher:Dispatcher;
+		private var service:WebRTCDeskshareService;
+		private var buttonShownOnToolbar:Boolean = false;
+
+		// Timer to auto-publish webcam. We need this timer to delay
+		// the auto-publishing until after the Viewers's window has loaded
+		// to receive the publishing events. Otherwise, the user joining next
+		// won't be able to view the webcam.
+		private var autoPublishTimer:Timer;
+
+		public function WebRTCPublishWindowManager(service:WebRTCDeskshareService) {
+			LOGGER.debug("PublishWindowManager init");
+			globalDispatcher = new Dispatcher();
+			this.service = service;
+		}
+
+		public function stopSharing():void {
+			if (shareWindow != null) shareWindow.stopSharing();
+		}
+
+		private function autopublishTimerHandler(event:TimerEvent):void {
+			shareWindow.shareScreen(true);
+		}
+
+		public function handleShareWindowCloseEvent():void {
+			closeWindow(shareWindow);
+		}
+
+		private function openWindow(window:IBbbModuleWindow):void {
+			var event:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
+			event.window = window;
+			globalDispatcher.dispatchEvent(event);
+		}
+
+		private function closeWindow(window:IBbbModuleWindow):void {
+			var event:CloseWindowEvent = new CloseWindowEvent(CloseWindowEvent.CLOSE_WINDOW_EVENT);
+			event.window = window;
+			globalDispatcher.dispatchEvent(event);
+		}
+
+		public function startViewing(rtmp:String, videoWidth:Number, videoHeight:Number):void{
+			shareWindow = new WebRTCDesktopPublishWindow();
+			shareWindow.visible = true;
+			openWindow(shareWindow);
+			shareWindow.startVideo(rtmp, videoWidth, videoHeight);
+		}
+	}
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/WebRTCViewerWindowManager.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/WebRTCViewerWindowManager.as
new file mode 100644
index 0000000000000000000000000000000000000000..752dc766c3062b4926bddc1ae046c328b945a6fa
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/managers/WebRTCViewerWindowManager.as
@@ -0,0 +1,78 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 3.0 of the License, or (at your option) any later
+* version.
+*
+* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+package org.bigbluebutton.modules.screenshare.managers
+{
+	import com.asfusion.mate.events.Dispatcher;
+
+	import org.as3commons.logging.api.ILogger;
+	import org.as3commons.logging.api.getClassLogger;
+	import org.bigbluebutton.common.IBbbModuleWindow;
+	import org.bigbluebutton.common.events.CloseWindowEvent;
+	import org.bigbluebutton.common.events.OpenWindowEvent;
+	import org.bigbluebutton.modules.screenshare.services.WebRTCDeskshareService;
+	import org.bigbluebutton.modules.screenshare.view.components.WebRTCDesktopPublishWindow;
+	import org.bigbluebutton.modules.screenshare.view.components.WebRTCDesktopViewWindow;
+
+	public class WebRTCViewerWindowManager {
+		private static const LOGGER:ILogger = getClassLogger(ViewerWindowManager);
+
+		private var viewWindow:WebRTCDesktopViewWindow;
+		private var shareWindow:WebRTCDesktopPublishWindow;
+		private var service:WebRTCDeskshareService;
+		private var isViewing:Boolean = false;
+		private var globalDispatcher:Dispatcher;
+
+		public function WebRTCViewerWindowManager(service:WebRTCDeskshareService) {
+			this.service = service;
+			globalDispatcher = new Dispatcher();
+		}
+
+		public function stopViewing():void {
+			if (isViewing) viewWindow.stopViewing();
+		}
+
+		private function openWindow(window:IBbbModuleWindow):void{
+			var event:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
+			event.window = window;
+			globalDispatcher.dispatchEvent(event);
+		}
+
+		public function handleViewWindowCloseEvent():void {
+			LOGGER.debug("ViewerWindowManager Received stop viewing command");
+			closeWindow(viewWindow);
+			isViewing = false;
+		}
+
+		private function closeWindow(window:IBbbModuleWindow):void {
+			var event:CloseWindowEvent = new CloseWindowEvent(CloseWindowEvent.CLOSE_WINDOW_EVENT);
+			event.window = window;
+			globalDispatcher.dispatchEvent(event);
+		}
+
+		public function startViewing(rtmp:String, videoWidth:Number, videoHeight:Number):void{
+			LOGGER.debug("ViewerWindowManager::startViewing");
+
+			viewWindow = new WebRTCDesktopViewWindow();
+			viewWindow.startVideo(rtmp, videoWidth, videoHeight);
+			openWindow(viewWindow);
+			isViewing = true;
+		}
+	}
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/maps/ScreenshareEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/maps/ScreenshareEventMap.mxml
index c2d2436a379dac30ff8e3de7908f676d4617fb69..27024013203e4a7f989cefdc32b49673f4934236 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/maps/ScreenshareEventMap.mxml
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/maps/ScreenshareEventMap.mxml
@@ -44,6 +44,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 			import org.bigbluebutton.modules.screenshare.events.ViewWindowEvent;
 			import org.bigbluebutton.modules.screenshare.managers.ScreenshareManager;
 			import org.bigbluebutton.modules.screenshare.services.red5.ConnectionEvent;
+			import org.bigbluebutton.modules.screenshare.events.UseJavaModeCommand;
+			import org.bigbluebutton.modules.screenshare.events.DeskshareToolbarEvent;
 		]]>
 	</mx:Script>
 	<EventHandlers type="{FlexEvent.PREINITIALIZE}">
@@ -115,5 +117,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
 		<MethodInvoker generator="{ScreenshareManager}" method="handleStartModuleEvent" arguments="{event.module}"/>
 	</EventHandlers>
 
-  	
+	<EventHandlers type="{UseJavaModeCommand.USE_JAVA_MODE}">
+		<MethodInvoker generator="{ScreenshareManager}" method="handleUseJavaModeCommand"/>
+	</EventHandlers>
+
+	<EventHandlers type="{DeskshareToolbarEvent.STOP}">
+		<MethodInvoker generator="{ScreenshareManager}" method="handleDeskshareToolbarStopEvent"/>
+	</EventHandlers>
+
 </EventMap>
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/maps/WebRTCDeskshareEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/maps/WebRTCDeskshareEventMap.mxml
new file mode 100755
index 0000000000000000000000000000000000000000..6ffaa3ed24ceee4e38eed17ff76e29d7f08461f2
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/maps/WebRTCDeskshareEventMap.mxml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+
+BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+
+Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+
+This program is free software; you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 3.0 of the License, or (at your option) any later
+version.
+
+BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+
+-->
+
+<EventMap xmlns:mx="http://www.adobe.com/2006/mxml"
+	xmlns="http://mate.asfusion.com/">
+
+	<mx:Script>
+		<![CDATA[
+			import mx.events.FlexEvent;
+
+			import org.bigbluebutton.main.events.BBBEvent;
+			import org.bigbluebutton.main.events.MadePresenterEvent;
+			import org.bigbluebutton.modules.screenshare.events.ModuleEvent;
+			import org.bigbluebutton.modules.screenshare.events.ShareEvent;
+			import org.bigbluebutton.modules.screenshare.events.UseJavaModeCommand;
+			import org.bigbluebutton.modules.screenshare.events.WebRTCShareWindowEvent;
+			import org.bigbluebutton.modules.screenshare.events.WebRTCStreamEvent;
+			import org.bigbluebutton.modules.screenshare.events.WebRTCViewStreamEvent;
+			import org.bigbluebutton.modules.screenshare.events.WebRTCViewWindowEvent;
+			import org.bigbluebutton.modules.screenshare.managers.WebRTCDeskshareManager;
+			import org.bigbluebutton.modules.screenshare.events.RequestToStartSharing;
+			import org.bigbluebutton.modules.screenshare.events.RequestToStopSharing;
+			import org.bigbluebutton.modules.screenshare.events.ShareStartRequestResponseEvent;
+			import org.bigbluebutton.modules.screenshare.events.StartedViewingEvent;
+			import org.bigbluebutton.modules.screenshare.events.StreamStartedEvent;
+			import org.bigbluebutton.modules.screenshare.events.IsSharingScreenEvent;
+			import org.bigbluebutton.modules.screenshare.services.red5.WebRTCConnectionEvent;
+		]]>
+	</mx:Script>
+
+	<EventHandlers type="{FlexEvent.PREINITIALIZE}">
+		<!--
+		The FlexEvent.PREINITIALIZE event is a good place for creating and initializing managers.
+		-->
+		<ObjectBuilder generator="{WebRTCDeskshareManager}"/>
+	</EventHandlers>
+
+	<EventHandlers type="{ShareEvent.START_SHARING}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStartSharingEvent" arguments="{false}"/>
+	</EventHandlers>
+
+	<EventHandlers type="{BBBEvent.START_DESKSHARE}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStartSharingEvent" arguments="{true}"/>
+	</EventHandlers>
+	
+	<EventHandlers type="{RequestToStartSharing.REQUEST_SHARE_START}">
+    <MethodInvoker generator="{WebRTCDeskshareManager}" method="handleRequestStartSharingEvent"/>
+  </EventHandlers>
+
+	<EventHandlers type="{RequestToStopSharing.REQUEST_SHARE_STOP}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleRequestStopSharingEvent"/>
+	</EventHandlers>
+	
+	<EventHandlers type="{ShareStartRequestResponseEvent.SHARE_START_REQUEST_RESPONSE}">
+    <MethodInvoker generator="{WebRTCDeskshareManager}" method="handleShareStartRequestResponseEvent" arguments="{event}"/>
+  </EventHandlers>
+
+	<EventHandlers type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleMadePresenterEvent" arguments="{event}"/>
+	</EventHandlers>
+
+	<EventHandlers type="{MadePresenterEvent.SWITCH_TO_VIEWER_MODE}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleMadeViewerEvent" arguments="{event}"/>
+	</EventHandlers>
+
+	<EventHandlers type="{WebRTCStreamEvent.STOP}" >
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStreamStoppedEvent"/>
+	</EventHandlers>
+
+	<EventHandlers type="{WebRTCViewStreamEvent.START}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStreamStartEvent" arguments="{event}"/>
+	</EventHandlers>
+
+	<EventHandlers type="{WebRTCViewStreamEvent.STOP}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStreamStopEvent" arguments="{event}"/>
+	</EventHandlers>
+	
+	<EventHandlers type="{StreamStartedEvent.STREAM_STARTED}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStreamStartedEvent" arguments="{event}"/>
+	</EventHandlers>
+	
+	<EventHandlers type="{IsSharingScreenEvent.IS_SCREENSHARING}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleIsSharingScreenEvent" arguments="{event}"/>
+	</EventHandlers>
+
+	<EventHandlers type="{WebRTCShareWindowEvent.CLOSE}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleShareWindowCloseEvent"/>
+	</EventHandlers>
+
+	<EventHandlers type="{WebRTCConnectionEvent.SUCCESS}">
+    <MethodInvoker generator="{WebRTCDeskshareManager}" method="handleConnectionSuccessEvent"/>
+  </EventHandlers>
+
+	<EventHandlers type="{StartedViewingEvent.STARTED_VIEWING_EVENT}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStartedViewingEvent" arguments="{event.stream}"/>
+	</EventHandlers>
+
+	<EventHandlers type="{WebRTCViewWindowEvent.CLOSE}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleViewWindowCloseEvent"/>
+	</EventHandlers>
+
+	<EventHandlers type="{ModuleEvent.STOP}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStopModuleEvent"/>
+	</EventHandlers>
+
+	<EventHandlers type="{ModuleEvent.START}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStartModuleEvent" arguments="{event.module}"/>
+	</EventHandlers>
+
+	<EventHandlers type="{UseJavaModeCommand.USE_JAVA_MODE}">
+		<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleUseJavaModeCommand"/>
+	</EventHandlers>
+
+</EventMap>
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/model/ScreenshareOptions.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/model/ScreenshareOptions.as
index 0813997de0e8f62e504215c67d14313b2844224d..6b02236b874b439d027fd7cfc2951ab4ccfb4aae 100755
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/model/ScreenshareOptions.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/model/ScreenshareOptions.as
@@ -19,15 +19,17 @@
 package org.bigbluebutton.modules.screenshare.model
 {
 	import org.bigbluebutton.core.BBB;
-	
+
 	public class ScreenshareOptions
 	{
 		[Bindable] public var showButton:Boolean = true;
 		[Bindable] public var autoStart:Boolean = false;
 		[Bindable] public var autoFullScreen:Boolean = false;
 		[Bindable] public var baseTabIndex:int;
+		[Bindable] public var useWebRTCIfAvailable:Boolean = true;
+		[Bindable] public var chromeExtensionKey:String = null;
     [Bindable] public var helpUrl:String;
-		
+
 		public function parseOptions():void {
 			var vxml:XML = BBB.getConfigForModule("ScreenshareModule");
 			if (vxml != null) {
@@ -44,7 +46,13 @@ package org.bigbluebutton.modules.screenshare.model
 					baseTabIndex = 201;
 				}
 				if (vxml.@showButton != undefined){
-					showButton = (vxml.@showButton.toString().toUpperCase() == "TRUE") ? true : false; 
+					showButton = (vxml.@showButton.toString().toUpperCase() == "TRUE") ? true : false;
+				}
+				if (vxml.@useWebRTCIfAvailable != undefined) {
+					useWebRTCIfAvailable = (vxml.@useWebRTCIfAvailable.toString().toUpperCase() == "TRUE") ? true : false;
+				}
+				if (vxml.@chromeExtensionKey != undefined) {
+					chromeExtensionKey = vxml.@chromeExtensionKey.toString();
 				}
         if (vxml.@help != undefined){
           helpUrl = vxml.@help; 
@@ -52,4 +60,4 @@ package org.bigbluebutton.modules.screenshare.model
 			}
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/services/WebRTCDeskshareService.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/services/WebRTCDeskshareService.as
new file mode 100755
index 0000000000000000000000000000000000000000..f6513ee62416b7e298ab423877b252add6133a1c
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/services/WebRTCDeskshareService.as
@@ -0,0 +1,74 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 3.0 of the License, or (at your option) any later
+* version.
+*
+* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+*
+*/
+package org.bigbluebutton.modules.screenshare.services
+{
+	import com.asfusion.mate.events.Dispatcher;
+
+	import flash.net.NetConnection;
+
+	import org.as3commons.logging.api.ILogger;
+	import org.as3commons.logging.api.getClassLogger;
+	import org.bigbluebutton.modules.screenshare.services.red5.WebRTCConnection;
+
+	/**
+	 * The DeskShareProxy communicates with the Red5 deskShare server application
+	 * @author Snap
+	 *
+	 */
+	public class WebRTCDeskshareService
+	{
+		private static const LOGGER:ILogger = getClassLogger(ScreenshareService);
+
+		private var conn:WebRTCConnection;
+
+		private var module:ScreenshareModule;
+		private var dispatcher:Dispatcher;
+
+		private var uri:String;
+		private var room:String;
+
+		public function WebRTCDeskshareService() {
+			this.dispatcher = new Dispatcher();
+		}
+
+		public function handleStartModuleEvent(module:ScreenshareModule):void {
+			LOGGER.debug("Deskshare Module starting");
+			this.module = module;
+			connect(module.uri, module.getRoom());
+		}
+
+		public function connect(uri:String, room:String):void {
+			this.uri = uri;
+			this.room = room;
+			LOGGER.debug("Deskshare Service connecting to {0}", [uri]);
+			conn = new WebRTCConnection(room); //to red5 deskshare
+
+			conn.setURI(uri);
+			conn.connect();
+		}
+
+		public function getConnection():NetConnection{
+			return conn.getConnection();
+		}
+
+		public function disconnect():void{
+			conn.disconnect();
+		}
+	}
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/services/red5/WebRTCConnection.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/services/red5/WebRTCConnection.as
new file mode 100755
index 0000000000000000000000000000000000000000..a6965b457b45195a7a497535f7b29aa5052eaab8
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/services/red5/WebRTCConnection.as
@@ -0,0 +1,323 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 3.0 of the License, or (at your option) any later
+* version.
+*
+* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+package org.bigbluebutton.modules.screenshare.services.red5
+{
+	import com.asfusion.mate.events.Dispatcher;
+
+	import flash.events.AsyncErrorEvent;
+	import flash.events.NetStatusEvent;
+	import flash.events.SecurityErrorEvent;
+	import flash.events.TimerEvent;
+	import flash.net.NetConnection;
+	import flash.net.ObjectEncoding;
+	import flash.net.Responder;
+	import flash.utils.Timer;
+
+	import mx.utils.ObjectUtil;
+
+	import org.as3commons.logging.api.ILogger;
+	import org.as3commons.logging.api.getClassLogger;
+  import org.bigbluebutton.core.BBB;
+	import org.bigbluebutton.core.UsersUtil;
+	import org.bigbluebutton.core.managers.ReconnectionManager;
+	import org.bigbluebutton.main.events.BBBEvent;
+	import org.bigbluebutton.modules.screenshare.events.WebRTCViewStreamEvent;
+	import org.bigbluebutton.modules.screenshare.services.red5.WebRTCConnectionEvent;
+
+	public class WebRTCConnection {
+	private static const LOGGER:ILogger = getClassLogger(Connection);
+
+		private var nc:NetConnection;
+		private var uri:String;
+		private var retryTimer:Timer = null;
+		private var retryCount:int = 0;
+		private const MAX_RETRIES:int = 5;
+		private var responder:Responder;
+		private var width:Number;
+		private var height:Number;
+		private var room:String;
+		private var logoutOnUserCommand:Boolean = false;
+		private var reconnecting:Boolean = false;
+		private var wasPresenterBeforeDisconnect:Boolean = false;
+
+		private var dispatcher:Dispatcher = new Dispatcher();
+
+		public function WebRTCConnection(room:String) {
+			this.room = room;
+
+			responder = new Responder(
+				function(result:Object):void {
+					if (result != null && (result.publishing as Boolean)){
+						width = result.width as Number;
+						height = result.height as Number;
+						LOGGER.debug("Desk Share stream is streaming [{0},{1}]", [width, height]);
+						var event:WebRTCViewStreamEvent = new WebRTCViewStreamEvent(WebRTCViewStreamEvent.START);
+						event.videoWidth = width;
+						event.videoHeight = height;
+						dispatcher.dispatchEvent(event); //TODO why?
+					} else {
+						LOGGER.debug("No screenshare stream being published");
+						var connEvent:WebRTCConnectionEvent = new WebRTCConnectionEvent();
+						connEvent.status = WebRTCConnectionEvent.NO_DESKSHARE_STREAM;
+						dispatcher.dispatchEvent(connEvent); //TODO why?
+					}
+				},
+				function(status:Object):void{
+					var checkFailedEvent:WebRTCConnectionEvent = new WebRTCConnectionEvent();
+					checkFailedEvent.status = WebRTCConnectionEvent.FAIL_CHECK_FOR_DESKSHARE_STREAM;
+					dispatcher.dispatchEvent(checkFailedEvent);
+					LOGGER.debug("Error while trying to call remote mathod on server");
+				}
+			);
+		}
+
+		public function connect(retry:Boolean = false):void {
+			nc = new NetConnection();
+			nc.proxyType = "best";
+			nc.objectEncoding = ObjectEncoding.AMF0;
+			nc.client = this;
+
+			nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, debugAsyncErrorHandler);
+			nc.addEventListener(NetStatusEvent.NET_STATUS, debugNetStatusHandler);
+			nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
+			nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
+
+			if (getURI().length == 0){
+				LOGGER.error("please provide a valid URI connection string. URI Connection String missing");
+				return;
+			} else if (nc.connected){
+				LOGGER.error("You are already connected to {0}", [getURI()]);
+				return;
+			}
+
+			LOGGER.debug("Trying to connect to [{0}] retry=[{1}]", [getURI(), retry]);
+			if (! (retryCount > 0)) {
+				var ce:WebRTCConnectionEvent = new WebRTCConnectionEvent();
+				ce.status = WebRTCConnectionEvent.CONNECTING;
+
+				dispatcher.dispatchEvent(ce);
+			}
+
+			nc.connect(getURI(), UsersUtil.getInternalMeetingID());
+		}
+
+		private function connectTimeoutHandler(e:TimerEvent):void {
+			LOGGER.debug("Connection attempt to [{0}] timedout. Retrying.", [getURI()]);
+			retryTimer.stop();
+			retryTimer = null;
+
+			nc.close();
+			nc = null;
+
+			var ce:WebRTCConnectionEvent = new WebRTCConnectionEvent();;
+
+			retryCount++;
+			if (retryCount < MAX_RETRIES) {
+				ce.status = WebRTCConnectionEvent.CONNECTING_RETRY;
+				ce.retryAttempts = retryCount;
+				dispatcher.dispatchEvent(ce);
+
+				connect(false);
+			} else {
+				ce.status = WebRTCConnectionEvent.CONNECTING_MAX_RETRY;
+				dispatcher.dispatchEvent(ce);
+			}
+		}
+
+		public function close():void{
+			nc.close();
+		}
+
+		public function setURI(p_URI:String):void{
+			uri = p_URI;
+		}
+
+		public function getURI():String{
+			return uri;
+		}
+
+		public function onBWCheck(... rest):Number {
+			return 0;
+		}
+
+		public function onBWDone(... rest):void {
+			var p_bw:Number;
+			if (rest.length > 0) p_bw = rest[0];
+			// your application should do something here
+			// when the bandwidth check is complete
+			LOGGER.debug("bandwidth = {0} Kbps.", [p_bw]);
+		}
+
+		private function netStatusHandler(event:NetStatusEvent):void {
+			LOGGER.debug("Connected to [" + getURI() + "]. [" + event.info.code + "]");
+
+			if (retryTimer) {
+				retryCount = 0;
+				LOGGER.debug("Cancelling retry timer.");
+				retryTimer.stop();
+				retryTimer = null;
+			}
+
+			var ce:WebRTCConnectionEvent = new WebRTCConnectionEvent();
+
+			switch(event.info.code){
+				case "NetConnection.Connect.Failed":
+					if (reconnecting) {
+						var attemptFailedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT);
+						attemptFailedEvent.payload.type = ReconnectionManager.DESKSHARE_CONNECTION;
+						dispatcher.dispatchEvent(attemptFailedEvent);
+					}
+					ce.status = WebRTCConnectionEvent.FAILED;
+
+					dispatcher.dispatchEvent(ce);
+				break;
+
+				case "NetConnection.Connect.Success":
+					ce.status = WebRTCConnectionEvent.SUCCESS;
+					if (reconnecting) {
+						reconnecting = false;
+						if (wasPresenterBeforeDisconnect) {
+							wasPresenterBeforeDisconnect = false;
+							// stopSharingDesktop(room, room) //TODO
+						}
+
+						var attemptSucceeded:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT);
+						attemptSucceeded.payload.type = ReconnectionManager.DESKSHARE_CONNECTION;
+						dispatcher.dispatchEvent(attemptSucceeded);
+					}
+
+					// request desktop sharing info (as a late joiner)
+					LOGGER.debug("Sending [desktopSharing.requestDeskShareInfo] to server.");
+					var _nc:ConnectionManager = BBB.initConnectionManager();
+					_nc.sendMessage("desktopSharing.requestDeskShareInfo",
+						function(result:String):void { // On successful result
+							LOGGER.debug(result);
+						},
+						function(status:String):void { // status - On error occurred
+					LOGGER.error(status);
+						}
+					);
+
+					dispatcher.dispatchEvent(ce);
+				break;
+
+				case "NetConnection.Connect.Rejected":
+					ce.status = WebRTCConnectionEvent.REJECTED;
+					dispatcher.dispatchEvent(ce);
+				break;
+
+				case "NetConnection.Connect.Closed":
+					LOGGER.debug("Deskshare connection closed.");
+					ce.status = WebRTCConnectionEvent.CLOSED;
+					if (UsersUtil.amIPresenter()) {
+						// Let's keep our presenter status before disconnected. We can't
+						// tell the other user's to stop desktop sharing as our connection is broken. (ralam july 24, 2015)
+						wasPresenterBeforeDisconnect = true;
+
+					} else {
+						// stopViewing(); //TODO
+					}
+
+					if (!logoutOnUserCommand) {
+						reconnecting = true;
+
+						var disconnectedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_DISCONNECTED_EVENT);
+						disconnectedEvent.payload.type = ReconnectionManager.DESKSHARE_CONNECTION;
+						disconnectedEvent.payload.callback = connect;
+						disconnectedEvent.payload.callbackParameters = [];
+						dispatcher.dispatchEvent(disconnectedEvent);
+					}
+				break;
+
+				case "NetConnection.Connect.InvalidApp":
+					ce.status = WebRTCConnectionEvent.INVALIDAPP;
+					dispatcher.dispatchEvent(ce);
+				break;
+
+				case "NetConnection.Connect.AppShutdown":
+					ce.status = WebRTCConnectionEvent.APPSHUTDOWN;
+					dispatcher.dispatchEvent(ce);
+				break;
+
+				case "NetConnection.Connect.NetworkChange":
+					LOGGER.info("Detected network change. User might be on a wireless and temporarily dropped connection. Doing nothing. Just making a note.");
+					break;
+
+				default :
+					// I dispatch DISCONNECTED incase someone just simply wants to know if we're not connected'
+					// rather than having to subscribe to the events individually
+					ce.status = WebRTCConnectionEvent.DISCONNECTED;
+					dispatcher.dispatchEvent(ce);
+					break;
+			}
+		}
+
+		private function securityErrorHandler(event:SecurityErrorEvent):void{
+			var ce:WebRTCConnectionEvent = new WebRTCConnectionEvent();
+			ce.status = WebRTCConnectionEvent.SECURITYERROR;
+			dispatcher.dispatchEvent(ce);
+		}
+
+		/**
+		 * Check if anybody is publishing the stream for this room
+		 * This method is useful for clients which have joined a room where somebody is already publishing
+		 *
+		 */
+		private function checkIfStreamIsPublishing(room: String):void{
+			LOGGER.debug("checking if desk share stream is publishing");
+			var event:WebRTCConnectionEvent = new WebRTCConnectionEvent();
+			event.status = WebRTCConnectionEvent.CHECK_FOR_DESKSHARE_STREAM;
+			dispatcher.dispatchEvent(event); // TODO anton send to akka-bbb-apps
+
+			nc.call("screenshare.checkIfStreamIsPublishing", responder, room);
+		}
+
+		public function disconnect():void{
+			logoutOnUserCommand = true;
+			if (nc != null) nc.close();
+		}
+
+		public function connectionSuccessHandler():void{
+			LOGGER.debug("Successully connection to {0}", [uri]);
+
+			checkIfStreamIsPublishing(room);
+		}
+
+		private function debugNetStatusHandler(e:NetStatusEvent):void {
+		LOGGER.debug("netStatusHandler target={0} info={1}", [e.target, ObjectUtil.toString(e.info)]);
+		}
+
+		private function debugAsyncErrorHandler(e:AsyncErrorEvent):void {
+		LOGGER.debug("asyncErrorHandler target={0} info={1}", [e.target, e.text]);
+		}
+
+		public function getConnection():NetConnection{
+			return nc;
+		}
+
+		public function connectionFailedHandler(e:WebRTCConnectionEvent):void{
+			LOGGER.error("connection failed to {0} with message {1}", [uri, e.toString()]);
+		}
+
+		public function connectionRejectedHandler(e:WebRTCConnectionEvent):void{
+		LOGGER.error("connection rejected to {0} with message {1}", [uri, e.toString()]);
+		}
+	}
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/services/red5/WebRTCConnectionEvent.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/services/red5/WebRTCConnectionEvent.as
new file mode 100755
index 0000000000000000000000000000000000000000..d2e60b05554de7142da580cda395e4417f543af1
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/services/red5/WebRTCConnectionEvent.as
@@ -0,0 +1,49 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 3.0 of the License, or (at your option) any later
+* version.
+*
+* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+*
+*/
+package org.bigbluebutton.modules.screenshare.services.red5
+{
+	import flash.events.Event;
+
+	public class WebRTCConnectionEvent extends Event {
+		public static const DESKSHARE_CONNECTION_EVENT:String = "webrtcDeskshare connection event";
+
+		public static const SUCCESS:String = "connection success";
+		public static const FAILED:String = "connection failed";
+		public static const CLOSED:String = "connection closed";
+		public static const REJECTED:String = "connection rejected";
+		public static const INVALIDAPP:String = "connection invalidApp";
+		public static const APPSHUTDOWN:String = "connection appShutdown";
+		public static const SECURITYERROR:String = "connection securityError";
+		public static const DISCONNECTED:String = "connection disconnected";
+		public static const CONNECTING:String = "connection connecting";
+		public static const CONNECTING_RETRY:String = "connection retry";
+		public static const CONNECTING_MAX_RETRY:String = "connection max retry";
+		public static const CHECK_FOR_DESKSHARE_STREAM:String = "connection check webrtcDeskshare publishing";
+		public static const NO_DESKSHARE_STREAM:String = "connection webrtcDeskshare publishing";
+		public static const FAIL_CHECK_FOR_DESKSHARE_STREAM:String = "connection failed check webrtcDeskshare publishing";
+
+		public var status:String = "";
+
+		public var retryAttempts:int = 0;
+
+		public function WebRTCConnectionEvent():void {
+			super(DESKSHARE_CONNECTION_EVENT, true, false);
+		}
+	}
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/utils/BrowserCheck.as b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/utils/BrowserCheck.as
new file mode 100755
index 0000000000000000000000000000000000000000..1aa35b662cf1778eff2aeaa05cb20738d93b0092
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/utils/BrowserCheck.as
@@ -0,0 +1,62 @@
+/**
+* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+*
+* Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
+*
+* This program is free software; you can redistribute it and/or modify it under the
+* terms of the GNU Lesser General Public License as published by the Free Software
+* Foundation; either version 3.0 of the License, or (at your option) any later
+* version.
+*
+* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License along
+* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+package org.bigbluebutton.modules.screenshare.utils
+{
+	import flash.external.ExternalInterface;
+	import org.bigbluebutton.core.BBB;
+	import flash.system.Capabilities;
+	import org.as3commons.logging.api.getClassLogger;
+	import org.as3commons.logging.api.ILogger;
+
+	public class BrowserCheck {
+		private static const LOGGER:ILogger = getClassLogger(BrowserCheck);
+
+		public static function isChrome42OrHigher():Boolean {
+			var browser:Array = ExternalInterface.call("determineBrowser");
+			return ((browser[0] == "Chrome") && (parseInt(browser[1]) >= 42));
+		}
+
+		public static function isUsingLessThanChrome38OnMac():Boolean {
+			var browser:Array = ExternalInterface.call("determineBrowser");
+			return ((browser[0] == "Chrome") && (parseInt(browser[1]) <= 38) && (Capabilities.os.indexOf("Mac") >= 0));
+		}
+
+		public static function isWebRTCSupported():Boolean {
+			/*LOGGER.debug("isWebRTCSupported - ExternalInterface.available=[{0}], isWebRTCAvailable=[{1}]", [ExternalInterface.available, ExternalInterface.call("isWebRTCAvailable")]);*/
+			return (ExternalInterface.available && ExternalInterface.call("isWebRTCAvailable"));
+		}
+
+		public static function isUsingEdgePluginUnsupported():Boolean {
+			var browser:Array = ExternalInterface.call("determineBrowser");
+			/* Currently no browser version of Edge supports plugins */
+			return browser[0] == "Edge";
+		}
+
+		public static function isChrome():Boolean {
+			var browser:Array = ExternalInterface.call("determineBrowser");
+			return browser[0] == "Chrome";
+		}
+
+		public static function isFirefox():Boolean {
+			var browser:Array = ExternalInterface.call("determineBrowser");
+			return browser[0] == "Firefox";
+		}
+	}
+}
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/view/components/WebRTCDesktopPublishWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/view/components/WebRTCDesktopPublishWindow.mxml
new file mode 100755
index 0000000000000000000000000000000000000000..9d4fd5a2d2deb4f56987d19bd928a305a14feb5a
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/view/components/WebRTCDesktopPublishWindow.mxml
@@ -0,0 +1,384 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+
+BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+
+Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+
+This program is free software; you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 3.0 of the License, or (at your option) any later
+version.
+
+BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+
+-->
+
+<dspub:CustomMdiWindow
+	xmlns:mx="http://www.adobe.com/2006/mxml"
+	implements="org.bigbluebutton.common.IBbbModuleWindow"
+	xmlns:mate="http://mate.asfusion.com/"
+	xmlns:dspub="org.bigbluebutton.common.*"
+	backgroundColor="#C0C0C0"
+	initialize="init()"
+	creationComplete="onCreationComplete()"
+	verticalScrollPolicy="off" horizontalScrollPolicy="off"
+	width="365" height="350"
+	title="{ResourceUtil.getInstance().getString('bbb.desktopPublish.title')}"
+	resizable="false">
+
+	<mate:Listener type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}" method="onChangedPresenter" />
+	<mate:Listener type="{MadePresenterEvent.SWITCH_TO_VIEWER_MODE}" method="onChangedPresenter" />
+	<mate:Listener type="{WebRTCViewStreamEvent.STOP}" method="closePublishWindow" />
+	<mate:Listener type="{LocaleChangeEvent.LOCALE_CHANGED}" method="localeChanged" />
+	<mate:Listener type="{StopSharingButtonEvent.STOP_SHARING}" method="stopSharingEvent" />
+	<mate:Listener type="{ShortcutEvent.REMOTE_FOCUS_DESKTOP}" method="remoteFocus" />
+
+
+	<mx:Script>
+		<![CDATA[
+			import com.asfusion.mate.events.Dispatcher;
+
+			import mx.core.UIComponent;
+
+			import org.as3commons.logging.api.ILogger;
+			import org.as3commons.logging.api.getClassLogger;
+			import org.bigbluebutton.common.IBbbModuleWindow;
+			import org.bigbluebutton.common.Images;
+			import org.bigbluebutton.common.events.LocaleChangeEvent;
+			import org.bigbluebutton.main.events.BBBEvent;
+			import org.bigbluebutton.main.events.MadePresenterEvent;
+			import org.bigbluebutton.main.events.ShortcutEvent;
+			import org.bigbluebutton.main.views.MainCanvas;
+			import org.bigbluebutton.modules.screenshare.events.WebRTCShareWindowEvent;
+			import org.bigbluebutton.modules.screenshare.events.StopSharingButtonEvent;
+			import org.bigbluebutton.modules.screenshare.events.WebRTCStreamEvent;
+			import org.bigbluebutton.modules.screenshare.events.WebRTCViewStreamEvent;
+			import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions;
+			import org.bigbluebutton.modules.screenshare.services.red5.WebRTCConnectionEvent;
+			import org.bigbluebutton.util.i18n.ResourceUtil;
+			import org.bigbluebutton.main.api.JSLog;
+
+			private static const LOGGER:ILogger = getClassLogger(WebRTCDesktopPublishWindow);
+
+			public static const SCALE:Number = 5;
+			private static const VID_HEIGHT_PAD:Number = 73;
+			private static const VID_WIDTH_PAD:Number = 6;
+
+			private var images:Images = new Images();
+			[Bindable] public var bbbLogo:Class = images.bbb_logo;
+
+			private var connection:NetConnection;
+			private var uri:String;
+			private var useTLS:Boolean;
+			private var room:String;
+			private var streaming:Boolean = false;
+
+			private var video:Video;
+			private var ns:NetStream;
+			[Bindable] private var videoHolder:UIComponent;
+			private var stream:String;
+			private var videoHeight:Number;
+			private var videoWidth:Number;
+			private var captureHeight:Number = Capabilities.screenResolutionY;
+			private var captureWidth:Number = Capabilities.screenResolutionX;
+			private var autoStart:Boolean = false;
+			private var globalDispatcher:Dispatcher = new Dispatcher();
+
+			[Bindable] private var dsOptions:ScreenshareOptions;
+
+			private function init():void {
+				dsOptions = new ScreenshareOptions();
+			}
+
+			private function onCreationComplete():void {
+				windowControls.maximizeRestoreBtn.enabled = false;
+
+				setCurrentState("dispFullRegionControlBar"); //TODO ANTON
+				resourcesChanged();
+
+				titleBarOverlay.tabIndex = dsOptions.baseTabIndex;
+				titleBarOverlay.focusEnabled = true;
+			}
+
+			private function remoteFocus(e:ShortcutEvent):void{
+				focusManager.setFocus(minimizeBtn);
+			}
+
+			public function get defaultWidth():int{
+				return this.width;
+			}
+
+			public function get defaultHeight():int{
+				return this.height;
+			}
+
+			public function set defaultHeight(height:int):void{
+				this.height = height;
+			}
+
+			public function set defaultWidth(width:int):void{
+				this.width = width;
+			}
+
+			public function getPrefferedPosition():String{
+				return MainCanvas.DESKTOP_SHARING_PUBLISH;
+			}
+
+			/*
+			 * Implement resizeable interface.
+			 */
+			public function resetWidthAndHeight():void{/* do nothing */}
+
+			public function initWindow(connection:NetConnection, uri:String, useTLS:Boolean , room:String, autoStart:Boolean, autoFullScreen:Boolean):void {
+				this.connection = connection;
+				this.uri = uri;
+				this.useTLS = useTLS;
+				this.room = room;
+				this.autoStart = autoStart;
+				/*if(autoFullScreen)
+					shareScreen(true);*/
+			}
+
+			public function shareScreen(fullScreen:Boolean):void {
+				LOGGER.debug("Calling shareScreen");
+				startSharing(connection, uri, useTLS , room, fullScreen);
+			}
+
+			private function startSharing(connection:NetConnection, uri:String , useTLS:Boolean , room:String, fullScreen:Boolean):void {
+				var captureX:Number = 0;
+				var captureY:Number = 0;
+				
+			}
+
+			public function stopSharing():void{
+				if (streaming) {
+					stopStream();
+					var streamEvent:WebRTCStreamEvent = new WebRTCStreamEvent(WebRTCStreamEvent.STOP);
+					dispatchEvent(streamEvent);
+				}
+				streaming = false;
+				closeWindow();
+			}
+
+			public function stopSharingEvent(evt:StopSharingButtonEvent):void{
+				JSLog.warn("stopSharingEvent WebRTCDesktopPublishWindow.mxml", {});
+				stopSharing();
+			}
+
+			// TODO make this function private and trigger it via message
+			public function startVideo(rtmp:String, videoWidth:Number, videoHeight:Number):void {
+				var myArray :Array = rtmp.split('/');
+				var meetingUrl:String = rtmp.substring(0, rtmp.lastIndexOf('/'));
+				stream = rtmp.substring(rtmp.lastIndexOf('/')+1, rtmp.length);
+				LOGGER.debug("WebRTCDesktopPublishWindow::startVideo meetingurl=[{0}] and stream=[{1}]",[meetingUrl, stream]);
+				
+				JSLog.warn("WebRTCDesktopPublishWindow::startVideo meetingurl= ",meetingUrl);
+				JSLog.warn("WebRTCDesktopPublishWindow::startVideo stream=", stream);
+
+
+				connection = new NetConnection();
+				connection.proxyType = "best";
+				connection.objectEncoding = ObjectEncoding.AMF0;
+				connection.client = this;
+
+				connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
+				connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
+				connection.connect(meetingUrl);
+			}
+
+			private function netStatusHandler(event:NetStatusEvent):void {
+				var ce:WebRTCConnectionEvent = new WebRTCConnectionEvent();
+				LOGGER.debug("netStatusHandler   [{0}]",[event.info.code]);
+				switch(event.info.code){
+					case "NetConnection.Connect.Failed":
+						ce.status = WebRTCConnectionEvent.FAILED;
+
+						break;
+
+					case "NetConnection.Connect.Success":
+						ce.status = WebRTCConnectionEvent.SUCCESS;
+						startPreviewStream(stream, videoWidth, videoHeight);
+						break;
+
+					case "NetConnection.Connect.Rejected":
+						ce.status = WebRTCConnectionEvent.REJECTED;
+						break;
+
+					case "NetConnection.Connect.Closed":
+						trace("Deskshare connection closed.");
+						ce.status = WebRTCConnectionEvent.CLOSED;
+						break;
+
+					case "NetConnection.Connect.InvalidApp":
+						ce.status = WebRTCConnectionEvent.INVALIDAPP;
+						break;
+
+					case "NetConnection.Connect.AppShutdown":
+						ce.status = WebRTCConnectionEvent.APPSHUTDOWN;
+						break;
+
+					case "NetConnection.Connect.NetworkChange":
+						trace("Detected network change. User might be on a wireless and " +
+							"temporarily dropped connection. Doing nothing. Just making a note.");
+						break;
+				}
+			}
+
+			private function securityErrorHandler(event:SecurityErrorEvent):void {
+				LOGGER.debug("ERROR WebRTCDesktopPublishWindow::securityErrorHandler ");
+			}
+
+			private function startPreviewStream(streamName:String, capWidth:Number, capHeight:Number):void{
+				streaming = true;
+
+				videoHolder = new UIComponent();
+
+				var vidW:Number = captureWidth;
+				var vidH:Number = captureHeight;
+
+				// Don't scale if capture dimension is smaller than window.
+				if((captureWidth > this.width - VID_WIDTH_PAD) && (captureHeight < this.height - VID_HEIGHT_PAD)){
+					vidW = this.width - VID_WIDTH_PAD;
+					vidH = (captureHeight / captureWidth) * vidW;
+				}
+				else if( ((captureWidth < this.width - VID_WIDTH_PAD) && (captureHeight > this.height - VID_HEIGHT_PAD))
+				|| ((captureWidth > this.width - VID_WIDTH_PAD) && (captureHeight > this.height - VID_HEIGHT_PAD)) ){
+					vidH = this.height - VID_HEIGHT_PAD;
+					vidW = (captureWidth / captureHeight) * vidH;
+				}
+				else{
+					vidW = captureWidth;
+					vidH = captureHeight;
+				}
+
+				LOGGER.debug("webrtcDeskshare preview[{0},{1}][{2},{3}]", [captureWidth, captureHeight, vidW, vidH]);
+				video = new Video(vidW, vidH);
+				video.width = vidW;
+				video.height = vidH;
+				videoHolder.width = vidW;
+				videoHolder.height = vidH;
+				video.x = videoHolder.x = (this.width - VID_WIDTH_PAD - vidW) / 2;
+				video.y = videoHolder.y = (this.height - VID_HEIGHT_PAD - vidH) / 2;
+
+				videoHolder.addChild(video);
+				this.addChild(videoHolder);
+
+				ns = new NetStream(connection);
+				ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
+				ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
+				ns.client = this;
+				ns.bufferTime = 0;
+				ns.receiveVideo(true);
+				ns.receiveAudio(false);
+				video.attachNetStream(ns);
+
+				ns.play(stream);
+
+				btnClosePublish.enabled = true;
+			}
+
+			private function stopStream():void{
+				streaming = false;
+				captureHeight = Capabilities.screenResolutionY;
+				captureWidth = Capabilities.screenResolutionX;
+				ns.close();
+			}
+
+			private function onAsyncError(e:AsyncErrorEvent):void{
+				LOGGER.debug("VIdeoWindow::asyncerror {0}", [e.toString()]);
+			}
+
+			private function onNetStatus(e:NetStatusEvent):void{
+				switch(e.info.code){
+				case "NetStream.Publish.Start":
+					LOGGER.debug("NetStream.Publish.Start for broadcast stream {0}", [stream]);
+					break;
+				case "NetStream.Play.UnpublishNotify":
+					LOGGER.debug("NetStream.Play.UnpublishNotify for broadcast stream {0}", [stream]);
+					stopSharing();
+					break;
+				case "NetStream.Play.Start":
+					LOGGER.debug("Netstatus: {0}", [e.info.code]);
+					globalDispatcher.dispatchEvent(new BBBEvent(BBBEvent.DESKSHARE_STARTED));
+				}
+			}
+
+			private function onChangedPresenter(e:Event):void{
+				stopSharing();
+				closeWindow();
+			}
+
+			private function closeWindow():void {
+				// LOGGER.debug("Calling stopApplet in closeWindow()");
+				dispatchEvent(new WebRTCShareWindowEvent(WebRTCShareWindowEvent.CLOSE));
+			}
+
+			/*
+			 * Override the close handler. We want the Event Map to send a message to
+			 * the MDIManager to close this window;
+			 */
+			override public function close(event:MouseEvent = null):void {
+				stopSharing();
+				closeWindow();
+			}
+
+			override protected function resourcesChanged():void{
+				super.resourcesChanged();
+				this.title = ResourceUtil.getInstance().getString('bbb.desktopPublish.title');
+
+				if (titleBarOverlay != null) {
+					titleBarOverlay.accessibilityName = ResourceUtil.getInstance().getString('bbb.desktopPublish.title');
+				}
+
+				if (windowControls != null) {
+					minimizeBtn.toolTip = ResourceUtil.getInstance().getString('bbb.desktopPublish.minimizeBtn.toolTip');
+					minimizeBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.desktopPublish.minimizeBtn.accessibilityName");
+
+					maximizeRestoreBtn.toolTip = ResourceUtil.getInstance().getString('bbb.desktopPublish.maximizeRestoreBtn.toolTip');
+					maximizeRestoreBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.desktopPublish.maximizeRestoreBtn.accessibilityName");
+
+					closeBtn.toolTip = ResourceUtil.getInstance().getString('bbb.desktopPublish.closeBtn.toolTip');
+					closeBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.desktopPublish.closeBtn.accessibilityName");
+				}
+			}
+
+			private function localeChanged(e:Event):void{
+				resourcesChanged();
+			}
+
+			private function closePublishWindow(event:WebRTCViewStreamEvent):void{
+				stopStream();
+				closeWindow();
+			}
+		]]>
+	</mx:Script>
+
+	<dspub:TabIndexer startIndex="{dsOptions.baseTabIndex + 1}"
+					  tabIndices="{[minimizeBtn, maximizeRestoreBtn, closeBtn, btnClosePublish]}"/>
+	<dspub:states>
+		<mx:State name="dispFullRegionControlBar">
+			<mx:AddChild>
+			  <mx:ControlBar id="fullRegionBottomBar">
+				<mx:VBox width="100%" height="100%" horizontalAlign="center">
+				  <mx:HBox horizontalAlign="center">
+					<mx:Button id="btnClosePublish"
+							   toolTip="{ResourceUtil.getInstance().getString('bbb.desktopPublish.stop.tooltip')}"
+							   label="{ResourceUtil.getInstance().getString('bbb.desktopPublish.stop.label')}"
+							   visible="true"
+							   enabled="false"
+							   click="stopSharing()" />
+				  </mx:HBox>
+				</mx:VBox>
+			  </mx:ControlBar>
+			</mx:AddChild>
+		</mx:State>
+	</dspub:states>
+
+</dspub:CustomMdiWindow>
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/view/components/WebRTCDesktopViewWindow.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/view/components/WebRTCDesktopViewWindow.mxml
new file mode 100755
index 0000000000000000000000000000000000000000..f035b557b01639d0779f4a9e201cdcaefe660dbb
--- /dev/null
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/view/components/WebRTCDesktopViewWindow.mxml
@@ -0,0 +1,388 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+
+BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
+
+Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
+
+This program is free software; you can redistribute it and/or modify it under the
+terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 3.0 of the License, or (at your option) any later
+version.
+
+BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
+
+-->
+
+<CustomMdiWindow xmlns="org.bigbluebutton.common.*"
+	xmlns:mx="http://www.adobe.com/2006/mxml"
+	xmlns:common="org.bigbluebutton.common.*"
+	width="600" height="400"
+	initialize="init()"
+	implements="org.bigbluebutton.common.IBbbModuleWindow"
+	xmlns:mate="http://mate.asfusion.com/"
+	title="{ResourceUtil.getInstance().getString('bbb.desktopView.title')}"
+	showCloseButton="false"
+	resize="fitToWindow()" >
+
+	<mate:Listener type="{ViewStreamEvent.STOP}" method="onStopViewStreamEvent" />
+	<mate:Listener type="{LocaleChangeEvent.LOCALE_CHANGED}" method="localeChanged" />
+
+	<mx:Script>
+		<![CDATA[
+			import mx.core.UIComponent;
+
+			import flexlib.mdi.events.MDIWindowEvent;
+
+			import org.as3commons.logging.api.ILogger;
+			import org.as3commons.logging.api.getClassLogger;
+			import org.bigbluebutton.common.IBbbModuleWindow;
+			import org.bigbluebutton.common.Images;
+			import org.bigbluebutton.common.events.LocaleChangeEvent;
+			import org.bigbluebutton.main.views.MainCanvas;
+			import org.bigbluebutton.modules.screenshare.events.ViewStreamEvent;
+			import org.bigbluebutton.modules.screenshare.events.ViewWindowEvent;
+			import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions;
+			import org.bigbluebutton.util.i18n.ResourceUtil;
+			import org.bigbluebutton.modules.screenshare.services.red5.WebRTCConnectionEvent;
+
+			public static const LOG:String = "Deskshare::DesktopViewWindow - ";
+			private var screenHeight:Number = Capabilities.screenResolutionY;
+			private var screenWidth:Number = Capabilities.screenResolutionX;
+
+			private var images:Images = new Images();
+			[Bindable] public var fitToWidthIcon:Class = images.magnifier;
+			[Bindable] public var fitToActualSizeIcon:Class = images.mag_reset;
+
+			private var video:Video;
+			private var ns:NetStream;
+			private var videoHolder:UIComponent = new UIComponent();
+			private var stream:String;
+			private var videoHeight:Number;
+			private var videoWidth:Number;
+
+			private static const LOGGER:ILogger = getClassLogger(WebRTCDesktopViewWindow);
+
+			private static const VIDEO_WIDTH_PADDING:int = 7;
+			private static const VIDEO_HEIGHT_PADDING:int = 65;
+
+			// The following code block is to deal with a bug in FLexLib
+			// with MDI windows not responding well to being maximized
+			private var savedWindowWidth:Number;
+			private var savedWindowHeight:Number;
+			private var savedX:Number;
+			private var savedY:Number;
+			private var isMaximized:Boolean = false;
+			private var streamID:String;
+			private var nc:NetConnection;
+
+			[Bindable] private var baseIndex:int;
+
+			[Bindable] private var dsOptions:ScreenshareOptions;
+
+			private function init():void{
+				dsOptions = new ScreenshareOptions();
+			}
+
+			private function displayVideo():void{
+				videoHolder.addChild(video);
+				this.addChild(videoHolder);
+				videoHolder.percentWidth = 100;
+				videoHolder.percentHeight = 100;
+				addEventListener(MDIWindowEvent.RESIZE_END, onResizeEndEvent);
+				fitToActualSize();
+				maximize();
+
+				resourcesChanged();
+
+				titleBarOverlay.tabIndex = dsOptions.baseTabIndex;
+				minimizeBtn.tabIndex = baseIndex+1;
+				maximizeRestoreBtn.tabIndex = baseIndex+2;
+				closeBtn.tabIndex = baseIndex+3;
+			}
+
+			private function onResizeEndEvent(event:MDIWindowEvent):void {
+				if (event.window == this) {
+					fitToWindow();
+				}
+			}
+
+			public function startVideo(rtmp:String, width:Number, height:Number):void{
+				var videoURL:String;
+
+				// split rtmp extract the streamId
+				this.videoWidth = width;
+				this.videoHeight = height;
+
+				var myArray :Array = rtmp.split('/');
+				// myArray.forEach ( function ( item:*, i:int, arr:Array) : void { JSLog.debug("^^^^" + i +"aa=" + item, logData); } );
+
+				streamID = myArray[5]; // Grab the streamID from the end of the rtmp string
+				videoURL = rtmp.substring(0, rtmp.lastIndexOf('/'));
+
+				connect(videoURL);
+			}
+
+			public function connect(rtmpUrl: String):void {
+				nc = new NetConnection();
+				nc.proxyType = "best";
+				nc.objectEncoding = ObjectEncoding.AMF0;
+				nc.client = this;
+
+				nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
+				nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
+				nc.connect(rtmpUrl);
+			}
+
+			public function connectionSuccessHandler():void{
+				LOGGER.debug("SUCCESS  DesktopViewWindow::connectionSuccessHandler   SUCCESS" + videoHeight + videoWidth + streamID);
+				ns = new NetStream(nc);
+				ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
+				ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
+				ns.client = this;
+
+				ns.bufferTime = 0;
+				ns.receiveVideo(true);
+				ns.receiveAudio(false);
+
+				video = new Video(videoWidth, videoHeight);
+
+				video.attachNetStream(ns);
+				ns.play(streamID);
+				displayVideo();
+				this.title = "Viewing Remote Desktop";
+			}
+
+
+
+			private function netStatusHandler(event:NetStatusEvent):void {
+//				trace(LOG + "Connected to [" + getURI() + "]. [" + event.info.code + "]");
+				var ce:WebRTCConnectionEvent = new WebRTCConnectionEvent();
+
+				switch(event.info.code){
+					case "NetConnection.Connect.Failed":
+						ce.status = WebRTCConnectionEvent.FAILED;
+
+						break;
+
+					case "NetConnection.Connect.Success":
+						ce.status = WebRTCConnectionEvent.SUCCESS;
+						connectionSuccessHandler();
+						break;
+
+					case "NetConnection.Connect.Rejected":
+						ce.status = WebRTCConnectionEvent.REJECTED;
+						break;
+
+					case "NetConnection.Connect.Closed":
+						trace(LOG + "Deskshare connection closed.");
+						ce.status = WebRTCConnectionEvent.CLOSED;
+						break;
+
+					case "NetConnection.Connect.InvalidApp":
+						ce.status = WebRTCConnectionEvent.INVALIDAPP;
+						break;
+
+					case "NetConnection.Connect.AppShutdown":
+						ce.status = WebRTCConnectionEvent.APPSHUTDOWN;
+						break;
+
+					case "NetConnection.Connect.NetworkChange":
+						trace(LOG + "Detected network change. User might be on a wireless and temporarily dropped connection. Doing nothing. Just making a note.");
+						break;
+				}
+			}
+
+			private function securityErrorHandler(event:SecurityErrorEvent):void {
+				LOGGER.debug("ERROR DesktopViewWindow::securityErrorHandler ");
+			}
+
+			public function stopViewing():void {
+				ns.close();
+				closeWindow();
+			}
+
+			private function onStopViewStreamEvent(event:ViewStreamEvent):void {
+				stopViewing();
+			}
+
+			protected function updateButtonsPosition():void {
+				if (this.width < bottomBar.width) {
+					bottomBar.visible = false;
+				}
+
+				if (bottomBar.visible == false) {
+					bottomBar.y = bottomBar.x = 0;
+				} else {
+					bottomBar.y = (this.height - bottomBar.height) / 2;
+					bottomBar.x = (this.width - bottomBar.width) / 2;
+				}
+			}
+
+			private function onAsyncError(e:AsyncErrorEvent):void{
+				LOGGER.debug("VIdeoWindow::asyncerror {0}", [e.toString()]);
+			}
+
+			private function onNetStatus(e:NetStatusEvent):void{
+				LOGGER.debug("onNetStatus info={0}", [e.info.text]);
+
+				switch(e.info.code){
+				case "NetStream.Play.Start":
+					LOGGER.debug("NetStream.Publish.Start for broadcast stream {0}", [stream]);
+					LOGGER.debug("Dispatching start viewing event");
+
+					// TODO - do we want to know when a client has displayed ds view?
+					// var dispatcher: Dispatcher  = new Dispatcher();
+					// var viewEvent:StartedViewingEvent = new StartedViewingEvent(StartedViewingEvent.STARTED_VIEWING_EVENT);
+
+					// viewEvent.stream = stream;
+					// dispatcher.dispatchEvent(viewEvent);
+					break;
+				case "NetStream.Play.UnpublishNotify":
+					LOGGER.debug("NetStream.Play.UnpublishNotify for broadcast stream {0}", [stream]);
+					stopViewing();
+					break;
+				}
+			}
+
+			public function onBWCheck(... rest):Number {
+				return 0;
+			}
+
+			public function onBWDone(... rest):void {
+				var p_bw:Number;
+				if (rest.length > 0) p_bw = rest[0];
+				// your application should do something here
+				// when the bandwidth check is complete
+				trace("bandwidth = " + p_bw + " Kbps.");
+			}
+
+			public function getPrefferedPosition():String{
+				return MainCanvas.DESKTOP_SHARING_VIEW;
+			}
+
+			/**
+			 * resizes the desktop sharing video to fit to this window
+			 */
+			private function fitToWindow():void{
+				if (videoIsSmallerThanWindow()) {
+					fitWindowToVideo();
+				}
+
+				// Ignore if we are displaying the actual size of the video
+				if (! btnActualSize.selected) {
+					fitVideoToWindow();
+				}
+			}
+
+			private function fitVideoToWindow():void {
+				if (this.width < this.height) {
+					fitToWidthAndAdjustHeightToMaintainAspectRatio();
+				} else {
+					fitToHeightAndAdjustWidthToMaintainAspectRatio();
+				}
+			}
+
+			private function fitWindowToVideo():void {
+				video.width = videoWidth;
+				videoHolder.width = videoWidth;
+				video.height = videoHeight;
+				videoHolder.height = videoHeight;
+				this.height = videoHeight + VIDEO_HEIGHT_PADDING;
+				this.width = videoWidth + VIDEO_WIDTH_PADDING;
+			}
+
+			private function videoIsSmallerThanWindow():Boolean {
+				return (videoHeight < this.height) && (videoWidth < this.width);
+			}
+
+
+			private function fitToWidthAndAdjustHeightToMaintainAspectRatio():void {
+					video.width = this.width - VIDEO_WIDTH_PADDING;
+					videoHolder.width = video.width;
+					// Maintain aspect-ratio
+					video.height = (videoHeight * video.width) / videoWidth;
+					videoHolder.height = video.height;
+					this.height = video.height + VIDEO_HEIGHT_PADDING;
+			}
+
+			private function fitToHeightAndAdjustWidthToMaintainAspectRatio():void {
+					video.height = this.height - VIDEO_HEIGHT_PADDING;
+					videoHolder.height = video.height;
+					// Maintain aspect-ratio
+					video.width = (videoWidth * video.height) / videoHeight;
+					videoHolder.width = video.width;
+					this.width = video.width + VIDEO_WIDTH_PADDING;
+			}
+
+			/**
+			 * resizes the desktop sharing video to actual video resolution
+			 */
+			private function fitToActualSize():void{
+				if (videoIsSmallerThanWindow()) {
+					fitWindowToVideo();
+				} else {
+					video.width = videoWidth;
+					videoHolder.width = videoWidth;
+					video.height = videoHeight;
+					videoHolder.height = videoHeight;
+				}
+			}
+
+			private function determineHowToDisplayVideo():void {
+				if (btnActualSize.selected) {
+					fitToActualSize();
+					btnActualSize.toolTip = ResourceUtil.getInstance().getString('bbb.desktopView.fitToWindow');
+					btnActualSize.label = ResourceUtil.getInstance().getString('bbb.desktopView.fitToWindow');
+				} else {
+					fitToWindow();
+					btnActualSize.toolTip = ResourceUtil.getInstance().getString('bbb.desktopView.actualSize');
+					btnActualSize.label = ResourceUtil.getInstance().getString('bbb.desktopView.actualSize');
+				}
+			}
+
+			private function closeWindow():void {
+				dispatchEvent(new ViewWindowEvent(ViewWindowEvent.CLOSE));
+			}
+
+			override protected function resourcesChanged():void{
+				super.resourcesChanged();
+				this.title = ResourceUtil.getInstance().getString('bbb.desktopView.title');
+
+				if (windowControls != null) {
+					minimizeBtn.toolTip = ResourceUtil.getInstance().getString("bbb.window.minimizeBtn.toolTip");
+					minimizeBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.desktopView.minimizeBtn.accessibilityName");
+
+					maximizeRestoreBtn.toolTip = ResourceUtil.getInstance().getString("bbb.window.maximizeRestoreBtn.toolTip");
+					maximizeRestoreBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.desktopView.maximizeRestoreBtn.accessibilityName");
+
+					closeBtn.toolTip = ResourceUtil.getInstance().getString("bbb.window.closeBtn.toolTip");
+					closeBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.desktopView.closeBtn.accessibilityName");
+				}
+			}
+
+			private function localeChanged(e:Event):void{
+				resourcesChanged();
+			}
+
+		]]>
+	</mx:Script>
+
+	<common:TabIndexer startIndex="{dsOptions.baseTabIndex + 1}" tabIndices="{[minimizeBtn, maximizeRestoreBtn, closeBtn, btnActualSize]}"/>
+
+	<mx:HBox id="bottomBar" visible="true" height="30" horizontalAlign="center" paddingTop="0" paddingBottom="0" width="100%" >
+		<mx:Button id="btnActualSize" paddingTop="0" paddingBottom="0" styleName="deskshareControlButtonStyle"
+				   toggle="true"
+				   click="determineHowToDisplayVideo()"
+				   selected="false"
+				   height="90%"
+				   label="{btnActualSize.selected ? ResourceUtil.getInstance().getString('bbb.desktopView.fitToWindow') : ResourceUtil.getInstance().getString('bbb.desktopView.actualSize')}"
+				   toolTip="{btnActualSize.selected ? ResourceUtil.getInstance().getString('bbb.desktopView.fitToWindow') : ResourceUtil.getInstance().getString('bbb.desktopView.actualSize')}"/>
+	</mx:HBox>
+</CustomMdiWindow>
diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as
index 2892eeb7f01d1a1e19d4cdfb2432cf5c60afe72c..04a0eb0151bdf2669dfffa3544dbf0462bf4f75b 100644
--- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as
+++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as
@@ -53,8 +53,8 @@ package org.bigbluebutton.modules.users.services
   import org.bigbluebutton.modules.present.events.RemovePresentationEvent;
   import org.bigbluebutton.modules.present.events.UploadEvent;
   import org.bigbluebutton.modules.users.events.MeetingMutedEvent;
-  import org.bigbluebutton.modules.deskshare.events.ViewStreamEvent;
-  import org.bigbluebutton.modules.deskshare.events.WebRTCViewStreamEvent;
+  import org.bigbluebutton.modules.screenshare.events.ViewStreamEvent;
+  import org.bigbluebutton.modules.screenshare.events.WebRTCViewStreamEvent;
   import org.bigbluebutton.main.api.JSLog;
   import org.bigbluebutton.modules.users.events.MeetingMutedEvent;
 
@@ -175,6 +175,8 @@ package org.bigbluebutton.modules.users.services
 
     private function handleDeskShareRTMPBroadcastNotification(msg:Object):void {
       LOGGER.debug("*** handleDeskShareRTMPBroadcastNotification **** \n", [msg]);
+      JSLog.warn("*** handleDeskShareRTMPBroadcastNotification **** url=", msg.rtmpUrl);
+      JSLog.warn("*** handleDeskShareRTMPBroadcastNotification **** broadcasting=", msg.broadcasting);
 
       var event:WebRTCViewStreamEvent;
       if (msg.broadcasting) {
diff --git a/bigbluebutton-html5/client/main.jsx b/bigbluebutton-html5/client/main.jsx
index 8fdad251e000d148aa694f4fbcd11fd3881935c2..95002c1a2b43020a592d46d8e5560458de8b1f7b 100755
--- a/bigbluebutton-html5/client/main.jsx
+++ b/bigbluebutton-html5/client/main.jsx
@@ -32,8 +32,9 @@ function loadLib(libname, success, fail) {
     }
   };
 
-  const failCallback = function (cb) {
-    console.log(`failed to load lib - ${this}`);
+  const failCallback = function (cb, issue) {
+    console.error(`failed to load lib - ${this}`);
+    console.error(issue);
     if (typeof (cb) == 'function' || cb instanceof Function) {
       cb();
     }
@@ -49,12 +50,9 @@ Meteor.startup(() => {
   loadLib('bbblogger.js');
   loadLib('jquery.json-2.4.min.js');
   loadLib('jquery.FSRTC.js');
-  loadLib('jquery.verto.js', function () {
-    loadLib('verto_extension.js');
-  });
-
+  loadLib('jquery.verto.js');
+  loadLib('verto_extension.js');
   loadLib('jquery.jsonrpcclient.js');
-  loadLib('verto_extension_share.js');
 
   render((
     <IntlProvider locale={locale} messages={messages}>
diff --git a/bigbluebutton-html5/config.js b/bigbluebutton-html5/config.js
index 3f5bfed1ec7a5d2d1bb0a395d006e9127c33af3b..78a422057ad553369befa83ccab8746a6096862e 100755
--- a/bigbluebutton-html5/config.js
+++ b/bigbluebutton-html5/config.js
@@ -58,14 +58,6 @@ clientConfig.media = {};
 
 clientConfig.media.WebRTCHangupRetryInterval = 2000;
 
-// IP address of FreeSWITCH server for use of mod_verto and WebRTC deshsharing
-clientConfig.media.vertoServerAddress = 'HOST';
-
-// Allows a caller to access a FreeSWITCH dialplan
-clientConfig.media.freeswitchProfilePassword = '1234';
-
-clientConfig.media.vertoPort = '8082';
-
 // specifies whether to use SIP.js for audio over mod_verto
 clientConfig.media.useSIPAudio = true;
 
diff --git a/bigbluebutton-html5/imports/api/phone/index.js b/bigbluebutton-html5/imports/api/phone/index.js
index 47a9294a49f359d8bbd8ba4f2e8fcd71c8ba8efa..af4b49c0b82cf592ff1ccf20ed870cabb4de74a7 100755
--- a/bigbluebutton-html5/imports/api/phone/index.js
+++ b/bigbluebutton-html5/imports/api/phone/index.js
@@ -3,7 +3,7 @@ import Meetings from '/imports/api/meetings';
 import Auth from '/imports/ui/services/auth';
 import {callServer} from '/imports/ui/services/api';
 import {clientConfig} from '/config';
-import {createVertoUserName, joinVertoAudio} from '/imports/api/verto';
+import {vertoExitAudio, vertoJoinListenOnly, vertoJoinMicrophone} from '/imports/api/verto';
 
 let triedHangup = false;
 
@@ -18,10 +18,9 @@ function amIListenOnly() {
 
 // Periodically check the status of the WebRTC call, when a call has been established attempt to
 // hangup, retry if a call is in progress, send the leave voice conference message to BBB
-function exitVoiceCall(afterExitCall) {
+function exitAudio(afterExitCall) {
   if (!clientConfig.media.useSIPAudio) {
-    window.leaveWebRTCVoiceConference_verto();
-    window.cur_call = null;
+    vertoExitAudio();
     return;
   } else {
     // To be called when the hangup is initiated
@@ -72,7 +71,7 @@ function exitVoiceCall(afterExitCall) {
 }
 
 // join the conference. If listen only send the request to the server
-function joinVoiceCall(options) {
+function joinVoiceCallSIP(options) {
   const extension = getVoiceBridge();
   console.log(options);
   if (clientConfig.media.useSIPAudio) {
@@ -82,10 +81,6 @@ function joinVoiceCall(options) {
       console.log('Beginning WebRTC Conference Call');
     };
 
-    if (options.isListenOnly) {
-      callServer('listenOnlyRequestToggle', true);
-    }
-
     window.BBB = {};
     window.BBB.getMyUserInfo = function (callback) {
       const uid = Auth.userID;
@@ -104,12 +99,24 @@ function joinVoiceCall(options) {
 
     callIntoConference(extension, function () {}, options.isListenOnly);
     return;
+  }
+}
+
+function joinListenOnly() {
+  callServer('listenOnlyRequestToggle', true);
+  if (clientConfig.media.useSIPAudio) {
+    joinVoiceCallSIP({ isListenOnly: true });
+  } else {
+    vertoJoinListenOnly();
+  }
+}
+
+function joinMicrophone() {
+  if (clientConfig.media.useSIPAudio) {
+    joinVoiceCallSIP({ isListenOnly: false });
   } else {
-    const conferenceUsername = createVertoUserName();
-    conferenceIdNumber = '1009';
-    joinVertoAudio({ extension, conferenceUsername, conferenceIdNumber,
-      listenOnly: options.isListenOnly, });
+    vertoJoinMicrophone();
   }
 }
 
-export { joinVoiceCall, exitVoiceCall, getVoiceBridge, };
+export { joinListenOnly, joinMicrophone, exitAudio, getVoiceBridge, };
diff --git a/bigbluebutton-html5/imports/api/verto/index.js b/bigbluebutton-html5/imports/api/verto/index.js
index 703c7aa19e7d4f3e770e7edc59415c7154b7a317..9bae27ad39bdae8a8f3e485138ea55e7d9f591bd 100755
--- a/bigbluebutton-html5/imports/api/verto/index.js
+++ b/bigbluebutton-html5/imports/api/verto/index.js
@@ -1,3 +1,5 @@
+import {getInStorage} from '/imports/ui/components/app/service';
+import Users from '/imports/api/users';
 import Auth from '/imports/ui/services/auth';
 import { clientConfig } from '/config';
 import { getVoiceBridge } from '/imports/api/phone';
@@ -9,61 +11,50 @@ function createVertoUserName() {
   return conferenceUsername;
 }
 
-function joinVertoAudio(options) {
-  joinVertoCall(options);
+function vertoExitAudio() {
+  window.vertoExitAudio();
 }
 
-function watchVertoVideo(options) {
-  joinVertoCall(options);
+function vertoJoinListenOnly() {
+  window.vertoJoinListenOnly(
+    'remote-media',
+    getVoiceBridge(),
+    createVertoUserName(),
+    null,
+  );
 }
 
-function joinVertoCall(options) {
-  console.log('joinVertoCall');
-  const extension = options.extension || getVoiceBridge();
-
-  if (!isWebRTCAvailable()) {
-    return;
-  }
-
-  if (!clientConfig.useSIPAudio) {
-    const vertoServerCredentials = {
-      vertoPort: clientConfig.media.vertoPort,
-      hostName: clientConfig.media.vertoServerAddress,
-      login: conferenceIdNumber,
-      password: clientConfig.media.freeswitchProfilePassword,
-    };
-
-    let wasCallSuccessful = false;
-    let conferenceUsername = createVertoUserName();
-    let debuggerCallback = function (message) {
-      console.log('CALLBACK: ' + JSON.stringify(message));
-
-      //
-      // Beginning of hacky method to make Firefox media calls succeed.
-      // Always fail the first time. Retry on failure.
-      //
-      if (!!navigator.mozGetUserMedia && message.errorcode == 1001) {
-        const logCallback = function (m) {
-          console.log('CALLBACK: ' + JSON.stringify(m));
-        };
-
-        callIntoConference_verto(extension, conferenceUsername, conferenceIdNumber, logCallback,
-          'webcam', options, vertoServerCredentials);
-      }
+function vertoJoinMicrophone() {
+  window.vertoJoinMicrophone(
+    'remote-media',
+    getVoiceBridge(),
+    createVertoUserName(),
+    null,
+  );
+}
 
-      //
-      // End of hacky method
-      //
-    };
+function vertoWatchVideo() {
+  window.vertoWatchVideo(
+    'deskshareVideo',
+    getVoiceBridge(),
+    createVertoUserName(),
+    null,
+  );
+}
 
-    callIntoConference_verto(extension, conferenceUsername, conferenceIdNumber, debuggerCallback,
-      'webcam', options, vertoServerCredentials);
-    return;
-  }
+function shareVertoScreen() {
+  vertoManager.shareScreen(
+    'deskshareVideo',
+    getVoiceBridge(),
+    createVertoUserName(),
+    null,
+  );
 }
 
 export {
-  createVertoUserName,
-  joinVertoAudio,
-  watchVertoVideo,
+  vertoJoinListenOnly,
+  vertoJoinMicrophone,
+  vertoWatchVideo,
+  vertoExitAudio,
+  shareVertoScreen,
 };
diff --git a/bigbluebutton-html5/imports/ui/components/deskshare/component.jsx b/bigbluebutton-html5/imports/ui/components/deskshare/component.jsx
index 9e4a16c8296c50685ac11ac90fab3f1143946fd0..162bab54ccfb10a16270774cabb36cc0cf3c7e65 100755
--- a/bigbluebutton-html5/imports/ui/components/deskshare/component.jsx
+++ b/bigbluebutton-html5/imports/ui/components/deskshare/component.jsx
@@ -3,7 +3,7 @@ import React from 'react';
 export default class DeskshareComponent extends React.Component {
   render() {
     return (
-      <video id="webcam" style={{ height: '100%', width: '100%', }} controls/>
+      <video id="deskshareVideo" style={{ height: '100%', width: '100%', }} controls/>
     );
   }
 };
diff --git a/bigbluebutton-html5/imports/ui/components/deskshare/service.js b/bigbluebutton-html5/imports/ui/components/deskshare/service.js
index 0d1826f5fa0a18aabfe41399fda38341240ab3ca..99145dd25837b7492294f2547cccd215a48b5e4b 100755
--- a/bigbluebutton-html5/imports/ui/components/deskshare/service.js
+++ b/bigbluebutton-html5/imports/ui/components/deskshare/service.js
@@ -1,5 +1,5 @@
 import Deskshare from '/imports/api/deskshare';
-import {createVertoUserName, joinVertoAudio, watchVertoVideo} from '/imports/api/verto';
+import {createVertoUserName, watchVertoVideo} from '/imports/api/verto';
 import Auth from '/imports/ui/services/auth';
 import {getVoiceBridge} from '/imports/api/phone';
 
@@ -27,14 +27,6 @@ function videoIsBroadcasting() {
   }
 }
 
-function watchDeskshare(options) {
-  const extension = options.extension || getVoiceBridge();
-  const conferenceUsername = createVertoUserName();
-  conferenceIdNumber = '1009';
-  watchVertoVideo({ extension, conferenceUsername, conferenceIdNumber,
-    watchOnly: true, });
-}
-
 // if remote deskshare has been ended disconnect and hide the video stream
 function presenterDeskshareHasEnded() {
   // exitVoiceCall();
@@ -42,13 +34,9 @@ function presenterDeskshareHasEnded() {
 
 // if remote deskshare has been started connect and display the video stream
 function presenterDeskshareHasStarted() {
-  const voiceBridge = Deskshare.findOne().deskshare.voiceBridge;
-  watchDeskshare({
-    watchOnly: true,
-    extension: voiceBridge,
-  });
+  vertoWatchVideo();
 };
 
-export { videoIsBroadcasting, watchDeskshare, presenterDeskshareHasEnded,
-  presenterDeskshareHasStarted
+export {
+  videoIsBroadcasting, presenterDeskshareHasEnded, presenterDeskshareHasStarted
 };
diff --git a/bigbluebutton-html5/imports/ui/components/modals/settings/submenus/AudioMenu.jsx b/bigbluebutton-html5/imports/ui/components/modals/settings/submenus/AudioMenu.jsx
index 6c48e8db2f2429cfa3647911e1e8dd2c1fa3d2b1..f7cfaad2058513e54efcf5de2303572c53861a68 100755
--- a/bigbluebutton-html5/imports/ui/components/modals/settings/submenus/AudioMenu.jsx
+++ b/bigbluebutton-html5/imports/ui/components/modals/settings/submenus/AudioMenu.jsx
@@ -3,7 +3,7 @@ import Modal from 'react-modal';
 import Icon from '/imports/ui/components/icon/component';
 import Button from '/imports/ui/components/button/component';
 import BaseMenu from './BaseMenu';
-import {joinVoiceCall, exitVoiceCall} from '/imports/api/phone';
+import {joinListenOnly, joinMicrophone, exitAudio} from '/imports/api/phone';
 
 export default class AudioMenu extends BaseMenu {
   constructor(props) {
@@ -14,24 +14,6 @@ export default class AudioMenu extends BaseMenu {
     return (
       <div>
         <p>inside audio menu</p>
-
-        <button onClick={
-          function () {
-            exitVoiceCall(function () {console.log('exit callback');});
-          }
-        }>exit voice call</button>
-        <br/>
-        <button onClick={
-          function () {
-            joinVoiceCall({ isListenOnly: true });
-          }
-        }>listen only</button>
-
-        <button onClick={
-          function () {
-            joinVoiceCall({ isListenOnly: false });
-          }
-        }>join mic</button>
       </div>
     );
   }
diff --git a/video-broadcast/build.gradle b/video-broadcast/build.gradle
index c0df10b2ac9bac5943d6fe95aed0cc139f36b2af..2982d5f96f8887d9f5e95750d1efaecf949afaad 100755
--- a/video-broadcast/build.gradle
+++ b/video-broadcast/build.gradle
@@ -59,7 +59,7 @@ dependencies {
 
 	//redis
 	compile 'redis.clients:jedis:2.0.0'
-	providedCompile 'commons-pool:commons-pool:1.5.6'
+	compile 'commons-pool:commons-pool:1.5.6'
 	compile 'com.google.code.gson:gson:1.7.1'
 
 	compile 'org.bigbluebutton:bbb-common-message:0.0.18-SNAPSHOT'
diff --git a/video-broadcast/src/main/java/org/bigbluebutton/app/videobroadcast/set-perm-video-broadcast.sh b/video-broadcast/src/main/java/org/bigbluebutton/app/videobroadcast/set-perm-video-broadcast.sh
new file mode 100644
index 0000000000000000000000000000000000000000..a540f868aefe30e316065faf3a40f9d0e19b36d0
--- /dev/null
+++ b/video-broadcast/src/main/java/org/bigbluebutton/app/videobroadcast/set-perm-video-broadcast.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+
+cd /var/lib/red5/webapps
+echo 'Starting with:'
+ls -dl video-broadcast
+sudo chown root:root video-broadcast
+sudo chmod -R 777 video-broadcast
+
+cd video-broadcast
+
+ls -al
+sudo chown root:root META-INF
+sudo chown red5:red5 streams
+sudo chown root:root WEB-INF
+
+echo 'After the procedure:'
+ls -al