Stop allocating RtpPacket and VideoPacket objects in the reassembly path

This commit is contained in:
Cameron Gutman 2014-06-22 13:52:40 -07:00
parent 6c5ec3d2e9
commit 86e2657613
4 changed files with 84 additions and 51 deletions

View File

@ -8,6 +8,8 @@ public class RtpPacket {
private short seqNum; private short seqNum;
private ByteBufferDescriptor buffer; private ByteBufferDescriptor buffer;
public static final int HEADER_SIZE = 12;
public RtpPacket(ByteBufferDescriptor buffer) public RtpPacket(ByteBufferDescriptor buffer)
{ {
this.buffer = new ByteBufferDescriptor(buffer); this.buffer = new ByteBufferDescriptor(buffer);
@ -41,6 +43,6 @@ public class RtpPacket {
public ByteBufferDescriptor getNewPayloadDescriptor() public ByteBufferDescriptor getNewPayloadDescriptor()
{ {
return new ByteBufferDescriptor(buffer.data, buffer.offset+12, buffer.length-12); return new ByteBufferDescriptor(buffer.data, buffer.offset+HEADER_SIZE, buffer.length-HEADER_SIZE);
} }
} }

View File

@ -6,7 +6,6 @@ import java.util.concurrent.LinkedBlockingQueue;
import com.limelight.LimeLog; import com.limelight.LimeLog;
import com.limelight.nvstream.av.ByteBufferDescriptor; import com.limelight.nvstream.av.ByteBufferDescriptor;
import com.limelight.nvstream.av.DecodeUnit; import com.limelight.nvstream.av.DecodeUnit;
import com.limelight.nvstream.av.RtpPacket;
import com.limelight.nvstream.av.ConnectionStatusListener; import com.limelight.nvstream.av.ConnectionStatusListener;
public class VideoDepacketizer { public class VideoDepacketizer {
@ -26,7 +25,8 @@ public class VideoDepacketizer {
private long frameStartTime; private long frameStartTime;
// Cached objects // Cached objects
private ByteBufferDescriptor cachedDesc = new ByteBufferDescriptor(null, 0, 0); private ByteBufferDescriptor cachedReassemblyDesc = new ByteBufferDescriptor(null, 0, 0);
private ByteBufferDescriptor cachedSpecialDesc = new ByteBufferDescriptor(null, 0, 0);
private ConnectionStatusListener controlListener; private ConnectionStatusListener controlListener;
@ -78,15 +78,15 @@ public class VideoDepacketizer {
int start = location.offset; int start = location.offset;
// Check for a special sequence // Check for a special sequence
if (NAL.getSpecialSequenceDescriptor(location, cachedDesc)) if (NAL.getSpecialSequenceDescriptor(location, cachedSpecialDesc))
{ {
if (NAL.isAvcStartSequence(cachedDesc)) if (NAL.isAvcStartSequence(cachedSpecialDesc))
{ {
// We're decoding H264 now // We're decoding H264 now
currentlyDecoding = DecodeUnit.TYPE_H264; currentlyDecoding = DecodeUnit.TYPE_H264;
// Check if it's the end of the last frame // Check if it's the end of the last frame
if (NAL.isAvcFrameStart(cachedDesc)) if (NAL.isAvcFrameStart(cachedSpecialDesc))
{ {
// Reassemble any pending AVC NAL // Reassemble any pending AVC NAL
reassembleAvcFrame(packet.getFrameIndex()); reassembleAvcFrame(packet.getFrameIndex());
@ -97,14 +97,14 @@ public class VideoDepacketizer {
} }
// Skip the start sequence // Skip the start sequence
location.length -= cachedDesc.length; location.length -= cachedSpecialDesc.length;
location.offset += cachedDesc.length; location.offset += cachedSpecialDesc.length;
} }
else else
{ {
// Check if this is padding after a full AVC frame // Check if this is padding after a full AVC frame
if (currentlyDecoding == DecodeUnit.TYPE_H264 && if (currentlyDecoding == DecodeUnit.TYPE_H264 &&
NAL.isPadding(cachedDesc)) { NAL.isPadding(cachedSpecialDesc)) {
// The decode unit is complete // The decode unit is complete
reassembleAvcFrame(packet.getFrameIndex()); reassembleAvcFrame(packet.getFrameIndex());
} }
@ -125,12 +125,12 @@ public class VideoDepacketizer {
if (location.data[location.offset] == 0x00) if (location.data[location.offset] == 0x00)
{ {
// Check if this should end the current NAL // Check if this should end the current NAL
if (NAL.getSpecialSequenceDescriptor(location, cachedDesc)) if (NAL.getSpecialSequenceDescriptor(location, cachedSpecialDesc))
{ {
// Only stop if we're decoding something or this // Only stop if we're decoding something or this
// isn't padding // isn't padding
if (currentlyDecoding != DecodeUnit.TYPE_UNKNOWN || if (currentlyDecoding != DecodeUnit.TYPE_UNKNOWN ||
!NAL.isPadding(cachedDesc)) !NAL.isPadding(cachedSpecialDesc))
{ {
break; break;
} }
@ -163,19 +163,20 @@ public class VideoDepacketizer {
} }
// Add the payload data to the chain // Add the payload data to the chain
avcFrameDataChain.add(location); avcFrameDataChain.add(new ByteBufferDescriptor(location));
avcFrameDataLength += location.length; avcFrameDataLength += location.length;
} }
public void addInputData(VideoPacket packet) public void addInputData(VideoPacket packet)
{ {
ByteBufferDescriptor location = packet.getNewPayloadDescriptor(); // Load our reassembly descriptor
packet.initializePayloadDescriptor(cachedReassemblyDesc);
// Runt packets get decoded using the slow path // Runt packets get decoded using the slow path
// These packets stand alone so there's no need to verify // These packets stand alone so there's no need to verify
// sequencing before submitting // sequencing before submitting
if (location.length < 968) { if (cachedReassemblyDesc.length < 968) {
addInputDataSlow(packet, location); addInputDataSlow(packet, cachedReassemblyDesc);
return; return;
} }
@ -277,21 +278,22 @@ public class VideoDepacketizer {
nextPacketNumber++; nextPacketNumber++;
// Remove extra padding // Remove extra padding
location.length = packet.getPayloadLength(); cachedReassemblyDesc.length = packet.getPayloadLength();
if (firstPacket) if (firstPacket)
{ {
if (NAL.getSpecialSequenceDescriptor(location, cachedDesc) && NAL.isAvcFrameStart(cachedDesc) if (NAL.getSpecialSequenceDescriptor(cachedReassemblyDesc, cachedSpecialDesc)
&& cachedDesc.data[cachedDesc.offset+cachedDesc.length] == 0x67) && NAL.isAvcFrameStart(cachedSpecialDesc)
&& cachedSpecialDesc.data[cachedSpecialDesc.offset+cachedSpecialDesc.length] == 0x67)
{ {
// SPS and PPS prefix is padded between NALs, so we must decode it with the slow path // SPS and PPS prefix is padded between NALs, so we must decode it with the slow path
clearAvcFrameState(); clearAvcFrameState();
addInputDataSlow(packet, location); addInputDataSlow(packet, cachedReassemblyDesc);
return; return;
} }
} }
addInputDataFast(packet, location, firstPacket); addInputDataFast(packet, cachedReassemblyDesc, firstPacket);
// We can't use the EOF flag here because real frames can be split across // We can't use the EOF flag here because real frames can be split across
// multiple "frames" when packetized to fit under the bandwidth ceiling // multiple "frames" when packetized to fit under the bandwidth ceiling
@ -313,12 +315,6 @@ public class VideoDepacketizer {
} }
} }
public void addInputData(RtpPacket packet)
{
ByteBufferDescriptor rtpPayload = packet.getNewPayloadDescriptor();
addInputData(new VideoPacket(rtpPayload));
}
public DecodeUnit takeNextDecodeUnit() throws InterruptedException public DecodeUnit takeNextDecodeUnit() throws InterruptedException
{ {
return decodedUnits.take(); return decodedUnits.take();

View File

@ -4,9 +4,13 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import com.limelight.nvstream.av.ByteBufferDescriptor; import com.limelight.nvstream.av.ByteBufferDescriptor;
import com.limelight.nvstream.av.RtpPacket;
public class VideoPacket { public class VideoPacket {
private ByteBufferDescriptor buffer; private ByteBufferDescriptor buffer;
private ByteBuffer byteBuffer;
private int dataOffset;
private int frameIndex; private int frameIndex;
private int packetIndex; private int packetIndex;
@ -18,19 +22,47 @@ public class VideoPacket {
public static final int FLAG_EOF = 0x2; public static final int FLAG_EOF = 0x2;
public static final int FLAG_SOF = 0x4; public static final int FLAG_SOF = 0x4;
public VideoPacket(ByteBufferDescriptor rtpPayload) public VideoPacket(byte[] buffer)
{ {
buffer = new ByteBufferDescriptor(rtpPayload); this.buffer = new ByteBufferDescriptor(buffer, 0, buffer.length);
this.byteBuffer = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
}
public void initializeWithLengthNoRtpHeader(int length)
{
// Read the video header fields
frameIndex = byteBuffer.getInt();
packetIndex = byteBuffer.getInt();
totalPackets = byteBuffer.getInt();
flags = byteBuffer.getInt();
payloadLength = byteBuffer.getInt();
streamPacketIndex = byteBuffer.getInt();
ByteBuffer bb = ByteBuffer.wrap(buffer.data).order(ByteOrder.LITTLE_ENDIAN); // Data offset without the RTP header
bb.position(buffer.offset); dataOffset = 56;
frameIndex = bb.getInt(); // Update descriptor length
packetIndex = bb.getInt(); buffer.length = length;
totalPackets = bb.getInt(); }
flags = bb.getInt();
payloadLength = bb.getInt(); public void initializeWithLength(int length)
streamPacketIndex = bb.getInt(); {
// Skip the RTP header
byteBuffer.position(RtpPacket.HEADER_SIZE);
// Read the video header fields
frameIndex = byteBuffer.getInt();
packetIndex = byteBuffer.getInt();
totalPackets = byteBuffer.getInt();
flags = byteBuffer.getInt();
payloadLength = byteBuffer.getInt();
streamPacketIndex = byteBuffer.getInt();
// Data offset includes the RTP header
dataOffset = RtpPacket.HEADER_SIZE + 56;
// Update descriptor length
buffer.length = length;
} }
public int getFlags() public int getFlags()
@ -63,8 +95,13 @@ public class VideoPacket {
return streamPacketIndex; return streamPacketIndex;
} }
public ByteBufferDescriptor getNewPayloadDescriptor() public byte[] getBuffer()
{ {
return new ByteBufferDescriptor(buffer.data, buffer.offset+56, buffer.length-56); return buffer.data;
}
public void initializePayloadDescriptor(ByteBufferDescriptor bb)
{
bb.reinitialize(buffer.data, buffer.offset+dataOffset, buffer.length-dataOffset);
} }
} }

View File

@ -12,8 +12,6 @@ import java.util.LinkedList;
import com.limelight.nvstream.NvConnectionListener; import com.limelight.nvstream.NvConnectionListener;
import com.limelight.nvstream.StreamConfiguration; import com.limelight.nvstream.StreamConfiguration;
import com.limelight.nvstream.av.ByteBufferDescriptor;
import com.limelight.nvstream.av.RtpPacket;
import com.limelight.nvstream.av.ConnectionStatusListener; import com.limelight.nvstream.av.ConnectionStatusListener;
public class VideoStream { public class VideoStream {
@ -118,7 +116,9 @@ public class VideoStream {
offset += bytesRead; offset += bytesRead;
} }
depacketizer.addInputData(new VideoPacket(new ByteBufferDescriptor(firstFrame, 0, offset))); VideoPacket packet = new VideoPacket(firstFrame);
packet.initializeWithLengthNoRtpHeader(offset);
depacketizer.addInputData(packet);
} finally { } finally {
firstFrameSocket.close(); firstFrameSocket.close();
firstFrameSocket = null; firstFrameSocket = null;
@ -179,31 +179,29 @@ public class VideoStream {
Thread t = new Thread() { Thread t = new Thread() {
@Override @Override
public void run() { public void run() {
ByteBufferDescriptor ring[] = new ByteBufferDescriptor[VIDEO_RING_SIZE]; VideoPacket ring[] = new VideoPacket[VIDEO_RING_SIZE];
int ringIndex = 0; int ringIndex = 0;
// Preinitialize the ring buffer // Preinitialize the ring buffer
for (int i = 0; i < VIDEO_RING_SIZE; i++) { for (int i = 0; i < VIDEO_RING_SIZE; i++) {
ring[i] = new ByteBufferDescriptor(new byte[MAX_PACKET_SIZE], 0, MAX_PACKET_SIZE); ring[i] = new VideoPacket(new byte[MAX_PACKET_SIZE]);
} }
ByteBufferDescriptor desc; byte[] buffer;
DatagramPacket packet = new DatagramPacket(new byte[1], 1); // Placeholder array DatagramPacket packet = new DatagramPacket(new byte[1], 1); // Placeholder array
while (!isInterrupted()) while (!isInterrupted())
{ {
try { try {
// Pull the next buffer in the ring and reset it // Pull the next buffer in the ring and reset it
desc = ring[ringIndex]; buffer = ring[ringIndex].getBuffer();
desc.length = MAX_PACKET_SIZE;
desc.offset = 0;
// Read the video data off the network // Read the video data off the network
packet.setData(desc.data, desc.offset, desc.length); packet.setData(buffer, 0, buffer.length);
rtp.receive(packet); rtp.receive(packet);
// Submit video data to the depacketizer // Submit video data to the depacketizer
desc.length = packet.getLength(); ring[ringIndex].initializeWithLength(packet.getLength());
depacketizer.addInputData(new RtpPacket(desc)); depacketizer.addInputData(ring[ringIndex]);
ringIndex = (ringIndex + 1) % VIDEO_RING_SIZE; ringIndex = (ringIndex + 1) % VIDEO_RING_SIZE;
} catch (IOException e) { } catch (IOException e) {
listener.connectionTerminated(e); listener.connectionTerminated(e);