Merge branch 'merge-webrtc-screenshare-2' of https://github.com/perroned/bigbluebutton into perroned-merge-webrtc-screenshare-2

This commit is contained in:
Richard Alam 2016-08-09 14:26:40 +00:00
commit dcd226dc9f
50 changed files with 2941 additions and 1068 deletions

View File

@ -114,7 +114,7 @@ public interface IBigBlueButtonInGW {
void editCaptionHistory(String meetingID, String userID, Integer startIndex, Integer endIndex, String locale, String text); void editCaptionHistory(String meetingID, String userID, Integer startIndex, Integer endIndex, String locale, String text);
// DeskShare // 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 deskShareStopped(String conferenceName, String callerId, String callerIdName);
void deskShareRTMPBroadcastStarted(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp); void deskShareRTMPBroadcastStarted(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp);
void deskShareRTMPBroadcastStopped(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp); void deskShareRTMPBroadcastStopped(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp);

View File

@ -37,11 +37,6 @@ class BigBlueButtonActor(val system: ActorSystem,
case msg: UserMutedInVoiceConfMessage => handleUserMutedInVoiceConfMessage(msg) case msg: UserMutedInVoiceConfMessage => handleUserMutedInVoiceConfMessage(msg)
case msg: UserTalkingInVoiceConfMessage => handleUserTalkingInVoiceConfMessage(msg) case msg: UserTalkingInVoiceConfMessage => handleUserTalkingInVoiceConfMessage(msg)
case msg: VoiceConfRecordingStartedMessage => handleVoiceConfRecordingStartedMessage(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 case _ => // do nothing
} }
@ -151,6 +146,7 @@ class BigBlueButtonActor(val system: ActorSystem,
/** Subscribe to meeting and voice events. **/ /** Subscribe to meeting and voice events. **/
eventBus.subscribe(m.actorRef, m.mProps.meetingID) eventBus.subscribe(m.actorRef, m.mProps.meetingID)
eventBus.subscribe(m.actorRef, m.mProps.voiceBridge) eventBus.subscribe(m.actorRef, m.mProps.voiceBridge)
eventBus.subscribe(m.actorRef, m.mProps.deskshareBridge)
meetings += m.mProps.meetingID -> m meetings += m.mProps.meetingID -> m
outGW.send(new MeetingCreated(m.mProps.meetingID, m.mProps.externalMeetingID, m.mProps.recorded, m.mProps.meetingName, 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)) 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 }
}
} }

View File

@ -60,6 +60,7 @@ class BigBlueButtonInGW(
msg.payload.name, msg.payload.name,
msg.payload.record, msg.payload.record,
msg.payload.voiceConfId, msg.payload.voiceConfId,
msg.payload.voiceConfId + "-DESKSHARE", // WebRTC Desktop conference id
msg.payload.durationInMinutes, msg.payload.durationInMinutes,
msg.payload.autoStartRecording, msg.payload.autoStartRecording,
msg.payload.allowStartStopRecording, msg.payload.allowStartStopRecording,
@ -498,8 +499,11 @@ class BigBlueButtonInGW(
* Message Interface for DeskShare * Message Interface for DeskShare
* ***************************************************************** * *****************************************************************
*/ */
def deskShareStarted(meetingId: String, callerId: String, callerIdName: String) { def deskShareStarted(confId: String, callerId: String, callerIdName: String) {
eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareStartedRequest(meetingId, callerId, callerIdName))) println("____BigBlueButtonInGW::deskShareStarted " + confId + callerId + " " +
callerIdName)
eventBus.publish(BigBlueButtonEvent(confId, new DeskShareStartedRequest(confId, callerId,
callerIdName)))
} }
def deskShareStopped(meetingId: String, callerId: String, callerIdName: String) { def deskShareStopped(meetingId: String, callerId: String, callerIdName: String) {

View File

@ -163,4 +163,80 @@ class LiveMeeting(val mProps: MeetingProperties,
def permissionsEqual(other: Permissions): Boolean = { def permissionsEqual(other: Permissions): Boolean = {
meetingModel.permissionsEqual(other) 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))
}
}
} }

View File

@ -178,77 +178,13 @@ class MeetingActor(val mProps: MeetingProperties,
case msg: UpdateCaptionOwnerRequest => liveMeeting.handleUpdateCaptionOwnerRequest(msg) case msg: UpdateCaptionOwnerRequest => liveMeeting.handleUpdateCaptionOwnerRequest(msg)
case msg: EditCaptionHistoryRequest => liveMeeting.handleEditCaptionHistoryRequest(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 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))
}
}
} }

View File

@ -5,9 +5,9 @@ import java.util.concurrent.TimeUnit
case object StopMeetingActor case object StopMeetingActor
case class MeetingProperties(meetingID: String, externalMeetingID: String, meetingName: String, recorded: Boolean, case class MeetingProperties(meetingID: String, externalMeetingID: String, meetingName: String, recorded: Boolean,
voiceBridge: String, duration: Int, autoStartRecording: Boolean, allowStartStopRecording: Boolean, voiceBridge: String, deskshareBridge: String, duration: Int, autoStartRecording: Boolean,
moderatorPass: String, viewerPass: String, createTime: Long, createDate: String, allowStartStopRecording: Boolean, moderatorPass: String, viewerPass: String, createTime: Long,
red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean) createDate: String, red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean)
case class MeetingExtensionProp(maxExtensions: Int = 2, numExtensions: Int = 0, extendByMinutes: Int = 20, case class MeetingExtensionProp(maxExtensions: Int = 2, numExtensions: Int = 0, extendByMinutes: Int = 20,
sendNotice: Boolean = true, sent15MinNotice: Boolean = false, sendNotice: Boolean = true, sent15MinNotice: Boolean = false,

View File

@ -7,6 +7,7 @@ trait AppsTestFixtures {
val meetingName = "test meeting" val meetingName = "test meeting"
val record = false val record = false
val voiceConfId = "85115" val voiceConfId = "85115"
val deskshareConfId = "85115-DESKSHARE"
val durationInMinutes = 10 val durationInMinutes = 10
val autoStartRecording = false val autoStartRecording = false
val allowStartStopRecording = false val allowStartStopRecording = false
@ -18,7 +19,7 @@ trait AppsTestFixtures {
val mProps = new MeetingProperties(meetingId, externalMeetingId, val mProps = new MeetingProperties(meetingId, externalMeetingId,
meetingName, record, meetingName, record,
voiceConfId, voiceConfId, deskshareConfId,
durationInMinutes, durationInMinutes,
autoStartRecording, allowStartStopRecording, autoStartRecording, allowStartStopRecording,
moderatorPassword, viewerPassword, moderatorPassword, viewerPassword,

View File

@ -13,20 +13,12 @@
<include> <include>
<context name="default"> <context name="default">
<extension name="public_extensions"> <extension name="public_extensions">
<condition field="destination_number" expression="^\d{5}$"> <condition field="destination_number" expression="^\d{5}-DESKSHARE$">
<action application="log" data="INFO AAAAAA transferring to $1 XML public!!"/> <action application="log" data="INFO AAAAAA transferring to $1 XML public!!"/>
<action application="transfer" data="${destination_number} XML public"/> <action application="transfer" data="${destination_number} XML public"/>
</condition> </condition>
</extension> </extension>
<extension name="public_extensions">
<condition field="destination_number" expression="^(\d{5})(-screen)$">
<action application="log" data="INFO BB $1 $2 BBBB transferring to $1 XML public!!"/>
<action application="transfer" data="$1 XML public"/>
</condition>
</extension>
<!-- other extensions --> <!-- other extensions -->
</context> </context>

View File

@ -16,10 +16,10 @@
<!-- other extensions --> <!-- other extensions -->
<extension name="public_extensions"> <extension name="public_extensions">
<condition field="destination_number" expression="^\d{5}$"> <condition field="destination_number" expression="^\d{5}-DESKSHARE$">
<action application="log" data="INFO ************ redirecting ${destination_number} to ${destination_number}-DESKSHARE@video-mcu-stereo ***********" /> <action application="log" data="INFO ************WEBRTC DESKSHARE EXT***********" />
<action application="answer"/> <action application="answer"/>
<action application="conference" data="${destination_number}-DESKSHARE@video-mcu-stereo"/> <action application="conference" data="${destination_number}@video-mcu-stereo"/>
</condition> </condition>
</extension> </extension>

View File

@ -28,8 +28,6 @@ public class ESLEventListener implements IEslEventListener {
private static final String STOP_RECORDING_EVENT = "stop-recording"; private static final String STOP_RECORDING_EVENT = "stop-recording";
private static final String DESKSHARE_CONFERENCE_NAME_SUFFIX = "-DESKSHARE"; 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; private final ConferenceEventListener conferenceEventListener;
@ -68,53 +66,53 @@ public class ESLEventListener implements IEslEventListener {
String voiceUserId = callerIdName; String voiceUserId = callerIdName;
System.out.println("User joined voice conference, user=[" + callerIdName + "], conf=[" + confName + "]");
Matcher gapMatcher = GLOBAL_AUDION_PATTERN.matcher(callerIdName); Matcher gapMatcher = GLOBAL_AUDION_PATTERN.matcher(callerIdName);
if (gapMatcher.matches()) { if (gapMatcher.matches()) {
System.out.println("Ignoring GLOBAL AUDIO USER [" + callerIdName + "]"); System.out.println("Ignoring GLOBAL AUDIO USER [" + callerIdName + "]");
return; return;
} }
// Deskstop sharing conferences have their name in the form ddddd-DESKSHARE // (WebRTC) Deskstop sharing conferences' name is of the form ddddd-DESKSHARE
// Deskstop sharing conferences have the user with the desktop video displayed in this way: // Voice conferences' name is of the form ddddd
// username (Screen) and usernum (screen) if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX)) {
if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX) && System.out.println("User joined deskshare conference, user=[" + callerIdName + "], " +
callerId.endsWith(DESKSHARE_CALLER_ID_SUFFIX) && "conf=[" + confName + "] callerId=[" + callerId + "]");
callerIdName.endsWith(DESKSHARE_CALLER_NAME_SUFFIX)) {
DeskShareStartedEvent dsStart = new DeskShareStartedEvent(confName, callerId, callerIdName); DeskShareStartedEvent dsStart = new DeskShareStartedEvent(confName, callerId, callerIdName);
conferenceEventListener.handleConferenceEvent(dsStart); 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); System.out.println("User joined voice conference, user=[" + callerIdName + "], conf=[" +
if (matcher.matches()) { confName + "] callerId=[" + callerId + "]");
voiceUserId = matcher.group(1).trim();
callerIdName = matcher.group(2).trim();
}
VoiceUserJoinedEvent pj = new VoiceUserJoinedEvent(voiceUserId, memberId.toString(), confName, callerId, callerIdName, muted, speaking, ""); VoiceUserJoinedEvent pj = new VoiceUserJoinedEvent(voiceUserId, memberId.toString(), confName, callerId, callerIdName, muted, speaking, "");
conferenceEventListener.handleConferenceEvent(pj); conferenceEventListener.handleConferenceEvent(pj);
}
} }
@Override @Override
public void conferenceEventLeave(String uniqueId, String confName, int confSize, EslEvent event) { public void conferenceEventLeave(String uniqueId, String confName, int confSize, EslEvent event) {
Integer memberId = this.getMemberIdFromEvent(event); Integer memberId = this.getMemberIdFromEvent(event);
System.out.println("User left voice conference, user=[" + memberId.toString() + "], conf=[" + confName + "]");
String callerId = this.getCallerIdFromEvent(event); String callerId = this.getCallerIdFromEvent(event);
String callerIdName = this.getCallerIdNameFromEvent(event); String callerIdName = this.getCallerIdNameFromEvent(event);
// Deskstop sharing conferences have their name in the form ddddd-DESKSHARE // (WebRTC) Deskstop sharing conferences' name is of the form ddddd-DESKSHARE
// Deskstop sharing conferences have the user with the desktop video displayed in this way: // Voice conferences' name is of the form ddddd
// username (Screen) and usernum (screen) if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX)) {
if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX) && System.out.println("User left deskshare conference, user=[" + memberId.toString() +
callerId.endsWith(DESKSHARE_CALLER_ID_SUFFIX) && "], " + "conf=[" + confName + "]");
callerIdName.endsWith(DESKSHARE_CALLER_NAME_SUFFIX)) {
DeskShareEndedEvent dsEnd = new DeskShareEndedEvent(confName, callerId, callerIdName); DeskShareEndedEvent dsEnd = new DeskShareEndedEvent(confName, callerId, callerIdName);
conferenceEventListener.handleConferenceEvent(dsEnd); 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 @Override
@ -231,7 +229,8 @@ public class ESLEventListener implements IEslEventListener {
@Override @Override
public void eventReceived(EslEvent event) { 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)) { // if (event.getEventName().equals(FreeswitchHeartbeatMonitor.EVENT_HEARTBEAT)) {
//// setChanged(); //// setChanged();
// notifyObservers(event); // notifyObservers(event);

View File

@ -14,8 +14,7 @@ import org.bigbluebutton.common.messages.DeskShareRTMPBroadcastStoppedEventMessa
class VoiceConferenceService(sender: RedisPublisher) extends IVoiceConferenceService { class VoiceConferenceService(sender: RedisPublisher) extends IVoiceConferenceService {
val FROM_VOICE_CONF_SYSTEM_CHAN = "bigbluebutton:from-voice-conf:system"; val FROM_VOICE_CONF_SYSTEM_CHAN = "bigbluebutton:from-voice-conf:system"
private final val DESKSHARE_CONFERENCE_NAME_SUFFIX = "-DESKSHARE"
def voiceConfRecordingStarted(voiceConfId: String, recordStream: String, recording: java.lang.Boolean, timestamp: String) { def voiceConfRecordingStarted(voiceConfId: String, recordStream: String, recording: java.lang.Boolean, timestamp: String) {
val msg = new VoiceConfRecordingStartedMessage(voiceConfId, recordStream, recording, timestamp) 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, def userJoinedVoiceConf(voiceConfId: String, voiceUserId: String, userId: String, callerIdName: String,
callerIdNum: String, muted: java.lang.Boolean, talking: java.lang.Boolean, avatarURL: 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) val msg = new UserJoinedVoiceConfMessage(voiceConfId, voiceUserId, userId, callerIdName, callerIdNum, muted, talking, avatarURL)
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson()) sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
} }
def userLeftVoiceConf(voiceConfId: String, voiceUserId: String) { 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) val msg = new UserLeftVoiceConfMessage(voiceConfId, voiceUserId)
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson()) 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) { def deskShareStarted(voiceConfId: String, callerIdNum: String, callerIdName: String) {
val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "") println("******** FreeswitchConferenceService send deskShareStarted to BBB " + voiceConfId)
println("******** FreeswitchConferenceService send deskShareStarted to BBB " + trimmedVoiceConfId) val msg = new DeskShareStartedEventMessage(voiceConfId, callerIdNum, callerIdName)
val msg = new DeskShareStartedEventMessage(trimmedVoiceConfId, callerIdNum, callerIdName)
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson()) sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
} }
def deskShareEnded(voiceConfId: String, callerIdNum: String, callerIdName: String) { def deskShareEnded(voiceConfId: String, callerIdNum: String, callerIdName: String) {
val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "") println("******** FreeswitchConferenceService send deskShareStopped to BBB " + voiceConfId)
println("******** FreeswitchConferenceService send deskShareStopped to BBB " + trimmedVoiceConfId) val msg = new DeskShareStoppedEventMessage(voiceConfId, callerIdNum, callerIdName)
val msg = new DeskShareStoppedEventMessage(trimmedVoiceConfId, callerIdNum, callerIdName)
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson()) 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) { 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 " + voiceConfId)
println("******** FreeswitchConferenceService send deskShareRTMPBroadcastStarted to BBB " + trimmedVoiceConfId) val msg = new DeskShareRTMPBroadcastStartedEventMessage(voiceConfId, streamname, vw, vh, timestamp)
val msg = new DeskShareRTMPBroadcastStartedEventMessage(trimmedVoiceConfId, streamname, vw, vh, timestamp)
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson()) 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) { 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 " + voiceConfId)
println("******** FreeswitchConferenceService send deskShareRTMPBroadcastStopped to BBB " + trimmedVoiceConfId) val msg = new DeskShareRTMPBroadcastStoppedEventMessage(voiceConfId, streamname, vw, vh, timestamp)
val msg = new DeskShareRTMPBroadcastStoppedEventMessage(trimmedVoiceConfId, streamname, vw, vh, timestamp)
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson()) sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
} }

View File

@ -42,13 +42,15 @@
baseTabIndex="301" baseTabIndex="301"
/> />
<module name="ScreenshareModule" <module name="ScreenshareModule"
url="http://HOST/client/ScreenshareModule.swf?v=VERSION" url="http://HOST/client/ScreenshareModule.swf?v=VERSION"
uri="rtmp://HOST/screenshare" uri="rtmp://HOST/screenshare"
showButton="true" showButton="true"
autoStart="false" autoStart="false"
autoFullScreen="false" autoFullScreen="false"
baseTabIndex="201" baseTabIndex="201"
useWebRTCIfAvailable="false"
chromeExtensionKey=""
help="http://HOST/client/help/screenshare-help.html" help="http://HOST/client/help/screenshare-help.html"
/> />

View File

@ -105,7 +105,6 @@
<script src="lib/jquery.verto.js" language="javascript"></script> <script src="lib/jquery.verto.js" language="javascript"></script>
<script src="lib/Screen-Capturing.js" language="javascript"></script> <script src="lib/Screen-Capturing.js" language="javascript"></script>
<script src="lib/verto_extension.js" language="javascript"></script> <script src="lib/verto_extension.js" language="javascript"></script>
<script src="lib/verto_extension_share.js" language="javascript"></script>
<script src="lib/bbb_deskshare.js?v=VERSION" language="javascript"></script> <script src="lib/bbb_deskshare.js?v=VERSION" language="javascript"></script>
<script src="lib/bbb_api_bridge.js?v=VERSION" language="javascript"></script> <script src="lib/bbb_api_bridge.js?v=VERSION" language="javascript"></script>

View File

@ -92,8 +92,6 @@ var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
var isChrome = !!window.chrome && !isOpera; var isChrome = !!window.chrome && !isOpera;
function getChromeExtensionStatus(extensionid, callback) { function getChromeExtensionStatus(extensionid, callback) {
callback = normalizeCallback(callback);
if (isFirefox) return callback('not-chrome'); if (isFirefox) return callback('not-chrome');
if (chromeMediaSource == 'desktop') return callback('installed-enabled'); if (chromeMediaSource == 'desktop') return callback('installed-enabled');

View File

@ -103,7 +103,7 @@
if (moz) { if (moz) {
this.constraints = { this.constraints = {
offerToReceiveAudio: true, offerToReceiveAudio: this.options.useSpeak === "none" ? false : true,
offerToReceiveVideo: this.options.useVideo ? true : false, offerToReceiveVideo: this.options.useVideo ? true : false,
}; };
} else { } else {
@ -111,7 +111,7 @@
optional: [{ optional: [{
'DtlsSrtpKeyAgreement': 'true' 'DtlsSrtpKeyAgreement': 'true'
}],mandatory: { }],mandatory: {
OfferToReceiveAudio: true, OfferToReceiveAudio: this.options.useSpeak === "none" ? false : true,
OfferToReceiveVideo: this.options.useVideo ? true : false, OfferToReceiveVideo: this.options.useVideo ? true : false,
} }
}; };
@ -342,8 +342,8 @@
if(typeof self.options.localVideoStream.stop == 'function') { if(typeof self.options.localVideoStream.stop == 'function') {
self.options.localVideoStream.stop(); self.options.localVideoStream.stop();
} else { } else {
if (self.localVideoStream.active){ if (self.options.localVideoStream.active){
var tracks = self.localVideoStream.getTracks(); var tracks = self.options.localVideoStream.getTracks();
console.error(tracks); console.error(tracks);
tracks.forEach(function(track, index){ tracks.forEach(function(track, index){
console.log(track); console.log(track);
@ -513,7 +513,7 @@
audio = false; audio = false;
} else { } else {
audio = { audio = {
mandatory: obj.options.audioParams, mandatory: {},
optional: [] optional: []
}; };
@ -521,6 +521,15 @@
audio.optional = [{sourceId: obj.options.useMic}] 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) { if (obj.options.useVideo && obj.options.localVideo) {
@ -558,8 +567,9 @@
video.optional.push({sourceId: obj.options.useCamera}); video.optional.push({sourceId: obj.options.useCamera});
} }
if (bestFrameRate && !window.moz) { if (bestFrameRate) {
video.optional.push({minFrameRate: bestFrameRate}); video.optional.push({minFrameRate: bestFrameRate});
video.optional.push({maxFrameRate: bestFrameRate});
} }
} else { } else {
@ -603,15 +613,7 @@
onICEComplete: function() { onICEComplete: function() {
return onICEComplete(self); return onICEComplete(self);
}, },
onRemoteStream: screen ? function(stream) { onRemoteStream: screen ? function(stream) {} : function(stream) {
// Added by Dan Perrone (perroned)
// https://github.com/perroned
// Date: January 13, 2016
// Commit: 279f40a4c280bba11052adc621fddbb3135ccb6d
verto_afterStreamPublish();} :
// ----------------------------------------------------
function(stream) {
return onRemoteStream(self, stream); return onRemoteStream(self, stream);
}, },
onOfferSDP: function(sdp) { onOfferSDP: function(sdp) {
@ -633,58 +635,12 @@
function onError(e) { function onError(e) {
onStreamError(self, e); onStreamError(self, e);
} }
var mediaParams = getMediaParams(self); var mediaParams = getMediaParams(self);
console.log("Audio constraints", mediaParams.audio); console.log("Audio constraints", mediaParams.audio);
console.log("Video constraints", mediaParams.video); 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) { if (mediaParams.audio || mediaParams.video) {
getUserMedia({ getUserMedia({
@ -1078,41 +1034,13 @@
var n = navigator, var n = navigator,
media; media;
n.getMedia = n.webkitGetUserMedia || n.mozGetUserMedia; n.getMedia = n.webkitGetUserMedia || n.mozGetUserMedia;
n.getMedia(options.constraints || {
// Added by Dan Perrone (perroned) audio: true,
// https://github.com/perroned video: video_constraints
// Date: February 17, 2016 },
// Commit: 05488dda5d9d1048e286b0bdee27515d217b15a5 streaming, options.onerror ||
function(e) {
var constraints = {}; console.error(e);
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});
}); });
function streaming(stream) { function streaming(stream) {

View File

@ -106,7 +106,7 @@
for (i = 0; i < loops; i++) { for (i = 0; i < loops; i++) {
socket.send("#SPB " + data); socket.send("#SPB " + data);
} }
if (rem) { if (rem) {
socket.send("#SPB " + data); socket.send("#SPB " + data);
} }
@ -418,12 +418,12 @@
var up_kps = (((this.speedBytes * 8) / (this.up_dur / 1000)) / 1024).toFixed(0); 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); var down_kps = (((this.speedBytes * 8) / (this.down_dur / 1000)) / 1024).toFixed(0);
console.info("Speed Test: Up: " + up_kps + " Down: " + down_kps); 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(event, { upDur: this.up_dur, downDur: this.down_dur, upKPS: up_kps, downKPS: down_kps });
this.speedCB = null; this.speedCB = null;
} }
return; return;
} }

View File

@ -132,8 +132,13 @@
} }
}); });
var tag = verto.options.tag;
if (typeof(tag) === "function") {
tag = tag();
}
if (verto.options.ringFile && verto.options.tag) { if (verto.options.ringFile && verto.options.tag) {
verto.ringer = $("#" + verto.options.tag); verto.ringer = $("#" + tag);
} }
verto.rpcClient.call('login', {}); verto.rpcClient.call('login', {});
@ -473,7 +478,7 @@
if (data.params.callID) { if (data.params.callID) {
var dialog = verto.dialogs[data.params.callID]; var dialog = verto.dialogs[data.params.callID];
if (data.method === "verto.attach" && dialog) { if (data.method === "verto.attach" && dialog) {
delete dialog.verto.dialogs[dialog.callID]; delete dialog.verto.dialogs[dialog.callID];
dialog.rtc.stop(); dialog.rtc.stop();
@ -1308,11 +1313,15 @@
this.modCommand("vid-write-png", null, file); this.modCommand("vid-write-png", null, file);
}; };
$.verto.conf.prototype.setVideoLayout = function(layout) { $.verto.conf.prototype.setVideoLayout = function(layout, canvasID) {
if (!this.params.hasVid) { if (!this.params.hasVid) {
throw 'Conference has no video'; 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) { $.verto.conf.prototype.kick = function(memberID) {
@ -1382,7 +1391,7 @@
} }
}); });
}; };
} }
$.verto.modfuncs = {}; $.verto.modfuncs = {};
@ -1405,7 +1414,7 @@
confMan.verto = verto; confMan.verto = verto;
confMan.serno = CONFMAN_SERNO++; confMan.serno = CONFMAN_SERNO++;
confMan.canvasCount = confMan.params.laData.canvasCount; confMan.canvasCount = confMan.params.laData.canvasCount;
function genMainMod(jq) { function genMainMod(jq) {
var play_id = "play_" + confMan.serno; var play_id = "play_" + confMan.serno;
var stop_id = "stop_" + confMan.serno; var stop_id = "stop_" + confMan.serno;
@ -1424,7 +1433,7 @@
jq.html(html); 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(); var val = $("#" + id + " option:selected").text();
if (val !== "none") { if (val !== "none") {
confMan.modCommand("vid-layout", null, [val, canvas_id]); confMan.modCommand("vid-layout", null, [val, canvas_id]);
@ -1435,11 +1444,11 @@
for (var j = 0; j < confMan.canvasCount; j++) { for (var j = 0; j < confMan.canvasCount; j++) {
var vlayout_id = "confman_vid_layout_" + j + "_" + confMan.serno; var vlayout_id = "confman_vid_layout_" + j + "_" + confMan.serno;
var vlselect_id = "confman_vl_select_" + j + "_" + confMan.serno; var vlselect_id = "confman_vl_select_" + j + "_" + confMan.serno;
var vlhtml = "<div id='" + vlayout_id + "'><br>" + var vlhtml = "<div id='" + vlayout_id + "'><br>" +
"<b>Video Layout Canvas " + (j+1) + "<b>Video Layout Canvas " + (j+1) +
"</b> <select onChange='$.verto.modfuncs.change_video_layout(\"" + vlayout_id + "\", \"" + j + "\")' id='" + vlselect_id + "'></select> " + "</b> <select onChange='$.verto.modfuncs.change_video_layout(\"" + vlayout_id + "\", \"" + (j+1) + "\")' id='" + vlselect_id + "'></select> " +
"<br><br></div>"; "<br><br></div>";
jq.append(vlhtml); jq.append(vlhtml);
} }
@ -1490,7 +1499,7 @@
var layer_set_id = "layer_set_" + x; var layer_set_id = "layer_set_" + x;
var layer_next_id = "layer_next_" + x; var layer_next_id = "layer_next_" + x;
var layer_prev_id = "layer_prev_" + x; var layer_prev_id = "layer_prev_" + x;
var tmute_id = "tmute_" + x; var tmute_id = "tmute_" + x;
var tvmute_id = "tvmute_" + x; var tvmute_id = "tvmute_" + x;
var vbanner_id = "vbanner_" + x; var vbanner_id = "vbanner_" + x;
@ -1502,7 +1511,7 @@
var volup_id = "vol_in_up" + x; var volup_id = "vol_in_up" + x;
var voldn_id = "vol_in_dn" + x; var voldn_id = "vol_in_dn" + x;
var transfer_id = "transfer" + x; var transfer_id = "transfer" + x;
var html = "<div id='" + box_id + "'>"; var html = "<div id='" + box_id + "'>";
@ -1515,7 +1524,7 @@
"<button class='ctlbtn' id='" + voldn_id + "'>Vol -</button>" + "<button class='ctlbtn' id='" + voldn_id + "'>Vol -</button>" +
"<button class='ctlbtn' id='" + volup_id + "'>Vol +</button>" + "<button class='ctlbtn' id='" + volup_id + "'>Vol +</button>" +
"<button class='ctlbtn' id='" + transfer_id + "'>Transfer</button>"; "<button class='ctlbtn' id='" + transfer_id + "'>Transfer</button>";
if (confMan.params.hasVid) { if (confMan.params.hasVid) {
html += "<br><br><b>Video Controls</b><hr noshade>"; html += "<br><br><b>Video Controls</b><hr noshade>";
@ -1530,14 +1539,14 @@
"<button class='ctlbtn' id='" + canvas_in_set_id + "'>Set Input Canvas</button>" + "<button class='ctlbtn' id='" + canvas_in_set_id + "'>Set Input Canvas</button>" +
"<button class='ctlbtn' id='" + canvas_in_prev_id + "'>Prev Input Canvas</button>" + "<button class='ctlbtn' id='" + canvas_in_prev_id + "'>Prev Input Canvas</button>" +
"<button class='ctlbtn' id='" + canvas_in_next_id + "'>Next Input Canvas</button>" + "<button class='ctlbtn' id='" + canvas_in_next_id + "'>Next Input Canvas</button>" +
"<br>" + "<br>" +
"<button class='ctlbtn' id='" + canvas_out_set_id + "'>Set Watching Canvas</button>" + "<button class='ctlbtn' id='" + canvas_out_set_id + "'>Set Watching Canvas</button>" +
"<button class='ctlbtn' id='" + canvas_out_prev_id + "'>Prev Watching Canvas</button>" + "<button class='ctlbtn' id='" + canvas_out_prev_id + "'>Prev Watching Canvas</button>" +
"<button class='ctlbtn' id='" + canvas_out_next_id + "'>Next Watching Canvas</button>"; "<button class='ctlbtn' id='" + canvas_out_next_id + "'>Next Watching Canvas</button>";
} }
html += "<br>" + html += "<br>" +
"<button class='ctlbtn' id='" + layer_set_id + "'>Set Layer</button>" + "<button class='ctlbtn' id='" + layer_set_id + "'>Set Layer</button>" +
@ -1620,7 +1629,7 @@
$("#" + canvas_out_prev_id).click(function() { $("#" + canvas_out_prev_id).click(function() {
confMan.modCommand("vid-watching-canvas", x, "prev"); confMan.modCommand("vid-watching-canvas", x, "prev");
}); });
$("#" + tmute_id).click(function() { $("#" + tmute_id).click(function() {
confMan.modCommand("tmute", x); confMan.modCommand("tmute", x);
}); });
@ -1697,14 +1706,14 @@
for (var j = 0; j < confMan.canvasCount; j++) { for (var j = 0; j < confMan.canvasCount; j++) {
var vlselect_id = "#confman_vl_select_" + j + "_" + confMan.serno; var vlselect_id = "#confman_vl_select_" + j + "_" + confMan.serno;
var vlayout_id = "#confman_vid_layout_" + j + "_" + confMan.serno; var vlayout_id = "#confman_vid_layout_" + j + "_" + confMan.serno;
var x = 0; var x = 0;
var options; var options;
$(vlselect_id).selectmenu({}); $(vlselect_id).selectmenu({});
$(vlselect_id).selectmenu("enable"); $(vlselect_id).selectmenu("enable");
$(vlselect_id).empty(); $(vlselect_id).empty();
$(vlselect_id).append(new Option("Choose a Layout", "none")); $(vlselect_id).append(new Option("Choose a Layout", "none"));
if (e.data.responseData) { if (e.data.responseData) {
@ -1713,15 +1722,15 @@
for (var i in e.data.responseData) { for (var i in e.data.responseData) {
rdata.push(e.data.responseData[i].name); rdata.push(e.data.responseData[i].name);
} }
options = rdata.sort(function(a, b) { options = rdata.sort(function(a, b) {
var ga = a.substring(0, 6) == "group:" ? true : false; var ga = a.substring(0, 6) == "group:" ? true : false;
var gb = b.substring(0, 6) == "group:" ? true : false; var gb = b.substring(0, 6) == "group:" ? true : false;
if ((ga || gb) && ga != gb) { if ((ga || gb) && ga != gb) {
return ga ? -1 : 1; return ga ? -1 : 1;
} }
return ( ( a == b ) ? 0 : ( ( a > b ) ? 1 : -1 ) ); return ( ( a == b ) ? 0 : ( ( a > b ) ? 1 : -1 ) );
}); });
@ -1881,6 +1890,11 @@
$.verto.dialog = function(direction, verto, params) { $.verto.dialog = function(direction, verto, params) {
var dialog = this; var dialog = this;
var tag = verto.options.tag;
if (typeof(tag) === "function") {
tag = tag();
}
dialog.params = $.extend({ dialog.params = $.extend({
useVideo: verto.options.useVideo, useVideo: verto.options.useVideo,
useStereo: verto.options.useStereo, useStereo: verto.options.useStereo,
@ -1888,12 +1902,12 @@
useCamera: verto.options.deviceParams.useCamera, useCamera: verto.options.deviceParams.useCamera,
useMic: verto.options.deviceParams.useMic, useMic: verto.options.deviceParams.useMic,
useSpeak: verto.options.deviceParams.useSpeak, useSpeak: verto.options.deviceParams.useSpeak,
tag: verto.options.tag, tag: tag,
localTag: verto.options.localTag, localTag: verto.options.localTag,
login: verto.options.login, login: verto.options.login,
videoParams: verto.options.videoParams videoParams: verto.options.videoParams
}, params); }, params);
dialog.verto = verto; dialog.verto = verto;
dialog.direction = direction; dialog.direction = direction;
dialog.lastState = null; dialog.lastState = null;
@ -1905,13 +1919,13 @@
dialog.useCamera = dialog.params.useCamera; dialog.useCamera = dialog.params.useCamera;
dialog.useMic = dialog.params.useMic; dialog.useMic = dialog.params.useMic;
dialog.useSpeak = dialog.params.useSpeak; dialog.useSpeak = dialog.params.useSpeak;
if (dialog.params.callID) { if (dialog.params.callID) {
dialog.callID = dialog.params.callID; dialog.callID = dialog.params.callID;
} else { } else {
dialog.callID = dialog.params.callID = generateGUID(); dialog.callID = dialog.params.callID = generateGUID();
} }
if (dialog.params.tag) { if (dialog.params.tag) {
dialog.audioStream = document.getElementById(dialog.params.tag); dialog.audioStream = document.getElementById(dialog.params.tag);
@ -1973,7 +1987,7 @@
}); });
} else { } else {
dialog.setState($.verto.enum.state.requesting); dialog.setState($.verto.enum.state.requesting);
dialog.sendMethod("verto.invite", { dialog.sendMethod("verto.invite", {
sdp: rtc.mediaData.SDP 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) { function find_name(id) {
for (var i in $.verto.audioOutDevices) { for (var i in $.verto.audioOutDevices) {
var source = $.verto.audioOutDevices[i]; var source = $.verto.audioOutDevices[i];
@ -2154,7 +2168,7 @@
var speaker = dialog.useSpeak; var speaker = dialog.useSpeak;
console.info("Using Speaker: ", speaker); console.info("Using Speaker: ", speaker);
if (speaker && speaker !== "any") { if (speaker && speaker !== "any" && speaker !== "none") {
setTimeout(function() { setTimeout(function() {
dialog.setAudioPlaybackDevice(speaker); dialog.setAudioPlaybackDevice(speaker);
}, 500); }, 500);
@ -2181,6 +2195,11 @@
dialog.setState($.verto.enum.state.destroy); dialog.setState($.verto.enum.state.destroy);
break; break;
case $.verto.enum.state.destroy: case $.verto.enum.state.destroy:
if (typeof(dialog.verto.options.tag) === "function") {
$('#' + dialog.params.tag).remove();
}
delete dialog.verto.dialogs[dialog.callID]; delete dialog.verto.dialogs[dialog.callID];
if (dialog.params.screenShare) { if (dialog.params.screenShare) {
dialog.rtc.stopPeer(); dialog.rtc.stopPeer();
@ -2314,7 +2333,7 @@
}; };
$.verto.dialog.prototype.getMute = function() { $.verto.dialog.prototype.getMute = function() {
var dialog = this; var dialog = this;
return dialog.rtc.getMute(); return dialog.rtc.getMute();
}; };
@ -2324,7 +2343,7 @@
}; };
$.verto.dialog.prototype.getVideoMute = function() { $.verto.dialog.prototype.getVideoMute = function() {
var dialog = this; var dialog = this;
return dialog.rtc.getVideoMute(); return dialog.rtc.getVideoMute();
}; };
@ -2411,7 +2430,7 @@
$.verto.dialog.prototype.answer = function(params) { $.verto.dialog.prototype.answer = function(params) {
var dialog = this; var dialog = this;
if (!dialog.answered) { if (!dialog.answered) {
if (!params) { if (!params) {
params = {}; params = {};
@ -2438,7 +2457,7 @@
dialog.useSpeak = params.useSpeak; dialog.useSpeak = params.useSpeak;
} }
} }
dialog.rtc.createAnswer(params); dialog.rtc.createAnswer(params);
dialog.answered = true; dialog.answered = true;
} }
@ -2605,7 +2624,7 @@
$.verto.enum = Object.freeze($.verto.enum); $.verto.enum = Object.freeze($.verto.enum);
$.verto.saved = []; $.verto.saved = [];
$.verto.unloadJobs = []; $.verto.unloadJobs = [];
$(window).bind('beforeunload', function() { $(window).bind('beforeunload', function() {
@ -2630,7 +2649,7 @@
var checkDevices = function(runtime) { var checkDevices = function(runtime) {
console.info("enumerating devices"); console.info("enumerating devices");
var aud_in = [], aud_out = [], vid = []; var aud_in = [], aud_out = [], vid = [];
if ((!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) && MediaStreamTrack.getSources) { if ((!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) && MediaStreamTrack.getSources) {
MediaStreamTrack.getSources(function (media_sources) { MediaStreamTrack.getSources(function (media_sources) {
@ -2642,17 +2661,17 @@
aud_in.push(media_sources[i]); aud_in.push(media_sources[i]);
} }
} }
$.verto.videoDevices = vid; $.verto.videoDevices = vid;
$.verto.audioInDevices = aud_in; $.verto.audioInDevices = aud_in;
console.info("Audio Devices", $.verto.audioInDevices); console.info("Audio Devices", $.verto.audioInDevices);
console.info("Video Devices", $.verto.videoDevices); console.info("Video Devices", $.verto.videoDevices);
runtime(true); runtime(true);
}); });
} else { } else {
/* of course it's a totally different API CALL with different element names for the same exact thing */ /* of course it's a totally different API CALL with different element names for the same exact thing */
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) { if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
console.log("enumerateDevices() not supported."); console.log("enumerateDevices() not supported.");
return; return;
@ -2667,7 +2686,7 @@
console.log(device.kind + ": " + device.label + console.log(device.kind + ": " + device.label +
" id = " + device.deviceId); " id = " + device.deviceId);
if (device.kind === "videoinput") { if (device.kind === "videoinput") {
vid.push({id: device.deviceId, kind: "video", label: device.label}); vid.push({id: device.deviceId, kind: "video", label: device.label});
} else if (device.kind === "audioinput") { } else if (device.kind === "audioinput") {
@ -2676,17 +2695,17 @@
aud_out.push({id: device.deviceId, kind: "audio_out", label: device.label}); aud_out.push({id: device.deviceId, kind: "audio_out", label: device.label});
} }
}); });
$.verto.videoDevices = vid; $.verto.videoDevices = vid;
$.verto.audioInDevices = aud_in; $.verto.audioInDevices = aud_in;
$.verto.audioOutDevices = aud_out; $.verto.audioOutDevices = aud_out;
console.info("Audio IN Devices", $.verto.audioInDevices); console.info("Audio IN Devices", $.verto.audioInDevices);
console.info("Audio Out Devices", $.verto.audioOutDevices); console.info("Audio Out Devices", $.verto.audioOutDevices);
console.info("Video Devices", $.verto.videoDevices); console.info("Video Devices", $.verto.videoDevices);
runtime(true); runtime(true);
}) })
.catch(function(err) { .catch(function(err) {
console.log(" Device Enumeration ERROR: " + err.name + ": " + err.message); console.log(" Device Enumeration ERROR: " + err.name + ": " + err.message);

View File

@ -1,14 +1,76 @@
var callback = function(message){console.log(message);}; // holds the user's callback for a global scope Verto = function (
callbacks = {}; tag,
var callICEConnected = false; voiceBridge,
var callPurposefullyEnded = false; // used to determine whether the user ended the call or the call was ended from somewhere else outside conferenceUsername,
var callTimeout = null; // function that will run if there is no call established userCallback,
var toDisplayDisconnectCallback = true; // if a call is dropped only display the error the first time onFail = null,
var wasCallSuccessful = false; // when the websocket connection is closed this determines whether a call was ever successfully established chromeExtension = null) {
webcamStream = "webcamStream";
window[webcamStream] = null; voiceBridge += "-DESKSHARE";
verto = null; this.cur_call = null;
videoTag = 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 // receives either a string variable holding the name of an actionscript
// registered callback, or a javascript function object. // 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 // or if it is an actionscript string it will return a javascript Function
// that when invokved will invoke the actionscript registered callback // that when invokved will invoke the actionscript registered callback
// and passes along parameters // and passes along parameters
function normalizeCallback(callback) { Verto.normalizeCallback = function (callback) {
if (typeof callback == "function") { if (typeof callback == 'function') {
return callback; return callback;
} else { } else {
return function(args) { return function (args) {
document.getElementById("BigBlueButton")[callback](args); document.getElementById('BigBlueButton')[callback](args);
}; };
} }
} };
// save a copy of the hangup function registered for the verto object Verto.prototype.onWSLogin = function (v, success) {
var oldHangup = $.verto.prototype.hangup; this.cur_call = null;
// overwrite the verto hangup handler with my own handler if (success) {
$.verto.prototype.hangup = function(callID, userCallback) { this.callWasSuccessful = true;
console.log("call state callbacks - bye"); this.mediaCallback();
if (userCallback) { return;
callback = userCallback; } else {
} // error logging verto into freeswitch
callActive = false; this.logError({ status: 'failed', errorcode: '10XX' });
this.callWasSuccessful = false;
this.onFail();
return;
}
};
if (cur_call) { Verto.prototype.registerCallbacks = function () {
console.log('call ended ' + cur_call.audioStream.currentTime); // the duration of the call var callbacks = {
if (callPurposefullyEnded === true) { // the user ended the call themself onMessage: function () {},
callback({'status':'ended'});
} else {
callback({'status':'failed', 'errorcode': 1005}); // Call ended unexpectedly
}
clearTimeout(callTimeout);
cur_call = null;
} else {
console.log('bye event already received');
}
// call the original hangup procedure
return oldHangup.apply(this, arguments);
}
// main entry point to making a verto call onDialogState: function (d) {},
callIntoConference_verto = function(voiceBridge, conferenceUsername, conferenceIdNumber, userCallback, videoTag, options, vertoServerCredentials) {
window.videoTag = videoTag;
// stores the user's callback in the global scope
if (userCallback) {
callback = userCallback;
}
if(!isLoggedIntoVerto()) { // start the verto log in procedure
// runs when a web socket is disconnected
callbacks.onWSClose = function(v, success) {
if(wasCallSuccessful) { // a call was established through the websocket
if(toDisplayDisconnectCallback) { // will only display the error the first time
// the connection was dropped in an already established call
console.log("websocket disconnected");
callback({'status':'failed', 'errorcode': 1001}); // WebSocket disconnected
toDisplayDisconnectCallback = false;
}
} else {
// this callback was triggered and a call was never successfully established
console.log("websocket connection could not be established");
callback({'status':'failed', 'errorcode': 1002}); // Could not make a WebSocket connection
}
}
// runs when the websocket is successfully created
callbacks.onWSLogin = function(v, success) {
cur_call = null;
ringing = false;
console.log("Inside onWSLogin");
if (success) { onWSLogin: this.onWSLogin.bind(this),
console.log("starting call");
toDisplayDisconnectCallback = true; // yes, display an error if the socket closes
wasCallSuccessful = true; // yes, a call was successfully established through the websocket
webrtc_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, callback, options);
} else {
callback({'status':'failed', 'errorcode': '10XX'}); // eror logging verto into freeswitch
}
}
// set up verto
// $.verto.init({}, init(videoTag));
init(videoTag, vertoServerCredentials);
} else {
console.log("already logged into verto, going straight to making a call");
webrtc_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, callback, options);
}
}
checkSupport = function(callback) { onWSClose: function (v, success) {
if(!isWebRTCAvailable_verto()) { cur_call = null;
callback({'status': 'failed', 'errorcode': 1003}); // Browser version not supported if (this.callWasSuccessful) {
} // the connection was dropped in an already established call
this.logError('websocket disconnected');
if (!navigator.getUserMedia) { // WebSocket disconnected
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; 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){ // Could not make a WebSocket connection
callback({'status': 'failed', 'errorcode': '10XX'}); // getUserMedia not supported in this browser this.logError({ status: 'failed', errorcode: 1002 });
} this.onFail();
} return;
}
}.bind(this),
};
this.callbacks = callbacks;
};
configStuns = function(callbacks, callback, videoTag, vertoServerCredentials) { Verto.prototype.hold = function () {
console.log("Fetching STUN/TURN server info for Verto initialization"); this.cur_call.toggleHold();
var stunsConfig = {}; };
$.ajax({
dataType: 'json',
url: '/bigbluebutton/api/stuns/'
}).done(function(data) {
console.log("ajax request done");
console.log(data);
if(data['response'] && data.response.returncode == "FAILED") {
console.error(data.response.message);
callback({'status':'failed', 'errorcode': data.response.message});
return;
}
stunsConfig['stunServers'] = ( data['stunServers'] ? data['stunServers'].map(function(data) {
return {'url': data['url']};
}) : [] );
stunsConfig['turnServers'] = ( data['turnServers'] ? data['turnServers'].map(function(data) {
return {
'urls': data['url'],
'username': data['username'],
'credential': data['password']
};
}) : [] );
stunsConfig = stunsConfig['stunServers'].concat(stunsConfig['turnServers']);
console.log("success got stun data, making verto");
makeVerto(callbacks, stunsConfig, videoTag, vertoServerCredentials);
}).fail(function(data, textStatus, errorThrown) {
// BBBLog.error("Could not fetch stun/turn servers", {error: textStatus, user: callerIdName, voiceBridge: conferenceVoiceBridge});
callback({'status':'failed', 'errorcode': 1009});
return;
});
}
docall_verto = function(extension, conferenceUsername, conferenceIdNumber, callbacks, options) { Verto.prototype.hangup = function () {
console.log(extension + ", " + conferenceUsername + ", " + conferenceIdNumber); 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 if (this.share_call) {
console.log("Quitting: Call already in progress"); // the duration of the call
return; this.logger('call ended ' + this.share_call.audioStream.currentTime);
} this.share_call.hangup();
// determine the resolution the user chose for webcam video this.share_call = null;
outgoingBandwidth = "default"; }
incomingBandwidth = "default";
var useVideo = useCamera = useMic = false;
// debugger;
if(options.watchOnly) {
window.watchOnly = true;
window.listenOnly = false;
window.joinAudio = false;
useVideo = true;
useCamera = false;
useMic = false;
} else if(options.listenOnly) {
window.listenOnly = true;
window.watchOnly = false;
window.joinAudio = false;
useVideo = false;
useCamera = false;
useMic = false;
} else if(options.joinAudio) {
window.joinAudio = true;
window.watchOnly = false;
window.listenOnly = false;
useVideo = false;
useCamera = false;
useMic = true;
}
cur_call = verto.newCall({ // the user ended the call themself
destination_number: extension, // if (callPurposefullyEnded === true) {
caller_id_name: conferenceUsername, if (true) {
caller_id_number: conferenceIdNumber, this.logger({ status: 'ended' });
outgoingBandwidth: outgoingBandwidth, } else {
incomingBandwidth: incomingBandwidth, // Call ended unexpectedly
useStereo: true, this.logError({ status: 'failed', errorcode: 1005 });
useVideo: useVideo, }
useCamera: useCamera, };
useMic: useMic,
dedEnc: false,
mirrorInput: false
});
if (callbacks != null) { // add user supplied callbacks to the current call Verto.prototype.mute = function () {
cur_call.rtc.options.callbacks = $.extend(cur_call.rtc.options.callbacks, callbacks); this.cur_call.dtmf('0');
} };
}
// check if logged into verto by seeing if there is a ready websocket connection Verto.prototype.localmute = function () {
function isLoggedIntoVerto() { // var muted = cur_call.setMute('toggle');
return (verto != null ? (ref = verto.rpcClient) != null ? ref.socketReady() : void 0 : void 0); // if (muted) {
} // display('Talking to: ' + cur_call.cidString() + ' [LOCALLY MUTED]');
// } else {
// display('Talking to: ' + cur_call.cidString());
// }
};
// overwrite and substitute my own init function Verto.prototype.localvidmute = function () {
init = function(videoTag, vertoServerCredentials) { // var muted = cur_call.setVideoMute('toggle');
videoTag = window.videoTag; // if (muted) {
cur_call = null; // display('Talking to: ' + cur_call.cidString() + ' [VIDEO LOCALLY MUTED]');
share_call = null; // } else {
incomingBandwidth = "default"; // display('Talking to: ' + cur_call.cidString());
configStuns(callbacks, callback, videoTag, vertoServerCredentials); // }
} };
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 // checks whether Google Chrome or Firefox have the WebRTCPeerConnection object
function isWebRTCAvailable_verto() { Verto.prototype.isWebRTCAvailable = function () {
return (typeof window.webkitRTCPeerConnection !== 'undefined' || typeof window.mozRTCPeerConnection !== 'undefined'); return (typeof window.webkitRTCPeerConnection !== 'undefined' ||
} typeof window.mozRTCPeerConnection !== 'undefined');
// exit point for conference
function leaveWebRTCVoiceConference_verto() {
console.log("Leaving the voice conference");
webrtc_hangup_verto();
}
function make_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, userCallback, server, recall, options) {
if (userCallback) {
callback = userCallback;
}
callPurposefullyEnded = false;
// after 15 seconds if a call hasn't been established display error, hangup and logout of verto
callTimeout = setTimeout(function() {
console.log('Ten seconds without updates sending timeout code');
callback({'status':'failed', 'errorcode': 1006}); // Failure on call
if (verto != null) {
verto.hangup();
verto.logout();
verto = null;
}
cur_call = null;
}, 10000*1.5);
var myRTCCallbacks = {
onError: function(vertoErrorObject, errorMessage) {
console.error("custom callback: onError");
console.error(vertoErrorObject);
console.error("ERROR:");
console.error(errorMessage);
if(errorMessage.name === "PermissionDeniedError") { // user denied access to media peripherals
console.error("User denied permission/access to hardware");
console.error("getUserMedia: failure - ", errorMessage);
callback({'status': 'mediafail', 'cause': errorMessage});
}
cur_call.hangup({cause: "Device or Permission Error"});
clearTimeout(callTimeout);
},
onICEComplete: function(self, candidate) { // ICE candidate negotiation is complete
console.log("custom callback: onICEComplete");
console.log('Received ICE status changed to completed');
if (callICEConnected === false) {
callICEConnected = true;
if (callActive === true) {
callback({'status':'started'});
}
clearTimeout(callTimeout);
}
},
onStream: function(rtc, stream) { // call has been established
console.log("getUserMicMedia: success");
callback({'status':'mediasuccess'});
console.log("custom callback: stream started");
callActive = true;
console.log('BigBlueButton call accepted');
if (callICEConnected === true) {
callback({'status':'started'});
} else {
callback({'status':'waitingforice'});
}
clearTimeout(callTimeout);
}
};
if(isLoggedIntoVerto()) {
console.log("Verto is logged into FreeSWITCH, socket is available, making call");
callICEConnected = false;
docall_verto(voiceBridge, conferenceUsername, conferenceIdNumber, myRTCCallbacks, options);
if(recall === false) {
console.log('call connecting');
callback({'status': 'connecting'});
} else {
console.log('call connecting again');
}
callback({'status':'mediarequest'});
} else {
console.error("Verto is NOT logged into FreeSWITCH, socket is NOT available, abandoning call request");
}
}
function makeVerto(callbacks, stunsConfig, videoTag, vertoServerCredentials) {
var vertoPort = vertoServerCredentials.vertoPort;
var hostName = vertoServerCredentials.hostName;
var socketUrl = "wss://" + hostName + ":" + vertoPort;
var login = vertoServerCredentials.login;
var password = vertoServerCredentials.password;
var minWidth = "640";
var minHeight = "480";
var maxWidth = "1920";
var maxHeight = "1080";
console.log("stuns info is");
console.log(stunsConfig);
// debugger;
verto = new $.verto({
login: login,
passwd: password,
socketUrl: socketUrl,
tag: videoTag,
ringFile: "sounds/bell_ring2.wav",
loginParams: {foo: true, bar: "yes"},
useVideo: false,
useCamera: false,
iceServers: stunsConfig, // use user supplied stun configuration
// iceServers: true, // use stun, use default verto configuration
}, callbacks);
}
// sets verto to begin using the resolution that the user selected
my_check_vid_res = function() {
var selectedVideoConstraints = getChosenWebcamResolution();
my_real_size(selectedVideoConstraints);
if (verto) {
verto.videoParams({
"minWidth": selectedVideoConstraints.constraints.minWidth,
"minHeight": selectedVideoConstraints.constraints.minHeight,
"maxWidth": selectedVideoConstraints.constraints.maxWidth,
"maxHeight": selectedVideoConstraints.constraints.maxHeight,
"minFrameRate": selectedVideoConstraints.constraints.minFrameRate,
"vertoBestFrameRate": selectedVideoConstraints.constraints.vertoBestFrameRate
});
}
}
my_real_size = function(selectedVideoConstraints) {
$("#" + window.videoTag).height("100%");
$("#" + window.videoTag).width("100%");
}
var RTCPeerConnectionCallbacks = {
iceFailed: function(e) {
console.log('received ice negotiation failed');
callback({'status':'failed', 'errorcode': 1007}); // Failure on call
//
// TODO unless I do this, the call only lasts for a few seconds.
// When I comment out the lines below, it works fine indefinitely
// Anton Georgiev Dec 10 2015
//
//cur_call = null;
//verto.hangup();
//verto = null;
//clearTimeout(callTimeout);
}
}; };
this.RTCPeerConnectionCallbacks = RTCPeerConnectionCallbacks;
window.verto_afterStreamPublish = function() {} this.VertoManager = function () {
this.vertoAudio = null;
this.vertoVideo = null;
this.vertoScreenShare = null;
window.vertoHandle = null;
};
function webrtc_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, userCallback, options) { Verto.prototype.logout = function () {
if (userCallback) { this.exitAudio();
callback = userCallback; this.exitVideo();
} this.exitScreenShare();
console.log("webrtc_call\n"+voiceBridge + ", " + conferenceUsername + ", " + conferenceIdNumber + ", " + callback); window.vertoHandle.logout();
};
if(!isWebRTCAvailable()) { VertoManager.prototype.exitAudio = function () {
callback({'status': 'failed', 'errorcode': 1003}); // Browser version not supported if (this.vertoAudio != null) {
return; console.log('Hanging up vertoAudio');
} this.vertoAudio.hangup();
this.vertoAudio = null;
}
};
var server = window.document.location.hostname; VertoManager.prototype.exitVideo = function () {
console.log("user " + conferenceUsername + " calling to " + voiceBridge); if (this.vertoVideo != null) {
if (isLoggedIntoVerto()) { console.log('Hanging up vertoVideo');
make_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, callback, "", false, options); this.vertoVideo.hangup();
} this.vertoVideo = null;
} }
};
function webrtc_hangup_verto(userCallback) { VertoManager.prototype.exitScreenShare = function () {
if (userCallback) { if (this.vertoScreenShare != null) {
callback = userCallback; console.log('Hanging up vertoScreenShare');
} this.vertoScreenShare.hangup();
callPurposefullyEnded = true; this.vertoScreenShare = null;
console.log("Hanging up current session"); }
if(verto) { };
verto.hangup(false, callback);
} 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);
};

View File

@ -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);
});
}

View File

@ -20,13 +20,14 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
--> -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" <mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:maps="org.bigbluebutton.modules.deskShare.maps.*"
implements="org.bigbluebutton.common.IBigBlueButtonModule" implements="org.bigbluebutton.common.IBigBlueButtonModule"
creationComplete="onCreationComplete()" xmlns:maps1="org.bigbluebutton.modules.screenshare.maps.*"> creationComplete="onCreationComplete()" xmlns:maps1="org.bigbluebutton.modules.screenshare.maps.*">
<maps1:ScreenshareEventMap id="deskshareGlobalEventMap" /> <maps1:ScreenshareEventMap id="deskshareGlobalEventMap" />
<maps1:WebRTCDeskshareEventMap id="webRTCDeskshareEventMap" />
<mx:Script> <mx:Script>
<![CDATA[ <![CDATA[
import com.asfusion.mate.events.Dispatcher; import com.asfusion.mate.events.Dispatcher;

View File

@ -109,23 +109,26 @@ package org.bigbluebutton.modules.deskshare.managers
private function startWebRTCDeskshare():void { private function startWebRTCDeskshare():void {
LOGGER.debug("DeskshareManager::startWebRTCDeskshare"); LOGGER.debug("DeskshareManager::startWebRTCDeskshare");
var result:String;
if (ExternalInterface.available) { if (ExternalInterface.available) {
var loggingCallback:Function = function(args:Object):void {LOGGER.debug(args); JSLog.warn("loggingCallback", args)};
ExternalInterface.addCallback("loggingCallback", loggingCallback);
var videoTag:String = "localVertoVideo"; var videoTag:String = "localVertoVideo";
var modifyResolution:Boolean = false;
// register these callbacks
var onSuccess:Function = function():void { LOGGER.debug("onSuccess"); JSLog.warn("onSuccess - as", {})};
ExternalInterface.addCallback("onSuccess", onSuccess);
var onFail:Function = function(args:Object):void { var onFail:Function = function(args:Object):void {
JSLog.warn("onFail - as", args); JSLog.warn("onFail - as", args);
JSLog.warn("WebRTCDeskshareManager::startWebRTCDeskshare - falling back to java", {}); JSLog.warn("WebRTCDeskshareManager::startWebRTCDeskshare - falling back to java", {});
globalDispatcher.dispatchEvent(new UseJavaModeCommand()) globalDispatcher.dispatchEvent(new UseJavaModeCommand())
}; };
ExternalInterface.addCallback("onFail", onFail); ExternalInterface.addCallback("onFail", onFail);
JSLog.warn("calling startScreenshare", {});
result = ExternalInterface.call("startScreenshare", "loggingCallback", videoTag, vertoServerCredentials, chromeExtensionKey, modifyResolution, "onSuccess", "onFail"); ExternalInterface.call(
'vertoShareScreen',
videoTag,
'3500',
'FreeSWITCH USers - abc',
'1008',
null,
vertoServerCredentials,
chromeExtensionKey,
onFail
);
} }
} }

View File

@ -0,0 +1,32 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.events
{
import flash.events.Event;
public class DeskshareToolbarEvent extends Event
{
public static const STOP:String = "deactivate toolbar status";
public function DeskshareToolbarEvent(type:String)
{
super(type, true, false);
}
}
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,33 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.events
{
import flash.events.Event;
public class WebRTCShareWindowEvent extends Event
{
public static const CLOSE:String = "WebRTC Deskshare Share Window Close Event";
public function WebRTCShareWindowEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}

View File

@ -0,0 +1,37 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.events
{
import flash.events.Event;
public class WebRTCStreamEvent extends Event
{
public static const START:String = "WebRTC Deskshare Stream Started Event";
public static const STOP:String = "WebRTC Deskshare Stream Stopped Event";
public var videoWidth:Number = 0;
public var videoHeight:Number = 0;
public function WebRTCStreamEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}

View File

@ -0,0 +1,40 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.events
{
import flash.events.Event;
import org.bigbluebutton.main.api.JSLog;
public class WebRTCViewStreamEvent extends Event
{
public static const START:String = "WebRTC Start Viewing Stream Event";
public static const STOP:String = "WebRTC Stop Viewing Stream Event";
public var videoWidth:Number = 0;
public var videoHeight:Number = 0;
public var rtmp:String = null;
public function WebRTCViewStreamEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
JSLog.warn("creating a WebRTCViewStreamEvent event " + type, null);
}
}
}

View File

@ -0,0 +1,33 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.events
{
import flash.events.Event;
public class WebRTCViewWindowEvent extends Event
{
public static const CLOSE:String = "WebRTC Deskshare View Window Close Event";
public function WebRTCViewWindowEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
}

View File

@ -32,6 +32,9 @@ package org.bigbluebutton.modules.screenshare.managers {
import org.bigbluebutton.modules.screenshare.model.ScreenshareModel; import org.bigbluebutton.modules.screenshare.model.ScreenshareModel;
import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions; import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions;
import org.bigbluebutton.modules.screenshare.services.ScreenshareService; 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 { public class ScreenshareManager {
private static const LOGGER:ILogger = getClassLogger(ScreenshareManager); private static const LOGGER:ILogger = getClassLogger(ScreenshareManager);
@ -43,6 +46,7 @@ package org.bigbluebutton.modules.screenshare.managers {
private var service:ScreenshareService; private var service:ScreenshareService;
private var globalDispatcher:Dispatcher; private var globalDispatcher:Dispatcher;
private var sharing:Boolean = false; private var sharing:Boolean = false;
private var usingJava:Boolean = true;
public function ScreenshareManager() { public function ScreenshareManager() {
service = new ScreenshareService(); service = new ScreenshareService();
@ -73,7 +77,7 @@ package org.bigbluebutton.modules.screenshare.managers {
LOGGER.debug("handle Connection Success Event"); LOGGER.debug("handle Connection Success Event");
service.checkIfPresenterIsSharingScreen(); service.checkIfPresenterIsSharingScreen();
} }
public function handleStreamStartedEvent(event:StreamStartedEvent):void { public function handleStreamStartedEvent(event:StreamStartedEvent):void {
ScreenshareModel.getInstance().streamId = event.streamId; ScreenshareModel.getInstance().streamId = event.streamId;
ScreenshareModel.getInstance().width = event.width; ScreenshareModel.getInstance().width = event.width;
@ -98,8 +102,8 @@ package org.bigbluebutton.modules.screenshare.managers {
ScreenshareModel.getInstance().url = event.url; ScreenshareModel.getInstance().url = event.url;
if (UsersUtil.amIPresenter()) { if (UsersUtil.amIPresenter()) {
// var dispatcher:Dispatcher = new Dispatcher(); // var dispatcher:Dispatcher = new Dispatcher();
// dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START)); // dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));
} else { } else {
handleStreamStartEvent(ScreenshareModel.getInstance().streamId, event.width, event.height); handleStreamStartEvent(ScreenshareModel.getInstance().streamId, event.width, event.height);
@ -108,8 +112,7 @@ package org.bigbluebutton.modules.screenshare.managers {
var dispatcher:Dispatcher = new Dispatcher(); var dispatcher:Dispatcher = new Dispatcher();
dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START)); dispatcher.dispatchEvent(new ViewStreamEvent(ViewStreamEvent.START));
} }
private function initDeskshare():void { private function initDeskshare():void {
sharing = false; sharing = false;
var option:ScreenshareOptions = new ScreenshareOptions(); var option:ScreenshareOptions = new ScreenshareOptions();
@ -141,10 +144,16 @@ package org.bigbluebutton.modules.screenshare.managers {
toolbarButtonManager.startedSharing(); toolbarButtonManager.startedSharing();
var option:ScreenshareOptions = new ScreenshareOptions(); var option:ScreenshareOptions = new ScreenshareOptions();
option.parseOptions(); option.parseOptions();
var autoStart:Boolean = false; // harcode for now
publishWindowManager.startSharing(module.getCaptureServerUri(), module.getRoom(), autoStart, option.autoFullScreen); if (option.useWebRTCIfAvailable && !BrowserCheck.isWebRTCSupported()) {
sharing = true; usingJava = true;
service.requestStartSharing(); 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 { public function handleRequestStopSharingEvent():void {
@ -187,8 +196,19 @@ package org.bigbluebutton.modules.screenshare.managers {
private function handleStreamStartEvent(streamId:String, videoWidth:Number, videoHeight:Number):void { private function handleStreamStartEvent(streamId:String, videoWidth:Number, videoHeight:Number):void {
LOGGER.debug("Received start vieweing command"); LOGGER.debug("Received start vieweing command");
if (!usingJava) { return; }
viewWindowManager.startViewing(streamId, videoWidth, videoHeight); 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();
}
} }
} }

View File

@ -0,0 +1,298 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.managers
{
import com.asfusion.mate.events.Dispatcher;
import flash.external.ExternalInterface;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.managers.UserManager;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.modules.screenshare.events.UseJavaModeCommand;
import org.bigbluebutton.modules.screenshare.events.WebRTCViewStreamEvent;
import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions;
import org.bigbluebutton.modules.screenshare.events.ShareWindowEvent;
import org.bigbluebutton.modules.screenshare.services.WebRTCDeskshareService;
import org.bigbluebutton.modules.screenshare.utils.BrowserCheck;
import org.bigbluebutton.modules.screenshare.events.DeskshareToolbarEvent;
import org.bigbluebutton.main.api.JSLog;
public class WebRTCDeskshareManager {
private static const LOGGER:ILogger = getClassLogger(WebRTCDeskshareManager);
private var publishWindowManager:WebRTCPublishWindowManager;
private var viewWindowManager:WebRTCViewerWindowManager;
private var toolbarButtonManager:ToolbarButtonManager;
private var module:ScreenshareModule;
private var service:WebRTCDeskshareService;
private var globalDispatcher:Dispatcher;
private var sharing:Boolean = false;
private var usingWebRTC:Boolean = false;
private var chromeExtensionKey:String = null;
public function WebRTCDeskshareManager() {
JSLog.warn("WebRTCDeskshareManager::WebRTCDeskshareManager", {});
service = new WebRTCDeskshareService();
globalDispatcher = new Dispatcher();
publishWindowManager = new WebRTCPublishWindowManager(service);
viewWindowManager = new WebRTCViewerWindowManager(service);
}
public function handleStartModuleEvent(module:ScreenshareModule):void {
LOGGER.debug("WebRTC Screenshare Module starting");
JSLog.warn("WebRTCDeskshareManager::handleStartModuleEvent", {});
this.module = module;
service.handleStartModuleEvent(module);
if (UsersUtil.amIPresenter()) {
initDeskshare();
}
}
public function handleStopModuleEvent():void {
LOGGER.debug("WebRTC Deskshare Module stopping");
publishWindowManager.stopSharing();
viewWindowManager.stopViewing();
service.disconnect();
}
/*presenter stopped their program stream*/
public function handleStreamStoppedEvent():void {
LOGGER.debug("WebRTCDeskshareManager::handleStreamStoppedEvent Sending deskshare stopped command");
JSLog.warn("WebRTCDeskshareManager::handleStreamStoppedEvent", {});
stopWebRTCDeskshare();
}
/*viewer being told there is no more stream*/
public function handleStreamStopEvent(args:Object):void {
LOGGER.debug("WebRTCDeskshareManager::handleStreamStopEvent");
JSLog.warn("WebRTCDeskshareManager::handleStreamStopEvent", {});
viewWindowManager.handleViewWindowCloseEvent();
}
public function handleRequestStopSharingEvent():void {
JSLog.warn("WebRTCDeskshareManager::handleRequestStopSharingEvent", {});
/* stopping WebRTC deskshare. Alert DeskshareManager to reset toolbar */
globalDispatcher.dispatchEvent(new DeskshareToolbarEvent(DeskshareToolbarEvent.STOP));
stopWebRTCDeskshare();
}
private function stopWebRTCDeskshare():void {
LOGGER.debug("WebRTCDeskshareManager::stopWebRTCDeskshare");
JSLog.warn("WebRTCDeskshareManager::stopWebRTCDeskshare", {});
viewWindowManager.stopViewing();
globalDispatcher.dispatchEvent(new ShareWindowEvent(ShareWindowEvent.CLOSE));
if (ExternalInterface.available) {
ExternalInterface.call("vertoExitScreenShare");
}
}
private function startWebRTCDeskshare():void {
LOGGER.debug("WebRTCDeskshareManager::startWebRTCDeskshare");
JSLog.warn("WebRTCDeskshareManager::startWebRTCDeskshare", {});
if (ExternalInterface.available) {
var videoTag:String = "localVertoVideo";
var onFail:Function = function(args:Object):void {
JSLog.warn("onFail - as", args);
JSLog.warn("WebRTCDeskshareManager::startWebRTCDeskshare - falling back to java", {});
globalDispatcher.dispatchEvent(new UseJavaModeCommand())
};
ExternalInterface.addCallback("onFail", onFail);
var voiceBridge:String = UserManager.getInstance().getConference().voiceBridge;
var myName:String = 'FreeSWITCH Users - ';
myName += UserManager.getInstance().getConference().getMyName();
ExternalInterface.call(
'vertoShareScreen',
videoTag,
voiceBridge,
myName,
null,
"onFail",
chromeExtensionKey
);
}
}
private function initDeskshare():void {
JSLog.warn("WebRTCDeskshareManager::initDeskshare", {});
sharing = false;
var options:ScreenshareOptions = new ScreenshareOptions();
options.parseOptions();
if (options.chromeExtensionKey) {
chromeExtensionKey = options.chromeExtensionKey;
}
if (options.autoStart) {
handleStartSharingEvent(true);
}
}
public function handleMadePresenterEvent(e:MadePresenterEvent):void {
LOGGER.debug("Got MadePresenterEvent ");
initDeskshare();
}
public function handleMadeViewerEvent(e:MadePresenterEvent):void{
LOGGER.debug("Got MadeViewerEvent ");
if (sharing) {
publishWindowManager.stopSharing();
stopWebRTCDeskshare();
}
sharing = false;
}
private function canIUseVertoOnThisBrowser(onFailure:Function, onSuccess:Function):void {
LOGGER.debug("DeskshareManager::canIUseVertoOnThisBrowser");
JSLog.warn("WebRTCDeskshareManager::canIUseVertoOnThisBrowser", {});
var options:ScreenshareOptions = new ScreenshareOptions();
options.parseOptions();
if (options.useWebRTCIfAvailable && BrowserCheck.isWebRTCSupported()) {
JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent WebRTC Supported", {});
if (BrowserCheck.isFirefox()) {
onSuccess("Firefox, lets try");
} else {
if (chromeExtensionKey != null) {
JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent chrome extension key exists - ", chromeExtensionKey);
if (ExternalInterface.available) {
var success:Function = function(status:String):void {
ExternalInterface.addCallback("gCETCallback", null);
JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent inside onSuccess", {});
if (status == "installed-enabled") {
JSLog.warn("Chrome Extension exists", {});
onSuccess("worked");
} else {
onFailure("No Chrome Extension");
}
};
ExternalInterface.addCallback("gCETCallback", success);
ExternalInterface.call("vertoExtensionGetChromeExtensionStatus", chromeExtensionKey, "gCETCallback");
}
} else {
onFailure("No chromeExtensionKey in config.xml");
return;
}
}
} else {
onFailure("Web browser doesn't support WebRTC");
return;
}
}
/*handle start sharing event*/
public function handleStartSharingEvent(autoStart:Boolean):void {
LOGGER.debug("WebRTCDeskshareManager::handleStartSharingEvent");
JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent", {});
var onFailure:Function = function(message:String):void {
JSLog.warn(message, {});
usingWebRTC = false;
// send out event to fallback to Java
JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent - falling back to java", {});
globalDispatcher.dispatchEvent(new UseJavaModeCommand());
return;
};
var onSuccess:Function = function(message:String):void {
JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent onSuccess", {});
JSLog.warn(message, {});
usingWebRTC = true;
startWebRTCDeskshare();
};
canIUseVertoOnThisBrowser(onFailure, onSuccess);
}
public function handleShareWindowCloseEvent():void {
publishWindowManager.handleShareWindowCloseEvent();
sharing = false;
stopWebRTCDeskshare();
}
public function handleViewWindowCloseEvent():void {
LOGGER.debug("Received stop viewing command");
JSLog.warn("WebRTCDeskshareManager::handleViewWindowCloseEvent", {});
viewWindowManager.handleViewWindowCloseEvent();
}
public function handleStreamStartEvent(e:WebRTCViewStreamEvent):void{
JSLog.warn("WebRTCDeskshareManager::handleStreamStartEvent rtmp=", e.rtmp);
// if (!usingWebRTC) { return; } //TODO this was causing issues
if (sharing) return; //TODO must uncomment this for the non-webrtc desktop share
var isPresenter:Boolean = UserManager.getInstance().getConference().amIPresenter;
JSLog.warn("WebRTCDeskshareManager::handleStreamStartEvent isPresenter=", isPresenter);
LOGGER.debug("Received start viewing command when isPresenter==[{0}]",[isPresenter]);
if(isPresenter) {
publishWindowManager.startViewing(e.rtmp, e.videoWidth, e.videoHeight);
} else {
viewWindowManager.startViewing(e.rtmp, e.videoWidth, e.videoHeight);
}
sharing = true; //TODO must uncomment this for the non-webrtc desktop share
}
public function handleUseJavaModeCommand():void {
JSLog.warn("WebRTCDeskshareManager::handleUseJavaModeCommand", {});
usingWebRTC = false;
}
public function handleRequestStartSharingEvent():void {
JSLog.warn("WebRTCDeskshareManager::handleRequestStartSharingEvent", {});
initDeskshare();
handleStartSharingEvent(true);
}
public function handleStreamStartedEvent(event: WebRTCViewStreamEvent):void {
if (UsersUtil.amIPresenter()) {
} else {
/*handleStreamStartEvent(ScreenshareModel.getInstance().streamId, event.width, event.height);*/
handleStreamStartEvent(null);
}
var dispatcher:Dispatcher = new Dispatcher();
dispatcher.dispatchEvent(new WebRTCViewStreamEvent(WebRTCViewStreamEvent.START));
}
/*public function handleIsSharingScreenEvent(event: IsSharingScreenEvent):void {*/
public function handleIsSharingScreenEvent():void {
if (UsersUtil.amIPresenter()) {
} else {
/*handleStreamStartEvent(ScreenshareModel.getInstance().streamId, event.width, event.height);*/
handleStreamStartEvent(null);
}
var dispatcher:Dispatcher = new Dispatcher();
dispatcher.dispatchEvent(new WebRTCViewStreamEvent(WebRTCViewStreamEvent.START));
}
}
}

View File

@ -0,0 +1,86 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.managers
{
import com.asfusion.mate.events.Dispatcher;
import flash.events.TimerEvent;
import flash.utils.Timer;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.OpenWindowEvent;
import org.bigbluebutton.modules.screenshare.services.WebRTCDeskshareService;
import org.bigbluebutton.modules.screenshare.view.components.WebRTCDesktopPublishWindow;
public class WebRTCPublishWindowManager {
private static const LOGGER:ILogger = getClassLogger(PublishWindowManager);
private var shareWindow:WebRTCDesktopPublishWindow;
private var globalDispatcher:Dispatcher;
private var service:WebRTCDeskshareService;
private var buttonShownOnToolbar:Boolean = false;
// Timer to auto-publish webcam. We need this timer to delay
// the auto-publishing until after the Viewers's window has loaded
// to receive the publishing events. Otherwise, the user joining next
// won't be able to view the webcam.
private var autoPublishTimer:Timer;
public function WebRTCPublishWindowManager(service:WebRTCDeskshareService) {
LOGGER.debug("PublishWindowManager init");
globalDispatcher = new Dispatcher();
this.service = service;
}
public function stopSharing():void {
if (shareWindow != null) shareWindow.stopSharing();
}
private function autopublishTimerHandler(event:TimerEvent):void {
shareWindow.shareScreen(true);
}
public function handleShareWindowCloseEvent():void {
closeWindow(shareWindow);
}
private function openWindow(window:IBbbModuleWindow):void {
var event:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
event.window = window;
globalDispatcher.dispatchEvent(event);
}
private function closeWindow(window:IBbbModuleWindow):void {
var event:CloseWindowEvent = new CloseWindowEvent(CloseWindowEvent.CLOSE_WINDOW_EVENT);
event.window = window;
globalDispatcher.dispatchEvent(event);
}
public function startViewing(rtmp:String, videoWidth:Number, videoHeight:Number):void{
shareWindow = new WebRTCDesktopPublishWindow();
shareWindow.visible = true;
openWindow(shareWindow);
shareWindow.startVideo(rtmp, videoWidth, videoHeight);
}
}
}

View File

@ -0,0 +1,78 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.managers
{
import com.asfusion.mate.events.Dispatcher;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.events.CloseWindowEvent;
import org.bigbluebutton.common.events.OpenWindowEvent;
import org.bigbluebutton.modules.screenshare.services.WebRTCDeskshareService;
import org.bigbluebutton.modules.screenshare.view.components.WebRTCDesktopPublishWindow;
import org.bigbluebutton.modules.screenshare.view.components.WebRTCDesktopViewWindow;
public class WebRTCViewerWindowManager {
private static const LOGGER:ILogger = getClassLogger(ViewerWindowManager);
private var viewWindow:WebRTCDesktopViewWindow;
private var shareWindow:WebRTCDesktopPublishWindow;
private var service:WebRTCDeskshareService;
private var isViewing:Boolean = false;
private var globalDispatcher:Dispatcher;
public function WebRTCViewerWindowManager(service:WebRTCDeskshareService) {
this.service = service;
globalDispatcher = new Dispatcher();
}
public function stopViewing():void {
if (isViewing) viewWindow.stopViewing();
}
private function openWindow(window:IBbbModuleWindow):void{
var event:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
event.window = window;
globalDispatcher.dispatchEvent(event);
}
public function handleViewWindowCloseEvent():void {
LOGGER.debug("ViewerWindowManager Received stop viewing command");
closeWindow(viewWindow);
isViewing = false;
}
private function closeWindow(window:IBbbModuleWindow):void {
var event:CloseWindowEvent = new CloseWindowEvent(CloseWindowEvent.CLOSE_WINDOW_EVENT);
event.window = window;
globalDispatcher.dispatchEvent(event);
}
public function startViewing(rtmp:String, videoWidth:Number, videoHeight:Number):void{
LOGGER.debug("ViewerWindowManager::startViewing");
viewWindow = new WebRTCDesktopViewWindow();
viewWindow.startVideo(rtmp, videoWidth, videoHeight);
openWindow(viewWindow);
isViewing = true;
}
}
}

View File

@ -43,6 +43,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
import org.bigbluebutton.modules.screenshare.events.ViewWindowEvent; import org.bigbluebutton.modules.screenshare.events.ViewWindowEvent;
import org.bigbluebutton.modules.screenshare.managers.ScreenshareManager; import org.bigbluebutton.modules.screenshare.managers.ScreenshareManager;
import org.bigbluebutton.modules.screenshare.services.red5.ConnectionEvent; import org.bigbluebutton.modules.screenshare.services.red5.ConnectionEvent;
import org.bigbluebutton.modules.screenshare.events.UseJavaModeCommand;
import org.bigbluebutton.modules.screenshare.events.DeskshareToolbarEvent;
]]> ]]>
</mx:Script> </mx:Script>
<EventHandlers type="{FlexEvent.PREINITIALIZE}"> <EventHandlers type="{FlexEvent.PREINITIALIZE}">
@ -110,5 +112,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<MethodInvoker generator="{ScreenshareManager}" method="handleStartModuleEvent" arguments="{event.module}"/> <MethodInvoker generator="{ScreenshareManager}" method="handleStartModuleEvent" arguments="{event.module}"/>
</EventHandlers> </EventHandlers>
<EventHandlers type="{UseJavaModeCommand.USE_JAVA_MODE}">
<MethodInvoker generator="{ScreenshareManager}" method="handleUseJavaModeCommand"/>
</EventHandlers>
<EventHandlers type="{DeskshareToolbarEvent.STOP}">
<MethodInvoker generator="{ScreenshareManager}" method="handleDeskshareToolbarStopEvent"/>
</EventHandlers>
</EventMap> </EventMap>

View File

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
-->
<EventMap xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="http://mate.asfusion.com/">
<mx:Script>
<![CDATA[
import mx.events.FlexEvent;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.modules.screenshare.events.ModuleEvent;
import org.bigbluebutton.modules.screenshare.events.ShareEvent;
import org.bigbluebutton.modules.screenshare.events.UseJavaModeCommand;
import org.bigbluebutton.modules.screenshare.events.WebRTCShareWindowEvent;
import org.bigbluebutton.modules.screenshare.events.WebRTCStreamEvent;
import org.bigbluebutton.modules.screenshare.events.WebRTCViewStreamEvent;
import org.bigbluebutton.modules.screenshare.events.WebRTCViewWindowEvent;
import org.bigbluebutton.modules.screenshare.managers.WebRTCDeskshareManager;
import org.bigbluebutton.modules.screenshare.events.RequestToStartSharing;
import org.bigbluebutton.modules.screenshare.events.RequestToStopSharing;
import org.bigbluebutton.modules.screenshare.events.ShareStartRequestResponseEvent;
import org.bigbluebutton.modules.screenshare.events.StartedViewingEvent;
import org.bigbluebutton.modules.screenshare.events.StreamStartedEvent;
import org.bigbluebutton.modules.screenshare.events.IsSharingScreenEvent;
import org.bigbluebutton.modules.screenshare.services.red5.WebRTCConnectionEvent;
]]>
</mx:Script>
<EventHandlers type="{FlexEvent.PREINITIALIZE}">
<!--
The FlexEvent.PREINITIALIZE event is a good place for creating and initializing managers.
-->
<ObjectBuilder generator="{WebRTCDeskshareManager}"/>
</EventHandlers>
<EventHandlers type="{ShareEvent.START_SHARING}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStartSharingEvent" arguments="{false}"/>
</EventHandlers>
<EventHandlers type="{BBBEvent.START_DESKSHARE}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStartSharingEvent" arguments="{true}"/>
</EventHandlers>
<EventHandlers type="{RequestToStartSharing.REQUEST_SHARE_START}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleRequestStartSharingEvent"/>
</EventHandlers>
<EventHandlers type="{RequestToStopSharing.REQUEST_SHARE_STOP}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleRequestStopSharingEvent"/>
</EventHandlers>
<EventHandlers type="{ShareStartRequestResponseEvent.SHARE_START_REQUEST_RESPONSE}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleShareStartRequestResponseEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleMadePresenterEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{MadePresenterEvent.SWITCH_TO_VIEWER_MODE}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleMadeViewerEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{WebRTCStreamEvent.STOP}" >
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStreamStoppedEvent"/>
</EventHandlers>
<EventHandlers type="{WebRTCViewStreamEvent.START}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStreamStartEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{WebRTCViewStreamEvent.STOP}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStreamStopEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{StreamStartedEvent.STREAM_STARTED}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStreamStartedEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{IsSharingScreenEvent.IS_SCREENSHARING}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleIsSharingScreenEvent" arguments="{event}"/>
</EventHandlers>
<EventHandlers type="{WebRTCShareWindowEvent.CLOSE}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleShareWindowCloseEvent"/>
</EventHandlers>
<EventHandlers type="{WebRTCConnectionEvent.SUCCESS}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleConnectionSuccessEvent"/>
</EventHandlers>
<EventHandlers type="{StartedViewingEvent.STARTED_VIEWING_EVENT}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStartedViewingEvent" arguments="{event.stream}"/>
</EventHandlers>
<EventHandlers type="{WebRTCViewWindowEvent.CLOSE}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleViewWindowCloseEvent"/>
</EventHandlers>
<EventHandlers type="{ModuleEvent.STOP}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStopModuleEvent"/>
</EventHandlers>
<EventHandlers type="{ModuleEvent.START}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleStartModuleEvent" arguments="{event.module}"/>
</EventHandlers>
<EventHandlers type="{UseJavaModeCommand.USE_JAVA_MODE}">
<MethodInvoker generator="{WebRTCDeskshareManager}" method="handleUseJavaModeCommand"/>
</EventHandlers>
</EventMap>

View File

@ -19,15 +19,17 @@
package org.bigbluebutton.modules.screenshare.model package org.bigbluebutton.modules.screenshare.model
{ {
import org.bigbluebutton.core.BBB; import org.bigbluebutton.core.BBB;
public class ScreenshareOptions public class ScreenshareOptions
{ {
[Bindable] public var showButton:Boolean = true; [Bindable] public var showButton:Boolean = true;
[Bindable] public var autoStart:Boolean = false; [Bindable] public var autoStart:Boolean = false;
[Bindable] public var autoFullScreen:Boolean = false; [Bindable] public var autoFullScreen:Boolean = false;
[Bindable] public var baseTabIndex:int; [Bindable] public var baseTabIndex:int;
[Bindable] public var useWebRTCIfAvailable:Boolean = true;
[Bindable] public var chromeExtensionKey:String = null;
[Bindable] public var helpUrl:String; [Bindable] public var helpUrl:String;
public function parseOptions():void { public function parseOptions():void {
var vxml:XML = BBB.getConfigForModule("ScreenshareModule"); var vxml:XML = BBB.getConfigForModule("ScreenshareModule");
if (vxml != null) { if (vxml != null) {
@ -44,7 +46,13 @@ package org.bigbluebutton.modules.screenshare.model
baseTabIndex = 201; baseTabIndex = 201;
} }
if (vxml.@showButton != undefined){ 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){ if (vxml.@help != undefined){
helpUrl = vxml.@help; helpUrl = vxml.@help;
@ -52,4 +60,4 @@ package org.bigbluebutton.modules.screenshare.model
} }
} }
} }
} }

View File

@ -0,0 +1,74 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.services
{
import com.asfusion.mate.events.Dispatcher;
import flash.net.NetConnection;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.modules.screenshare.services.red5.WebRTCConnection;
/**
* The DeskShareProxy communicates with the Red5 deskShare server application
* @author Snap
*
*/
public class WebRTCDeskshareService
{
private static const LOGGER:ILogger = getClassLogger(ScreenshareService);
private var conn:WebRTCConnection;
private var module:ScreenshareModule;
private var dispatcher:Dispatcher;
private var uri:String;
private var room:String;
public function WebRTCDeskshareService() {
this.dispatcher = new Dispatcher();
}
public function handleStartModuleEvent(module:ScreenshareModule):void {
LOGGER.debug("Deskshare Module starting");
this.module = module;
connect(module.uri, module.getRoom());
}
public function connect(uri:String, room:String):void {
this.uri = uri;
this.room = room;
LOGGER.debug("Deskshare Service connecting to {0}", [uri]);
conn = new WebRTCConnection(room); //to red5 deskshare
conn.setURI(uri);
conn.connect();
}
public function getConnection():NetConnection{
return conn.getConnection();
}
public function disconnect():void{
conn.disconnect();
}
}
}

View File

@ -0,0 +1,323 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.services.red5
{
import com.asfusion.mate.events.Dispatcher;
import flash.events.AsyncErrorEvent;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.events.TimerEvent;
import flash.net.NetConnection;
import flash.net.ObjectEncoding;
import flash.net.Responder;
import flash.utils.Timer;
import mx.utils.ObjectUtil;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.core.managers.ReconnectionManager;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.modules.screenshare.events.WebRTCViewStreamEvent;
import org.bigbluebutton.modules.screenshare.services.red5.WebRTCConnectionEvent;
public class WebRTCConnection {
private static const LOGGER:ILogger = getClassLogger(Connection);
private var nc:NetConnection;
private var uri:String;
private var retryTimer:Timer = null;
private var retryCount:int = 0;
private const MAX_RETRIES:int = 5;
private var responder:Responder;
private var width:Number;
private var height:Number;
private var room:String;
private var logoutOnUserCommand:Boolean = false;
private var reconnecting:Boolean = false;
private var wasPresenterBeforeDisconnect:Boolean = false;
private var dispatcher:Dispatcher = new Dispatcher();
public function WebRTCConnection(room:String) {
this.room = room;
responder = new Responder(
function(result:Object):void {
if (result != null && (result.publishing as Boolean)){
width = result.width as Number;
height = result.height as Number;
LOGGER.debug("Desk Share stream is streaming [{0},{1}]", [width, height]);
var event:WebRTCViewStreamEvent = new WebRTCViewStreamEvent(WebRTCViewStreamEvent.START);
event.videoWidth = width;
event.videoHeight = height;
dispatcher.dispatchEvent(event); //TODO why?
} else {
LOGGER.debug("No screenshare stream being published");
var connEvent:WebRTCConnectionEvent = new WebRTCConnectionEvent();
connEvent.status = WebRTCConnectionEvent.NO_DESKSHARE_STREAM;
dispatcher.dispatchEvent(connEvent); //TODO why?
}
},
function(status:Object):void{
var checkFailedEvent:WebRTCConnectionEvent = new WebRTCConnectionEvent();
checkFailedEvent.status = WebRTCConnectionEvent.FAIL_CHECK_FOR_DESKSHARE_STREAM;
dispatcher.dispatchEvent(checkFailedEvent);
LOGGER.debug("Error while trying to call remote mathod on server");
}
);
}
public function connect(retry:Boolean = false):void {
nc = new NetConnection();
nc.proxyType = "best";
nc.objectEncoding = ObjectEncoding.AMF0;
nc.client = this;
nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, debugAsyncErrorHandler);
nc.addEventListener(NetStatusEvent.NET_STATUS, debugNetStatusHandler);
nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
if (getURI().length == 0){
LOGGER.error("please provide a valid URI connection string. URI Connection String missing");
return;
} else if (nc.connected){
LOGGER.error("You are already connected to {0}", [getURI()]);
return;
}
LOGGER.debug("Trying to connect to [{0}] retry=[{1}]", [getURI(), retry]);
if (! (retryCount > 0)) {
var ce:WebRTCConnectionEvent = new WebRTCConnectionEvent();
ce.status = WebRTCConnectionEvent.CONNECTING;
dispatcher.dispatchEvent(ce);
}
nc.connect(getURI(), UsersUtil.getInternalMeetingID());
}
private function connectTimeoutHandler(e:TimerEvent):void {
LOGGER.debug("Connection attempt to [{0}] timedout. Retrying.", [getURI()]);
retryTimer.stop();
retryTimer = null;
nc.close();
nc = null;
var ce:WebRTCConnectionEvent = new WebRTCConnectionEvent();;
retryCount++;
if (retryCount < MAX_RETRIES) {
ce.status = WebRTCConnectionEvent.CONNECTING_RETRY;
ce.retryAttempts = retryCount;
dispatcher.dispatchEvent(ce);
connect(false);
} else {
ce.status = WebRTCConnectionEvent.CONNECTING_MAX_RETRY;
dispatcher.dispatchEvent(ce);
}
}
public function close():void{
nc.close();
}
public function setURI(p_URI:String):void{
uri = p_URI;
}
public function getURI():String{
return uri;
}
public function onBWCheck(... rest):Number {
return 0;
}
public function onBWDone(... rest):void {
var p_bw:Number;
if (rest.length > 0) p_bw = rest[0];
// your application should do something here
// when the bandwidth check is complete
LOGGER.debug("bandwidth = {0} Kbps.", [p_bw]);
}
private function netStatusHandler(event:NetStatusEvent):void {
LOGGER.debug("Connected to [" + getURI() + "]. [" + event.info.code + "]");
if (retryTimer) {
retryCount = 0;
LOGGER.debug("Cancelling retry timer.");
retryTimer.stop();
retryTimer = null;
}
var ce:WebRTCConnectionEvent = new WebRTCConnectionEvent();
switch(event.info.code){
case "NetConnection.Connect.Failed":
if (reconnecting) {
var attemptFailedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_FAILED_EVENT);
attemptFailedEvent.payload.type = ReconnectionManager.DESKSHARE_CONNECTION;
dispatcher.dispatchEvent(attemptFailedEvent);
}
ce.status = WebRTCConnectionEvent.FAILED;
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.Success":
ce.status = WebRTCConnectionEvent.SUCCESS;
if (reconnecting) {
reconnecting = false;
if (wasPresenterBeforeDisconnect) {
wasPresenterBeforeDisconnect = false;
// stopSharingDesktop(room, room) //TODO
}
var attemptSucceeded:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_CONNECTION_ATTEMPT_SUCCEEDED_EVENT);
attemptSucceeded.payload.type = ReconnectionManager.DESKSHARE_CONNECTION;
dispatcher.dispatchEvent(attemptSucceeded);
}
// request desktop sharing info (as a late joiner)
LOGGER.debug("Sending [desktopSharing.requestDeskShareInfo] to server.");
var _nc:ConnectionManager = BBB.initConnectionManager();
_nc.sendMessage("desktopSharing.requestDeskShareInfo",
function(result:String):void { // On successful result
LOGGER.debug(result);
},
function(status:String):void { // status - On error occurred
LOGGER.error(status);
}
);
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.Rejected":
ce.status = WebRTCConnectionEvent.REJECTED;
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.Closed":
LOGGER.debug("Deskshare connection closed.");
ce.status = WebRTCConnectionEvent.CLOSED;
if (UsersUtil.amIPresenter()) {
// Let's keep our presenter status before disconnected. We can't
// tell the other user's to stop desktop sharing as our connection is broken. (ralam july 24, 2015)
wasPresenterBeforeDisconnect = true;
} else {
// stopViewing(); //TODO
}
if (!logoutOnUserCommand) {
reconnecting = true;
var disconnectedEvent:BBBEvent = new BBBEvent(BBBEvent.RECONNECT_DISCONNECTED_EVENT);
disconnectedEvent.payload.type = ReconnectionManager.DESKSHARE_CONNECTION;
disconnectedEvent.payload.callback = connect;
disconnectedEvent.payload.callbackParameters = [];
dispatcher.dispatchEvent(disconnectedEvent);
}
break;
case "NetConnection.Connect.InvalidApp":
ce.status = WebRTCConnectionEvent.INVALIDAPP;
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.AppShutdown":
ce.status = WebRTCConnectionEvent.APPSHUTDOWN;
dispatcher.dispatchEvent(ce);
break;
case "NetConnection.Connect.NetworkChange":
LOGGER.info("Detected network change. User might be on a wireless and temporarily dropped connection. Doing nothing. Just making a note.");
break;
default :
// I dispatch DISCONNECTED incase someone just simply wants to know if we're not connected'
// rather than having to subscribe to the events individually
ce.status = WebRTCConnectionEvent.DISCONNECTED;
dispatcher.dispatchEvent(ce);
break;
}
}
private function securityErrorHandler(event:SecurityErrorEvent):void{
var ce:WebRTCConnectionEvent = new WebRTCConnectionEvent();
ce.status = WebRTCConnectionEvent.SECURITYERROR;
dispatcher.dispatchEvent(ce);
}
/**
* Check if anybody is publishing the stream for this room
* This method is useful for clients which have joined a room where somebody is already publishing
*
*/
private function checkIfStreamIsPublishing(room: String):void{
LOGGER.debug("checking if desk share stream is publishing");
var event:WebRTCConnectionEvent = new WebRTCConnectionEvent();
event.status = WebRTCConnectionEvent.CHECK_FOR_DESKSHARE_STREAM;
dispatcher.dispatchEvent(event); // TODO anton send to akka-bbb-apps
nc.call("screenshare.checkIfStreamIsPublishing", responder, room);
}
public function disconnect():void{
logoutOnUserCommand = true;
if (nc != null) nc.close();
}
public function connectionSuccessHandler():void{
LOGGER.debug("Successully connection to {0}", [uri]);
checkIfStreamIsPublishing(room);
}
private function debugNetStatusHandler(e:NetStatusEvent):void {
LOGGER.debug("netStatusHandler target={0} info={1}", [e.target, ObjectUtil.toString(e.info)]);
}
private function debugAsyncErrorHandler(e:AsyncErrorEvent):void {
LOGGER.debug("asyncErrorHandler target={0} info={1}", [e.target, e.text]);
}
public function getConnection():NetConnection{
return nc;
}
public function connectionFailedHandler(e:WebRTCConnectionEvent):void{
LOGGER.error("connection failed to {0} with message {1}", [uri, e.toString()]);
}
public function connectionRejectedHandler(e:WebRTCConnectionEvent):void{
LOGGER.error("connection rejected to {0} with message {1}", [uri, e.toString()]);
}
}
}

View File

@ -0,0 +1,49 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.services.red5
{
import flash.events.Event;
public class WebRTCConnectionEvent extends Event {
public static const DESKSHARE_CONNECTION_EVENT:String = "webrtcDeskshare connection event";
public static const SUCCESS:String = "connection success";
public static const FAILED:String = "connection failed";
public static const CLOSED:String = "connection closed";
public static const REJECTED:String = "connection rejected";
public static const INVALIDAPP:String = "connection invalidApp";
public static const APPSHUTDOWN:String = "connection appShutdown";
public static const SECURITYERROR:String = "connection securityError";
public static const DISCONNECTED:String = "connection disconnected";
public static const CONNECTING:String = "connection connecting";
public static const CONNECTING_RETRY:String = "connection retry";
public static const CONNECTING_MAX_RETRY:String = "connection max retry";
public static const CHECK_FOR_DESKSHARE_STREAM:String = "connection check webrtcDeskshare publishing";
public static const NO_DESKSHARE_STREAM:String = "connection webrtcDeskshare publishing";
public static const FAIL_CHECK_FOR_DESKSHARE_STREAM:String = "connection failed check webrtcDeskshare publishing";
public var status:String = "";
public var retryAttempts:int = 0;
public function WebRTCConnectionEvent():void {
super(DESKSHARE_CONNECTION_EVENT, true, false);
}
}
}

View File

@ -0,0 +1,62 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.modules.screenshare.utils
{
import flash.external.ExternalInterface;
import org.bigbluebutton.core.BBB;
import flash.system.Capabilities;
import org.as3commons.logging.api.getClassLogger;
import org.as3commons.logging.api.ILogger;
public class BrowserCheck {
private static const LOGGER:ILogger = getClassLogger(BrowserCheck);
public static function isChrome42OrHigher():Boolean {
var browser:Array = ExternalInterface.call("determineBrowser");
return ((browser[0] == "Chrome") && (parseInt(browser[1]) >= 42));
}
public static function isUsingLessThanChrome38OnMac():Boolean {
var browser:Array = ExternalInterface.call("determineBrowser");
return ((browser[0] == "Chrome") && (parseInt(browser[1]) <= 38) && (Capabilities.os.indexOf("Mac") >= 0));
}
public static function isWebRTCSupported():Boolean {
/*LOGGER.debug("isWebRTCSupported - ExternalInterface.available=[{0}], isWebRTCAvailable=[{1}]", [ExternalInterface.available, ExternalInterface.call("isWebRTCAvailable")]);*/
return (ExternalInterface.available && ExternalInterface.call("isWebRTCAvailable"));
}
public static function isUsingEdgePluginUnsupported():Boolean {
var browser:Array = ExternalInterface.call("determineBrowser");
/* Currently no browser version of Edge supports plugins */
return browser[0] == "Edge";
}
public static function isChrome():Boolean {
var browser:Array = ExternalInterface.call("determineBrowser");
return browser[0] == "Chrome";
}
public static function isFirefox():Boolean {
var browser:Array = ExternalInterface.call("determineBrowser");
return browser[0] == "Firefox";
}
}
}

View File

@ -0,0 +1,384 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
-->
<dspub:CustomMdiWindow
xmlns:mx="http://www.adobe.com/2006/mxml"
implements="org.bigbluebutton.common.IBbbModuleWindow"
xmlns:mate="http://mate.asfusion.com/"
xmlns:dspub="org.bigbluebutton.common.*"
backgroundColor="#C0C0C0"
initialize="init()"
creationComplete="onCreationComplete()"
verticalScrollPolicy="off" horizontalScrollPolicy="off"
width="365" height="350"
title="{ResourceUtil.getInstance().getString('bbb.desktopPublish.title')}"
resizable="false">
<mate:Listener type="{MadePresenterEvent.SWITCH_TO_PRESENTER_MODE}" method="onChangedPresenter" />
<mate:Listener type="{MadePresenterEvent.SWITCH_TO_VIEWER_MODE}" method="onChangedPresenter" />
<mate:Listener type="{WebRTCViewStreamEvent.STOP}" method="closePublishWindow" />
<mate:Listener type="{LocaleChangeEvent.LOCALE_CHANGED}" method="localeChanged" />
<mate:Listener type="{StopSharingButtonEvent.STOP_SHARING}" method="stopSharingEvent" />
<mate:Listener type="{ShortcutEvent.REMOTE_FOCUS_DESKTOP}" method="remoteFocus" />
<mx:Script>
<![CDATA[
import com.asfusion.mate.events.Dispatcher;
import mx.core.UIComponent;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.common.events.LocaleChangeEvent;
import org.bigbluebutton.main.events.BBBEvent;
import org.bigbluebutton.main.events.MadePresenterEvent;
import org.bigbluebutton.main.events.ShortcutEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.screenshare.events.WebRTCShareWindowEvent;
import org.bigbluebutton.modules.screenshare.events.StopSharingButtonEvent;
import org.bigbluebutton.modules.screenshare.events.WebRTCStreamEvent;
import org.bigbluebutton.modules.screenshare.events.WebRTCViewStreamEvent;
import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions;
import org.bigbluebutton.modules.screenshare.services.red5.WebRTCConnectionEvent;
import org.bigbluebutton.util.i18n.ResourceUtil;
import org.bigbluebutton.main.api.JSLog;
private static const LOGGER:ILogger = getClassLogger(WebRTCDesktopPublishWindow);
public static const SCALE:Number = 5;
private static const VID_HEIGHT_PAD:Number = 73;
private static const VID_WIDTH_PAD:Number = 6;
private var images:Images = new Images();
[Bindable] public var bbbLogo:Class = images.bbb_logo;
private var connection:NetConnection;
private var uri:String;
private var useTLS:Boolean;
private var room:String;
private var streaming:Boolean = false;
private var video:Video;
private var ns:NetStream;
[Bindable] private var videoHolder:UIComponent;
private var stream:String;
private var videoHeight:Number;
private var videoWidth:Number;
private var captureHeight:Number = Capabilities.screenResolutionY;
private var captureWidth:Number = Capabilities.screenResolutionX;
private var autoStart:Boolean = false;
private var globalDispatcher:Dispatcher = new Dispatcher();
[Bindable] private var dsOptions:ScreenshareOptions;
private function init():void {
dsOptions = new ScreenshareOptions();
}
private function onCreationComplete():void {
windowControls.maximizeRestoreBtn.enabled = false;
setCurrentState("dispFullRegionControlBar"); //TODO ANTON
resourcesChanged();
titleBarOverlay.tabIndex = dsOptions.baseTabIndex;
titleBarOverlay.focusEnabled = true;
}
private function remoteFocus(e:ShortcutEvent):void{
focusManager.setFocus(minimizeBtn);
}
public function get defaultWidth():int{
return this.width;
}
public function get defaultHeight():int{
return this.height;
}
public function set defaultHeight(height:int):void{
this.height = height;
}
public function set defaultWidth(width:int):void{
this.width = width;
}
public function getPrefferedPosition():String{
return MainCanvas.DESKTOP_SHARING_PUBLISH;
}
/*
* Implement resizeable interface.
*/
public function resetWidthAndHeight():void{/* do nothing */}
public function initWindow(connection:NetConnection, uri:String, useTLS:Boolean , room:String, autoStart:Boolean, autoFullScreen:Boolean):void {
this.connection = connection;
this.uri = uri;
this.useTLS = useTLS;
this.room = room;
this.autoStart = autoStart;
/*if(autoFullScreen)
shareScreen(true);*/
}
public function shareScreen(fullScreen:Boolean):void {
LOGGER.debug("Calling shareScreen");
startSharing(connection, uri, useTLS , room, fullScreen);
}
private function startSharing(connection:NetConnection, uri:String , useTLS:Boolean , room:String, fullScreen:Boolean):void {
var captureX:Number = 0;
var captureY:Number = 0;
}
public function stopSharing():void{
if (streaming) {
stopStream();
var streamEvent:WebRTCStreamEvent = new WebRTCStreamEvent(WebRTCStreamEvent.STOP);
dispatchEvent(streamEvent);
}
streaming = false;
closeWindow();
}
public function stopSharingEvent(evt:StopSharingButtonEvent):void{
JSLog.warn("stopSharingEvent WebRTCDesktopPublishWindow.mxml", {});
stopSharing();
}
// TODO make this function private and trigger it via message
public function startVideo(rtmp:String, videoWidth:Number, videoHeight:Number):void {
var myArray :Array = rtmp.split('/');
var meetingUrl:String = rtmp.substring(0, rtmp.lastIndexOf('/'));
stream = rtmp.substring(rtmp.lastIndexOf('/')+1, rtmp.length);
LOGGER.debug("WebRTCDesktopPublishWindow::startVideo meetingurl=[{0}] and stream=[{1}]",[meetingUrl, stream]);
JSLog.warn("WebRTCDesktopPublishWindow::startVideo meetingurl= ",meetingUrl);
JSLog.warn("WebRTCDesktopPublishWindow::startVideo stream=", stream);
connection = new NetConnection();
connection.proxyType = "best";
connection.objectEncoding = ObjectEncoding.AMF0;
connection.client = this;
connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
connection.connect(meetingUrl);
}
private function netStatusHandler(event:NetStatusEvent):void {
var ce:WebRTCConnectionEvent = new WebRTCConnectionEvent();
LOGGER.debug("netStatusHandler [{0}]",[event.info.code]);
switch(event.info.code){
case "NetConnection.Connect.Failed":
ce.status = WebRTCConnectionEvent.FAILED;
break;
case "NetConnection.Connect.Success":
ce.status = WebRTCConnectionEvent.SUCCESS;
startPreviewStream(stream, videoWidth, videoHeight);
break;
case "NetConnection.Connect.Rejected":
ce.status = WebRTCConnectionEvent.REJECTED;
break;
case "NetConnection.Connect.Closed":
trace("Deskshare connection closed.");
ce.status = WebRTCConnectionEvent.CLOSED;
break;
case "NetConnection.Connect.InvalidApp":
ce.status = WebRTCConnectionEvent.INVALIDAPP;
break;
case "NetConnection.Connect.AppShutdown":
ce.status = WebRTCConnectionEvent.APPSHUTDOWN;
break;
case "NetConnection.Connect.NetworkChange":
trace("Detected network change. User might be on a wireless and " +
"temporarily dropped connection. Doing nothing. Just making a note.");
break;
}
}
private function securityErrorHandler(event:SecurityErrorEvent):void {
LOGGER.debug("ERROR WebRTCDesktopPublishWindow::securityErrorHandler ");
}
private function startPreviewStream(streamName:String, capWidth:Number, capHeight:Number):void{
streaming = true;
videoHolder = new UIComponent();
var vidW:Number = captureWidth;
var vidH:Number = captureHeight;
// Don't scale if capture dimension is smaller than window.
if((captureWidth > this.width - VID_WIDTH_PAD) && (captureHeight < this.height - VID_HEIGHT_PAD)){
vidW = this.width - VID_WIDTH_PAD;
vidH = (captureHeight / captureWidth) * vidW;
}
else if( ((captureWidth < this.width - VID_WIDTH_PAD) && (captureHeight > this.height - VID_HEIGHT_PAD))
|| ((captureWidth > this.width - VID_WIDTH_PAD) && (captureHeight > this.height - VID_HEIGHT_PAD)) ){
vidH = this.height - VID_HEIGHT_PAD;
vidW = (captureWidth / captureHeight) * vidH;
}
else{
vidW = captureWidth;
vidH = captureHeight;
}
LOGGER.debug("webrtcDeskshare preview[{0},{1}][{2},{3}]", [captureWidth, captureHeight, vidW, vidH]);
video = new Video(vidW, vidH);
video.width = vidW;
video.height = vidH;
videoHolder.width = vidW;
videoHolder.height = vidH;
video.x = videoHolder.x = (this.width - VID_WIDTH_PAD - vidW) / 2;
video.y = videoHolder.y = (this.height - VID_HEIGHT_PAD - vidH) / 2;
videoHolder.addChild(video);
this.addChild(videoHolder);
ns = new NetStream(connection);
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
ns.client = this;
ns.bufferTime = 0;
ns.receiveVideo(true);
ns.receiveAudio(false);
video.attachNetStream(ns);
ns.play(stream);
btnClosePublish.enabled = true;
}
private function stopStream():void{
streaming = false;
captureHeight = Capabilities.screenResolutionY;
captureWidth = Capabilities.screenResolutionX;
ns.close();
}
private function onAsyncError(e:AsyncErrorEvent):void{
LOGGER.debug("VIdeoWindow::asyncerror {0}", [e.toString()]);
}
private function onNetStatus(e:NetStatusEvent):void{
switch(e.info.code){
case "NetStream.Publish.Start":
LOGGER.debug("NetStream.Publish.Start for broadcast stream {0}", [stream]);
break;
case "NetStream.Play.UnpublishNotify":
LOGGER.debug("NetStream.Play.UnpublishNotify for broadcast stream {0}", [stream]);
stopSharing();
break;
case "NetStream.Play.Start":
LOGGER.debug("Netstatus: {0}", [e.info.code]);
globalDispatcher.dispatchEvent(new BBBEvent(BBBEvent.DESKSHARE_STARTED));
}
}
private function onChangedPresenter(e:Event):void{
stopSharing();
closeWindow();
}
private function closeWindow():void {
// LOGGER.debug("Calling stopApplet in closeWindow()");
dispatchEvent(new WebRTCShareWindowEvent(WebRTCShareWindowEvent.CLOSE));
}
/*
* Override the close handler. We want the Event Map to send a message to
* the MDIManager to close this window;
*/
override public function close(event:MouseEvent = null):void {
stopSharing();
closeWindow();
}
override protected function resourcesChanged():void{
super.resourcesChanged();
this.title = ResourceUtil.getInstance().getString('bbb.desktopPublish.title');
if (titleBarOverlay != null) {
titleBarOverlay.accessibilityName = ResourceUtil.getInstance().getString('bbb.desktopPublish.title');
}
if (windowControls != null) {
minimizeBtn.toolTip = ResourceUtil.getInstance().getString('bbb.desktopPublish.minimizeBtn.toolTip');
minimizeBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.desktopPublish.minimizeBtn.accessibilityName");
maximizeRestoreBtn.toolTip = ResourceUtil.getInstance().getString('bbb.desktopPublish.maximizeRestoreBtn.toolTip');
maximizeRestoreBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.desktopPublish.maximizeRestoreBtn.accessibilityName");
closeBtn.toolTip = ResourceUtil.getInstance().getString('bbb.desktopPublish.closeBtn.toolTip');
closeBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.desktopPublish.closeBtn.accessibilityName");
}
}
private function localeChanged(e:Event):void{
resourcesChanged();
}
private function closePublishWindow(event:WebRTCViewStreamEvent):void{
stopStream();
closeWindow();
}
]]>
</mx:Script>
<dspub:TabIndexer startIndex="{dsOptions.baseTabIndex + 1}"
tabIndices="{[minimizeBtn, maximizeRestoreBtn, closeBtn, btnClosePublish]}"/>
<dspub:states>
<mx:State name="dispFullRegionControlBar">
<mx:AddChild>
<mx:ControlBar id="fullRegionBottomBar">
<mx:VBox width="100%" height="100%" horizontalAlign="center">
<mx:HBox horizontalAlign="center">
<mx:Button id="btnClosePublish"
toolTip="{ResourceUtil.getInstance().getString('bbb.desktopPublish.stop.tooltip')}"
label="{ResourceUtil.getInstance().getString('bbb.desktopPublish.stop.label')}"
visible="true"
enabled="false"
click="stopSharing()" />
</mx:HBox>
</mx:VBox>
</mx:ControlBar>
</mx:AddChild>
</mx:State>
</dspub:states>
</dspub:CustomMdiWindow>

View File

@ -0,0 +1,388 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2015 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
-->
<CustomMdiWindow xmlns="org.bigbluebutton.common.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:common="org.bigbluebutton.common.*"
width="600" height="400"
initialize="init()"
implements="org.bigbluebutton.common.IBbbModuleWindow"
xmlns:mate="http://mate.asfusion.com/"
title="{ResourceUtil.getInstance().getString('bbb.desktopView.title')}"
showCloseButton="false"
resize="fitToWindow()" >
<mate:Listener type="{ViewStreamEvent.STOP}" method="onStopViewStreamEvent" />
<mate:Listener type="{LocaleChangeEvent.LOCALE_CHANGED}" method="localeChanged" />
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
import flexlib.mdi.events.MDIWindowEvent;
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.common.IBbbModuleWindow;
import org.bigbluebutton.common.Images;
import org.bigbluebutton.common.events.LocaleChangeEvent;
import org.bigbluebutton.main.views.MainCanvas;
import org.bigbluebutton.modules.screenshare.events.ViewStreamEvent;
import org.bigbluebutton.modules.screenshare.events.ViewWindowEvent;
import org.bigbluebutton.modules.screenshare.model.ScreenshareOptions;
import org.bigbluebutton.util.i18n.ResourceUtil;
import org.bigbluebutton.modules.screenshare.services.red5.WebRTCConnectionEvent;
public static const LOG:String = "Deskshare::DesktopViewWindow - ";
private var screenHeight:Number = Capabilities.screenResolutionY;
private var screenWidth:Number = Capabilities.screenResolutionX;
private var images:Images = new Images();
[Bindable] public var fitToWidthIcon:Class = images.magnifier;
[Bindable] public var fitToActualSizeIcon:Class = images.mag_reset;
private var video:Video;
private var ns:NetStream;
private var videoHolder:UIComponent = new UIComponent();
private var stream:String;
private var videoHeight:Number;
private var videoWidth:Number;
private static const LOGGER:ILogger = getClassLogger(WebRTCDesktopViewWindow);
private static const VIDEO_WIDTH_PADDING:int = 7;
private static const VIDEO_HEIGHT_PADDING:int = 65;
// The following code block is to deal with a bug in FLexLib
// with MDI windows not responding well to being maximized
private var savedWindowWidth:Number;
private var savedWindowHeight:Number;
private var savedX:Number;
private var savedY:Number;
private var isMaximized:Boolean = false;
private var streamID:String;
private var nc:NetConnection;
[Bindable] private var baseIndex:int;
[Bindable] private var dsOptions:ScreenshareOptions;
private function init():void{
dsOptions = new ScreenshareOptions();
}
private function displayVideo():void{
videoHolder.addChild(video);
this.addChild(videoHolder);
videoHolder.percentWidth = 100;
videoHolder.percentHeight = 100;
addEventListener(MDIWindowEvent.RESIZE_END, onResizeEndEvent);
fitToActualSize();
maximize();
resourcesChanged();
titleBarOverlay.tabIndex = dsOptions.baseTabIndex;
minimizeBtn.tabIndex = baseIndex+1;
maximizeRestoreBtn.tabIndex = baseIndex+2;
closeBtn.tabIndex = baseIndex+3;
}
private function onResizeEndEvent(event:MDIWindowEvent):void {
if (event.window == this) {
fitToWindow();
}
}
public function startVideo(rtmp:String, width:Number, height:Number):void{
var videoURL:String;
// split rtmp extract the streamId
this.videoWidth = width;
this.videoHeight = height;
var myArray :Array = rtmp.split('/');
// myArray.forEach ( function ( item:*, i:int, arr:Array) : void { JSLog.debug("^^^^" + i +"aa=" + item, logData); } );
streamID = myArray[5]; // Grab the streamID from the end of the rtmp string
videoURL = rtmp.substring(0, rtmp.lastIndexOf('/'));
connect(videoURL);
}
public function connect(rtmpUrl: String):void {
nc = new NetConnection();
nc.proxyType = "best";
nc.objectEncoding = ObjectEncoding.AMF0;
nc.client = this;
nc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
nc.connect(rtmpUrl);
}
public function connectionSuccessHandler():void{
LOGGER.debug("SUCCESS DesktopViewWindow::connectionSuccessHandler SUCCESS" + videoHeight + videoWidth + streamID);
ns = new NetStream(nc);
ns.addEventListener( NetStatusEvent.NET_STATUS, onNetStatus );
ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onAsyncError);
ns.client = this;
ns.bufferTime = 0;
ns.receiveVideo(true);
ns.receiveAudio(false);
video = new Video(videoWidth, videoHeight);
video.attachNetStream(ns);
ns.play(streamID);
displayVideo();
this.title = "Viewing Remote Desktop";
}
private function netStatusHandler(event:NetStatusEvent):void {
// trace(LOG + "Connected to [" + getURI() + "]. [" + event.info.code + "]");
var ce:WebRTCConnectionEvent = new WebRTCConnectionEvent();
switch(event.info.code){
case "NetConnection.Connect.Failed":
ce.status = WebRTCConnectionEvent.FAILED;
break;
case "NetConnection.Connect.Success":
ce.status = WebRTCConnectionEvent.SUCCESS;
connectionSuccessHandler();
break;
case "NetConnection.Connect.Rejected":
ce.status = WebRTCConnectionEvent.REJECTED;
break;
case "NetConnection.Connect.Closed":
trace(LOG + "Deskshare connection closed.");
ce.status = WebRTCConnectionEvent.CLOSED;
break;
case "NetConnection.Connect.InvalidApp":
ce.status = WebRTCConnectionEvent.INVALIDAPP;
break;
case "NetConnection.Connect.AppShutdown":
ce.status = WebRTCConnectionEvent.APPSHUTDOWN;
break;
case "NetConnection.Connect.NetworkChange":
trace(LOG + "Detected network change. User might be on a wireless and temporarily dropped connection. Doing nothing. Just making a note.");
break;
}
}
private function securityErrorHandler(event:SecurityErrorEvent):void {
LOGGER.debug("ERROR DesktopViewWindow::securityErrorHandler ");
}
public function stopViewing():void {
ns.close();
closeWindow();
}
private function onStopViewStreamEvent(event:ViewStreamEvent):void {
stopViewing();
}
protected function updateButtonsPosition():void {
if (this.width < bottomBar.width) {
bottomBar.visible = false;
}
if (bottomBar.visible == false) {
bottomBar.y = bottomBar.x = 0;
} else {
bottomBar.y = (this.height - bottomBar.height) / 2;
bottomBar.x = (this.width - bottomBar.width) / 2;
}
}
private function onAsyncError(e:AsyncErrorEvent):void{
LOGGER.debug("VIdeoWindow::asyncerror {0}", [e.toString()]);
}
private function onNetStatus(e:NetStatusEvent):void{
LOGGER.debug("onNetStatus info={0}", [e.info.text]);
switch(e.info.code){
case "NetStream.Play.Start":
LOGGER.debug("NetStream.Publish.Start for broadcast stream {0}", [stream]);
LOGGER.debug("Dispatching start viewing event");
// TODO - do we want to know when a client has displayed ds view?
// var dispatcher: Dispatcher = new Dispatcher();
// var viewEvent:StartedViewingEvent = new StartedViewingEvent(StartedViewingEvent.STARTED_VIEWING_EVENT);
// viewEvent.stream = stream;
// dispatcher.dispatchEvent(viewEvent);
break;
case "NetStream.Play.UnpublishNotify":
LOGGER.debug("NetStream.Play.UnpublishNotify for broadcast stream {0}", [stream]);
stopViewing();
break;
}
}
public function onBWCheck(... rest):Number {
return 0;
}
public function onBWDone(... rest):void {
var p_bw:Number;
if (rest.length > 0) p_bw = rest[0];
// your application should do something here
// when the bandwidth check is complete
trace("bandwidth = " + p_bw + " Kbps.");
}
public function getPrefferedPosition():String{
return MainCanvas.DESKTOP_SHARING_VIEW;
}
/**
* resizes the desktop sharing video to fit to this window
*/
private function fitToWindow():void{
if (videoIsSmallerThanWindow()) {
fitWindowToVideo();
}
// Ignore if we are displaying the actual size of the video
if (! btnActualSize.selected) {
fitVideoToWindow();
}
}
private function fitVideoToWindow():void {
if (this.width < this.height) {
fitToWidthAndAdjustHeightToMaintainAspectRatio();
} else {
fitToHeightAndAdjustWidthToMaintainAspectRatio();
}
}
private function fitWindowToVideo():void {
video.width = videoWidth;
videoHolder.width = videoWidth;
video.height = videoHeight;
videoHolder.height = videoHeight;
this.height = videoHeight + VIDEO_HEIGHT_PADDING;
this.width = videoWidth + VIDEO_WIDTH_PADDING;
}
private function videoIsSmallerThanWindow():Boolean {
return (videoHeight < this.height) && (videoWidth < this.width);
}
private function fitToWidthAndAdjustHeightToMaintainAspectRatio():void {
video.width = this.width - VIDEO_WIDTH_PADDING;
videoHolder.width = video.width;
// Maintain aspect-ratio
video.height = (videoHeight * video.width) / videoWidth;
videoHolder.height = video.height;
this.height = video.height + VIDEO_HEIGHT_PADDING;
}
private function fitToHeightAndAdjustWidthToMaintainAspectRatio():void {
video.height = this.height - VIDEO_HEIGHT_PADDING;
videoHolder.height = video.height;
// Maintain aspect-ratio
video.width = (videoWidth * video.height) / videoHeight;
videoHolder.width = video.width;
this.width = video.width + VIDEO_WIDTH_PADDING;
}
/**
* resizes the desktop sharing video to actual video resolution
*/
private function fitToActualSize():void{
if (videoIsSmallerThanWindow()) {
fitWindowToVideo();
} else {
video.width = videoWidth;
videoHolder.width = videoWidth;
video.height = videoHeight;
videoHolder.height = videoHeight;
}
}
private function determineHowToDisplayVideo():void {
if (btnActualSize.selected) {
fitToActualSize();
btnActualSize.toolTip = ResourceUtil.getInstance().getString('bbb.desktopView.fitToWindow');
btnActualSize.label = ResourceUtil.getInstance().getString('bbb.desktopView.fitToWindow');
} else {
fitToWindow();
btnActualSize.toolTip = ResourceUtil.getInstance().getString('bbb.desktopView.actualSize');
btnActualSize.label = ResourceUtil.getInstance().getString('bbb.desktopView.actualSize');
}
}
private function closeWindow():void {
dispatchEvent(new ViewWindowEvent(ViewWindowEvent.CLOSE));
}
override protected function resourcesChanged():void{
super.resourcesChanged();
this.title = ResourceUtil.getInstance().getString('bbb.desktopView.title');
if (windowControls != null) {
minimizeBtn.toolTip = ResourceUtil.getInstance().getString("bbb.window.minimizeBtn.toolTip");
minimizeBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.desktopView.minimizeBtn.accessibilityName");
maximizeRestoreBtn.toolTip = ResourceUtil.getInstance().getString("bbb.window.maximizeRestoreBtn.toolTip");
maximizeRestoreBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.desktopView.maximizeRestoreBtn.accessibilityName");
closeBtn.toolTip = ResourceUtil.getInstance().getString("bbb.window.closeBtn.toolTip");
closeBtn.accessibilityName = ResourceUtil.getInstance().getString("bbb.desktopView.closeBtn.accessibilityName");
}
}
private function localeChanged(e:Event):void{
resourcesChanged();
}
]]>
</mx:Script>
<common:TabIndexer startIndex="{dsOptions.baseTabIndex + 1}" tabIndices="{[minimizeBtn, maximizeRestoreBtn, closeBtn, btnActualSize]}"/>
<mx:HBox id="bottomBar" visible="true" height="30" horizontalAlign="center" paddingTop="0" paddingBottom="0" width="100%" >
<mx:Button id="btnActualSize" paddingTop="0" paddingBottom="0" styleName="deskshareControlButtonStyle"
toggle="true"
click="determineHowToDisplayVideo()"
selected="false"
height="90%"
label="{btnActualSize.selected ? ResourceUtil.getInstance().getString('bbb.desktopView.fitToWindow') : ResourceUtil.getInstance().getString('bbb.desktopView.actualSize')}"
toolTip="{btnActualSize.selected ? ResourceUtil.getInstance().getString('bbb.desktopView.fitToWindow') : ResourceUtil.getInstance().getString('bbb.desktopView.actualSize')}"/>
</mx:HBox>
</CustomMdiWindow>

View File

@ -53,8 +53,8 @@ package org.bigbluebutton.modules.users.services
import org.bigbluebutton.modules.present.events.RemovePresentationEvent; import org.bigbluebutton.modules.present.events.RemovePresentationEvent;
import org.bigbluebutton.modules.present.events.UploadEvent; import org.bigbluebutton.modules.present.events.UploadEvent;
import org.bigbluebutton.modules.users.events.MeetingMutedEvent; import org.bigbluebutton.modules.users.events.MeetingMutedEvent;
import org.bigbluebutton.modules.deskshare.events.ViewStreamEvent; import org.bigbluebutton.modules.screenshare.events.ViewStreamEvent;
import org.bigbluebutton.modules.deskshare.events.WebRTCViewStreamEvent; import org.bigbluebutton.modules.screenshare.events.WebRTCViewStreamEvent;
import org.bigbluebutton.main.api.JSLog; import org.bigbluebutton.main.api.JSLog;
import org.bigbluebutton.modules.users.events.MeetingMutedEvent; import org.bigbluebutton.modules.users.events.MeetingMutedEvent;
@ -175,6 +175,8 @@ package org.bigbluebutton.modules.users.services
private function handleDeskShareRTMPBroadcastNotification(msg:Object):void { private function handleDeskShareRTMPBroadcastNotification(msg:Object):void {
LOGGER.debug("*** handleDeskShareRTMPBroadcastNotification **** \n", [msg]); LOGGER.debug("*** handleDeskShareRTMPBroadcastNotification **** \n", [msg]);
JSLog.warn("*** handleDeskShareRTMPBroadcastNotification **** url=", msg.rtmpUrl);
JSLog.warn("*** handleDeskShareRTMPBroadcastNotification **** broadcasting=", msg.broadcasting);
var event:WebRTCViewStreamEvent; var event:WebRTCViewStreamEvent;
if (msg.broadcasting) { if (msg.broadcasting) {

View File

@ -32,8 +32,9 @@ function loadLib(libname, success, fail) {
} }
}; };
const failCallback = function (cb) { const failCallback = function (cb, issue) {
console.log(`failed to load lib - ${this}`); console.error(`failed to load lib - ${this}`);
console.error(issue);
if (typeof (cb) == 'function' || cb instanceof Function) { if (typeof (cb) == 'function' || cb instanceof Function) {
cb(); cb();
} }
@ -49,12 +50,9 @@ Meteor.startup(() => {
loadLib('bbblogger.js'); loadLib('bbblogger.js');
loadLib('jquery.json-2.4.min.js'); loadLib('jquery.json-2.4.min.js');
loadLib('jquery.FSRTC.js'); loadLib('jquery.FSRTC.js');
loadLib('jquery.verto.js', function () { loadLib('jquery.verto.js');
loadLib('verto_extension.js'); loadLib('verto_extension.js');
});
loadLib('jquery.jsonrpcclient.js'); loadLib('jquery.jsonrpcclient.js');
loadLib('verto_extension_share.js');
render(( render((
<IntlProvider locale={locale} messages={messages}> <IntlProvider locale={locale} messages={messages}>

View File

@ -58,14 +58,6 @@ clientConfig.media = {};
clientConfig.media.WebRTCHangupRetryInterval = 2000; 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 // specifies whether to use SIP.js for audio over mod_verto
clientConfig.media.useSIPAudio = true; clientConfig.media.useSIPAudio = true;

View File

@ -3,7 +3,7 @@ import Meetings from '/imports/api/meetings';
import Auth from '/imports/ui/services/auth'; import Auth from '/imports/ui/services/auth';
import {callServer} from '/imports/ui/services/api'; import {callServer} from '/imports/ui/services/api';
import {clientConfig} from '/config'; import {clientConfig} from '/config';
import {createVertoUserName, joinVertoAudio} from '/imports/api/verto'; import {vertoExitAudio, vertoJoinListenOnly, vertoJoinMicrophone} from '/imports/api/verto';
let triedHangup = false; 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 // 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 // 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) { if (!clientConfig.media.useSIPAudio) {
window.leaveWebRTCVoiceConference_verto(); vertoExitAudio();
window.cur_call = null;
return; return;
} else { } else {
// To be called when the hangup is initiated // 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 // join the conference. If listen only send the request to the server
function joinVoiceCall(options) { function joinVoiceCallSIP(options) {
const extension = getVoiceBridge(); const extension = getVoiceBridge();
console.log(options); console.log(options);
if (clientConfig.media.useSIPAudio) { if (clientConfig.media.useSIPAudio) {
@ -82,10 +81,6 @@ function joinVoiceCall(options) {
console.log('Beginning WebRTC Conference Call'); console.log('Beginning WebRTC Conference Call');
}; };
if (options.isListenOnly) {
callServer('listenOnlyRequestToggle', true);
}
window.BBB = {}; window.BBB = {};
window.BBB.getMyUserInfo = function (callback) { window.BBB.getMyUserInfo = function (callback) {
const uid = Auth.userID; const uid = Auth.userID;
@ -104,12 +99,24 @@ function joinVoiceCall(options) {
callIntoConference(extension, function () {}, options.isListenOnly); callIntoConference(extension, function () {}, options.isListenOnly);
return; 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, };

View File

@ -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 Auth from '/imports/ui/services/auth';
import { clientConfig } from '/config'; import { clientConfig } from '/config';
import { getVoiceBridge } from '/imports/api/phone'; import { getVoiceBridge } from '/imports/api/phone';
@ -9,61 +11,50 @@ function createVertoUserName() {
return conferenceUsername; return conferenceUsername;
} }
function joinVertoAudio(options) { function vertoExitAudio() {
joinVertoCall(options); window.vertoExitAudio();
} }
function watchVertoVideo(options) { function vertoJoinListenOnly() {
joinVertoCall(options); window.vertoJoinListenOnly(
'remote-media',
getVoiceBridge(),
createVertoUserName(),
null,
);
} }
function joinVertoCall(options) { function vertoJoinMicrophone() {
console.log('joinVertoCall'); window.vertoJoinMicrophone(
const extension = options.extension || getVoiceBridge(); 'remote-media',
getVoiceBridge(),
createVertoUserName(),
null,
);
}
if (!isWebRTCAvailable()) { function vertoWatchVideo() {
return; window.vertoWatchVideo(
} 'deskshareVideo',
getVoiceBridge(),
createVertoUserName(),
null,
);
}
if (!clientConfig.useSIPAudio) { function shareVertoScreen() {
const vertoServerCredentials = { vertoManager.shareScreen(
vertoPort: clientConfig.media.vertoPort, 'deskshareVideo',
hostName: clientConfig.media.vertoServerAddress, getVoiceBridge(),
login: conferenceIdNumber, createVertoUserName(),
password: clientConfig.media.freeswitchProfilePassword, null,
}; );
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;
}
} }
export { export {
createVertoUserName, vertoJoinListenOnly,
joinVertoAudio, vertoJoinMicrophone,
watchVertoVideo, vertoWatchVideo,
vertoExitAudio,
shareVertoScreen,
}; };

View File

@ -3,7 +3,7 @@ import React from 'react';
export default class DeskshareComponent extends React.Component { export default class DeskshareComponent extends React.Component {
render() { render() {
return ( return (
<video id="webcam" style={{ height: '100%', width: '100%', }} controls/> <video id="deskshareVideo" style={{ height: '100%', width: '100%', }} controls/>
); );
} }
}; };

View File

@ -1,5 +1,5 @@
import Deskshare from '/imports/api/deskshare'; import Deskshare from '/imports/api/deskshare';
import {createVertoUserName, joinVertoAudio, watchVertoVideo} from '/imports/api/verto'; import {createVertoUserName, watchVertoVideo} from '/imports/api/verto';
import Auth from '/imports/ui/services/auth'; import Auth from '/imports/ui/services/auth';
import {getVoiceBridge} from '/imports/api/phone'; import {getVoiceBridge} from '/imports/api/phone';
@ -27,14 +27,6 @@ function videoIsBroadcasting() {
} }
} }
function watchDeskshare(options) {
const extension = options.extension || getVoiceBridge();
const conferenceUsername = createVertoUserName();
conferenceIdNumber = '1009';
watchVertoVideo({ extension, conferenceUsername, conferenceIdNumber,
watchOnly: true, });
}
// if remote deskshare has been ended disconnect and hide the video stream // if remote deskshare has been ended disconnect and hide the video stream
function presenterDeskshareHasEnded() { function presenterDeskshareHasEnded() {
// exitVoiceCall(); // exitVoiceCall();
@ -42,13 +34,9 @@ function presenterDeskshareHasEnded() {
// if remote deskshare has been started connect and display the video stream // if remote deskshare has been started connect and display the video stream
function presenterDeskshareHasStarted() { function presenterDeskshareHasStarted() {
const voiceBridge = Deskshare.findOne().deskshare.voiceBridge; vertoWatchVideo();
watchDeskshare({
watchOnly: true,
extension: voiceBridge,
});
}; };
export { videoIsBroadcasting, watchDeskshare, presenterDeskshareHasEnded, export {
presenterDeskshareHasStarted videoIsBroadcasting, presenterDeskshareHasEnded, presenterDeskshareHasStarted
}; };

View File

@ -3,7 +3,7 @@ import Modal from 'react-modal';
import Icon from '/imports/ui/components/icon/component'; import Icon from '/imports/ui/components/icon/component';
import Button from '/imports/ui/components/button/component'; import Button from '/imports/ui/components/button/component';
import BaseMenu from './BaseMenu'; import BaseMenu from './BaseMenu';
import {joinVoiceCall, exitVoiceCall} from '/imports/api/phone'; import {joinListenOnly, joinMicrophone, exitAudio} from '/imports/api/phone';
export default class AudioMenu extends BaseMenu { export default class AudioMenu extends BaseMenu {
constructor(props) { constructor(props) {
@ -14,24 +14,6 @@ export default class AudioMenu extends BaseMenu {
return ( return (
<div> <div>
<p>inside audio menu</p> <p>inside audio menu</p>
<button onClick={
function () {
exitVoiceCall(function () {console.log('exit callback');});
}
}>exit voice call</button>
<br/>
<button onClick={
function () {
joinVoiceCall({ isListenOnly: true });
}
}>listen only</button>
<button onClick={
function () {
joinVoiceCall({ isListenOnly: false });
}
}>join mic</button>
</div> </div>
); );
} }

View File

@ -59,7 +59,7 @@ dependencies {
//redis //redis
compile 'redis.clients:jedis:2.0.0' compile 'redis.clients:jedis:2.0.0'
providedCompile 'commons-pool:commons-pool:1.5.6' compile 'commons-pool:commons-pool:1.5.6'
compile 'com.google.code.gson:gson:1.7.1' compile 'com.google.code.gson:gson:1.7.1'
compile 'org.bigbluebutton:bbb-common-message:0.0.18-SNAPSHOT' compile 'org.bigbluebutton:bbb-common-message:0.0.18-SNAPSHOT'

View File

@ -0,0 +1,17 @@
#!/usr/bin/env bash
cd /var/lib/red5/webapps
echo 'Starting with:'
ls -dl video-broadcast
sudo chown root:root video-broadcast
sudo chmod -R 777 video-broadcast
cd video-broadcast
ls -al
sudo chown root:root META-INF
sudo chown red5:red5 streams
sudo chown root:root WEB-INF
echo 'After the procedure:'
ls -al