Merge pull request #678 from ritzalam/reconnect-to-bbb-video
Reconnect to bbb video
This commit is contained in:
commit
60f9dda8e8
@ -178,13 +178,13 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
|
|||||||
String streamId = stream.getPublishedName();
|
String streamId = stream.getPublishedName();
|
||||||
|
|
||||||
publisher.userSharedWebcamMessage(meetingId, userId, streamId);
|
publisher.userSharedWebcamMessage(meetingId, userId, streamId);
|
||||||
|
VideoStreamListener listener = new VideoStreamListener(conn.getScope(), stream, recordVideoStream);
|
||||||
|
listener.setEventRecordingService(recordingService);
|
||||||
|
stream.addStreamListener(listener);
|
||||||
|
streamListeners.put(conn.getScope().getName() + "-" + stream.getPublishedName(), listener);
|
||||||
|
|
||||||
if (recordVideoStream) {
|
if (recordVideoStream) {
|
||||||
recordStream(stream);
|
recordStream(stream);
|
||||||
VideoStreamListener listener = new VideoStreamListener();
|
|
||||||
listener.setEventRecordingService(recordingService);
|
|
||||||
stream.addStreamListener(listener);
|
|
||||||
streamListeners.put(conn.getScope().getName() + "-" + stream.getPublishedName(), listener);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,13 +212,13 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
|
|||||||
|
|
||||||
publisher.userUnshareWebcamRequestMessage(meetingId, userId, streamId);
|
publisher.userUnshareWebcamRequestMessage(meetingId, userId, streamId);
|
||||||
|
|
||||||
if (recordVideoStream) {
|
|
||||||
|
|
||||||
IStreamListener listener = streamListeners.remove(scopeName + "-" + stream.getPublishedName());
|
IStreamListener listener = streamListeners.remove(scopeName + "-" + stream.getPublishedName());
|
||||||
if (listener != null) {
|
if (listener != null) {
|
||||||
stream.removeStreamListener(listener);
|
stream.removeStreamListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recordVideoStream) {
|
||||||
|
|
||||||
long publishDuration = (System.currentTimeMillis() - stream.getCreationTime()) / 1000;
|
long publishDuration = (System.currentTimeMillis() - stream.getCreationTime()) / 1000;
|
||||||
log.info("streamBroadcastClose " + stream.getPublishedName() + " " + System.currentTimeMillis() + " " + scopeName);
|
log.info("streamBroadcastClose " + stream.getPublishedName() + " " + System.currentTimeMillis() + " " + scopeName);
|
||||||
Map<String, String> event = new HashMap<String, String>();
|
Map<String, String> event = new HashMap<String, String>();
|
||||||
|
84
bbb-video/src/main/java/org/bigbluebutton/app/video/VideoStreamListener.java
Normal file → Executable file
84
bbb-video/src/main/java/org/bigbluebutton/app/video/VideoStreamListener.java
Normal file → Executable file
@ -21,14 +21,20 @@ package org.bigbluebutton.app.video;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.mina.core.buffer.IoBuffer;
|
import org.apache.mina.core.buffer.IoBuffer;
|
||||||
import org.red5.server.api.IConnection;
|
import org.red5.server.api.IConnection;
|
||||||
import org.red5.server.api.Red5;
|
import org.red5.server.api.Red5;
|
||||||
|
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.IBroadcastStream;
|
||||||
import org.red5.server.api.stream.IStreamListener;
|
import org.red5.server.api.stream.IStreamListener;
|
||||||
import org.red5.server.api.stream.IStreamPacket;
|
import org.red5.server.api.stream.IStreamPacket;
|
||||||
import org.red5.server.net.rtmp.event.VideoData;
|
import org.red5.server.net.rtmp.event.VideoData;
|
||||||
|
import org.red5.server.scheduling.QuartzSchedulingService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.red5.logging.Red5LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to listen for the first video packet of the webcam.
|
* Class to listen for the first video packet of the webcam.
|
||||||
@ -46,12 +52,44 @@ import org.red5.server.net.rtmp.event.VideoData;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class VideoStreamListener implements IStreamListener {
|
public class VideoStreamListener implements IStreamListener {
|
||||||
|
private static final Logger log = Red5LoggerFactory.getLogger(VideoStreamListener.class, "video");
|
||||||
|
|
||||||
private EventRecordingService recordingService;
|
private EventRecordingService recordingService;
|
||||||
private volatile boolean firstPacketReceived = false;
|
private volatile boolean firstPacketReceived = false;
|
||||||
|
|
||||||
private Long genTimestamp() {
|
// Maximum time between video packets
|
||||||
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
private int videoTimeout = 10000;
|
||||||
}
|
|
||||||
|
// Last time video was received, not video timestamp
|
||||||
|
private long lastVideoTime;
|
||||||
|
|
||||||
|
// Stream being observed
|
||||||
|
private IBroadcastStream stream;
|
||||||
|
|
||||||
|
// if this stream is recorded or not
|
||||||
|
private boolean record;
|
||||||
|
|
||||||
|
// Scheduler
|
||||||
|
private QuartzSchedulingService scheduler;
|
||||||
|
|
||||||
|
// Event queue worker job name
|
||||||
|
private String timeoutJobName;
|
||||||
|
|
||||||
|
private IScope scope;
|
||||||
|
|
||||||
|
public VideoStreamListener(IScope scope, IBroadcastStream stream, Boolean record) {
|
||||||
|
this.scope = scope;
|
||||||
|
this.stream = stream;
|
||||||
|
this.record = record;
|
||||||
|
|
||||||
|
// get the scheduler
|
||||||
|
scheduler = (QuartzSchedulingService) scope.getParent().getContext().getBean(QuartzSchedulingService.BEAN_NAME);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long genTimestamp() {
|
||||||
|
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void packetReceived(IBroadcastStream stream, IStreamPacket packet) {
|
public void packetReceived(IBroadcastStream stream, IStreamPacket packet) {
|
||||||
@ -64,17 +102,25 @@ public class VideoStreamListener implements IStreamListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (packet instanceof VideoData) {
|
if (packet instanceof VideoData) {
|
||||||
|
// keep track of last time video was received
|
||||||
|
lastVideoTime = System.currentTimeMillis();
|
||||||
|
|
||||||
if (! firstPacketReceived) {
|
if (! firstPacketReceived) {
|
||||||
firstPacketReceived = true;
|
firstPacketReceived = true;
|
||||||
IConnection conn = Red5.getConnectionLocal();
|
// start the worker
|
||||||
Map<String, String> event = new HashMap<String, String>();
|
timeoutJobName = scheduler.addScheduledJob(videoTimeout, new TimeoutJob());
|
||||||
event.put("module", "WEBCAM");
|
|
||||||
event.put("timestamp", genTimestamp().toString());
|
|
||||||
event.put("meetingId", conn.getScope().getName());
|
|
||||||
event.put("stream", stream.getPublishedName());
|
|
||||||
event.put("eventName", "StartWebcamShareEvent");
|
|
||||||
|
|
||||||
recordingService.record(conn.getScope().getName(), event);
|
if (record) {
|
||||||
|
IConnection conn = Red5.getConnectionLocal();
|
||||||
|
Map<String, String> event = new HashMap<String, String>();
|
||||||
|
event.put("module", "WEBCAM");
|
||||||
|
event.put("timestamp", genTimestamp().toString());
|
||||||
|
event.put("meetingId", scope.getName());
|
||||||
|
event.put("stream", stream.getPublishedName());
|
||||||
|
event.put("eventName", "StartWebcamShareEvent");
|
||||||
|
|
||||||
|
recordingService.record(conn.getScope().getName(), event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,4 +129,18 @@ public class VideoStreamListener implements IStreamListener {
|
|||||||
recordingService = s;
|
recordingService = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TimeoutJob implements IScheduledJob {
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -548,7 +548,7 @@ package org.bigbluebutton.modules.users.services
|
|||||||
UserManager.getInstance().getConference().addUser(user);
|
UserManager.getInstance().getConference().addUser(user);
|
||||||
|
|
||||||
if (joinedUser.hasStream) {
|
if (joinedUser.hasStream) {
|
||||||
var streams:Array = joinedUser.webcamStream.split("|");
|
var streams:Array = joinedUser.webcamStream;
|
||||||
for each(var stream:String in streams) {
|
for each(var stream:String in streams) {
|
||||||
UserManager.getInstance().getConference().sharedWebcam(user.userID, stream);
|
UserManager.getInstance().getConference().sharedWebcam(user.userID, stream);
|
||||||
}
|
}
|
||||||
|
@ -464,6 +464,7 @@ package org.bigbluebutton.modules.videoconf.maps
|
|||||||
stopAllBroadcasting();
|
stopAllBroadcasting();
|
||||||
trace("VideoEventMapDelegate:: Closing all webcam windows.");
|
trace("VideoEventMapDelegate:: Closing all webcam windows.");
|
||||||
closeAllWindows()
|
closeAllWindows()
|
||||||
|
openWebcamWindows();
|
||||||
} else {
|
} else {
|
||||||
addToolbarButton();
|
addToolbarButton();
|
||||||
openWebcamWindows();
|
openWebcamWindows();
|
||||||
|
2
bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/UserVideo.as
Normal file → Executable file
2
bigbluebutton-client/src/org/bigbluebutton/modules/videoconf/views/UserVideo.as
Normal file → Executable file
@ -66,7 +66,7 @@ package org.bigbluebutton.modules.videoconf.views
|
|||||||
|
|
||||||
protected function getVideoProfile(stream:String):VideoProfile {
|
protected function getVideoProfile(stream:String):VideoProfile {
|
||||||
trace("Parsing stream name [" + stream + "]");
|
trace("Parsing stream name [" + stream + "]");
|
||||||
var pattern:RegExp = new RegExp("([A-Za-z0-9]+)-([A-Za-z0-9]+)-\\d+", "");
|
var pattern:RegExp = new RegExp("([A-Za-z0-9]+)-([A-Za-z0-9_]+)-\\d+", "");
|
||||||
if (pattern.test(stream)) {
|
if (pattern.test(stream)) {
|
||||||
trace("The stream name is well formatted");
|
trace("The stream name is well formatted");
|
||||||
trace("Video profile resolution is [" + pattern.exec(stream)[1] + "]");
|
trace("Video profile resolution is [" + pattern.exec(stream)[1] + "]");
|
||||||
|
Loading…
Reference in New Issue
Block a user