diff --git a/akka-bbb-apps/build.sbt b/akka-bbb-apps/build.sbt index b3328562cf3ec4aec17d5e9243eb47ad7aa06d99..bb8b3fd1d8985b46a238a074df4dfff5dcb009ee 100755 --- a/akka-bbb-apps/build.sbt +++ b/akka-bbb-apps/build.sbt @@ -50,7 +50,7 @@ libraryDependencies ++= { "com.google.code.gson" % "gson" % "1.7.1", "redis.clients" % "jedis" % "2.1.0", "org.apache.commons" % "commons-lang3" % "3.2", - "org.bigbluebutton" % "bbb-common-message" % "0.0.5" + "org.bigbluebutton" % "bbb-common-message" % "0.0.6" )} diff --git a/akka-bbb-fsesl/build.sbt b/akka-bbb-fsesl/build.sbt index d55a4f48642d68bfcc46ec215a14de20720de550..206751d0d698c196b296f7e3c84513666fe539f0 100755 --- a/akka-bbb-fsesl/build.sbt +++ b/akka-bbb-fsesl/build.sbt @@ -50,7 +50,7 @@ libraryDependencies ++= { "com.google.code.gson" % "gson" % "1.7.1", "redis.clients" % "jedis" % "2.1.0", "org.apache.commons" % "commons-lang3" % "3.2", - "org.bigbluebutton" % "bbb-common-message" % "0.0.5", + "org.bigbluebutton" % "bbb-common-message" % "0.0.6", "org.bigbluebutton" % "bbb-fsesl-client" % "0.0.2" )} diff --git a/bbb-common-message/build.sbt b/bbb-common-message/build.sbt index 804867129e4897dee5f0a212b6bb327cb0d5a8d5..647816435d5739592618dfba598f518a74508d5e 100755 --- a/bbb-common-message/build.sbt +++ b/bbb-common-message/build.sbt @@ -4,7 +4,7 @@ name := "bbb-common-message" organization := "org.bigbluebutton" -version := "0.0.5" +version := "0.0.6" // We want to have our jar files in lib_managed dir. // This way we'll have the right path when we import diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/GetUsersReplyMessage.java b/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/GetUsersReplyMessage.java index 4cbb1bc99dc3731f92d9ecbc6cb33554df97649e..d10914b126f62523303da3b4bf58040c368e139a 100755 --- a/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/GetUsersReplyMessage.java +++ b/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/GetUsersReplyMessage.java @@ -30,6 +30,7 @@ public class GetUsersReplyMessage implements ISubscribedMessage { return MessageBuilder.buildJson(header, payload); } + public static GetUsersReplyMessage fromJson(String message) { JsonParser parser = new JsonParser(); JsonObject obj = (JsonObject) parser.parse(message); diff --git a/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/Util.java b/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/Util.java index 313d4e0fe3b6cc3e8fe16878367e1c8182e94856..6d36bbe261f9d38dd566167a27f9d2ec23983fee 100755 --- a/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/Util.java +++ b/bbb-common-message/src/main/java/org/bigbluebutton/common/messages/Util.java @@ -73,7 +73,7 @@ public class Util { && user.has(Constants.RAISE_HAND) && user.has(Constants.PHONE_USER) && user.has(Constants.PRESENTER) && user.has(Constants.LOCKED) && user.has(Constants.EXTERN_USERID) && user.has(Constants.ROLE) - && user.has(Constants.VOICEUSER)){ + && user.has(Constants.VOICEUSER) && user.has(Constants.WEBCAM_STREAM)){ Map<String, Object> userMap = new HashMap<String, Object>(); @@ -87,11 +87,15 @@ public class Util { Boolean locked = user.get(Constants.LOCKED).getAsBoolean(); String extUserId = user.get(Constants.EXTERN_USERID).getAsString(); String role = user.get(Constants.ROLE).getAsString(); - + + JsonArray webcamStreamJArray = user.get(Constants.WEBCAM_STREAM).getAsJsonArray(); + ArrayList<String> webcamStreams = extractWebcamStreams(webcamStreamJArray); + userMap.put("userId", userid); userMap.put("name", username); userMap.put("listenOnly", listenOnly); userMap.put("hasStream", hasStream); + userMap.put("webcamStream", webcamStreams); userMap.put("raiseHand", raiseHand); userMap.put("externUserID", extUserId); userMap.put("phoneUser", phoneUser); @@ -169,6 +173,19 @@ public class Util { return collection; } + + public ArrayList<String> extractWebcamStreams(JsonArray webcamStreams) { + ArrayList<String> collection = new ArrayList<String>(); + + Iterator<JsonElement> webcamStreamsIter = webcamStreams.iterator(); + while (webcamStreamsIter.hasNext()){ + JsonElement stream = webcamStreamsIter.next(); + collection.add(stream.getAsString()); + } + + return collection; + + } public ArrayList<String> extractUserids(JsonArray users) { ArrayList<String> collection = new ArrayList<String>(); diff --git a/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java b/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java index 3c3731caa545f8fed0f6e34840283bdf63c3a50d..a983cbfb03449cba3183c09f256df084f8448fe3 100755 --- a/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java +++ b/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoApplication.java @@ -204,7 +204,7 @@ public class VideoApplication extends MultiThreadedApplicationAdapter { scopeName = stream.getScope().getName(); } - log.info("streamBroadcastClose " + stream.getPublishedName() + " " + System.currentTimeMillis() + " " + conn.getScope().getName()); + log.info("Stream broadcast closed for stream=[{}] meeting=[{}]", stream.getPublishedName(), scopeName); String userId = getUserId(); String meetingId = conn.getScope().getName(); @@ -214,13 +214,13 @@ public class VideoApplication extends MultiThreadedApplicationAdapter { IStreamListener listener = streamListeners.remove(scopeName + "-" + stream.getPublishedName()); if (listener != null) { - stream.removeStreamListener(listener); + ((VideoStreamListener) listener).streamStopped(); + stream.removeStreamListener(listener); } - if (recordVideoStream) { - + if (recordVideoStream) { long publishDuration = (System.currentTimeMillis() - stream.getCreationTime()) / 1000; - log.info("streamBroadcastClose " + stream.getPublishedName() + " " + System.currentTimeMillis() + " " + scopeName); + log.info("Stop recording event for stream=[{}] meeting=[{}]", stream.getPublishedName(), scopeName); Map<String, String> event = new HashMap<String, String>(); event.put("module", "WEBCAM"); event.put("timestamp", genTimestamp().toString()); diff --git a/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoStreamListener.java b/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoStreamListener.java index aceb4012afe0b107913c2a75c12a82935b8322c4..eefd27a4837c800742da319072b1116790c8aeb4 100755 --- a/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoStreamListener.java +++ b/bbb-video/src/main/java/org/bigbluebutton/app/video/VideoStreamListener.java @@ -75,6 +75,8 @@ public class VideoStreamListener implements IStreamListener { // Event queue worker job name private String timeoutJobName; + private volatile boolean publishing = false; + private IScope scope; public VideoStreamListener(IScope scope, IBroadcastStream stream, Boolean record) { @@ -107,11 +109,12 @@ public class VideoStreamListener implements IStreamListener { if (! firstPacketReceived) { firstPacketReceived = true; - // start the worker + publishing = true; + + // start the worker to monitor if we are still receiving video packets timeoutJobName = scheduler.addScheduledJob(videoTimeout, new TimeoutJob()); if (record) { - IConnection conn = Red5.getConnectionLocal(); Map<String, String> event = new HashMap<String, String>(); event.put("module", "WEBCAM"); event.put("timestamp", genTimestamp().toString()); @@ -119,7 +122,7 @@ public class VideoStreamListener implements IStreamListener { event.put("stream", stream.getPublishedName()); event.put("eventName", "StartWebcamShareEvent"); - recordingService.record(conn.getScope().getName(), event); + recordingService.record(scope.getName(), event); } } } @@ -129,15 +132,27 @@ public class VideoStreamListener implements IStreamListener { recordingService = s; } + public void streamStopped() { + this.publishing = false; + } + private class TimeoutJob implements IScheduledJob { - + private boolean streamStopped = false; + public void execute(ISchedulingService service) { if ((System.currentTimeMillis() - lastVideoTime) > videoTimeout) { log.warn("No data received for stream[{}] in the last few seconds. Close stream.", stream.getPublishedName()); - // remove the scheduled job - scheduler.removeScheduledJob(timeoutJobName); - // stop / clean up - stream.stop(); + + if (!streamStopped) { + streamStopped = true; + // remove the scheduled job + scheduler.removeScheduledJob(timeoutJobName); + // stop / clean up + if (publishing) { + stream.stop(); + } + + } } } diff --git a/bigbluebutton-apps/build.gradle b/bigbluebutton-apps/build.gradle index 72478ee971a7953a70b8fc228deef0fcae9b3e1c..a1843446baeddd40821a5465a198c90b6ae6220e 100755 --- a/bigbluebutton-apps/build.gradle +++ b/bigbluebutton-apps/build.gradle @@ -112,7 +112,7 @@ dependencies { compile 'com.google.code.gson:gson:1.7.1' providedCompile 'org.apache.commons:commons-lang3:3.2' - compile 'org.bigbluebutton:bbb-common-message:0.0.5' + compile 'org.bigbluebutton:bbb-common-message:0.0.6' } test { diff --git a/bigbluebutton-client/src/org/bigbluebutton/main/model/users/events/StreamStoppedEvent.as b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/events/StreamStoppedEvent.as new file mode 100755 index 0000000000000000000000000000000000000000..7b51bf4b31825e40bbfa53acb3cb10b8bbd2e788 --- /dev/null +++ b/bigbluebutton-client/src/org/bigbluebutton/main/model/users/events/StreamStoppedEvent.as @@ -0,0 +1,38 @@ +/** +* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +* +* Copyright (c) 2012 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.main.model.users.events +{ + import flash.events.Event; + + public class StreamStoppedEvent extends Event + { + public static const STREAM_STOPPED:String = "webcam stream stopped"; + + public var streamId:String; + public var userId:String + + public function StreamStoppedEvent(userId:String, streamId:String) + { + this.userId = userId; + this.streamId = streamId; + super(STREAM_STOPPED, true, false); + } + + } +} \ No newline at end of file diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as index a30af79cbd239e619b3829f1c682b086b418163a..486b8a5d7e0f6a4dfee672f4e790dbe65e625793 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/users/services/MessageReceiver.as @@ -42,6 +42,8 @@ package org.bigbluebutton.modules.users.services import org.bigbluebutton.main.model.users.Conference; import org.bigbluebutton.main.model.users.IMessageListener; import org.bigbluebutton.main.model.users.events.RoleChangeEvent; + import org.bigbluebutton.main.model.users.events.StreamStartedEvent; + import org.bigbluebutton.main.model.users.events.StreamStoppedEvent; import org.bigbluebutton.main.model.users.events.UsersConnectionEvent; import org.bigbluebutton.modules.present.events.CursorEvent; import org.bigbluebutton.modules.present.events.NavigationEvent; @@ -517,7 +519,13 @@ package org.bigbluebutton.modules.users.services trace(LOG + "*** handleUserUnsharedWebcam " + msg.msg + " **** \n"); var map:Object = JSON.parse(msg.msg); UserManager.getInstance().getConference().unsharedWebcam(map.userId, map.webcamStream); + sendStreamStoppedEvent(map.userId, map.webcamStream); } + + private function sendStreamStoppedEvent(userId: String, streamId: String):void{ + var dispatcher:Dispatcher = new Dispatcher(); + dispatcher.dispatchEvent(new StreamStoppedEvent(userId, streamId)); + } public function participantStatusChange(userID:String, status:String, value:Object):void { trace(LOG + "Received status change [" + userID + "," + status + "," + value + "]") diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml index b0b72c252cb3954ec158c4f4d3784ba6c1a1f7a9..4977769d58d7704f6f8c529abf2438445931ca23 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMap.mxml @@ -23,22 +23,23 @@ 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 org.bigbluebutton.core.events.ConnectAppEvent; - import org.bigbluebutton.main.events.BBBEvent; - import org.bigbluebutton.main.events.MadePresenterEvent; - import org.bigbluebutton.main.events.StoppedViewingWebcamEvent; - import org.bigbluebutton.main.events.UserJoinedEvent; - import org.bigbluebutton.main.events.UserLeftEvent; - import org.bigbluebutton.main.model.users.events.StreamStartedEvent; - import org.bigbluebutton.modules.users.events.ViewCameraEvent; - import org.bigbluebutton.modules.videoconf.events.ClosePublishWindowEvent; - import org.bigbluebutton.modules.videoconf.events.ConnectedEvent; - import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent; - import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent; - import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent; - import org.bigbluebutton.modules.videoconf.events.StopShareCameraRequestEvent; - import org.bigbluebutton.modules.videoconf.events.VideoModuleStartEvent; - import org.bigbluebutton.modules.videoconf.events.VideoModuleStopEvent; + import org.bigbluebutton.core.events.ConnectAppEvent; + import org.bigbluebutton.main.events.BBBEvent; + import org.bigbluebutton.main.events.MadePresenterEvent; + import org.bigbluebutton.main.events.StoppedViewingWebcamEvent; + import org.bigbluebutton.main.events.UserJoinedEvent; + import org.bigbluebutton.main.events.UserLeftEvent; + import org.bigbluebutton.main.model.users.events.StreamStartedEvent; + import org.bigbluebutton.main.model.users.events.StreamStoppedEvent; + import org.bigbluebutton.modules.users.events.ViewCameraEvent; + import org.bigbluebutton.modules.videoconf.events.ClosePublishWindowEvent; + import org.bigbluebutton.modules.videoconf.events.ConnectedEvent; + import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent; + import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent; + import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent; + import org.bigbluebutton.modules.videoconf.events.StopShareCameraRequestEvent; + import org.bigbluebutton.modules.videoconf.events.VideoModuleStartEvent; + import org.bigbluebutton.modules.videoconf.events.VideoModuleStopEvent; ]]> </mx:Script> @@ -85,6 +86,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. <MethodInvoker generator="{VideoEventMapDelegate}" method="viewCamera" arguments="{[event.userID, event.stream, event.user]}" /> </EventHandlers> + <EventHandlers type="{StreamStoppedEvent.STREAM_STOPPED}"> + <MethodInvoker generator="{VideoEventMapDelegate}" method="handleStreamStoppedEvent" arguments="{event}" /> + </EventHandlers> + <EventHandlers type="{ViewCameraEvent.VIEW_CAMERA_EVENT}"> <MethodInvoker generator="{VideoEventMapDelegate}" method="viewCamera" arguments="{[event.userID, event.stream, event.viewedName]}" /> </EventHandlers> diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as index 5aae027995abc93c4337e867f2aac033d9d5bdcd..b0e4d9d653beb9807ab7251ee78fc7fc586ba6c2 100755 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/maps/VideoEventMapDelegate.as @@ -18,23 +18,17 @@ */ package org.bigbluebutton.modules.videoconf.maps { - import com.asfusion.mate.utils.debug.Debugger; - import com.asfusion.mate.utils.debug.DebuggerUtil; - import flash.events.IEventDispatcher; - import flash.external.ExternalInterface; import flash.media.Camera; - + import mx.collections.ArrayCollection; import mx.collections.ArrayList; - + import org.bigbluebutton.common.LogUtil; - import org.bigbluebutton.common.events.CloseWindowEvent; import org.bigbluebutton.common.events.OpenWindowEvent; import org.bigbluebutton.common.events.ToolbarButtonEvent; import org.bigbluebutton.core.BBB; import org.bigbluebutton.core.UsersUtil; - import org.bigbluebutton.core.events.ConnectAppEvent; import org.bigbluebutton.core.managers.UserManager; import org.bigbluebutton.core.model.VideoProfile; import org.bigbluebutton.core.vo.CameraSettingsVO; @@ -46,27 +40,18 @@ package org.bigbluebutton.modules.videoconf.maps import org.bigbluebutton.main.model.users.BBBUser; import org.bigbluebutton.main.model.users.events.BroadcastStartedEvent; import org.bigbluebutton.main.model.users.events.BroadcastStoppedEvent; - import org.bigbluebutton.main.model.users.events.StreamStartedEvent; + import org.bigbluebutton.main.model.users.events.StreamStoppedEvent; import org.bigbluebutton.modules.videoconf.business.VideoProxy; - import org.bigbluebutton.modules.videoconf.events.CloseAllWindowsEvent; import org.bigbluebutton.modules.videoconf.events.ClosePublishWindowEvent; import org.bigbluebutton.modules.videoconf.events.ConnectedEvent; - import org.bigbluebutton.modules.videoconf.events.OpenVideoWindowEvent; import org.bigbluebutton.modules.videoconf.events.ShareCameraRequestEvent; import org.bigbluebutton.modules.videoconf.events.StartBroadcastEvent; import org.bigbluebutton.modules.videoconf.events.StopBroadcastEvent; import org.bigbluebutton.modules.videoconf.events.StopShareCameraRequestEvent; - import org.bigbluebutton.modules.videoconf.events.WebRTCWebcamRequestEvent; import org.bigbluebutton.modules.videoconf.model.VideoConfOptions; - import org.bigbluebutton.modules.videoconf.views.AvatarWindow; import org.bigbluebutton.modules.videoconf.views.GraphicsWrapper; import org.bigbluebutton.modules.videoconf.views.ToolbarPopupButton; - import org.bigbluebutton.modules.videoconf.views.UserAvatar; - import org.bigbluebutton.modules.videoconf.views.UserGraphic; - import org.bigbluebutton.modules.videoconf.views.UserGraphicHolder; - import org.bigbluebutton.modules.videoconf.views.UserVideo; import org.bigbluebutton.modules.videoconf.views.VideoDock; - import org.flexunit.runner.manipulation.filters.IncludeAllFilter; public class VideoEventMapDelegate { @@ -121,6 +106,14 @@ package org.bigbluebutton.modules.videoconf.maps } } + public function handleStreamStoppedEvent(event:StreamStoppedEvent):void { + if (UserManager.getInstance().getConference().amIThisUser(event.userId)) { + closePublishWindowWithStream(event.userId, event.streamId); + } else { + closeViewWindowWithStream(event.userId, event.streamId); + } + } + public function handleUserLeftEvent(event:UserLeftEvent):void { trace("VideoEventMapDelegate:: [" + me + "] handleUserLeftEvent. ready = [" + _ready + "]"); @@ -288,7 +281,7 @@ package org.bigbluebutton.modules.videoconf.maps } public function startPublishing(e:StartBroadcastEvent):void{ - LogUtil.debug("VideoEventMapDelegate:: [" + me + "] startPublishing:: Publishing stream to: " + proxy.connection.uri + "/" + e.stream); + trace("VideoEventMapDelegate:: [" + me + "] startPublishing:: Publishing stream to: " + proxy.connection.uri + "/" + e.stream); proxy.startPublishing(e); _isWaitingActivation = false; diff --git a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/UserGraphicHolder.mxml b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/UserGraphicHolder.mxml old mode 100644 new mode 100755 index fafb7809c8f28be25838b557d4cd4a97988a31f2..f783ab8a9c7a7bd51bbdb3d96bfdad6d7be64c58 --- a/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/UserGraphicHolder.mxml +++ b/bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/UserGraphicHolder.mxml @@ -134,7 +134,6 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. } public function shutdown():void { - trace("[UserGraphicHolder:shutdown]"); video.shutdown(); }