From e5126ebe012cdceb051227a5cfbfc4bd183a17a5 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Tue, 29 Oct 2013 21:39:57 -0400 Subject: [PATCH] Use a packet buffer pool to reduce memory pressure --- src/com/limelight/nvstream/NvVideoStream.java | 25 +++++++--------- .../limelight/nvstream/av/AvBufferPool.java | 30 +++++++++++++++++++ src/com/limelight/nvstream/av/AvPacket.java | 9 ++---- src/com/limelight/nvstream/av/AvParser.java | 18 +++++------ 4 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 src/com/limelight/nvstream/av/AvBufferPool.java diff --git a/src/com/limelight/nvstream/NvVideoStream.java b/src/com/limelight/nvstream/NvVideoStream.java index 6ec2e3c2..1fe24d0c 100644 --- a/src/com/limelight/nvstream/NvVideoStream.java +++ b/src/com/limelight/nvstream/NvVideoStream.java @@ -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; - } } diff --git a/src/com/limelight/nvstream/av/AvBufferPool.java b/src/com/limelight/nvstream/av/AvBufferPool.java new file mode 100644 index 00000000..b0fe335e --- /dev/null +++ b/src/com/limelight/nvstream/av/AvBufferPool.java @@ -0,0 +1,30 @@ +package com.limelight.nvstream.av; + +import java.util.LinkedList; + +public class AvBufferPool { + private LinkedList bufferList = new LinkedList(); + 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); + } +} diff --git a/src/com/limelight/nvstream/av/AvPacket.java b/src/com/limelight/nvstream/av/AvPacket.java index cf49aedb..95c83ce1 100644 --- a/src/com/limelight/nvstream/av/AvPacket.java +++ b/src/com/limelight/nvstream/av/AvPacket.java @@ -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); } } diff --git a/src/com/limelight/nvstream/av/AvParser.java b/src/com/limelight/nvstream/av/AvParser.java index 0878831d..765c1100 100644 --- a/src/com/limelight/nvstream/av/AvParser.java +++ b/src/com/limelight/nvstream/av/AvParser.java @@ -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(); 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;