diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/Meeting.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/Meeting.java new file mode 100755 index 0000000000000000000000000000000000000000..9a932c938b3d6ba9fcb029610b6d147bb0fad2a9 --- /dev/null +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/Meeting.java @@ -0,0 +1,44 @@ +package org.bigbluebutton.app.screenshare; + +import java.util.HashMap; +import java.util.Map; + +public class Meeting { + public final String id; + + private Map<String, VideoStream> videoStreams = new HashMap<String, VideoStream>(); + + public Meeting(String id) { + this.id = id; + } + + public synchronized void addStream(VideoStream stream) { + videoStreams.put(stream.getStreamId(), stream); + } + + public synchronized void removeStream(String streamId) { + VideoStream vs = videoStreams.remove(streamId); + } + + public synchronized void streamBroadcastClose(String streamId) { + VideoStream vs = videoStreams.remove(streamId); + if (vs != null) { + vs.streamBroadcastClose(); + } + } + + public synchronized boolean hasVideoStreams() { + return !videoStreams.isEmpty(); + } + + public synchronized void stopStartRecording(String streamId) { + VideoStream vs = videoStreams.get(streamId); + if (vs != null) vs.stopStartRecording(); + } + + public synchronized void stopStartAllRecordings() { + for (VideoStream vs : videoStreams.values()) { + stopStartRecording(vs.getStreamId()); + } + } +} diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/MeetingManager.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/MeetingManager.java new file mode 100755 index 0000000000000000000000000000000000000000..76861013ed9c7767457fc484986129263e08ce49 --- /dev/null +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/MeetingManager.java @@ -0,0 +1,53 @@ +package org.bigbluebutton.app.screenshare; + +import java.util.HashMap; +import java.util.Map; + +public class MeetingManager { + + private Map<String, Meeting> meetings = new HashMap<String, Meeting>(); + + private void add(Meeting m) { + meetings.put(m.id, m); + } + + private void remove(String id) { + Meeting m = meetings.remove(id); + } + + public void addStream(String meetingId, VideoStream vs) { + Meeting m = meetings.get(meetingId); + if (m != null) { + m.addStream(vs); + } else { + Meeting nm = new Meeting(meetingId); + nm.addStream(vs); + add(nm); + } + } + + public void removeStream(String meetingId, String streamId) { + Meeting m = meetings.get(meetingId); + if (m != null) { + m.removeStream(streamId); + } + } + + public void streamBroadcastClose(String meetingId, String streamId) { + Meeting m = meetings.get(meetingId); + if (m != null) { + m.streamBroadcastClose(streamId); + if (!m.hasVideoStreams()) { + remove(m.id); + } + } + } + + public synchronized void stopStartAllRecordings(String meetingId) { + Meeting m = meetings.get(meetingId); + if (m != null) { + m.stopStartAllRecordings(); + } + } +} + diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/VideoStream.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/VideoStream.java new file mode 100755 index 0000000000000000000000000000000000000000..0ff5b30d93b538347494354f922b100d841a16cf --- /dev/null +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/VideoStream.java @@ -0,0 +1,65 @@ +package org.bigbluebutton.app.screenshare; + +import org.red5.logging.Red5LoggerFactory; +import org.red5.server.api.IConnection; +import org.red5.server.api.Red5; +import org.red5.server.api.scope.IScope; +import org.red5.server.api.stream.IBroadcastStream; +import org.red5.server.stream.ClientBroadcastStream; +import org.slf4j.Logger; + +public class VideoStream { + private static Logger log = Red5LoggerFactory.getLogger(VideoStream.class, "screenshare"); + + private VideoStreamListener videoStreamListener; + private IScope scope; + private String streamId; + private IBroadcastStream stream; + private String recordingStreamName; + private ClientBroadcastStream cstream; + + public VideoStream(IBroadcastStream stream, VideoStreamListener videoStreamListener, ClientBroadcastStream cstream) { + this.stream = stream; + this.videoStreamListener = videoStreamListener; + stream.addStreamListener(videoStreamListener); + this.cstream = cstream; + } + + public String getStreamId() { + return streamId; + } + + public synchronized void startRecording() { + long now = System.currentTimeMillis(); + recordingStreamName = stream.getPublishedName() + "-" + now; + try { + log.info("Recording stream " + recordingStreamName); + videoStreamListener.setStreamId(recordingStreamName); + cstream.saveAs(recordingStreamName, false); + } catch (Exception e) { + log.error("ERROR while recording stream " + e.getMessage()); + e.printStackTrace(); + } + } + + public synchronized void stopRecording() { + if (cstream.isRecording()) { + cstream.stopRecording(); + videoStreamListener.stopRecording(); + videoStreamListener.reset(); + } + } + + public synchronized void stopStartRecording() { + stopRecording(); + videoStreamListener.reset(); + startRecording(); + } + + public synchronized void streamBroadcastClose() { + stopRecording(); + + videoStreamListener.streamStopped(); + stream.removeStreamListener(videoStreamListener); + } +} diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/VideoStreamListener.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/VideoStreamListener.java new file mode 100755 index 0000000000000000000000000000000000000000..49d461c4241d1ac2d81e47c2bd978c3ebe2f4ea2 --- /dev/null +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/VideoStreamListener.java @@ -0,0 +1,236 @@ +/** + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * <p> + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * <p> + * 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. + * <p> + * 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. + * <p> + * 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.app.screenshare; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.mina.core.buffer.IoBuffer; +import org.red5.server.api.scheduling.IScheduledJob; +import org.red5.server.api.scheduling.ISchedulingService; +import org.red5.server.api.scope.IScope; +import org.red5.server.api.stream.IBroadcastStream; +import org.red5.server.api.stream.IStreamListener; +import org.red5.server.api.stream.IStreamPacket; +import org.red5.server.net.rtmp.event.VideoData; +import org.red5.server.scheduling.QuartzSchedulingService; +import org.slf4j.Logger; +import org.red5.logging.Red5LoggerFactory; + +import com.google.gson.Gson; + +/** + * Class to listen for the first video packet of the webcam. + * We need to listen for the first packet and send a startWebcamEvent. + * The reason is that when starting the webcam, sometimes Flash Player + * needs to prompt the user for permission to access the webcam. However, + * while waiting for the user to click OK to the prompt, Red5 has already + * called the startBroadcast method which we take as the start of the recording. + * When the user finally clicks OK, the packets then start to flow through. + * This introduces a delay of when we assume the start of the recording and + * the webcam actually publishes video packets. When we do the ingest and + * processing of the video and multiplex the audio, the video and audio will + * be un-synched by at least this amount of delay. + * @author Richard Alam + * + */ +public class VideoStreamListener implements IStreamListener { + private static final Logger log = Red5LoggerFactory.getLogger(VideoStreamListener.class, "video"); + + private EventRecordingService recordingService; + private volatile boolean firstPacketReceived = false; + + // Maximum time between video packets + private int videoTimeout = 10000; + private long firstPacketTime = 0L; + private long packetCount = 0L; + + // Last time video was received, not video timestamp + private long lastVideoTime; + + private String recordingDir; + + // Stream being observed + private String streamId; + + // if this stream is recorded or not + private boolean record; + + // Scheduler + private QuartzSchedulingService scheduler; + + // Event queue worker job name + private String timeoutJobName; + + private volatile boolean publishing = false; + + private volatile boolean streamPaused = false; + + private String meetingId; + + private long recordingStartTime; + private String filename; + + public VideoStreamListener(String meetingId, String streamId, Boolean record, + String recordingDir, int packetTimeout, + QuartzSchedulingService scheduler, + EventRecordingService recordingService) { + this.meetingId = meetingId; + this.streamId = streamId; + this.record = record; + this.videoTimeout = packetTimeout; + this.recordingDir = recordingDir; + this.scheduler = scheduler; + this.recordingService = recordingService; + } + + private Long genTimestamp() { + return TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); + } + + public void reset() { + firstPacketReceived = false; + } + + public void setStreamId(String streamId) { + this.streamId = streamId; + } + + @Override + public void packetReceived(IBroadcastStream stream, IStreamPacket packet) { + IoBuffer buf = packet.getData(); + if (buf != null) + buf.rewind(); + + if (buf == null || buf.remaining() == 0) { + return; + } + + if (packet instanceof VideoData) { + // keep track of last time video was received + lastVideoTime = System.currentTimeMillis(); + packetCount++; + + if (!firstPacketReceived) { + firstPacketReceived = true; + publishing = true; + firstPacketTime = lastVideoTime; + + // start the worker to monitor if we are still receiving video packets + timeoutJobName = scheduler.addScheduledJob(videoTimeout, new TimeoutJob()); + + if (record) { + recordingStartTime = System.currentTimeMillis(); + filename = recordingDir; + if (!filename.endsWith("/")) { + filename.concat("/"); + } + + filename = filename.concat(meetingId).concat("/").concat(streamId).concat(".flv"); + recordingStartTime = System.currentTimeMillis(); + Map<String, String> event = new HashMap<String, String>(); + event.put("module", "Deskshare"); + event.put("timestamp", genTimestamp().toString()); + event.put("meetingId", meetingId); + event.put("file", filename); + event.put("stream", streamId); + event.put("eventName", "DeskshareStartedEvent"); + + recordingService.record(meetingId, event); + } + } + + + if (streamPaused) { + streamPaused = false; + long now = System.currentTimeMillis(); + long numSeconds = (now - lastVideoTime) / 1000; + + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", meetingId); + logData.put("stream", streamId); + logData.put("packetCount", packetCount); + logData.put("publishing", publishing); + logData.put("pausedFor (sec)", numSeconds); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + + log.warn("Screenshare stream restarted. data={}", logStr); + } + + } + } + + public void stopRecording() { + if (record) { + long publishDuration = (System.currentTimeMillis() - recordingStartTime) / 1000; + + Map<String, String> event = new HashMap<String, String>(); + event.put("module", "Deskshare"); + event.put("timestamp", genTimestamp().toString()); + event.put("meetingId", meetingId); + event.put("stream", streamId); + event.put("file", filename); + event.put("duration", new Long(publishDuration).toString()); + event.put("eventName", "DeskshareStoppedEvent"); + recordingService.record(meetingId, event); + } + } + + public void streamStopped() { + this.publishing = false; + } + + private class TimeoutJob implements IScheduledJob { + private boolean streamStopped = false; + + public void execute(ISchedulingService service) { + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", meetingId); + logData.put("stream", streamId); + logData.put("packetCount", packetCount); + logData.put("publishing", publishing); + + Gson gson = new Gson(); + + long now = System.currentTimeMillis(); + if ((now - lastVideoTime) > videoTimeout && !streamPaused) { + streamPaused = true; + long numSeconds = (now - lastVideoTime) / 1000; + + logData.put("lastPacketTime (sec)", numSeconds); + + String logStr = gson.toJson(logData); + + log.warn("Screenshare packet timeout. data={}", logStr); + + } + + String logStr = gson.toJson(logData); + if (!publishing) { + log.warn("Removing scheduled job. data={}", logStr); + // remove the scheduled job + scheduler.removeScheduledJob(timeoutJobName); + } + } + + } + +} diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/events/RecordChapterBreakMessage.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/events/RecordChapterBreakMessage.java new file mode 100755 index 0000000000000000000000000000000000000000..afaf2c61eec3961874e6e00c8b8992f5d1c2b3d5 --- /dev/null +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/events/RecordChapterBreakMessage.java @@ -0,0 +1,11 @@ +package org.bigbluebutton.app.screenshare.events; + +public class RecordChapterBreakMessage implements IEvent { + public final String meetingId; + public final Long timestamp; + + public RecordChapterBreakMessage(String meetingId, Long timestamp) { + this.meetingId = meetingId; + this.timestamp = timestamp; + } +} diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/EventListenerImp.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/EventListenerImp.java index d862ee9d1edc2f080e6c288363fcba71d2e24052..438c6f81f9ab158ac873fc7b020c42278344df3e 100755 --- a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/EventListenerImp.java +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/EventListenerImp.java @@ -6,11 +6,13 @@ import org.bigbluebutton.app.screenshare.events.*; import com.google.gson.Gson; import org.red5.logging.Red5LoggerFactory; import org.slf4j.Logger; +import org.bigbluebutton.app.screenshare.MeetingManager; public class EventListenerImp implements IEventListener { private static Logger log = Red5LoggerFactory.getLogger(EventListenerImp.class, "screenshare"); private ConnectionInvokerService sender; - + private MeetingManager meetingManager; + @Override public void handleMessage(IEvent event) { if (event instanceof ScreenShareStartedEvent) { @@ -27,6 +29,9 @@ public class EventListenerImp implements IEventListener { sendIsScreenSharingResponse((IsScreenSharingResponse) event); } else if (event instanceof ScreenShareClientPing) { sendScreenShareClientPing((ScreenShareClientPing) event); + } else if (event instanceof RecordChapterBreakMessage) { + RecordChapterBreakMessage rcbm = (RecordChapterBreakMessage) event; + meetingManager.stopStartAllRecordings(rcbm.meetingId); } } @@ -202,7 +207,10 @@ public class EventListenerImp implements IEventListener { } - + + public void setMeetingManager(MeetingManager meetingManager) { + this.meetingManager = meetingManager; + } public void setMessageSender(ConnectionInvokerService sender) { this.sender = sender; diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/Red5AppAdapter.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/Red5AppAdapter.java index ac7e46022893738bd814e29231c66dfb190c3844..394846d26fa8fe953a549f64d40bab365b738d00 100755 --- a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/Red5AppAdapter.java +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/Red5AppAdapter.java @@ -34,9 +34,11 @@ import org.red5.server.api.stream.IServerStream; import org.red5.server.api.stream.IStreamListener; import org.red5.server.stream.ClientBroadcastStream; import org.slf4j.Logger; - +import org.red5.server.scheduling.QuartzSchedulingService; import com.google.gson.Gson; - +import org.bigbluebutton.app.screenshare.MeetingManager; +import org.bigbluebutton.app.screenshare.VideoStreamListener; +import org.bigbluebutton.app.screenshare.VideoStream; import org.bigbluebutton.app.screenshare.EventRecordingService; import org.bigbluebutton.app.screenshare.IScreenShareApplication; import org.bigbluebutton.app.screenshare.ScreenshareStreamListener; @@ -44,21 +46,27 @@ import org.bigbluebutton.app.screenshare.ScreenshareStreamListener; public class Red5AppAdapter extends MultiThreadedApplicationAdapter { private static Logger log = Red5LoggerFactory.getLogger(Red5AppAdapter.class, "screenshare"); - private EventRecordingService recordingService; - private final Map<String, IStreamListener> streamListeners = new HashMap<String, IStreamListener>(); + // Scheduler + private QuartzSchedulingService scheduler; + private EventRecordingService recordingService; private IScreenShareApplication app; private String streamBaseUrl; private ConnectionInvokerService sender; private String recordingDirectory; private final Pattern STREAM_ID_PATTERN = Pattern.compile("(.*)-(.*)-(.*)$"); - + + private MeetingManager meetingManager; + private int packetTimeout = 10000; + @Override public boolean appStart(IScope app) { super.appStart(app); log.info("BBB Screenshare appStart"); sender.setAppScope(app); + // get the scheduler + scheduler = (QuartzSchedulingService) getContext().getBean(QuartzSchedulingService.BEAN_NAME); return true; } @@ -148,12 +156,14 @@ public class Red5AppAdapter extends MultiThreadedApplicationAdapter { app.streamStarted(meetingId, streamId, url); boolean recordVideoStream = app.recordStream(meetingId, streamId); - if (recordVideoStream) { - recordStream(stream); - ScreenshareStreamListener listener = new ScreenshareStreamListener(recordingService, recordingDirectory); - stream.addStreamListener(listener); - streamListeners.put(conn.getScope().getName() + "-" + stream.getPublishedName(), listener); - } + VideoStreamListener listener = new VideoStreamListener(meetingId, streamId, + recordVideoStream, recordingDirectory, packetTimeout, scheduler, recordingService); + ClientBroadcastStream cstream = (ClientBroadcastStream) this.getBroadcastStream(conn.getScope(), stream.getPublishedName()); + stream.addStreamListener(listener); + VideoStream vstream = new VideoStream(stream, listener, cstream); + vstream.startRecording(); + + meetingManager.addStream(meetingId, vstream); Map<String, Object> logData = new HashMap<String, Object>(); logData.put("meetingId", meetingId); @@ -182,43 +192,12 @@ public class Red5AppAdapter extends MultiThreadedApplicationAdapter { String streamId = stream.getPublishedName(); Matcher matcher = STREAM_ID_PATTERN.matcher(stream.getPublishedName()); if (matcher.matches()) { - String meetingId = matcher.group(1).trim(); - app.streamStopped(meetingId, streamId); - - boolean recordVideoStream = app.recordStream(meetingId, streamId); - if (recordVideoStream) { - IConnection conn = Red5.getConnectionLocal(); - String scopeName; - if (conn != null) { - scopeName = conn.getScope().getName(); - } else { - log.info("Connection local was null, using scope name from the stream: {}", stream); - scopeName = stream.getScope().getName(); - } - IStreamListener listener = streamListeners.remove(scopeName + "-" + stream.getPublishedName()); - if (listener != null) { - stream.removeStreamListener(listener); - } - - String filename = recordingDirectory; - if (!filename.endsWith("/")) { - filename.concat("/"); - } - - filename = filename.concat(meetingId).concat("/").concat(stream.getPublishedName()).concat(".flv"); - - long publishDuration = (System.currentTimeMillis() - stream.getCreationTime()) / 1000; - - Map<String, String> event = new HashMap<String, String>(); - event.put("module", "Deskshare"); - event.put("timestamp", genTimestamp().toString()); - event.put("meetingId", scopeName); - event.put("stream", stream.getPublishedName()); - event.put("file", filename); - event.put("duration", new Long(publishDuration).toString()); - event.put("eventName", "DeskshareStoppedEvent"); - recordingService.record(scopeName, event); - } + String meetingId = matcher.group(1).trim(); + app.streamStopped(meetingId, streamId); + + boolean recordVideoStream = app.recordStream(meetingId, streamId); + meetingManager.streamBroadcastClose(meetingId, streamId); + Map<String, Object> logData = new HashMap<String, Object>(); logData.put("meetingId", meetingId); @@ -234,27 +213,10 @@ public class Red5AppAdapter extends MultiThreadedApplicationAdapter { } } - /** - * A hook to record a stream. A file is written in webapps/video/streams/ - * @param stream - */ - private void recordStream(IBroadcastStream stream) { - IConnection conn = Red5.getConnectionLocal(); - long now = System.currentTimeMillis(); - String recordingStreamName = stream.getPublishedName(); // + "-" + now; /** Comment out for now...forgot why I added this - ralam */ - - try { - log.info("Recording stream " + recordingStreamName ); - ClientBroadcastStream cstream = (ClientBroadcastStream) this.getBroadcastStream(conn.getScope(), stream.getPublishedName()); - cstream.saveAs(recordingStreamName, false); - } catch(Exception e) { - log.error("ERROR while recording stream " + e.getMessage()); - e.printStackTrace(); - } + public void setMeetingManager(MeetingManager meetingManager) { + this.meetingManager = meetingManager; } - - public void setEventRecordingService(EventRecordingService s) { recordingService = s; } diff --git a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/redis/ReceivedJsonMsgHandlerActor.scala b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/redis/ReceivedJsonMsgHandlerActor.scala index 0e360cd810cf60dd08347d85ece9f94502339c94..7bc9e89f34ad0c3e18d7b5ee751e2fc4fee460cc 100755 --- a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/redis/ReceivedJsonMsgHandlerActor.scala +++ b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/redis/ReceivedJsonMsgHandlerActor.scala @@ -1,8 +1,8 @@ package org.bigbluebutton.app.screenshare.redis -import akka.actor.{Actor, ActorLogging, ActorRef, Props} import com.fasterxml.jackson.databind.JsonNode -import org.bigbluebutton.app.screenshare.server.sessions.messages.{MeetingCreated, MeetingEnded} +import org.bigbluebutton.app.screenshare.server.sessions.messages.{MeetingCreated, MeetingEnded, RecordingChapterBreak} +import akka.actor.{Actor, ActorLogging, ActorRef, Props} import scala.reflect.runtime.universe._ import org.bigbluebutton.common2.msgs._ @@ -56,6 +56,12 @@ class ReceivedJsonMsgHandlerActor(screenshareManager: ActorRef) } yield { screenshareManager ! new MeetingEnded(m.body.meetingId) } + case RecordingChapterBreakSysMsg.NAME => + for { + m <- deserialize[RecordingChapterBreakSysMsg](jsonNode) + } yield { + screenshareManager ! new RecordingChapterBreak(m.body.meetingId, m.body.timestamp) + } case _ => // log.error("Cannot route envelope name " + envelope.name) // do nothing diff --git a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/ScreenshareManager.scala b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/ScreenshareManager.scala index 5fbe4f31a5e9d4fcec37ed03c89c5d31e7975eea..d3cfbb370fd9efe1a48157a944c36a528e88e0da 100755 --- a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/ScreenshareManager.scala +++ b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/ScreenshareManager.scala @@ -18,11 +18,11 @@ */ package org.bigbluebutton.app.screenshare.server.sessions -import akka.actor.{Actor, ActorLogging, ActorSystem, Props} import org.bigbluebutton.app.screenshare.StreamInfo import org.bigbluebutton.app.screenshare.server.sessions.Session.StopSession +import akka.actor.{Actor, ActorLogging, ActorSystem, Props} import scala.collection.mutable.HashMap -import org.bigbluebutton.app.screenshare.events.{IEventsMessageBus, IsScreenSharingResponse, ScreenShareRequestTokenFailedResponse} +import org.bigbluebutton.app.screenshare.events.{IEventsMessageBus, IsScreenSharingResponse, RecordChapterBreakMessage, ScreenShareRequestTokenFailedResponse} import org.bigbluebutton.app.screenshare.server.sessions.messages._ object ScreenshareManager { @@ -57,10 +57,15 @@ class ScreenshareManager(val aSystem: ActorSystem, val bus: IEventsMessageBus) case msg: MeetingEnded => handleMeetingHasEnded(msg) case msg: MeetingCreated => handleMeetingCreated(msg) case msg: ClientPongMessage => handleClientPongMessage(msg) + case msg: RecordingChapterBreak => handleRecordingChapterBreak(msg) case msg: Any => log.warning("Unknown message " + msg) } + private def handleRecordingChapterBreak(msg: RecordingChapterBreak): Unit = { + bus.send(new RecordChapterBreakMessage(msg.meetingId, msg.timestamp)) + } + private def handleClientPongMessage(msg: ClientPongMessage) { if (log.isDebugEnabled) { log.debug("Received ClientPongMessage message for meeting=[" + msg.meetingId + "]") diff --git a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/messages/IMessage.scala b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/messages/IMessage.scala index 9819d807237dd969a5e531f3ee2dd47a9c227611..23cbcb2212b3b9e23982d789a4d8458bbdd8ee7e 100755 --- a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/messages/IMessage.scala +++ b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/messages/IMessage.scala @@ -47,4 +47,6 @@ case class MeetingEnded(meetingId: String) case class MeetingCreated(meetingId: String, record: Boolean) -case class ClientPongMessage(meetingId: String, userId: String, streamId: String, timestamp: Long) \ No newline at end of file +case class ClientPongMessage(meetingId: String, userId: String, streamId: String, timestamp: Long) + +case class RecordingChapterBreak(meetingId: String, timestamp: Long) \ No newline at end of file diff --git a/bbb-screenshare/app/src/main/webapp/WEB-INF/red5-web.xml b/bbb-screenshare/app/src/main/webapp/WEB-INF/red5-web.xml index 1773918626992aed94e15e4aa3e6ec59458e4e46..cab5891b7ea3e795dd5cb0cdb27310b8db5786f3 100755 --- a/bbb-screenshare/app/src/main/webapp/WEB-INF/red5-web.xml +++ b/bbb-screenshare/app/src/main/webapp/WEB-INF/red5-web.xml @@ -52,7 +52,10 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. <property name="recordingDirectory" value="${recordingDirectory}"/> <property name="application" ref="screenShareApplication"/> <property name="messageSender" ref="connectionInvokerService"/> + <property name="meetingManager" ref="meetingManager"/> </bean> + + <bean id="meetingManager" class="org.bigbluebutton.app.screenshare.MeetingManager"/> <bean id="screenshare.service" class="org.bigbluebutton.app.screenshare.red5.Red5AppService"> <property name="appHandler" ref="red5AppHandler"/> @@ -77,6 +80,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. <bean id="eventListenerImp" class="org.bigbluebutton.app.screenshare.red5.EventListenerImp"> <property name="messageSender" ref="connectionInvokerService"/> + <property name="meetingManager" ref="meetingManager"/> </bean> <bean id="jnlpConfigurator" class="org.bigbluebutton.app.screenshare.server.servlet.JnlpConfigurator">