Reuse a single AvByteBufferDescriptor when depacketizing H264 instead of allocating many of them per packet

This commit is contained in:
Cameron Gutman 2013-11-24 21:51:09 -05:00
parent fb8b6fd7f5
commit 7d39ac2a45
3 changed files with 41 additions and 23 deletions

View File

@ -19,6 +19,13 @@ public class AvByteBufferDescriptor {
this.length = desc.length; this.length = desc.length;
} }
public void reinitialize(byte[] data, int offset, int length)
{
this.data = data;
this.offset = offset;
this.length = length;
}
public void print() public void print()
{ {
print(offset, length); print(offset, length);

View File

@ -18,4 +18,11 @@ public class AvShortBufferDescriptor {
this.offset = desc.offset; this.offset = desc.offset;
this.length = desc.length; this.length = desc.length;
} }
public void reinitialize(short[] data, int offset, int length)
{
this.data = data;
this.offset = offset;
this.length = length;
}
} }

View File

@ -17,6 +17,10 @@ public class AvVideoDepacketizer {
private int avcNalDataLength = 0; private int avcNalDataLength = 0;
private int currentlyDecoding; private int currentlyDecoding;
// Cached buffer descriptor to save on allocations
// Only safe to use in decode thread!!!!
private AvByteBufferDescriptor cachedDesc;
// Sequencing state // Sequencing state
private short lastSequenceNumber; private short lastSequenceNumber;
@ -28,6 +32,7 @@ public class AvVideoDepacketizer {
public AvVideoDepacketizer(ConnectionStatusListener controlListener) public AvVideoDepacketizer(ConnectionStatusListener controlListener)
{ {
this.controlListener = controlListener; this.controlListener = controlListener;
this.cachedDesc = new AvByteBufferDescriptor(null, 0, 0);
} }
private void clearAvcNalState() private void clearAvcNalState()
@ -45,12 +50,11 @@ public class AvVideoDepacketizer {
// Check if this is a special NAL unit // Check if this is a special NAL unit
AvByteBufferDescriptor header = avcNalDataChain.getFirst(); AvByteBufferDescriptor header = avcNalDataChain.getFirst();
AvByteBufferDescriptor specialSeq = NAL.getSpecialSequenceDescriptor(header);
if (specialSeq != null) if (NAL.getSpecialSequenceDescriptor(header, cachedDesc))
{ {
// The next byte after the special sequence is the NAL header // The next byte after the special sequence is the NAL header
byte nalHeader = specialSeq.data[specialSeq.offset+specialSeq.length]; byte nalHeader = cachedDesc.data[cachedDesc.offset+cachedDesc.length];
switch (nalHeader) switch (nalHeader)
{ {
@ -112,16 +116,15 @@ public class AvVideoDepacketizer {
int start = location.offset; int start = location.offset;
// Check for a special sequence // Check for a special sequence
AvByteBufferDescriptor specialSeq = NAL.getSpecialSequenceDescriptor(location); if (NAL.getSpecialSequenceDescriptor(location, cachedDesc))
if (specialSeq != null)
{ {
if (NAL.isAvcStartSequence(specialSeq)) if (NAL.isAvcStartSequence(cachedDesc))
{ {
// We're decoding H264 now // We're decoding H264 now
currentlyDecoding = AvDecodeUnit.TYPE_H264; currentlyDecoding = AvDecodeUnit.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(specialSeq)) if (NAL.isAvcFrameStart(cachedDesc))
{ {
// Reassemble any pending AVC NAL // Reassemble any pending AVC NAL
reassembleAvcNal(); reassembleAvcNal();
@ -132,14 +135,14 @@ public class AvVideoDepacketizer {
} }
// Skip the start sequence // Skip the start sequence
location.length -= specialSeq.length; location.length -= cachedDesc.length;
location.offset += specialSeq.length; location.offset += cachedDesc.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 == AvDecodeUnit.TYPE_H264 && if (currentlyDecoding == AvDecodeUnit.TYPE_H264 &&
NAL.isPadding(specialSeq)) { NAL.isPadding(cachedDesc)) {
// The decode unit is complete // The decode unit is complete
reassembleAvcNal(); reassembleAvcNal();
} }
@ -159,15 +162,13 @@ public class AvVideoDepacketizer {
// Catch the easy case first where byte 0 != 0x00 // Catch the easy case first where byte 0 != 0x00
if (location.data[location.offset] == 0x00) if (location.data[location.offset] == 0x00)
{ {
specialSeq = NAL.getSpecialSequenceDescriptor(location);
// Check if this should end the current NAL // Check if this should end the current NAL
if (specialSeq != null) if (NAL.getSpecialSequenceDescriptor(location, cachedDesc))
{ {
// 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 != AvDecodeUnit.TYPE_UNKNOWN || if (currentlyDecoding != AvDecodeUnit.TYPE_UNKNOWN ||
!NAL.isPadding(specialSeq)) !NAL.isPadding(cachedDesc))
{ {
break; break;
} }
@ -249,11 +250,11 @@ class NAL {
} }
// Returns a buffer descriptor describing the start sequence // Returns a buffer descriptor describing the start sequence
public static AvByteBufferDescriptor getSpecialSequenceDescriptor(AvByteBufferDescriptor buffer) public static boolean getSpecialSequenceDescriptor(AvByteBufferDescriptor buffer, AvByteBufferDescriptor outputDesc)
{ {
// NAL start sequence is 00 00 00 01 or 00 00 01 // NAL start sequence is 00 00 00 01 or 00 00 01
if (buffer.length < 3) if (buffer.length < 3)
return null; return false;
// 00 00 is magic // 00 00 is magic
if (buffer.data[buffer.offset] == 0x00 && if (buffer.data[buffer.offset] == 0x00 &&
@ -267,19 +268,21 @@ 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 new AvByteBufferDescriptor(buffer.data, buffer.offset, 4); outputDesc.reinitialize(buffer.data, buffer.offset, 4);
} }
else else
{ {
// It's 00 00 00 // It's 00 00 00
return new AvByteBufferDescriptor(buffer.data, buffer.offset, 3); outputDesc.reinitialize(buffer.data, buffer.offset, 3);
} }
return true;
} }
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 new AvByteBufferDescriptor(buffer.data, buffer.offset, 3); outputDesc.reinitialize(buffer.data, buffer.offset, 3);
return true;
} }
else if (buffer.data[buffer.offset+2] == 0x03) else if (buffer.data[buffer.offset+2] == 0x03)
{ {
@ -290,22 +293,23 @@ class NAL {
// or whether it's something else // or whether it's something else
if (buffer.length < 4) if (buffer.length < 4)
return null; return false;
if (buffer.data[buffer.offset+3] >= 0x00 && if (buffer.data[buffer.offset+3] >= 0x00 &&
buffer.data[buffer.offset+3] <= 0x03) buffer.data[buffer.offset+3] <= 0x03)
{ {
// It's not really a special sequence after all // It's not really a special sequence after all
return null; return false;
} }
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 new AvByteBufferDescriptor(buffer.data, buffer.offset, 3); outputDesc.reinitialize(buffer.data, buffer.offset, 3);
return true;
} }
} }
} }
return null; return false;
} }
} }