Revert "WIP optimization of object allocation and pools. There's currently a double free on the AvByteBufferDescriptor that I'm trying to track down."

This reverts commit 6600329580aace81321220d2e4000099ad27f4f7.
This commit is contained in:
Cameron Gutman 2013-11-21 16:25:10 -05:00
parent 3efa356bb8
commit 1d7460e8b3
12 changed files with 90 additions and 213 deletions

View File

@ -168,7 +168,6 @@ public class NvAudioStream {
depacketizer.decodeInputData(packet); depacketizer.decodeInputData(packet);
pool.free(packet.getBackingBuffer()); pool.free(packet.getBackingBuffer());
packet.free();
} }
} }
}; };
@ -210,7 +209,7 @@ public class NvAudioStream {
@Override @Override
public void run() { public void run() {
DatagramPacket packet = new DatagramPacket(pool.allocate(), 1500); DatagramPacket packet = new DatagramPacket(pool.allocate(), 1500);
AvByteBufferDescriptor desc = AvByteBufferDescriptor.newDescriptor(null, 0, 0); AvByteBufferDescriptor desc = new AvByteBufferDescriptor(null, 0, 0);
while (!isInterrupted()) while (!isInterrupted())
{ {
@ -226,7 +225,7 @@ public class NvAudioStream {
desc.data = packet.getData(); desc.data = packet.getData();
// Give the packet to the depacketizer thread // Give the packet to the depacketizer thread
packets.add(AvRtpPacket.create(desc)); packets.add(new AvRtpPacket(desc));
// Get a new buffer from the buffer pool // Get a new buffer from the buffer pool
packet.setData(pool.allocate(), 0, 1500); packet.setData(pool.allocate(), 0, 1500);

View File

@ -110,7 +110,7 @@ public class NvVideoStream {
} }
System.out.println("VID: First frame read ("+offset+" bytes)"); System.out.println("VID: First frame read ("+offset+" bytes)");
depacketizer.addInputData(AvVideoPacket.createNoCopy(AvByteBufferDescriptor.newDescriptor(firstFrame, 0, offset))); depacketizer.addInputData(new AvVideoPacket(new AvByteBufferDescriptor(firstFrame, 0, offset)));
} finally { } finally {
firstFrameSocket.close(); firstFrameSocket.close();
firstFrameSocket = null; firstFrameSocket = null;
@ -249,8 +249,6 @@ public class NvVideoStream {
// !!! We no longer own the data buffer at this point !!! // !!! We no longer own the data buffer at this point !!!
depacketizer.addInputData(packet); depacketizer.addInputData(packet);
packet.free();
} }
} }
}; };
@ -265,7 +263,7 @@ public class NvVideoStream {
@Override @Override
public void run() { public void run() {
DatagramPacket packet = new DatagramPacket(depacketizer.allocatePacketBuffer(), 1500); DatagramPacket packet = new DatagramPacket(depacketizer.allocatePacketBuffer(), 1500);
AvByteBufferDescriptor desc = AvByteBufferDescriptor.newDescriptor(null, 0, 0); AvByteBufferDescriptor desc = new AvByteBufferDescriptor(null, 0, 0);
while (!isInterrupted()) while (!isInterrupted())
{ {
@ -281,7 +279,7 @@ public class NvVideoStream {
desc.data = packet.getData(); desc.data = packet.getData();
// Give the packet to the depacketizer thread // Give the packet to the depacketizer thread
packets.add(AvRtpPacket.create(desc)); packets.add(new AvRtpPacket(desc));
// Get a new buffer from the buffer pool // Get a new buffer from the buffer pool
packet.setData(depacketizer.allocatePacketBuffer(), 0, 1500); packet.setData(depacketizer.allocatePacketBuffer(), 0, 1500);

View File

@ -6,42 +6,35 @@ public class AvByteBufferDescriptor {
public int length; public int length;
public Object context; public Object context;
private static AvObjectPool<AvByteBufferDescriptor> pool = new AvObjectPool<AvByteBufferDescriptor>(); public AvByteBufferDescriptor(byte[] data, int offset, int length)
public static AvByteBufferDescriptor newDescriptor(byte[] data, int offset, int length) {
AvByteBufferDescriptor buffer = pool.tryAllocate();
if (buffer != null) {
buffer.data = data;
buffer.offset = offset;
buffer.length = length;
buffer.context = null;
return buffer;
}
else {
return new AvByteBufferDescriptor(data, offset, length);
}
}
public static AvByteBufferDescriptor newDescriptor(AvByteBufferDescriptor buffer) {
return newDescriptor(buffer.data, buffer.offset, buffer.length);
}
private AvByteBufferDescriptor(byte[] data, int offset, int length)
{ {
this.data = data; this.data = data;
this.offset = offset; this.offset = offset;
this.length = length; this.length = length;
this.context = null;
} }
private AvByteBufferDescriptor(AvByteBufferDescriptor desc) public AvByteBufferDescriptor(AvByteBufferDescriptor desc)
{ {
this.data = desc.data; this.data = desc.data;
this.offset = desc.offset; this.offset = desc.offset;
this.length = desc.length; this.length = desc.length;
this.context = null;
} }
public void free() { public void print()
pool.free(this); {
print(offset, length);
}
public void print(int length)
{
print(this.offset, length);
}
public void print(int offset, int length)
{
for (int i = offset; i < offset+length; i++) {
System.out.printf("%d: %02x \n", i, data[i]);
}
System.out.println();
} }
} }

View File

@ -1,9 +1,9 @@
package com.limelight.nvstream.av; package com.limelight.nvstream.av;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.LinkedList;
public class AvByteBufferPool { public class AvByteBufferPool {
private ConcurrentLinkedQueue<byte[]> bufferList = new ConcurrentLinkedQueue<byte[]>(); private LinkedList<byte[]> bufferList = new LinkedList<byte[]>();
private int bufferSize; private int bufferSize;
public AvByteBufferPool(int size) public AvByteBufferPool(int size)
@ -11,22 +11,25 @@ public class AvByteBufferPool {
this.bufferSize = size; this.bufferSize = size;
} }
public void purge() public synchronized void purge()
{ {
bufferList.clear(); this.bufferList = new LinkedList<byte[]>();
} }
public byte[] allocate() public synchronized byte[] allocate()
{ {
byte[] buff = bufferList.poll(); if (bufferList.isEmpty())
if (buff == null) { {
buff = new byte[bufferSize]; return new byte[bufferSize];
}
else
{
return bufferList.removeFirst();
} }
return buff;
} }
public void free(byte[] buffer) public synchronized void free(byte[] buffer)
{ {
bufferList.add(buffer); bufferList.addFirst(buffer);
} }
} }

View File

@ -12,19 +12,7 @@ public class AvDecodeUnit {
private int dataLength; private int dataLength;
private int flags; private int flags;
private static AvObjectPool<AvDecodeUnit> pool = new AvObjectPool<AvDecodeUnit>(); public AvDecodeUnit(int type, List<AvByteBufferDescriptor> bufferList, int dataLength, int flags)
public static AvDecodeUnit newDecodeUnit(int type, List<AvByteBufferDescriptor> bufferList, int dataLength, int flags) {
AvDecodeUnit du = pool.tryAllocate();
if (du == null) {
du = new AvDecodeUnit();
}
du.initialize(type, bufferList, dataLength, flags);
return du;
}
private AvDecodeUnit() { }
public void initialize(int type, List<AvByteBufferDescriptor> bufferList, int dataLength, int flags)
{ {
this.type = type; this.type = type;
this.bufferList = bufferList; this.bufferList = bufferList;
@ -51,9 +39,4 @@ public class AvDecodeUnit {
{ {
return dataLength; return dataLength;
} }
public void free()
{
pool.free(this);
}
} }

View File

@ -1,22 +0,0 @@
package com.limelight.nvstream.av;
import java.util.concurrent.ConcurrentLinkedQueue;
public class AvObjectPool<T> {
private ConcurrentLinkedQueue<T> objectList = new ConcurrentLinkedQueue<T>();
public void purge()
{
objectList.clear();
}
public T tryAllocate()
{
return objectList.poll();
}
public void free(T object)
{
objectList.add(object);
}
}

View File

@ -8,26 +8,9 @@ public class AvRtpPacket {
private short seqNum; private short seqNum;
private AvByteBufferDescriptor buffer; private AvByteBufferDescriptor buffer;
private static AvObjectPool<AvRtpPacket> pool = new AvObjectPool<AvRtpPacket>(); public AvRtpPacket(AvByteBufferDescriptor buffer)
public static AvRtpPacket create(AvByteBufferDescriptor payload) {
return createNoCopy(AvByteBufferDescriptor.newDescriptor(payload));
}
public static AvRtpPacket createNoCopy(AvByteBufferDescriptor payload) {
AvRtpPacket pkt = pool.tryAllocate();
if (pkt == null) {
pkt = new AvRtpPacket();
}
pkt.initialize(payload);
return pkt;
}
private AvRtpPacket() { }
private void initialize(AvByteBufferDescriptor buffer)
{ {
this.buffer = buffer; this.buffer = new AvByteBufferDescriptor(buffer);
ByteBuffer bb = ByteBuffer.wrap(buffer.data, buffer.offset, buffer.length); ByteBuffer bb = ByteBuffer.wrap(buffer.data, buffer.offset, buffer.length);
@ -56,14 +39,8 @@ public class AvRtpPacket {
return buffer.data; return buffer.data;
} }
public void free()
{
buffer.free();
pool.free(this);
}
public AvByteBufferDescriptor getNewPayloadDescriptor() public AvByteBufferDescriptor getNewPayloadDescriptor()
{ {
return AvByteBufferDescriptor.newDescriptor(buffer.data, buffer.offset+12, buffer.length-12); return new AvByteBufferDescriptor(buffer.data, buffer.offset+12, buffer.length-12);
} }
} }

View File

@ -5,39 +5,17 @@ public class AvShortBufferDescriptor {
public int offset; public int offset;
public int length; public int length;
private static AvObjectPool<AvShortBufferDescriptor> pool = new AvObjectPool<AvShortBufferDescriptor>(); public AvShortBufferDescriptor(short[] data, int offset, int length)
public static AvShortBufferDescriptor newDescriptor(short[] data, int offset, int length) {
AvShortBufferDescriptor buffer = pool.tryAllocate();
if (buffer != null) {
buffer.data = data;
buffer.offset = offset;
buffer.length = length;
return buffer;
}
else {
return new AvShortBufferDescriptor(data, offset, length);
}
}
public static AvShortBufferDescriptor newDescriptor(AvShortBufferDescriptor buffer) {
return newDescriptor(buffer.data, buffer.offset, buffer.length);
}
private AvShortBufferDescriptor(short[] data, int offset, int length)
{ {
this.data = data; this.data = data;
this.offset = offset; this.offset = offset;
this.length = length; this.length = length;
} }
private AvShortBufferDescriptor(AvShortBufferDescriptor desc) public AvShortBufferDescriptor(AvShortBufferDescriptor desc)
{ {
this.data = desc.data; this.data = desc.data;
this.offset = desc.offset; this.offset = desc.offset;
this.length = desc.length; this.length = desc.length;
} }
public void free() {
pool.free(this);
}
} }

View File

@ -1,9 +1,9 @@
package com.limelight.nvstream.av; package com.limelight.nvstream.av;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.LinkedList;
public class AvShortBufferPool { public class AvShortBufferPool {
private ConcurrentLinkedQueue<short[]> bufferList = new ConcurrentLinkedQueue<short[]>(); private LinkedList<short[]> bufferList = new LinkedList<short[]>();
private int bufferSize; private int bufferSize;
public AvShortBufferPool(int size) public AvShortBufferPool(int size)
@ -11,22 +11,25 @@ public class AvShortBufferPool {
this.bufferSize = size; this.bufferSize = size;
} }
public void purge() public synchronized void purge()
{ {
bufferList.clear(); this.bufferList = new LinkedList<short[]>();
} }
public short[] allocate() public synchronized short[] allocate()
{ {
short[] buff = bufferList.poll(); if (bufferList.isEmpty())
if (buff == null) { {
buff = new short[bufferSize]; return new short[bufferSize];
}
else
{
return bufferList.removeFirst();
} }
return buff;
} }
public void free(short[] buffer) public synchronized void free(short[] buffer)
{ {
bufferList.add(buffer); bufferList.addFirst(buffer);
} }
} }

View File

@ -32,13 +32,13 @@ public class AvAudioDepacketizer {
decodeLen *= OpusDecoder.getChannelCount(); decodeLen *= OpusDecoder.getChannelCount();
// Put it on the decoded queue // Put it on the decoded queue
AvShortBufferDescriptor sbd = AvShortBufferDescriptor.newDescriptor(pcmData, 0, decodeLen); if (!decodedUnits.offer(new AvShortBufferDescriptor(pcmData, 0, decodeLen)))
if (!decodedUnits.offer(sbd))
{ {
releaseBuffer(sbd); pool.free(pcmData);
} }
} }
else { else {
System.out.println("decode failed: "+decodeLen);
pool.free(pcmData); pool.free(pcmData);
} }
} }
@ -66,13 +66,11 @@ public class AvAudioDepacketizer {
// This is all the depacketizing we need to do // This is all the depacketizing we need to do
AvByteBufferDescriptor rtpPayload = packet.getNewPayloadDescriptor(); AvByteBufferDescriptor rtpPayload = packet.getNewPayloadDescriptor();
decodeData(rtpPayload.data, rtpPayload.offset, rtpPayload.length); decodeData(rtpPayload.data, rtpPayload.offset, rtpPayload.length);
rtpPayload.free();
} }
public void releaseBuffer(AvShortBufferDescriptor decodedData) public void releaseBuffer(AvShortBufferDescriptor decodedData)
{ {
pool.free(decodedData.data); pool.free(decodedData.data);
decodedData.free();
} }
public AvShortBufferDescriptor getNextDecodedData() throws InterruptedException public AvShortBufferDescriptor getNextDecodedData() throws InterruptedException

View File

@ -13,7 +13,7 @@ import android.media.MediaCodec;
public class AvVideoDepacketizer { public class AvVideoDepacketizer {
// Current NAL state // Current NAL state
private LinkedList<AvByteBufferDescriptor> avcNalDataChain = new LinkedList<AvByteBufferDescriptor>(); private LinkedList<AvByteBufferDescriptor> avcNalDataChain = null;
private int avcNalDataLength = 0; private int avcNalDataLength = 0;
private int currentlyDecoding; private int currentlyDecoding;
@ -22,33 +22,33 @@ public class AvVideoDepacketizer {
private LinkedBlockingQueue<AvDecodeUnit> decodedUnits = new LinkedBlockingQueue<AvDecodeUnit>(); private LinkedBlockingQueue<AvDecodeUnit> decodedUnits = new LinkedBlockingQueue<AvDecodeUnit>();
private AvByteBufferPool bbPool = new AvByteBufferPool(1500); private AvByteBufferPool pool = new AvByteBufferPool(1500);
public byte[] allocatePacketBuffer() public byte[] allocatePacketBuffer()
{ {
return bbPool.allocate(); return pool.allocate();
} }
public void trim() public void trim()
{ {
bbPool.purge(); pool.purge();
} }
private void clearAvcNalState() private void clearAvcNalState()
{
if (avcNalDataChain != null)
{ {
for (AvByteBufferDescriptor avbb : avcNalDataChain) for (AvByteBufferDescriptor avbb : avcNalDataChain)
{ {
AvVideoPacket packet = (AvVideoPacket) avbb.context; AvVideoPacket packet = (AvVideoPacket) avbb.context;
if (packet.release() == 0) { if (packet.release() == 0) {
bbPool.free(avbb.data); pool.free(avbb.data);
packet.free(); }
}
} }
avbb.free(); avcNalDataChain = null;
}
avcNalDataChain.clear();
avcNalDataLength = 0; avcNalDataLength = 0;
} }
@ -60,20 +60,15 @@ public class AvVideoDepacketizer {
AvVideoPacket packet = (AvVideoPacket) buff.context; AvVideoPacket packet = (AvVideoPacket) buff.context;
if (packet.release() == 0) { if (packet.release() == 0) {
bbPool.free(buff.data); pool.free(buff.data);
packet.free();
} }
buff.free();
} }
decodeUnit.free();
} }
private void reassembleAvcNal() private void reassembleAvcNal()
{ {
// This is the start of a new NAL // This is the start of a new NAL
if (!avcNalDataChain.isEmpty() && avcNalDataLength != 0) if (avcNalDataChain != null && avcNalDataLength != 0)
{ {
int flags = 0; int flags = 0;
@ -113,8 +108,6 @@ public class AvVideoDepacketizer {
header.data[header.offset+4]); header.data[header.offset+4]);
break; break;
} }
specialSeq.free();
} }
else else
{ {
@ -125,14 +118,14 @@ public class AvVideoDepacketizer {
} }
// Construct the H264 decode unit // Construct the H264 decode unit
AvDecodeUnit du = AvDecodeUnit.newDecodeUnit(AvDecodeUnit.TYPE_H264, avcNalDataChain, avcNalDataLength, flags); AvDecodeUnit du = new AvDecodeUnit(AvDecodeUnit.TYPE_H264, avcNalDataChain, avcNalDataLength, flags);
if (!decodedUnits.offer(du)) if (!decodedUnits.offer(du))
{ {
releaseDecodeUnit(du); releaseDecodeUnit(du);
} }
// Clear old state // Clear old state
avcNalDataChain.clear(); avcNalDataChain = null;
avcNalDataLength = 0; avcNalDataLength = 0;
} }
} }
@ -165,7 +158,7 @@ public class AvVideoDepacketizer {
reassembleAvcNal(); reassembleAvcNal();
// Setup state for the new NAL // Setup state for the new NAL
avcNalDataChain.clear(); avcNalDataChain = new LinkedList<AvByteBufferDescriptor>();
avcNalDataLength = 0; avcNalDataLength = 0;
} }
@ -217,10 +210,9 @@ public class AvVideoDepacketizer {
location.length--; location.length--;
} }
if (currentlyDecoding == AvDecodeUnit.TYPE_H264) if (currentlyDecoding == AvDecodeUnit.TYPE_H264 && avcNalDataChain != null)
{ {
// This is release if the NAL is cleared or decoded AvByteBufferDescriptor data = new AvByteBufferDescriptor(location.data, start, location.offset-start);
AvByteBufferDescriptor data = AvByteBufferDescriptor.newDescriptor(location.data, start, location.offset-start);
// Attach the current packet as the buffer context and increment the refcount // Attach the current packet as the buffer context and increment the refcount
data.context = packet; data.context = packet;
@ -234,12 +226,8 @@ public class AvVideoDepacketizer {
// If nothing useful came out of this, release the packet now // If nothing useful came out of this, release the packet now
if (packet.release() == 0) { if (packet.release() == 0) {
bbPool.free(location.data); pool.free(location.data);
packet.free();
} }
// Done with the buffer descriptor
location.free();
} }
public void addInputData(AvRtpPacket packet) public void addInputData(AvRtpPacket packet)
@ -260,9 +248,9 @@ public class AvVideoDepacketizer {
lastSequenceNumber = seq; lastSequenceNumber = seq;
// Pass the payload to the non-sequencing parser. It now owns that descriptor. // Pass the payload to the non-sequencing parser
AvByteBufferDescriptor rtpPayload = packet.getNewPayloadDescriptor(); AvByteBufferDescriptor rtpPayload = packet.getNewPayloadDescriptor();
addInputData(AvVideoPacket.createNoCopy(rtpPayload)); addInputData(new AvVideoPacket(rtpPayload));
} }
public AvDecodeUnit getNextDecodeUnit() throws InterruptedException public AvDecodeUnit getNextDecodeUnit() throws InterruptedException
@ -316,19 +304,19 @@ class NAL {
buffer.data[buffer.offset+3] == 0x01) buffer.data[buffer.offset+3] == 0x01)
{ {
// It's the AVC start sequence 00 00 00 01 // It's the AVC start sequence 00 00 00 01
return AvByteBufferDescriptor.newDescriptor(buffer.data, buffer.offset, 4); return new AvByteBufferDescriptor(buffer.data, buffer.offset, 4);
} }
else else
{ {
// It's 00 00 00 // It's 00 00 00
return AvByteBufferDescriptor.newDescriptor(buffer.data, buffer.offset, 3); return new AvByteBufferDescriptor(buffer.data, buffer.offset, 3);
} }
} }
else if (buffer.data[buffer.offset+2] == 0x01 || else if (buffer.data[buffer.offset+2] == 0x01 ||
buffer.data[buffer.offset+2] == 0x02) buffer.data[buffer.offset+2] == 0x02)
{ {
// These are easy: 00 00 01 or 00 00 02 // These are easy: 00 00 01 or 00 00 02
return AvByteBufferDescriptor.newDescriptor(buffer.data, buffer.offset, 3); return new AvByteBufferDescriptor(buffer.data, buffer.offset, 3);
} }
else if (buffer.data[buffer.offset+2] == 0x03) else if (buffer.data[buffer.offset+2] == 0x03)
{ {
@ -350,7 +338,7 @@ class NAL {
else else
{ {
// It's not a standard replacement so it's a special sequence // It's not a standard replacement so it's a special sequence
return AvByteBufferDescriptor.newDescriptor(buffer.data, buffer.offset, 3); return new AvByteBufferDescriptor(buffer.data, buffer.offset, 3);
} }
} }
} }

View File

@ -1,34 +1,19 @@
package com.limelight.nvstream.av.video; package com.limelight.nvstream.av.video;
import com.limelight.nvstream.av.AvByteBufferDescriptor; import com.limelight.nvstream.av.AvByteBufferDescriptor;
import com.limelight.nvstream.av.AvObjectPool;
public class AvVideoPacket { public class AvVideoPacket {
private AvByteBufferDescriptor buffer; private AvByteBufferDescriptor buffer;
private int refCount; private int refCount;
private static AvObjectPool<AvVideoPacket> pool = new AvObjectPool<AvVideoPacket>(); public AvVideoPacket(AvByteBufferDescriptor rtpPayload)
public static AvVideoPacket createNoCopy(AvByteBufferDescriptor payload) {
AvVideoPacket pkt = pool.tryAllocate();
if (pkt != null) {
pkt.buffer = payload;
pkt.refCount = 0;
return pkt;
}
else {
return new AvVideoPacket(payload);
}
}
private AvVideoPacket(AvByteBufferDescriptor rtpPayload)
{ {
buffer = rtpPayload; buffer = new AvByteBufferDescriptor(rtpPayload);
} }
public AvByteBufferDescriptor getNewPayloadDescriptor() public AvByteBufferDescriptor getNewPayloadDescriptor()
{ {
return AvByteBufferDescriptor.newDescriptor(buffer.data, buffer.offset+56, buffer.length-56); return new AvByteBufferDescriptor(buffer.data, buffer.offset+56, buffer.length-56);
} }
public int addRef() public int addRef()
@ -40,10 +25,4 @@ public class AvVideoPacket {
{ {
return --refCount; return --refCount;
} }
public void free()
{
buffer.free();
pool.free(this);
}
} }