From 7d39ac2a45fb9b9a7995ae8471e95ffc7f2b090b Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 24 Nov 2013 21:51:09 -0500 Subject: [PATCH] Reuse a single AvByteBufferDescriptor when depacketizing H264 instead of allocating many of them per packet --- .../nvstream/av/AvByteBufferDescriptor.java | 7 +++ .../nvstream/av/AvShortBufferDescriptor.java | 7 +++ .../av/video/AvVideoDepacketizer.java | 50 ++++++++++--------- 3 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/com/limelight/nvstream/av/AvByteBufferDescriptor.java b/src/com/limelight/nvstream/av/AvByteBufferDescriptor.java index 2d6298f1..8f11a956 100644 --- a/src/com/limelight/nvstream/av/AvByteBufferDescriptor.java +++ b/src/com/limelight/nvstream/av/AvByteBufferDescriptor.java @@ -19,6 +19,13 @@ public class AvByteBufferDescriptor { 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() { print(offset, length); diff --git a/src/com/limelight/nvstream/av/AvShortBufferDescriptor.java b/src/com/limelight/nvstream/av/AvShortBufferDescriptor.java index 9d028326..901783f4 100644 --- a/src/com/limelight/nvstream/av/AvShortBufferDescriptor.java +++ b/src/com/limelight/nvstream/av/AvShortBufferDescriptor.java @@ -18,4 +18,11 @@ public class AvShortBufferDescriptor { this.offset = desc.offset; this.length = desc.length; } + + public void reinitialize(short[] data, int offset, int length) + { + this.data = data; + this.offset = offset; + this.length = length; + } } diff --git a/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java b/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java index c5881f0e..8dff6a19 100644 --- a/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java +++ b/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java @@ -17,6 +17,10 @@ public class AvVideoDepacketizer { private int avcNalDataLength = 0; private int currentlyDecoding; + // Cached buffer descriptor to save on allocations + // Only safe to use in decode thread!!!! + private AvByteBufferDescriptor cachedDesc; + // Sequencing state private short lastSequenceNumber; @@ -28,6 +32,7 @@ public class AvVideoDepacketizer { public AvVideoDepacketizer(ConnectionStatusListener controlListener) { this.controlListener = controlListener; + this.cachedDesc = new AvByteBufferDescriptor(null, 0, 0); } private void clearAvcNalState() @@ -45,12 +50,11 @@ public class AvVideoDepacketizer { // Check if this is a special NAL unit 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 - byte nalHeader = specialSeq.data[specialSeq.offset+specialSeq.length]; + byte nalHeader = cachedDesc.data[cachedDesc.offset+cachedDesc.length]; switch (nalHeader) { @@ -112,16 +116,15 @@ public class AvVideoDepacketizer { int start = location.offset; // Check for a special sequence - AvByteBufferDescriptor specialSeq = NAL.getSpecialSequenceDescriptor(location); - if (specialSeq != null) + if (NAL.getSpecialSequenceDescriptor(location, cachedDesc)) { - if (NAL.isAvcStartSequence(specialSeq)) + if (NAL.isAvcStartSequence(cachedDesc)) { // We're decoding H264 now currentlyDecoding = AvDecodeUnit.TYPE_H264; // Check if it's the end of the last frame - if (NAL.isAvcFrameStart(specialSeq)) + if (NAL.isAvcFrameStart(cachedDesc)) { // Reassemble any pending AVC NAL reassembleAvcNal(); @@ -132,14 +135,14 @@ public class AvVideoDepacketizer { } // Skip the start sequence - location.length -= specialSeq.length; - location.offset += specialSeq.length; + location.length -= cachedDesc.length; + location.offset += cachedDesc.length; } else { // Check if this is padding after a full AVC frame if (currentlyDecoding == AvDecodeUnit.TYPE_H264 && - NAL.isPadding(specialSeq)) { + NAL.isPadding(cachedDesc)) { // The decode unit is complete reassembleAvcNal(); } @@ -159,15 +162,13 @@ public class AvVideoDepacketizer { // Catch the easy case first where byte 0 != 0x00 if (location.data[location.offset] == 0x00) { - specialSeq = NAL.getSpecialSequenceDescriptor(location); - // 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 // isn't padding if (currentlyDecoding != AvDecodeUnit.TYPE_UNKNOWN || - !NAL.isPadding(specialSeq)) + !NAL.isPadding(cachedDesc)) { break; } @@ -249,11 +250,11 @@ class NAL { } // 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 if (buffer.length < 3) - return null; + return false; // 00 00 is magic if (buffer.data[buffer.offset] == 0x00 && @@ -267,19 +268,21 @@ class NAL { buffer.data[buffer.offset+3] == 0x01) { // 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 { // 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 || buffer.data[buffer.offset+2] == 0x02) { // 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) { @@ -290,22 +293,23 @@ class NAL { // or whether it's something else if (buffer.length < 4) - return null; + return false; if (buffer.data[buffer.offset+3] >= 0x00 && buffer.data[buffer.offset+3] <= 0x03) { // It's not really a special sequence after all - return null; + return false; } else { // 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; } }