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 java.nio.ByteOrder;
import com.limelight.nvstream.av.AvBufferDescriptor; import com.limelight.nvstream.av.AvBufferDescriptor;
import com.limelight.nvstream.av.AvBufferPool;
import com.limelight.nvstream.av.AvDecodeUnit; import com.limelight.nvstream.av.AvDecodeUnit;
import com.limelight.nvstream.av.AvPacket; import com.limelight.nvstream.av.AvPacket;
import com.limelight.nvstream.av.AvParser; import com.limelight.nvstream.av.AvParser;
@ -35,10 +36,11 @@ public class NvVideoStream {
public static final int RTCP_PORT = 47999; public static final int RTCP_PORT = 47999;
public static final int FIRST_FRAME_PORT = 47996; public static final int FIRST_FRAME_PORT = 47996;
private static final int FRAME_RATE = 60;
private ByteBuffer[] videoDecoderInputBuffers = null; private ByteBuffer[] videoDecoderInputBuffers = null;
private MediaCodec videoDecoder; private MediaCodec videoDecoder;
private AvBufferPool pool = new AvBufferPool(1500);
private AvParser parser = new AvParser(); private AvParser parser = new AvParser();
private InputStream getFirstFrame(String host) throws UnknownHostException, IOException private InputStream getFirstFrame(String host) throws UnknownHostException, IOException
@ -151,6 +153,9 @@ public class NvVideoStream {
for (AvBufferDescriptor desc : du.getBufferList()) for (AvBufferDescriptor desc : du.getBufferList())
{ {
buf.put(desc.data, desc.offset, desc.length); buf.put(desc.data, desc.offset, desc.length);
// Release the buffer back to the buffer pool
pool.free(desc.data);
} }
videoDecoder.queueInputBuffer(inputIndex, videoDecoder.queueInputBuffer(inputIndex,
@ -175,13 +180,11 @@ public class NvVideoStream {
@Override @Override
public void run() { public void run() {
byte[] buffer = new byte[1500]; DatagramPacket packet = new DatagramPacket(pool.allocate(), 1500);
AvBufferDescriptor desc = new AvBufferDescriptor(null, 0, 0); AvBufferDescriptor desc = new AvBufferDescriptor(null, 0, 0);
for (;;) for (;;)
{ {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
try { try {
rtp.receive(packet); rtp.receive(packet);
} catch (IOException e) { } catch (IOException e) {
@ -198,12 +201,13 @@ public class NvVideoStream {
desc.offset += 12; desc.offset += 12;
desc.length -= 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)); parser.addInputData(new AvPacket(desc));
// Get a new buffer from the buffer pool
packet.setData(pool.allocate(), 0, 1500);
} }
} }
}).start(); }).start();
for (;;) for (;;)
@ -232,11 +236,4 @@ public class NvVideoStream {
} }
}).start(); }).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) public AvPacket(AvBufferDescriptor rtpPayload)
{ {
byte[] data = new byte[rtpPayload.length]; buffer = new AvBufferDescriptor(rtpPayload.data, rtpPayload.offset, rtpPayload.length);
System.arraycopy(rtpPayload.data, rtpPayload.offset, data, 0, rtpPayload.length);
buffer = new AvBufferDescriptor(data, 0, data.length);
} }
public AvBufferDescriptor getPayload() public AvBufferDescriptor getNewPayloadDescriptor()
{ {
int payloadOffset = buffer.offset+56; return new AvBufferDescriptor(buffer.data, buffer.offset+56, buffer.length-56);
return new AvBufferDescriptor(buffer.data, payloadOffset, buffer.length-payloadOffset);
} }
} }

View File

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