- generate key frames regularly instead of just when the user joins. This allows us to enable

dropping of packets for slow connections in red5
This commit is contained in:
Richard Alam 2015-07-21 20:16:14 +00:00
parent 134facbbf4
commit bef5e77357
2 changed files with 46 additions and 26 deletions

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.api.event.IEvent;
import org.red5.server.api.scope.IScope;
@ -38,12 +39,12 @@ import org.red5.server.messaging.PipeConnectionEvent;
import org.red5.server.net.rtmp.event.IRTMPEvent;
import org.red5.server.net.rtmp.event.Notify;
import org.red5.server.net.rtmp.event.VideoData;
import org.red5.server.net.rtmp.message.Constants;
import org.red5.server.stream.message.RTMPMessage;
import org.red5.codec.IVideoStreamCodec;
import org.red5.codec.IStreamCodecInfo;
import org.red5.codec.StreamCodecInfo;
import org.slf4j.Logger;
import org.red5.server.api.stream.IStreamPacket;;
public class ScreenVideoBroadcastStream implements IBroadcastStream, IProvider, IPipeConnectionListener {
@ -189,7 +190,7 @@ public class ScreenVideoBroadcastStream implements IBroadcastStream, IProvider,
if (event instanceof IRTMPEvent) {
IRTMPEvent rtmpEvent = (IRTMPEvent) event;
if (livePipe != null) {
RTMPMessage msg = RTMPMessage.build(rtmpEvent);
RTMPMessage msg = RTMPMessage.build(rtmpEvent, Constants.SOURCE_TYPE_LIVE);
if (creationTime == null)
creationTime = (long)rtmpEvent.getTimestamp();
@ -199,7 +200,7 @@ public class ScreenVideoBroadcastStream implements IBroadcastStream, IProvider,
streamCodecInfo.setHasVideo(true);
streamCodecInfo.setVideoCodec(videoStreamCodec);
videoStreamCodec.reset();
videoStreamCodec.addData(((VideoData) rtmpEvent).getData());
videoStreamCodec.addData(((VideoData) rtmpEvent).getData());
livePipe.pushMessage(msg);
// Notify listeners about received packet

View File

@ -44,10 +44,12 @@ class SessionSVC(sessionManager:SessionManagerSVC, room: String, screenDim: Dime
private var stop = true
private var mouseLoc:Point = new Point(100,100)
private var pendingGenKeyFrameRequest = false
private var timestamp = 0L;
private var lastUserKeyFrameRequest = 0L
private var sentInitialKeyFrame = false;
private var lastKeyFrameSentOn = 0L
private var streamStartedOn = 0L
private var streamStarted = false
/*
* Schedule to generate a key frame after 30seconds of a request.
* This prevents us from generating unnecessary key frames when
@ -64,13 +66,23 @@ class SessionSVC(sessionManager:SessionManagerSVC, room: String, screenDim: Dime
}
}
def scheduleGenerateFrame() {
val mainActor = self
actor {
Thread.sleep(interframeInterval)
mainActor ! "GenerateFrame"
}
}
def scheduleGenerateFrame() {
val mainActor = self
actor {
Thread.sleep(interframeInterval)
val now = System.currentTimeMillis()
if ((now - lastKeyFrameSentOn) > 60000) {
// Generate a key frame every 1 minute. The reason is that if
// packets are dropped for a user with slow connection, packets
// will continue to be dropped for that user until a key frame
// is sent. (ralam july 15, 2015)
mainActor ! "GenerateAKeyFrame"
} else {
mainActor ! "GenerateFrame"
}
}
}
def act() = {
loop {
@ -87,20 +99,20 @@ class SessionSVC(sessionManager:SessionManagerSVC, room: String, screenDim: Dime
}
}
case GenerateKeyFrame => {
val now = System.currentTimeMillis()
// Wait 30sec between keyframe request from the users. This prevents
// creating many keyframes when users join the session close to one
// another.
if (now - lastUserKeyFrameRequest > 30000) {
lastUserKeyFrameRequest = now
scheduleGenerateKeyFrame(keyFrameInterval)
}
// do not generate a key frame every time a user joins as we
// generate key frames regularly now.
//scheduleGenerateKeyFrame(keyFrameInterval)
}
case "GenerateAKeyFrame" => {
pendingGenKeyFrameRequest = false
log.debug("Session: Generating Key Frame for room %s", room)
generateFrame(true)
pendingGenKeyFrameRequest = false
log.debug("Session: Generating Key Frame for room %s", room)
generateFrame(true)
lastKeyFrameSentOn = System.currentTimeMillis()
if (!stop) {
scheduleGenerateFrame()
} else {
exit()
}
}
case b: UpdateSessionBlock => updateBlock(b.position, b.blockData, b.keyframe, b.seqNum)
case m: Any => log.warning("Session: Unknown message [%s]", m)
@ -153,8 +165,15 @@ class SessionSVC(sessionManager:SessionManagerSVC, room: String, screenDim: Dime
sessionManager ! new RemoveSession(room)
} else {
if (blockManager != null) {
timestamp += 50;
stream ! new UpdateStream(room, blockManager.generateFrame(keyframe), timestamp)
val now = System.currentTimeMillis()
if (!streamStarted) {
streamStarted = true
streamStartedOn = now
}
val ts = now - streamStartedOn
stream ! new UpdateStream(room, blockManager.generateFrame(keyframe), ts)
stream ! new UpdateStreamMouseLocation(room, mouseLoc)
}
}