}
RTMP rtmp = conn.getState();
// Get the header size and length
byte headerSize = RTMPUtils.decodeHeaderSize(headerValue, byteCount);
int headerLength = RTMPUtils.getHeaderLength(headerSize);
Header lastHeader = rtmp.getLastReadHeader(channelId);
headerLength += byteCount - 1;
switch (headerSize) {
case HEADER_NEW:
case HEADER_SAME_SOURCE:
case HEADER_TIMER_CHANGE:
if (remaining >= headerLength) {
int timeValue = RTMPUtils.readUnsignedMediumInt(in);
if (timeValue == 0xffffff) {
headerLength += 4;
}
}
break;
case HEADER_CONTINUE:
if (lastHeader != null && lastHeader.getExtendedTimestamp() != 0) {
headerLength += 4;
}
break;
default:
throw new ProtocolException("Unexpected header size " + headerSize + " check for error");
}
if (remaining < headerLength) {
log.trace("Header too small (hlen: {}), buffering. remaining: {}", headerLength, remaining);
in.position(position);
state.bufferDecoding(headerLength);
return null;
}
// Move the position back to the start
in.position(position);
final Header header = decodeHeader(in, lastHeader);
if (header == null) {
throw new ProtocolException("Header is null, check for error");
}
rtmp.setLastReadHeader(channelId, header);
// check to see if this is a new packets or continue decoding an existing one
Packet packet = rtmp.getLastReadPacket(channelId);
if (packet == null) {
packet = new Packet(header.clone());
rtmp.setLastReadPacket(channelId, packet);
}
final IoBuffer buf = packet.getData();
final int readRemaining = header.getSize() - buf.position();
final int chunkSize = rtmp.getReadChunkSize();
final int readAmount = (readRemaining > chunkSize) ? chunkSize : readRemaining;
if (in.remaining() < readAmount) {
log.debug("Chunk too small, buffering ({},{})", in.remaining(), readAmount);
// skip the position back to the start
in.position(position);
state.bufferDecoding(headerLength + readAmount);
return null;
}
BufferUtils.put(buf, in, readAmount);
if (buf.position() < header.getSize()) {
state.continueDecoding();
return null;
}
if (buf.position() > header.getSize()) {
log.warn("Packet size expanded from {} to {} ({})", new Object[] { (header.getSize()), buf.position(), header });
}
buf.flip();
try {
final IRTMPEvent message = decodeMessage(conn, packet.getHeader(), buf);
message.setHeader(packet.getHeader());
// Unfortunately flash will, especially when resetting a video stream with a new key frame, sometime
// send an earlier time stamp. To avoid dropping it, we just give it the minimal increment since the
// last message. But to avoid relative time stamps being mis-computed, we don't reset the header we stored.
final Header lastReadHeader = rtmp.getLastReadPacketHeader(channelId);
if (lastReadHeader != null && (message instanceof AudioData || message instanceof VideoData)
&& RTMPUtils.compareTimestamps(lastReadHeader.getTimer(), packet.getHeader().getTimer()) >= 0) {
log.trace("Non-monotonically increasing timestamps; type: {}; adjusting to {}; ts: {}; last: {}", new Object[] { header.getDataType(),
lastReadHeader.getTimer() + 1, header.getTimer(), lastReadHeader.getTimer() });
message.setTimestamp(lastReadHeader.getTimer() + 1);
} else {
message.setTimestamp(header.getTimer());
}
rtmp.setLastReadPacketHeader(channelId, packet.getHeader());
packet.setMessage(message);