Support for Large CodecSpecificData in ESDS box

Some external media files have CodecSpecificData greater than 128 bytes. Currently, that size
isn't fitting in one byte. Hence, added support to store large CodecSpecificDataSize, as per
ISO standard, by extending to more than one byte as required.

PiperOrigin-RevId: 650972472
This commit is contained in:
Googler 2024-07-10 05:13:18 -07:00 committed by Copybara-Service
parent 9d4e43cf55
commit cf90d2624d

View File

@ -39,6 +39,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.primitives.Bytes; import com.google.common.primitives.Bytes;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -1343,37 +1344,37 @@ import java.util.List;
ByteBuffer csd0ByteBuffer = ByteBuffer.wrap(csd0); ByteBuffer csd0ByteBuffer = ByteBuffer.wrap(csd0);
int peakBitrate = format.peakBitrate; int peakBitrate = format.peakBitrate;
int averageBitrate = format.averageBitrate; int averageBitrate = format.averageBitrate;
int csd0Size = csd0ByteBuffer.limit(); int csd0Size = csd0ByteBuffer.limit();
ByteBuffer dsiSizeBuffer = getSizeBuffer(csd0Size);
ByteBuffer dcdSizeBuffer = getSizeBuffer(csd0Size + dsiSizeBuffer.remaining() + 14);
ByteBuffer esdSizeBuffer =
getSizeBuffer(csd0Size + dsiSizeBuffer.remaining() + dcdSizeBuffer.remaining() + 21);
ByteBuffer contents = ByteBuffer.allocate(csd0Size + MAX_FIXED_LEAF_BOX_SIZE); ByteBuffer contents = ByteBuffer.allocate(csd0Size + MAX_FIXED_LEAF_BOX_SIZE);
contents.putInt(0x0); // version and flags. contents.putInt(0x00); // Version and flags.
contents.put((byte) 0x03); // ES_DescrTag contents.put((byte) 0x03); // ES_DescrTag
// We're normally using a variable-length encoding for the length of various sub-packages (esds contents.put(esdSizeBuffer);
// etc.), in a nested way, so outer lengths need to account for variable-length inner lengths
// too (to save ~10 bytes per video file). Meanwhile, AAC codec-specific
// data is typically just 2 bytes, so every length actually fits into a byte. Here, we're just
// skipping the entire complex story by asserting that we won't ever need variable-length sizes.
checkArgument(csd0Size + 21 < 127, "CSD too long; we might need variable-length encoding?");
contents.put((byte) (23 + csd0Size)); contents.putShort((short) 0x0000); // First 16 bits of ES_ID.
contents.put((byte) 0x00); // Last 8 bits of ES_ID.
contents.putShort((short) 0x0000); // ES_ID
contents.put((byte) 0x00);
contents.put((byte) 0x04); // DecoderConfigDescrTag contents.put((byte) 0x04); // DecoderConfigDescrTag
contents.put((byte) (15 + csd0Size)); contents.put(dcdSizeBuffer);
contents.put((byte) 0x40); // objectTypeIndication
contents.put((byte) 0x15); // streamType AudioStream
contents.putShort((short) 0x03); contents.put((byte) 0x40); // objectTypeIndication
contents.put((byte) 0x00); // 24-bit buffer size (0x300) // streamType (6 bits) | upStream (1 bit) | reserved = 1 (1 bit)
contents.put((byte) ((0x05 << 2) | 0x01)); // streamType AudioStream
contents.putShort((short) 0x03); // First 16 bits of buffer size (0x300).
contents.put((byte) 0x00); // Last 8 bits of buffer size (0x300).
contents.putInt(peakBitrate != Format.NO_VALUE ? peakBitrate : 0); contents.putInt(peakBitrate != Format.NO_VALUE ? peakBitrate : 0);
contents.putInt(averageBitrate != Format.NO_VALUE ? averageBitrate : 0); contents.putInt(averageBitrate != Format.NO_VALUE ? averageBitrate : 0);
contents.put((byte) 0x05); // DecoderSpecificInfoTag contents.put((byte) 0x05); // DecoderSpecificInfoTag
contents.put((byte) csd0Size); contents.put(dsiSizeBuffer);
contents.put(csd0ByteBuffer); contents.put(csd0ByteBuffer);
csd0ByteBuffer.rewind(); csd0ByteBuffer.rewind();
@ -1385,6 +1386,23 @@ import java.util.List;
return BoxUtils.wrapIntoBox("esds", contents); return BoxUtils.wrapIntoBox("esds", contents);
} }
private static ByteBuffer getSizeBuffer(int length) {
int prefix = 0;
ArrayDeque<Byte> esdsSizeBytes = new ArrayDeque<>();
do {
esdsSizeBytes.push((byte) (prefix | (length & 0x7F)));
length >>= 7;
prefix = 0x80;
} while (length > 0);
ByteBuffer sizeBuffer = ByteBuffer.allocate(esdsSizeBytes.size());
while (!esdsSizeBytes.isEmpty()) {
sizeBuffer.put(esdsSizeBytes.removeFirst());
}
sizeBuffer.flip();
return sizeBuffer;
}
/** Returns the audio damr box. */ /** Returns the audio damr box. */
private static ByteBuffer damrBox(short mode) { private static ByteBuffer damrBox(short mode) {