Use a packet buffer pool to reduce memory pressure

This commit is contained in:
Cameron Gutman 2013-10-29 21:39:57 -04:00
parent e6af9df142
commit e5126ebe01
4 changed files with 53 additions and 29 deletions

View File

@ -16,6 +16,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import com.limelight.nvstream.av.AvBufferDescriptor;
import com.limelight.nvstream.av.AvBufferPool;
import com.limelight.nvstream.av.AvDecodeUnit;
import com.limelight.nvstream.av.AvPacket;
import com.limelight.nvstream.av.AvParser;
@ -35,10 +36,11 @@ public class NvVideoStream {
public static final int RTCP_PORT = 47999;
public static final int FIRST_FRAME_PORT = 47996;
private static final int FRAME_RATE = 60;
private ByteBuffer[] videoDecoderInputBuffers = null;
private MediaCodec videoDecoder;
private AvBufferPool pool = new AvBufferPool(1500);
private AvParser parser = new AvParser();
private InputStream getFirstFrame(String host) throws UnknownHostException, IOException
@ -151,6 +153,9 @@ public class NvVideoStream {
for (AvBufferDescriptor desc : du.getBufferList())
{
buf.put(desc.data, desc.offset, desc.length);
// Release the buffer back to the buffer pool
pool.free(desc.data);
}
videoDecoder.queueInputBuffer(inputIndex,
@ -175,13 +180,11 @@ public class NvVideoStream {
@Override
public void run() {
byte[] buffer = new byte[1500];
DatagramPacket packet = new DatagramPacket(pool.allocate(), 1500);
AvBufferDescriptor desc = new AvBufferDescriptor(null, 0, 0);
for (;;)
{
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
{
try {
rtp.receive(packet);
} catch (IOException e) {
@ -198,12 +201,13 @@ public class NvVideoStream {
desc.offset += 12;
desc.length -= 12;
// Give the data to the AV parser
// !!! We no longer own the data buffer at this point !!!
parser.addInputData(new AvPacket(desc));
// Get a new buffer from the buffer pool
packet.setData(pool.allocate(), 0, 1500);
}
}
}).start();
for (;;)
@ -232,11 +236,4 @@ public class NvVideoStream {
}
}).start();
}
/**
* Generates the presentation time for frame N, in microseconds.
*/
private static long computePresentationTime(int frameIndex) {
return 132 + frameIndex * 1000000 / FRAME_RATE;
}
}

View File

@ -0,0 +1,30 @@
package com.limelight.nvstream.av;
import java.util.LinkedList;
public class AvBufferPool {
private LinkedList<byte[]> bufferList = new LinkedList<byte[]>();
private int bufferSize;
public AvBufferPool(int size)
{
this.bufferSize = size;
}
public synchronized byte[] allocate()
{
if (bufferList.isEmpty())
{
return new byte[bufferSize];
}
else
{
return bufferList.removeFirst();
}
}
public synchronized void free(byte[] buffer)
{
bufferList.addFirst(buffer);
}
}

View File

@ -5,14 +5,11 @@ public class AvPacket {
public AvPacket(AvBufferDescriptor rtpPayload)
{
byte[] data = new byte[rtpPayload.length];
System.arraycopy(rtpPayload.data, rtpPayload.offset, data, 0, rtpPayload.length);
buffer = new AvBufferDescriptor(data, 0, data.length);
buffer = new AvBufferDescriptor(rtpPayload.data, rtpPayload.offset, rtpPayload.length);
}
public AvBufferDescriptor getPayload()
public AvBufferDescriptor getNewPayloadDescriptor()
{
int payloadOffset = buffer.offset+56;
return new AvBufferDescriptor(buffer.data, payloadOffset, buffer.length-payloadOffset);
return new AvBufferDescriptor(buffer.data, buffer.offset+56, buffer.length-56);
}
}

View File

@ -1,7 +1,6 @@
package com.limelight.nvstream.av;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
public class AvParser {
@ -16,7 +15,7 @@ public class AvParser {
{
// This is the start of a new NAL
if (nalDataChain != null && nalDataLength != 0)
{
{
// Construct the H264 decode unit
AvDecodeUnit du = new AvDecodeUnit(AvDecodeUnit.TYPE_H264, nalDataChain, nalDataLength);
decodedUnits.add(du);
@ -28,9 +27,10 @@ public class AvParser {
}
public void addInputData(AvPacket packet)
{
AvBufferDescriptor payload = packet.getPayload();
AvBufferDescriptor location = new AvBufferDescriptor(payload.data, payload.offset, payload.length);
{
// This payload buffer descriptor belongs to us
AvBufferDescriptor location = packet.getNewPayloadDescriptor();
int payloadLength = location.length;
while (location.length != 0)
{
@ -47,9 +47,9 @@ public class AvParser {
nalDataChain = new LinkedList<AvBufferDescriptor>();
nalDataLength = 0;
// Skip the start sequence and the type byte
location.length -= 5;
location.offset += 5;
// Skip the start sequence
location.length -= 4;
location.offset += 4;
}
// If there's a NAL assembly in progress, add the current data
@ -58,7 +58,7 @@ public class AvParser {
// FIXME: This is a hack to make parsing full packets
// take less time. We assume if they don't start with
// a NAL start sequence, they're full of NAL data
if (payload.length == 968)
if (payloadLength == 968)
{
location.offset += location.length;
location.length = 0;