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 8c9811d1c0..957eca1d1f 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 a3986b2452..9fd760f88e 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 211ce68b02..508011e47f 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 ced4ccc8d1..8d5e313e3f 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 dfb42b5bd5..72d17227ea 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 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) + 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) - } - - 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)) - } - } - } 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 41bc576fe5..241bde0f85 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 ef901d3464..97bf4c3100 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 e79b78e64f..73219f1b2f 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 @@ - + - - - - - - - - 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 eb61209ed1..fce3a678b1 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 @@ - - + + - + 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 feac5d62a0..a8c45063fd 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 51db6186dc..fc0375dc84 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 093d326a93..d7e52643f3 100755 --- a/bigbluebutton-client/resources/config.xml.template +++ b/bigbluebutton-client/resources/config.xml.template @@ -42,13 +42,15 @@ baseTabIndex="301" /> - diff --git a/bigbluebutton-client/resources/prod/BigBlueButton.html b/bigbluebutton-client/resources/prod/BigBlueButton.html index c2bdad7cbf..c107caf3ff 100755 --- a/bigbluebutton-client/resources/prod/BigBlueButton.html +++ b/bigbluebutton-client/resources/prod/BigBlueButton.html @@ -105,7 +105,6 @@ - diff --git a/bigbluebutton-client/resources/prod/lib/Screen-Capturing.js b/bigbluebutton-client/resources/prod/lib/Screen-Capturing.js index 42526d4b9d..7761a70ecb 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 13fa548a6d..8c6b625a8e 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 6f4f07c72b..267eef8c8c 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 0fd9fb846a..ffb66a80dd 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 = "

" + - "Video Layout Canvas " + (j+1) + - " " + + "Video Layout Canvas " + (j+1) + + " " + "

"; 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 = "
"; @@ -1515,7 +1524,7 @@ "" + "" + ""; - + if (confMan.params.hasVid) { html += "

Video Controls
"; @@ -1530,14 +1539,14 @@ "" + "" + "" + - + "
" + - + "" + "" + ""; } - + html += "
" + "" + @@ -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 19037f9938..3bed5ec621 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); - }; - } -} +Verto.normalizeCallback = function (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; +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; + } +}; - 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); -} +Verto.prototype.registerCallbacks = function () { + var callbacks = { + onMessage: function () {}, -// 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"); + onDialogState: function (d) {}, - 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); - } -} + onWSLogin: this.onWSLogin.bind(this), -checkSupport = function(callback) { - if(!isWebRTCAvailable_verto()) { - callback({'status': 'failed', 'errorcode': 1003}); // Browser version not supported - } + onWSClose: function (v, success) { + cur_call = null; + if (this.callWasSuccessful) { + // the connection was dropped in an already established call + this.logError('websocket disconnected'); - if (!navigator.getUserMedia) { - navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; - } + // 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'); - if (!navigator.getUserMedia){ - callback({'status': 'failed', 'errorcode': '10XX'}); // getUserMedia not supported in this browser - } -} + // Could not make a WebSocket connection + this.logError({ status: 'failed', errorcode: 1002 }); + this.onFail(); + return; + } + }.bind(this), + }; + this.callbacks = callbacks; +}; -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; - }); -} +Verto.prototype.hold = function () { + this.cur_call.toggleHold(); +}; -docall_verto = function(extension, conferenceUsername, conferenceIdNumber, callbacks, options) { - console.log(extension + ", " + conferenceUsername + ", " + conferenceIdNumber); +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 (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; - } + 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; + } - 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 - }); + // 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 }); + } +}; - if (callbacks != null) { // add user supplied callbacks to the current call - cur_call.rtc.options.callbacks = $.extend(cur_call.rtc.options.callbacks, callbacks); - } -} +Verto.prototype.mute = function () { + this.cur_call.dtmf('0'); +}; -// 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); -} +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()); + // } +}; -// 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.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); - } +Verto.prototype.isWebRTCAvailable = function () { + return (typeof window.webkitRTCPeerConnection !== 'undefined' || + typeof window.mozRTCPeerConnection !== 'undefined'); }; -this.RTCPeerConnectionCallbacks = RTCPeerConnectionCallbacks; -window.verto_afterStreamPublish = function() {} +this.VertoManager = function () { + this.vertoAudio = null; + this.vertoVideo = null; + this.vertoScreenShare = null; + window.vertoHandle = null; +}; -function webrtc_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, userCallback, options) { - if (userCallback) { - callback = userCallback; - } - console.log("webrtc_call\n"+voiceBridge + ", " + conferenceUsername + ", " + conferenceIdNumber + ", " + callback); +Verto.prototype.logout = function () { + this.exitAudio(); + this.exitVideo(); + this.exitScreenShare(); + window.vertoHandle.logout(); +}; - if(!isWebRTCAvailable()) { - callback({'status': 'failed', 'errorcode': 1003}); // Browser version not supported - return; - } +VertoManager.prototype.exitAudio = function () { + if (this.vertoAudio != null) { + console.log('Hanging up vertoAudio'); + this.vertoAudio.hangup(); + this.vertoAudio = null; + } +}; - var server = window.document.location.hostname; - console.log("user " + conferenceUsername + " calling to " + voiceBridge); - if (isLoggedIntoVerto()) { - make_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, callback, "", false, options); - } -} +VertoManager.prototype.exitVideo = function () { + if (this.vertoVideo != null) { + console.log('Hanging up vertoVideo'); + this.vertoVideo.hangup(); + this.vertoVideo = null; + } +}; -function webrtc_hangup_verto(userCallback) { - if (userCallback) { - callback = userCallback; - } - callPurposefullyEnded = true; - console.log("Hanging up current session"); - if(verto) { - verto.hangup(false, callback); - } -} +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 79a5606cb9..0000000000 --- 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 606745dfc9..1c91614083 100755 --- a/bigbluebutton-client/src/ScreenshareModule.mxml +++ b/bigbluebutton-client/src/ScreenshareModule.mxml @@ -20,13 +20,14 @@ with BigBlueButton; if not, see . --> - - + - + + . +* +*/ +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 0000000000..081a3f11c6 --- /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 0000000000..33119e200a --- /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 . +* +*/ +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 0000000000..17301cec7e --- /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 . +* +*/ +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 0000000000..bccef0ad9d --- /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 . +* +*/ +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 0000000000..ff9ffa75e0 --- /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 . +* +*/ +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 45a876e560..ea9b21e4ea 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 handleRequestStopSharingEvent():void { @@ -187,8 +196,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 0000000000..f183f04db7 --- /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 . +* +*/ + +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 0000000000..f4f966979b --- /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 . +* +*/ + +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 0000000000..752dc766c3 --- /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 . +* +*/ + +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 18c42c875a..8bec2a5582 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/maps/ScreenshareEventMap.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/maps/ScreenshareEventMap.mxml @@ -43,6 +43,8 @@ with BigBlueButton; if not, see . 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; ]]> @@ -110,5 +112,12 @@ with BigBlueButton; if not, see . - + + + + + + + + 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 0000000000..6ffaa3ed24 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/maps/WebRTCDeskshareEventMap.mxml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 0813997de0..6b02236b87 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 0000000000..f6513ee624 --- /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 . +* +*/ +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 0000000000..a6965b457b --- /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 . +* +*/ + +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 0000000000..d2e60b0555 --- /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 . +* +*/ +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 0000000000..1aa35b662c --- /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 . +* +*/ + +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 0000000000..9d4fd5a2d2 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/view/components/WebRTCDesktopPublishWindow.mxml @@ -0,0 +1,384 @@ + + + + + + + + + + + + + + + + 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(); + } + ]]> + + + + + + + + + + + + + + + + + + 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 0000000000..f035b557b0 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/screenshare/view/components/WebRTCDesktopViewWindow.mxml @@ -0,0 +1,388 @@ + + + + + + + + + + + 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(); + } + + ]]> + + + + + + + + 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 2892eeb7f0..04a0eb0151 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 8fdad251e0..95002c1a2b 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(( diff --git a/bigbluebutton-html5/config.js b/bigbluebutton-html5/config.js index 3f5bfed1ec..78a422057a 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 47a9294a49..af4b49c0b8 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; - } else { - const conferenceUsername = createVertoUserName(); - conferenceIdNumber = '1009'; - joinVertoAudio({ extension, conferenceUsername, conferenceIdNumber, - listenOnly: options.isListenOnly, }); } } -export { joinVoiceCall, exitVoiceCall, getVoiceBridge, }; +function joinListenOnly() { + callServer('listenOnlyRequestToggle', true); + if (clientConfig.media.useSIPAudio) { + joinVoiceCallSIP({ isListenOnly: true }); + } else { + vertoJoinListenOnly(); + } +} + +function joinMicrophone() { + if (clientConfig.media.useSIPAudio) { + joinVoiceCallSIP({ isListenOnly: false }); + } else { + vertoJoinMicrophone(); + } +} + +export { joinListenOnly, joinMicrophone, exitAudio, getVoiceBridge, }; diff --git a/bigbluebutton-html5/imports/api/verto/index.js b/bigbluebutton-html5/imports/api/verto/index.js index 703c7aa19e..9bae27ad39 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(); +function vertoJoinMicrophone() { + window.vertoJoinMicrophone( + 'remote-media', + getVoiceBridge(), + createVertoUserName(), + null, + ); +} - if (!isWebRTCAvailable()) { - return; - } +function vertoWatchVideo() { + window.vertoWatchVideo( + 'deskshareVideo', + getVoiceBridge(), + createVertoUserName(), + null, + ); +} - 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); - } - - // - // End of hacky method - // - }; - - 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 9e4a16c829..162bab54cc 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 ( -