log.trace("Connection: {}", conn);
// get state
RTMP rtmp = conn.getState();
// determine working type
long timestamp = (message.getTimestamp() & 0xFFFFFFFFL);
LiveTimestampMapping mapping = rtmp.getLastTimestampMapping(channelId);
// just get the current time ONCE per packet
long now = System.currentTimeMillis();
if (mapping == null || timestamp < mapping.getLastStreamTime()) {
log.trace("Resetting clock time ({}) to stream time ({})", now, timestamp);
// either first time through, or time stamps were reset
mapping = rtmp.new LiveTimestampMapping(now, timestamp);
rtmp.setLastTimestampMapping(channelId, mapping);
}
mapping.setLastStreamTime(timestamp);
long clockTimeOfMessage = mapping.getClockStartTime() + timestamp - mapping.getStreamStartTime();
//determine tardiness / how late it is
long tardiness = clockTimeOfMessage - now;
//TDJ: EXPERIMENTAL dropping for LIVE packets in future (default false)
if (isLive && dropLiveFuture) {
tardiness = Math.abs(tardiness);
}
//subtract the ping time / latency from the tardiness value
if (conn != null) {
int lastPingTime = conn.getLastPingTime();
log.trace("Last ping time for connection: {} {} ms", conn.getId(), lastPingTime);
if (lastPingTime > 0) {
tardiness -= lastPingTime;
}
//subtract the buffer time
int streamId = conn.getStreamIdForChannel(channelId);
IClientStream stream = conn.getStreamById(streamId);
if (stream != null) {
int clientBufferDuration = stream.getClientBufferDuration();
if (clientBufferDuration > 0) {
//two times the buffer duration seems to work best with vod
if (isLive) {
tardiness -= clientBufferDuration;
} else {
tardiness -= clientBufferDuration * 2;
}
}
log.trace("Client buffer duration: {}", clientBufferDuration);
}
}
//TODO: how should we differ handling based on live or vod?
//TODO: if we are VOD do we "pause" the provider when we are consistently late?
if (log.isTraceEnabled()) {
log.trace("Packet timestamp: {}; tardiness: {}; now: {}; message clock time: {}, dropLiveFuture: {}", new Object[] { timestamp, tardiness, now, clockTimeOfMessage,
dropLiveFuture });
}
//anything coming in less than the base will be allowed to pass, it will not be
//dropped or manipulated
if (tardiness < baseTolerance) {
//frame is below lowest bounds, let it go
} else if (tardiness > highestTolerance) {
//frame is really late, drop it no matter what type
log.trace("Dropping late message: {}", message);
//if we're working with video, indicate that we will need a key frame to proceed
if (isVideo) {
mapping.setKeyFrameNeeded(true);
}
//drop it
drop = true;
} else {
if (isVideo) {
VideoData video = (VideoData) message;
if (video.getFrameType() == FrameType.KEYFRAME) {
//if its a key frame the inter and disposible checks can be skipped
log.trace("Resuming stream with key frame; message: {}", message);
mapping.setKeyFrameNeeded(false);
} else if (tardiness >= baseTolerance && tardiness < midTolerance) {
//drop disposable frames
if (video.getFrameType() == FrameType.DISPOSABLE_INTERFRAME) {
log.trace("Dropping disposible frame; message: {}", message);
drop = true;