Add support for processing 3 byte NAL start code in Mp4Muxer

Issue: androidx/media#725

Ideally the test to transmux a `ts` file (having 3 bytes NAL start code)
should go into `Mp4MuxerEndToEndTest`but `Mp4MuxerEndToEndTest` uses `MediaExtractor` and `MediaExtractor` is returning samples with `4 byte NAL start code` which will not exercise the newly added code path.
Hence the test is added in `TransformerWithInAppMuxerEndToEndTest` which
internally uses `media3 extractor` and feeds samples with `3 bytes NAL start code`
only.

PiperOrigin-RevId: 617985866
This commit is contained in:
sheenachhabra 2024-03-21 15:46:24 -07:00 committed by Copybara-Service
parent 6fc4f0263f
commit 16aac07bce
25 changed files with 723 additions and 228 deletions

View File

@ -32,7 +32,7 @@ public interface AnnexBToAvccConverter {
AnnexBToAvccConverter DEFAULT =
(ByteBuffer inputBuffer) -> {
if (!inputBuffer.hasRemaining()) {
return;
return inputBuffer;
}
checkArgument(
@ -40,25 +40,33 @@ public interface AnnexBToAvccConverter {
ImmutableList<ByteBuffer> nalUnitList = AnnexBUtils.findNalUnits(inputBuffer);
int totalBytesNeeded = 0;
for (int i = 0; i < nalUnitList.size(); i++) {
int currentNalUnitLength = nalUnitList.get(i).remaining();
// Replace the start code with the NAL unit length.
inputBuffer.putInt(currentNalUnitLength);
// Shift the input buffer's position to next start code.
int newPosition = inputBuffer.position() + currentNalUnitLength;
inputBuffer.position(newPosition);
// 4 bytes to store NAL unit length.
totalBytesNeeded += 4 + nalUnitList.get(i).remaining();
}
inputBuffer.rewind();
ByteBuffer outputBuffer = ByteBuffer.allocate(totalBytesNeeded);
for (int i = 0; i < nalUnitList.size(); i++) {
ByteBuffer currentNalUnit = nalUnitList.get(i);
int currentNalUnitLength = currentNalUnit.remaining();
// Rewrite NAL units with NAL unit length in place of start code.
outputBuffer.putInt(currentNalUnitLength);
outputBuffer.put(currentNalUnit);
}
outputBuffer.rewind();
return outputBuffer;
};
/**
* Processes a buffer in-place.
* Returns the processed {@link ByteBuffer}.
*
* <p>Expects a {@link ByteBuffer} input with a zero offset.
*
* @param inputBuffer The buffer to be converted.
*/
void process(ByteBuffer inputBuffer);
ByteBuffer process(ByteBuffer inputBuffer);
}

View File

@ -15,47 +15,87 @@
*/
package androidx.media3.muxer;
import androidx.media3.common.C;
import androidx.media3.common.MimeTypes;
import com.google.common.collect.ImmutableList;
import java.nio.ByteBuffer;
/** NAL unit utilities for start codes and emulation prevention. */
/* package */ final class AnnexBUtils {
private static final int THREE_BYTE_NAL_START_CODE_SIZE = 3;
private AnnexBUtils() {}
/**
* Splits a {@link ByteBuffer} into individual NAL units (0x00000001 start code).
* Splits a {@link ByteBuffer} into individual NAL units (0x000001 or 0x00000001 start code).
*
* <p>An empty list is returned if the input is not NAL units.
* <p>An {@link IllegalStateException} is thrown if the NAL units are invalid. The NAL units are
* identified as per ITU-T H264 spec:Annex B.2.
*
* <p>The position of the input buffer is unchanged after calling this method.
* <p>The input buffer must have position set to 0 and the position remains unchanged after
* calling this method.
*/
public static ImmutableList<ByteBuffer> findNalUnits(ByteBuffer input) {
if (input.remaining() < 4 || input.getInt(0) != 1) {
if (input.remaining() == 0) {
return ImmutableList.of();
}
ImmutableList.Builder<ByteBuffer> nalUnits = new ImmutableList.Builder<>();
int nalStartIndex = C.INDEX_UNSET;
int inputLimit = input.limit();
boolean readingNalUnit = false;
int lastStart = 4;
int zerosSeen = 0;
for (int i = 4; i < input.limit(); i++) {
if (input.get(i) == 1 && zerosSeen >= 3) {
// We're just looking at a start code.
nalUnits.add(getBytes(input, lastStart, i - 3 - lastStart));
lastStart = i + 1;
}
// Handle the end of the stream.
if (i == input.limit() - 1) {
nalUnits.add(getBytes(input, lastStart, input.limit() - lastStart));
}
if (input.get(i) == 0) {
zerosSeen++;
// The input must start with a NAL unit.
for (int i = 0; i < inputLimit; i++) {
if (isThreeByteNalStartCode(input, i)) {
nalStartIndex = i + THREE_BYTE_NAL_START_CODE_SIZE;
readingNalUnit = true;
break;
} else if (input.get(i) == 0) {
// Skip the leading zeroes.
} else {
zerosSeen = 0;
throw new IllegalStateException("Sample does not start with a NAL unit");
}
}
ImmutableList.Builder<ByteBuffer> nalUnits = new ImmutableList.Builder<>();
// Look for start code 0x000001. The logic will work for 0x00000001 start code as well because a
// NAL unit gets ended even when 0x000000 (which is a prefix of 0x00000001 start code) is found.
for (int i = nalStartIndex; i < inputLimit; ) {
if (readingNalUnit) {
// Found next start code 0x000001.
if (isThreeByteNalStartCode(input, i)) {
nalUnits.add(getBytes(input, nalStartIndex, i - nalStartIndex));
i = i + THREE_BYTE_NAL_START_CODE_SIZE;
nalStartIndex = i;
continue;
} else if (isThreeBytesZeroSequence(input, i)) {
// Found code 0x000000; The previous NAL unit should be ended.
nalUnits.add(getBytes(input, nalStartIndex, i - nalStartIndex));
// Stop reading NAL unit until next start code is found.
readingNalUnit = false;
i++;
} else {
// Continue reading NAL unit.
i++;
}
} else {
// Found new start code 0x000001.
if (isThreeByteNalStartCode(input, i)) {
i = i + THREE_BYTE_NAL_START_CODE_SIZE;
nalStartIndex = i;
readingNalUnit = true;
} else if (input.get(i) == 0x00) {
// Skip trailing zeroes.
i++;
} else {
// Found garbage data.
throw new IllegalStateException("Invalid NAL units");
}
}
// Add the last NAL unit.
if (i == inputLimit && readingNalUnit) {
nalUnits.add(getBytes(input, nalStartIndex, i - nalStartIndex));
}
}
input.rewind();
@ -97,6 +137,20 @@ import java.nio.ByteBuffer;
|| sampleMimeType.equals(MimeTypes.VIDEO_H265);
}
private static boolean isThreeByteNalStartCode(ByteBuffer input, int currentIndex) {
return (currentIndex < input.limit() - THREE_BYTE_NAL_START_CODE_SIZE
&& input.get(currentIndex) == 0x00
&& input.get(currentIndex + 1) == 0x00
&& input.get(currentIndex + 2) == 0x01);
}
private static boolean isThreeBytesZeroSequence(ByteBuffer input, int currentIndex) {
return (currentIndex < input.limit() - THREE_BYTE_NAL_START_CODE_SIZE
&& input.get(currentIndex) == 0x00
&& input.get(currentIndex + 1) == 0x00
&& input.get(currentIndex + 2) == 0x00);
}
private static ByteBuffer getBytes(ByteBuffer buf, int offset, int length) {
ByteBuffer result = buf.duplicate();
result.position(offset);

View File

@ -264,6 +264,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/** Writes out any pending samples to the file. */
private void flushPending(Track track) throws IOException {
checkState(track.pendingSamplesByteBuffer.size() == track.pendingSamplesBufferInfo.size());
if (track.pendingSamplesBufferInfo.isEmpty()) {
return;
}
@ -278,13 +279,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
bytesNeededInMdat += sample.limit();
}
// If the required number of bytes doesn't fit in the gap between the actual data and the moov
// box, extend the file and write out the moov box to the end again.
if (mdatDataEnd + bytesNeededInMdat >= mdatEnd) {
// Reserve some extra space than required, so that mdat box extension is less frequent.
rewriteMoovWithMdatEmptySpace(
/* bytesNeeded= */ getMdatExtensionAmount(mdatDataEnd) + bytesNeededInMdat);
}
extendMdatIfRequired(bytesNeededInMdat);
track.writtenChunkOffsets.add(mdatDataEnd);
track.writtenChunkSampleCounts.add(track.pendingSamplesBufferInfo.size());
@ -293,22 +288,38 @@ import java.util.concurrent.atomic.AtomicBoolean;
BufferInfo currentSampleBufferInfo = track.pendingSamplesBufferInfo.removeFirst();
ByteBuffer currentSampleByteBuffer = track.pendingSamplesByteBuffer.removeFirst();
track.writtenSamples.add(currentSampleBufferInfo);
// Convert the H.264/H.265 samples from Annex-B format (output by MediaCodec) to
// Avcc format (required by MP4 container).
if (doesSampleContainAnnexBNalUnits(checkNotNull(track.format.sampleMimeType))) {
annexBToAvccConverter.process(currentSampleByteBuffer);
currentSampleByteBuffer = annexBToAvccConverter.process(currentSampleByteBuffer);
currentSampleBufferInfo.set(
currentSampleByteBuffer.position(),
currentSampleByteBuffer.remaining(),
currentSampleBufferInfo.presentationTimeUs,
currentSampleBufferInfo.flags);
}
currentSampleByteBuffer.rewind();
// If the original sample had 3 bytes NAL start code instead of 4 bytes, then after AnnexB to
// Avcc conversion it will have 1 additional byte.
extendMdatIfRequired(currentSampleByteBuffer.remaining());
mdatDataEnd += output.write(currentSampleByteBuffer, mdatDataEnd);
track.writtenSamples.add(currentSampleBufferInfo);
} while (!track.pendingSamplesBufferInfo.isEmpty());
checkState(mdatDataEnd <= mdatEnd);
}
private void extendMdatIfRequired(long additionalBytesNeeded) throws IOException {
// If the required number of bytes doesn't fit in the gap between the actual data and the moov
// box, extend the file and write out the moov box to the end again.
if (mdatDataEnd + additionalBytesNeeded >= mdatEnd) {
// Reserve some extra space than required, so that mdat box extension is less frequent.
rewriteMoovWithMdatEmptySpace(
/* bytesNeeded= */ getMdatExtensionAmount(mdatDataEnd) + additionalBytesNeeded);
}
}
private void updateMdatSize() throws IOException {
// Assuming that the mdat box has a 64-bit length, skip the box type (4 bytes) and
// the 32-bit box length field (4 bytes).

View File

@ -277,22 +277,29 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private ProcessedTrackInfo processTrack(int trackId, Track track) {
checkState(track.pendingSamplesByteBuffer.size() == track.pendingSamplesBufferInfo.size());
ImmutableList.Builder<ByteBuffer> pendingSamplesByteBuffer = new ImmutableList.Builder<>();
ImmutableList.Builder<BufferInfo> pendingSamplesBufferInfoBuilder =
new ImmutableList.Builder<>();
if (doesSampleContainAnnexBNalUnits(checkNotNull(track.format.sampleMimeType))) {
while (!track.pendingSamplesByteBuffer.isEmpty()) {
ByteBuffer currentSampleByteBuffer = track.pendingSamplesByteBuffer.removeFirst();
annexBToAvccConverter.process(currentSampleByteBuffer);
currentSampleByteBuffer = annexBToAvccConverter.process(currentSampleByteBuffer);
pendingSamplesByteBuffer.add(currentSampleByteBuffer);
BufferInfo currentSampleBufferInfo = track.pendingSamplesBufferInfo.removeFirst();
currentSampleBufferInfo.set(
currentSampleByteBuffer.position(),
currentSampleByteBuffer.remaining(),
currentSampleBufferInfo.presentationTimeUs,
currentSampleBufferInfo.flags);
pendingSamplesBufferInfoBuilder.add(currentSampleBufferInfo);
}
} else {
pendingSamplesByteBuffer.addAll(track.pendingSamplesByteBuffer);
track.pendingSamplesByteBuffer.clear();
}
ImmutableList.Builder<BufferInfo> pendingSamplesBufferInfoBuilder =
new ImmutableList.Builder<>();
pendingSamplesBufferInfoBuilder.addAll(track.pendingSamplesBufferInfo);
track.pendingSamplesBufferInfo.clear();
}
ImmutableList<BufferInfo> pendingSamplesBufferInfo = pendingSamplesBufferInfoBuilder.build();
List<Long> sampleDurations =

View File

@ -17,6 +17,7 @@ package androidx.media3.muxer;
import static androidx.media3.common.util.Util.getBytesFromHexString;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
@ -29,76 +30,109 @@ import org.junit.runner.RunWith;
public class AnnexBUtilsTest {
@Test
public void findNalUnits_emptyBuffer_returnsEmptyList() {
ByteBuffer buf = ByteBuffer.wrap(getBytesFromHexString(""));
ByteBuffer buffer = ByteBuffer.wrap(getBytesFromHexString(""));
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buf);
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buffer);
assertThat(components).isEmpty();
}
@Test
public void findNalUnits_noNalUnit_returnsEmptyList() {
ByteBuffer buf = ByteBuffer.wrap(getBytesFromHexString("ABCDEFABC"));
public void findNalUnits_noNalUnit_throws() {
ByteBuffer buffer = ByteBuffer.wrap(getBytesFromHexString("ABCDEFABC"));
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buf);
assertThat(components).isEmpty();
assertThrows(IllegalStateException.class, () -> AnnexBUtils.findNalUnits(buffer));
}
@Test
public void findNalUnits_singleNalUnit_returnsSingleElement() {
ByteBuffer buf = ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF"));
public void findNalUnits_singleNalUnitWithFourByteStartCode_returnsSingleElement() {
ByteBuffer buffer = ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF"));
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buf);
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buffer);
assertThat(components).hasSize(1);
assertThat(components.get(0)).isEqualTo(ByteBuffer.wrap(getBytesFromHexString("ABCDEF")));
assertThat(components).containsExactly(ByteBuffer.wrap(getBytesFromHexString("ABCDEF")));
}
@Test
public void findNalUnits_multipleNalUnits_allReturned() {
ByteBuffer buf =
public void findNalUnits_singleNalUnitWithThreeByteStartCode_returnsSingleElement() {
ByteBuffer buffer = ByteBuffer.wrap(getBytesFromHexString("000001ABCDEF"));
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buffer);
assertThat(components).containsExactly(ByteBuffer.wrap(getBytesFromHexString("ABCDEF")));
}
@Test
public void findNalUnits_multipleNalUnitsWithFourByteStartCode_allReturned() {
ByteBuffer buffer =
ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF00000001DDCC00000001BBAA"));
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buf);
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buffer);
assertThat(components).hasSize(3);
assertThat(components.get(0)).isEqualTo(ByteBuffer.wrap(getBytesFromHexString("ABCDEF")));
assertThat(components.get(1)).isEqualTo(ByteBuffer.wrap(getBytesFromHexString("DDCC")));
assertThat(components.get(2)).isEqualTo(ByteBuffer.wrap(getBytesFromHexString("BBAA")));
assertThat(components)
.containsExactly(
ByteBuffer.wrap(getBytesFromHexString("ABCDEF")),
ByteBuffer.wrap(getBytesFromHexString("DDCC")),
ByteBuffer.wrap(getBytesFromHexString("BBAA")))
.inOrder();
}
@Test
public void findNalUnits_partialStartCodes_ignored() {
public void findNalUnits_multipleNalUnitsWithThreeByteStartCode_allReturned() {
ByteBuffer buffer = ByteBuffer.wrap(getBytesFromHexString("000001ABCDEF000001DDCC000001BBAA"));
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buffer);
assertThat(components)
.containsExactly(
ByteBuffer.wrap(getBytesFromHexString("ABCDEF")),
ByteBuffer.wrap(getBytesFromHexString("DDCC")),
ByteBuffer.wrap(getBytesFromHexString("BBAA")))
.inOrder();
}
@Test
public void findNalUnits_withTrainingZeroesFollowedByGarbageData_throws() {
// The NAL unit has lots of zeros but no start code.
ByteBuffer buf =
ByteBuffer buffer =
ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF0000AB0000CDEF00000000AB"));
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buf);
assertThat(components).hasSize(1);
assertThat(components.get(0))
.isEqualTo(ByteBuffer.wrap(getBytesFromHexString("ABCDEF0000AB0000CDEF00000000AB")));
assertThrows(IllegalStateException.class, () -> AnnexBUtils.findNalUnits(buffer));
}
@Test
public void findNalUnits_startCodeWithManyZeros_stillSplits() {
// The NAL unit has a start code that starts with more than 3 zeros (although too many zeros
// aren't allowed).
ByteBuffer buf = ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF000000000001AB"));
public void findNalUnits_withTrailingZeroes_stripsTrailingZeroes() {
// The first NAL unit has some training zeroes.
ByteBuffer buffer = ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF000000000001AB"));
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buf);
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buffer);
assertThat(components).hasSize(2);
assertThat(components.get(0)).isEqualTo(ByteBuffer.wrap(getBytesFromHexString("ABCDEF0000")));
assertThat(components.get(1)).isEqualTo(ByteBuffer.wrap(getBytesFromHexString("AB")));
assertThat(components)
.containsExactly(
ByteBuffer.wrap(getBytesFromHexString("ABCDEF")),
ByteBuffer.wrap(getBytesFromHexString("AB")))
.inOrder();
}
@Test
public void findNalUnits_withBothThreeBytesAndFourBytesNalStartCode_returnsAllNalUnits() {
ByteBuffer buffer = ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF000001AB000001CDEF"));
ImmutableList<ByteBuffer> components = AnnexBUtils.findNalUnits(buffer);
assertThat(components)
.containsExactly(
ByteBuffer.wrap(getBytesFromHexString("ABCDEF")),
ByteBuffer.wrap(getBytesFromHexString("AB")),
ByteBuffer.wrap(getBytesFromHexString("CDEF")))
.inOrder();
}
@Test
public void stripEmulationPrevention_noEmulationPreventionBytes_copiesInput() {
ByteBuffer buf = ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF000000000001AB"));
ByteBuffer buffer = ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF000000000001AB"));
ByteBuffer output = AnnexBUtils.stripEmulationPrevention(buf);
ByteBuffer output = AnnexBUtils.stripEmulationPrevention(buffer);
assertThat(output)
.isEqualTo(ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF000000000001AB")));
@ -107,9 +141,9 @@ public class AnnexBUtilsTest {
@Test
public void stripEmulationPrevention_emulationPreventionPresent_bytesStripped() {
// The NAL unit has a 00 00 03 * sequence.
ByteBuffer buf = ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF00000300000001AB"));
ByteBuffer buffer = ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF00000300000001AB"));
ByteBuffer output = AnnexBUtils.stripEmulationPrevention(buf);
ByteBuffer output = AnnexBUtils.stripEmulationPrevention(buffer);
assertThat(output)
.isEqualTo(ByteBuffer.wrap(getBytesFromHexString("00000001ABCDEF000000000001AB")));
@ -118,9 +152,9 @@ public class AnnexBUtilsTest {
@Test
public void stripEmulationPrevention_03WithoutEnoughZeros_notStripped() {
// The NAL unit has a 03 byte around, but not preceded by enough zeros.
ByteBuffer buf = ByteBuffer.wrap(getBytesFromHexString("ABCDEFABCD0003EFABCD03ABCD"));
ByteBuffer buffer = ByteBuffer.wrap(getBytesFromHexString("ABCDEFABCD0003EFABCD03ABCD"));
ByteBuffer output = AnnexBUtils.stripEmulationPrevention(buf);
ByteBuffer output = AnnexBUtils.stripEmulationPrevention(buffer);
assertThat(output)
.isEqualTo(ByteBuffer.wrap(getBytesFromHexString("ABCDEFABCD0003EFABCD03ABCD")));
@ -129,9 +163,9 @@ public class AnnexBUtilsTest {
@Test
public void stripEmulationPrevention_03AtEnd_stripped() {
// The NAL unit has a 03 byte at the very end of the input.
ByteBuffer buf = ByteBuffer.wrap(getBytesFromHexString("ABCDEF000003"));
ByteBuffer buffer = ByteBuffer.wrap(getBytesFromHexString("ABCDEF000003"));
ByteBuffer output = AnnexBUtils.stripEmulationPrevention(buf);
ByteBuffer output = AnnexBUtils.stripEmulationPrevention(buffer);
assertThat(output).isEqualTo(ByteBuffer.wrap(getBytesFromHexString("ABCDEF0000")));
}

View File

@ -16,6 +16,7 @@
package androidx.media3.muxer;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.nio.ByteBuffer;
@ -27,55 +28,49 @@ import org.junit.runner.RunWith;
public final class DefaultAnnexBToAvccConverterTest {
@Test
public void convertAnnexBToAvcc_singleNalUnit() {
ByteBuffer in = generateFakeNalUnitData(1000);
ByteBuffer input = generateFakeNalUnitData(1000);
// Add start code for the NAL unit.
in.put(0, (byte) 0);
in.put(1, (byte) 0);
in.put(2, (byte) 0);
in.put(3, (byte) 1);
input.put(0, (byte) 0);
input.put(1, (byte) 0);
input.put(2, (byte) 0);
input.put(3, (byte) 1);
AnnexBToAvccConverter annexBToAvccConverter = AnnexBToAvccConverter.DEFAULT;
annexBToAvccConverter.process(in);
ByteBuffer output = annexBToAvccConverter.process(input);
// The start code should get replaced with the length of the NAL unit.
assertThat(in.getInt(0)).isEqualTo(996);
assertThat(output.getInt(0)).isEqualTo(996);
}
@Test
public void convertAnnexBToAvcc_twoNalUnits() {
ByteBuffer in = generateFakeNalUnitData(1000);
ByteBuffer input = generateFakeNalUnitData(1000);
// Add start code for the first NAL unit.
in.put(0, (byte) 0);
in.put(1, (byte) 0);
in.put(2, (byte) 0);
in.put(3, (byte) 1);
input.put(0, (byte) 0);
input.put(1, (byte) 0);
input.put(2, (byte) 0);
input.put(3, (byte) 1);
// Add start code for the second NAL unit.
in.put(600, (byte) 0);
in.put(601, (byte) 0);
in.put(602, (byte) 0);
in.put(603, (byte) 1);
input.put(600, (byte) 0);
input.put(601, (byte) 0);
input.put(602, (byte) 0);
input.put(603, (byte) 1);
AnnexBToAvccConverter annexBToAvccConverter = AnnexBToAvccConverter.DEFAULT;
annexBToAvccConverter.process(in);
ByteBuffer output = annexBToAvccConverter.process(input);
// Both the NAL units should have length headers.
assertThat(in.getInt(0)).isEqualTo(596);
assertThat(in.getInt(600)).isEqualTo(396);
assertThat(output.getInt(0)).isEqualTo(596);
assertThat(output.getInt(600)).isEqualTo(396);
}
@Test
public void convertAnnexBToAvcc_noNalUnit_outputSameAsInput() {
public void convertAnnexBToAvcc_noNalUnit_throws() {
ByteBuffer input = generateFakeNalUnitData(1000);
ByteBuffer inputCopy = ByteBuffer.allocate(input.limit());
inputCopy.put(input);
input.rewind();
inputCopy.rewind();
AnnexBToAvccConverter annexBToAvccConverter = AnnexBToAvccConverter.DEFAULT;
annexBToAvccConverter.process(input);
assertThat(input).isEqualTo(inputCopy);
assertThrows(IllegalStateException.class, () -> annexBToAvccConverter.process(input));
}
/** Returns {@link ByteBuffer} filled with random NAL unit data without start code. */

View File

@ -7,13 +7,13 @@ seekMap:
getPosition(0) = [[timeUs=0, position=44]]
numberOfTracks = 1
track 0:
total output bytes = 55
total output bytes = 56
sample count = 1
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
colorInfo:
@ -27,5 +27,5 @@ track 0:
sample 0:
time = 0
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
tracksEnded = true

View File

@ -7,13 +7,13 @@ seekMap:
getPosition(0) = [[timeUs=0, position=44]]
numberOfTracks = 1
track 0:
total output bytes = 55
total output bytes = 56
sample count = 1
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
rotationDegrees = 180
@ -28,5 +28,5 @@ track 0:
sample 0:
time = 0
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
tracksEnded = true

View File

@ -7,13 +7,13 @@ seekMap:
getPosition(0) = [[timeUs=0, position=44]]
numberOfTracks = 1
track 0:
total output bytes = 55
total output bytes = 56
sample count = 1
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
rotationDegrees = 270
@ -28,5 +28,5 @@ track 0:
sample 0:
time = 0
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
tracksEnded = true

View File

@ -7,13 +7,13 @@ seekMap:
getPosition(0) = [[timeUs=0, position=44]]
numberOfTracks = 1
track 0:
total output bytes = 55
total output bytes = 56
sample count = 1
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
rotationDegrees = 90
@ -28,5 +28,5 @@ track 0:
sample 0:
time = 0
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
tracksEnded = true

View File

@ -2,18 +2,18 @@ seekMap:
isSeekable = true
duration = 200
getPosition(0) = [[timeUs=0, position=44]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=100, position=99]]
getPosition(100) = [[timeUs=100, position=99]]
getPosition(200) = [[timeUs=100, position=99]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=100, position=100]]
getPosition(100) = [[timeUs=100, position=100]]
getPosition(200) = [[timeUs=100, position=100]]
numberOfTracks = 2
track 0:
total output bytes = 110
total output bytes = 112
sample count = 2
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
frameRate = 20000.0
@ -28,19 +28,19 @@ track 0:
sample 0:
time = 0
flags = 1
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
sample 1:
time = 100
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
track 1:
total output bytes = 110
total output bytes = 112
sample count = 2
format 0:
id = 2
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
frameRate = 10000.0
@ -55,9 +55,9 @@ track 1:
sample 0:
time = 0
flags = 1
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
sample 1:
time = 200
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
tracksEnded = true

View File

@ -7,13 +7,13 @@ seekMap:
getPosition(0) = [[timeUs=0, position=44]]
numberOfTracks = 1
track 0:
total output bytes = 55
total output bytes = 56
sample count = 1
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
colorInfo:
@ -27,5 +27,5 @@ track 0:
sample 0:
time = 0
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
tracksEnded = true

View File

@ -7,13 +7,13 @@ seekMap:
getPosition(0) = [[timeUs=0, position=44]]
numberOfTracks = 1
track 0:
total output bytes = 55
total output bytes = 56
sample count = 1
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
colorInfo:
@ -27,5 +27,5 @@ track 0:
sample 0:
time = 0
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
tracksEnded = true

View File

@ -7,13 +7,13 @@ seekMap:
getPosition(0) = [[timeUs=0, position=44]]
numberOfTracks = 1
track 0:
total output bytes = 55
total output bytes = 56
sample count = 1
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
colorInfo:
@ -27,5 +27,5 @@ track 0:
sample 0:
time = 0
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
tracksEnded = true

View File

@ -7,13 +7,13 @@ seekMap:
getPosition(0) = [[timeUs=0, position=44]]
numberOfTracks = 1
track 0:
total output bytes = 55
total output bytes = 56
sample count = 1
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
colorInfo:
@ -27,5 +27,5 @@ track 0:
sample 0:
time = 0
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
tracksEnded = true

View File

@ -2,18 +2,18 @@ seekMap:
isSeekable = true
duration = 200
getPosition(0) = [[timeUs=0, position=44]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=100, position=99]]
getPosition(100) = [[timeUs=100, position=99]]
getPosition(200) = [[timeUs=100, position=99]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=100, position=100]]
getPosition(100) = [[timeUs=100, position=100]]
getPosition(200) = [[timeUs=100, position=100]]
numberOfTracks = 2
track 0:
total output bytes = 110
total output bytes = 112
sample count = 2
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
frameRate = 20000.0
@ -28,19 +28,19 @@ track 0:
sample 0:
time = 0
flags = 1
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
sample 1:
time = 100
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
track 1:
total output bytes = 110
total output bytes = 112
sample count = 2
format 0:
id = 2
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
frameRate = 10000.0
@ -55,9 +55,9 @@ track 1:
sample 0:
time = 0
flags = 1
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
sample 1:
time = 200
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
tracksEnded = true

View File

@ -1,7 +1,7 @@
ftyp (28 bytes):
Data = length 20, hash EF896440
mdat (71 bytes):
Data = length 55, hash 6B19F4A7
mdat (72 bytes):
Data = length 56, hash DB5662FB
moov (873 bytes):
mvhd (108 bytes):
Data = length 100, hash 2613A5C
@ -33,7 +33,7 @@ moov (873 bytes):
stts (24 bytes):
Data = length 16, hash E4FC6483
stsz (24 bytes):
Data = length 16, hash 50B7F5B9
Data = length 16, hash 50B7F5BA
stsc (28 bytes):
Data = length 20, hash 8F6E8285
co64 (24 bytes):

View File

@ -7,13 +7,13 @@ seekMap:
getPosition(0) = [[timeUs=0, position=44]]
numberOfTracks = 1
track 0:
total output bytes = 55
total output bytes = 56
sample count = 1
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.F4000A
maxInputSize = 85
maxInputSize = 86
width = 12
height = 10
colorInfo:
@ -27,5 +27,5 @@ track 0:
sample 0:
time = 0
flags = 536870913
data = length 55, hash A481CEF4
data = length 56, hash C4551A2E
tracksEnded = true

View File

@ -1,7 +1,7 @@
ftyp (28 bytes):
Data = length 20, hash EF896440
mdat (71 bytes):
Data = length 55, hash 6B19F4A7
mdat (72 bytes):
Data = length 56, hash DB5662FB
moov (658 bytes):
mvhd (108 bytes):
Data = length 100, hash 2613A5C
@ -24,7 +24,7 @@ moov (658 bytes):
stts (24 bytes):
Data = length 16, hash E4FC6483
stsz (24 bytes):
Data = length 16, hash 50B7F5B9
Data = length 16, hash 50B7F5BA
stsc (28 bytes):
Data = length 20, hash 8F6E8285
co64 (24 bytes):

View File

@ -1,7 +1,7 @@
ftyp (28 bytes):
Data = length 20, hash EF896440
mdat (126 bytes):
Data = length 110, hash 48173D41
mdat (128 bytes):
Data = length 112, hash 1AAF8FF5
moov (674 bytes):
mvhd (108 bytes):
Data = length 100, hash 105FA889
@ -24,7 +24,7 @@ moov (674 bytes):
stts (32 bytes):
Data = length 24, hash 4A7D0E0E
stsz (28 bytes):
Data = length 20, hash 3828E071
Data = length 20, hash 3836F7F3
stsc (28 bytes):
Data = length 20, hash 8F7C9A06
co64 (24 bytes):

View File

@ -201,13 +201,13 @@ track 0:
flags = 536870913
data = length 6, hash 31B22286
track 1:
total output bytes = 301392
total output bytes = 301222
sample count = 30
format 0:
id = 2
sampleMimeType = video/avc
codecs = avc1.640034
maxInputSize = 22910
maxInputSize = 22904
width = 1080
height = 720
frameRate = 31.004547
@ -224,7 +224,7 @@ track 1:
sample 0:
time = 0
flags = 1
data = length 7744, hash DDF91733
data = length 7735, hash 87BD736D
sample 1:
time = 33355
flags = 0
@ -232,7 +232,7 @@ track 1:
sample 2:
time = 66722
flags = 0
data = length 960, hash D4AD9EF0
data = length 955, hash 9626CB10
sample 3:
time = 100100
flags = 0
@ -248,7 +248,7 @@ track 1:
sample 6:
time = 200200
flags = 0
data = length 3856, hash 3D7DCD46
data = length 3850, hash 6E1AB9C6
sample 7:
time = 233555
flags = 0
@ -256,7 +256,7 @@ track 1:
sample 8:
time = 266922
flags = 0
data = length 6144, hash 8FD9AD7D
data = length 6137, hash AAA5D523
sample 9:
time = 300300
flags = 0
@ -268,43 +268,43 @@ track 1:
sample 11:
time = 367022
flags = 0
data = length 14496, hash E0F2E0BA
data = length 14490, hash C5F0743A
sample 12:
time = 400400
flags = 0
data = length 17664, hash 3E3189E
data = length 17652, hash 63C959E
sample 13:
time = 433755
flags = 0
data = length 5712, hash CA808ECF
data = length 5703, hash 44AFBC51
sample 14:
time = 467122
flags = 0
data = length 9776, hash C875D1AA
data = length 9766, hash 77D5FE2A
sample 15:
time = 500500
flags = 0
data = length 17712, hash 69AE17D4
data = length 17704, hash CF5D2BD4
sample 16:
time = 533855
flags = 0
data = length 11440, hash 7370E78C
data = length 11436, hash 9DCA8D8C
sample 17:
time = 567222
flags = 0
data = length 8544, hash 5A581986
data = length 8540, hash BEBDCC86
sample 18:
time = 600600
flags = 0
data = length 19904, hash 98AB5C44
data = length 19894, hash 6212144
sample 19:
time = 633955
flags = 0
data = length 14352, hash 74B754E3
data = length 14342, hash EC6E84A3
sample 20:
time = 667322
flags = 0
data = length 9568, hash 369746A6
data = length 9555, hash 601CE39A
sample 21:
time = 700700
flags = 0
@ -312,33 +312,33 @@ track 1:
sample 22:
time = 734055
flags = 0
data = length 22880, hash 75E833BA
data = length 22874, hash 4620073A
sample 23:
time = 767422
flags = 0
data = length 16832, hash E8BFCFE3
data = length 16821, hash 95A33FFD
sample 24:
time = 800800
flags = 0
data = length 5120, hash E04AEF94
data = length 5114, hash F0CB2E94
sample 25:
time = 834155
flags = 0
data = length 9888, hash 1166103E
data = length 9884, hash 103DF3E
sample 26:
time = 867522
flags = 0
data = length 17024, hash F9A96740
data = length 17011, hash 82DFD0C0
sample 27:
time = 900900
flags = 0
data = length 20864, hash DF9E88B8
data = length 20860, hash 5DEFA4B8
sample 28:
time = 934255
flags = 0
data = length 7216, hash BE22BE2F
data = length 7209, hash AFAC48B1
sample 29:
time = 967622
flags = 536870912
data = length 14240, hash E190BF31
data = length 14234, hash 101837F1
tracksEnded = true

View File

@ -7,13 +7,13 @@ seekMap:
getPosition(1065600) = [[timeUs=0, position=44]]
numberOfTracks = 2
track 0:
total output bytes = 301392
total output bytes = 301222
sample count = 30
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.640034
maxInputSize = 22910
maxInputSize = 22904
width = 1080
height = 720
frameRate = 31.004547
@ -30,7 +30,7 @@ track 0:
sample 0:
time = 0
flags = 1
data = length 7744, hash DDF91733
data = length 7735, hash 87BD736D
sample 1:
time = 33355
flags = 0
@ -38,7 +38,7 @@ track 0:
sample 2:
time = 66722
flags = 0
data = length 960, hash D4AD9EF0
data = length 955, hash 9626CB10
sample 3:
time = 100100
flags = 0
@ -54,7 +54,7 @@ track 0:
sample 6:
time = 200200
flags = 0
data = length 3856, hash 3D7DCD46
data = length 3850, hash 6E1AB9C6
sample 7:
time = 233555
flags = 0
@ -62,7 +62,7 @@ track 0:
sample 8:
time = 266922
flags = 0
data = length 6144, hash 8FD9AD7D
data = length 6137, hash AAA5D523
sample 9:
time = 300300
flags = 0
@ -74,43 +74,43 @@ track 0:
sample 11:
time = 367022
flags = 0
data = length 14496, hash E0F2E0BA
data = length 14490, hash C5F0743A
sample 12:
time = 400400
flags = 0
data = length 17664, hash 3E3189E
data = length 17652, hash 63C959E
sample 13:
time = 433755
flags = 0
data = length 5712, hash CA808ECF
data = length 5703, hash 44AFBC51
sample 14:
time = 467122
flags = 0
data = length 9776, hash C875D1AA
data = length 9766, hash 77D5FE2A
sample 15:
time = 500500
flags = 0
data = length 17712, hash 69AE17D4
data = length 17704, hash CF5D2BD4
sample 16:
time = 533855
flags = 0
data = length 11440, hash 7370E78C
data = length 11436, hash 9DCA8D8C
sample 17:
time = 567222
flags = 0
data = length 8544, hash 5A581986
data = length 8540, hash BEBDCC86
sample 18:
time = 600600
flags = 0
data = length 19904, hash 98AB5C44
data = length 19894, hash 6212144
sample 19:
time = 633955
flags = 0
data = length 14352, hash 74B754E3
data = length 14342, hash EC6E84A3
sample 20:
time = 667322
flags = 0
data = length 9568, hash 369746A6
data = length 9555, hash 601CE39A
sample 21:
time = 700700
flags = 0
@ -118,35 +118,35 @@ track 0:
sample 22:
time = 734055
flags = 0
data = length 22880, hash 75E833BA
data = length 22874, hash 4620073A
sample 23:
time = 767422
flags = 0
data = length 16832, hash E8BFCFE3
data = length 16821, hash 95A33FFD
sample 24:
time = 800800
flags = 0
data = length 5120, hash E04AEF94
data = length 5114, hash F0CB2E94
sample 25:
time = 834155
flags = 0
data = length 9888, hash 1166103E
data = length 9884, hash 103DF3E
sample 26:
time = 867522
flags = 0
data = length 17024, hash F9A96740
data = length 17011, hash 82DFD0C0
sample 27:
time = 900900
flags = 0
data = length 20864, hash DF9E88B8
data = length 20860, hash 5DEFA4B8
sample 28:
time = 934255
flags = 0
data = length 7216, hash BE22BE2F
data = length 7209, hash AFAC48B1
sample 29:
time = 967622
flags = 536870912
data = length 14240, hash E190BF31
data = length 14234, hash 101837F1
track 1:
total output bytes = 9529
sample count = 45

View File

@ -0,0 +1,344 @@
seekMap:
isSeekable = true
duration = 1065600
getPosition(0) = [[timeUs=0, position=44]]
getPosition(1) = [[timeUs=0, position=44]]
getPosition(532800) = [[timeUs=0, position=44]]
getPosition(1065600) = [[timeUs=0, position=44]]
numberOfTracks = 2
track 0:
total output bytes = 301430
sample count = 30
format 0:
id = 1
sampleMimeType = video/avc
codecs = avc1.640034
maxInputSize = 22910
width = 1080
height = 720
frameRate = 31.004547
colorInfo:
colorSpace = 1
colorRange = 2
colorTransfer = 3
lumaBitdepth = 8
chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=3000000000, modification time=4000000000, timescale=10000]
initializationData:
data = length 24, hash 489E4AD2
data = length 9, hash FBAFBC0C
sample 0:
time = 0
flags = 1
data = length 7773, hash 97BEE8E7
sample 1:
time = 33355
flags = 0
data = length 1175, hash 875A0AD8
sample 2:
time = 66722
flags = 0
data = length 961, hash 39E26608
sample 3:
time = 100100
flags = 0
data = length 979, hash 9843943C
sample 4:
time = 133455
flags = 0
data = length 1367, hash C795AD6F
sample 5:
time = 166822
flags = 0
data = length 2295, hash 90C61830
sample 6:
time = 200200
flags = 0
data = length 3856, hash E5178FCE
sample 7:
time = 233555
flags = 0
data = length 4055, hash DC2E0B64
sample 8:
time = 266922
flags = 0
data = length 6143, hash 6C8C0E1B
sample 9:
time = 300300
flags = 0
data = length 7639, hash 40ABA1B0
sample 10:
time = 333655
flags = 0
data = length 9795, hash 824B4D8D
sample 11:
time = 367022
flags = 0
data = length 14496, hash D3EEBA42
sample 12:
time = 400400
flags = 0
data = length 17658, hash 12A7A1A6
sample 13:
time = 433755
flags = 0
data = length 5709, hash ACD2349
sample 14:
time = 467122
flags = 0
data = length 9772, hash B00DF832
sample 15:
time = 500500
flags = 0
data = length 17710, hash 366FC3DC
sample 16:
time = 533855
flags = 0
data = length 11442, hash 9B226194
sample 17:
time = 567222
flags = 0
data = length 8546, hash D86DF08E
sample 18:
time = 600600
flags = 0
data = length 19900, hash C6748B4C
sample 19:
time = 633955
flags = 0
data = length 14348, hash D91E9EAB
sample 20:
time = 667322
flags = 0
data = length 9561, hash 819E9692
sample 21:
time = 700700
flags = 0
data = length 12195, hash 895ADC1E
sample 22:
time = 734055
flags = 0
data = length 22880, hash A618D42
sample 23:
time = 767422
flags = 0
data = length 16827, hash 4E6A74F5
sample 24:
time = 800800
flags = 0
data = length 5120, hash 3EF0149C
sample 25:
time = 834155
flags = 0
data = length 9890, hash 32BAC346
sample 26:
time = 867522
flags = 0
data = length 17017, hash 2D24A3B8
sample 27:
time = 900900
flags = 0
data = length 20866, hash 219DA8C0
sample 28:
time = 934255
flags = 0
data = length 7215, hash A853B1A9
sample 29:
time = 967622
flags = 536870912
data = length 14240, hash 4EE77DF9
track 1:
total output bytes = 9529
sample count = 45
format 0:
id = 2
sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2
maxInputSize = 294
channelCount = 1
sampleRate = 44100
language = und
metadata = entries=[Mp4Timestamp: creation time=3000000000, modification time=4000000000, timescale=10000]
initializationData:
data = length 2, hash 5F7
sample 0:
time = 0
flags = 1
data = length 23, hash 47DE9131
sample 1:
time = 67187
flags = 1
data = length 6, hash 31EC5206
sample 2:
time = 90416
flags = 1
data = length 148, hash 894A176B
sample 3:
time = 113625
flags = 1
data = length 189, hash CEF235A1
sample 4:
time = 136854
flags = 1
data = length 205, hash BBF5F7B0
sample 5:
time = 160062
flags = 1
data = length 210, hash F278B193
sample 6:
time = 183291
flags = 1
data = length 210, hash 82DA1589
sample 7:
time = 206520
flags = 1
data = length 207, hash 5BE231DF
sample 8:
time = 229729
flags = 1
data = length 225, hash 18819EE1
sample 9:
time = 252958
flags = 1
data = length 215, hash CA7FA67B
sample 10:
time = 276166
flags = 1
data = length 211, hash 581A1C18
sample 11:
time = 299395
flags = 1
data = length 216, hash ADB88187
sample 12:
time = 322604
flags = 1
data = length 229, hash 2E8BA4DC
sample 13:
time = 345833
flags = 1
data = length 232, hash 22F0C510
sample 14:
time = 369041
flags = 1
data = length 235, hash 867AD0DC
sample 15:
time = 392270
flags = 1
data = length 231, hash 84E823A8
sample 16:
time = 415500
flags = 1
data = length 226, hash 1BEF3A95
sample 17:
time = 438708
flags = 1
data = length 216, hash EAA345AE
sample 18:
time = 461937
flags = 1
data = length 229, hash 6957411F
sample 19:
time = 485145
flags = 1
data = length 219, hash 41275022
sample 20:
time = 508375
flags = 1
data = length 241, hash 6495DF96
sample 21:
time = 531583
flags = 1
data = length 228, hash 63D95906
sample 22:
time = 554812
flags = 1
data = length 238, hash 34F676F9
sample 23:
time = 578020
flags = 1
data = length 234, hash E5CBC045
sample 24:
time = 601250
flags = 1
data = length 231, hash 5FC43661
sample 25:
time = 624458
flags = 1
data = length 217, hash 682708ED
sample 26:
time = 647687
flags = 1
data = length 239, hash D43780FC
sample 27:
time = 670916
flags = 1
data = length 243, hash C5E17980
sample 28:
time = 694145
flags = 1
data = length 231, hash AC5837BA
sample 29:
time = 717354
flags = 1
data = length 230, hash 169EE895
sample 30:
time = 740583
flags = 1
data = length 238, hash C48FF3F1
sample 31:
time = 763791
flags = 1
data = length 225, hash 531E4599
sample 32:
time = 787020
flags = 1
data = length 232, hash CB3C6B8D
sample 33:
time = 810229
flags = 1
data = length 243, hash F8C94C7
sample 34:
time = 833458
flags = 1
data = length 232, hash A646A7D0
sample 35:
time = 856666
flags = 1
data = length 237, hash E8B787A5
sample 36:
time = 879895
flags = 1
data = length 228, hash 3FA7A29F
sample 37:
time = 903104
flags = 1
data = length 235, hash B9B33B0A
sample 38:
time = 926333
flags = 1
data = length 264, hash 71A4869E
sample 39:
time = 949562
flags = 1
data = length 257, hash D049B54C
sample 40:
time = 972770
flags = 1
data = length 227, hash 66757231
sample 41:
time = 996000
flags = 1
data = length 227, hash BD374F1B
sample 42:
time = 1019208
flags = 1
data = length 235, hash 999477F6
sample 43:
time = 1042437
flags = 1
data = length 229, hash FFF98DF0
sample 44:
time = 1065645
flags = 536870913
data = length 6, hash 31B22286
tracksEnded = true

View File

@ -16,6 +16,7 @@
package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.test.utils.TestUtil.extractAllSamplesFromFilePath;
import static androidx.media3.test.utils.TestUtil.retrieveTrackFormat;
import static com.google.common.truth.Truth.assertThat;
@ -51,6 +52,7 @@ import org.junit.runner.RunWith;
public class TransformerWithInAppMuxerEndToEndTest {
private static final String MP4_FILE_PATH = "asset:///media/mp4/sample_no_bframes.mp4";
private static final String MP4_FILE_NAME = "mp4/sample_no_bframes.mp4";
@Rule public final TemporaryFolder outputDir = new TemporaryFolder();
private final Context context = ApplicationProvider.getApplicationContext();
@ -85,7 +87,7 @@ public class TransformerWithInAppMuxerEndToEndTest {
TransformerTestRunner.runLooper(transformer);
FakeExtractorOutput fakeExtractorOutput =
androidx.media3.test.utils.TestUtil.extractAllSamplesFromFilePath(
extractAllSamplesFromFilePath(
new Mp4Extractor(new DefaultSubtitleParserFactory()), outputPath);
DumpFileAsserts.assertOutput(
context,
@ -95,6 +97,46 @@ public class TransformerWithInAppMuxerEndToEndTest {
/* modifications...= */ "transmuxed_with_inappmuxer"));
}
@Test
public void transmux_tsFileHavingThreeByteNalStartCode_outputMatchesExpected() throws Exception {
String tsFilePath = "asset:///media/ts/sample_no_bframes.ts";
String tsFileName = "ts/sample_no_bframes.ts";
Muxer.Factory inAppMuxerFactory =
new InAppMuxer.Factory.Builder()
.setMetadataProvider(
metadataEntries ->
// Add timestamp to make output file deterministic.
metadataEntries.add(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 3_000_000_000L,
/* modificationTimestampSeconds= */ 4_000_000_000L)))
.build();
Transformer transformer =
new Transformer.Builder(context)
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
.setMuxerFactory(inAppMuxerFactory)
.build();
MediaItem mediaItem =
new MediaItem.Builder()
.setUri(Uri.parse(tsFilePath))
.setClippingConfiguration(
new MediaItem.ClippingConfiguration.Builder().setEndPositionMs(2_000).build())
.build();
transformer.start(mediaItem, outputPath);
TransformerTestRunner.runLooper(transformer);
FakeExtractorOutput fakeExtractorOutput =
extractAllSamplesFromFilePath(
new Mp4Extractor(new DefaultSubtitleParserFactory()), outputPath);
DumpFileAsserts.assertOutput(
context,
fakeExtractorOutput,
TestUtil.getDumpFileName(
/* originalFileName= */ tsFileName,
/* modifications...= */ "transmuxed_with_inappmuxer"));
}
@Test
public void transmux_withLocationMetadata_writesSameLocationMetadata() throws Exception {
Mp4LocationData expectedLocationData =