diff --git a/libraries/container/src/main/java/androidx/media3/container/NalUnitUtil.java b/libraries/container/src/main/java/androidx/media3/container/NalUnitUtil.java
index 4f3fa76e34..75689dcfe6 100644
--- a/libraries/container/src/main/java/androidx/media3/container/NalUnitUtil.java
+++ b/libraries/container/src/main/java/androidx/media3/container/NalUnitUtil.java
@@ -682,16 +682,22 @@ public final class NalUnitUtil {
}
/**
- * Returns the number of bytes needed from the NAL unit to determine whether subsequent NAL units
- * can depend on the current NAL unit.
+ * Returns the number of bytes in the NAL unit header.
+ *
+ *
The NAL unit header can be used to determine the NAL unit type and whether subsequent NAL
+ * units can depend on the current NAL unit.
+ *
+ *
This is {@code nalUnitHeaderBytes} from the H.264 spec, or the size of {@code
+ * nal_unit_header()} in H.265.
*
* @param format The sample {@link Format}.
*/
- public static int numberOfBytesToDetermineSampleDependencies(Format format) {
+ public static int numberOfBytesInNalUnitHeader(Format format) {
if (Objects.equals(format.sampleMimeType, MimeTypes.VIDEO_H264)) {
return 1;
}
- if (Objects.equals(format.sampleMimeType, MimeTypes.VIDEO_H265)) {
+ if (Objects.equals(format.sampleMimeType, MimeTypes.VIDEO_H265)
+ || MimeTypes.containsCodecsCorrespondingToMimeType(format.codecs, MimeTypes.VIDEO_H265)) {
return 2;
}
return 0;
diff --git a/libraries/container/src/test/java/androidx/media3/container/NalUnitUtilTest.java b/libraries/container/src/test/java/androidx/media3/container/NalUnitUtilTest.java
index fa2cd5a6dc..57de655137 100644
--- a/libraries/container/src/test/java/androidx/media3/container/NalUnitUtilTest.java
+++ b/libraries/container/src/test/java/androidx/media3/container/NalUnitUtilTest.java
@@ -16,7 +16,7 @@
package androidx.media3.container;
import static androidx.media3.container.NalUnitUtil.isDependedOn;
-import static androidx.media3.container.NalUnitUtil.numberOfBytesToDetermineSampleDependencies;
+import static androidx.media3.container.NalUnitUtil.numberOfBytesInNalUnitHeader;
import static androidx.media3.test.utils.TestUtil.createByteArray;
import static com.google.common.truth.Truth.assertThat;
@@ -499,10 +499,10 @@ public final class NalUnitUtilTest {
}
@Test
- public void numberOfBytesToDetermineSampleDependencies_vp8_returnsZero() {
+ public void numberOfBytesInNalUnitHeader_vp8_returnsZero() {
Format vp8Video = new Format.Builder().setSampleMimeType(MimeTypes.VIDEO_VP8).build();
- assertThat(numberOfBytesToDetermineSampleDependencies(vp8Video)).isEqualTo(0);
+ assertThat(numberOfBytesInNalUnitHeader(vp8Video)).isEqualTo(0);
}
@Test
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java
index 6bd37531e3..7091ac0e6c 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java
@@ -149,6 +149,14 @@ public class FragmentedMp4Extractor implements Extractor {
*/
public static final int FLAG_READ_WITHIN_GOP_SAMPLE_DEPENDENCIES = 1 << 6; // 64
+ /**
+ * Flag to extract additional sample dependency information, and mark output buffers with {@link
+ * C#BUFFER_FLAG_NOT_DEPENDED_ON} for {@linkplain MimeTypes#VIDEO_H265 H.265} video.
+ *
+ *
See {@link #FLAG_READ_WITHIN_GOP_SAMPLE_DEPENDENCIES}.
+ */
+ public static final int FLAG_READ_WITHIN_GOP_SAMPLE_DEPENDENCIES_H265 = 1 << 7;
+
/**
* @deprecated Use {@link #newFactory(SubtitleParser.Factory)} instead.
*/
@@ -193,7 +201,7 @@ public class FragmentedMp4Extractor implements Extractor {
// Temporary arrays.
private final ParsableByteArray nalStartCode;
private final ParsableByteArray nalPrefix;
- private final ParsableByteArray nalBuffer;
+ private final ParsableByteArray nalUnitWithoutHeaderBuffer;
private final byte[] scratchBytes;
private final ParsableByteArray scratch;
@@ -400,8 +408,8 @@ public class FragmentedMp4Extractor implements Extractor {
eventMessageEncoder = new EventMessageEncoder();
atomHeader = new ParsableByteArray(Mp4Box.LONG_HEADER_SIZE);
nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
- nalPrefix = new ParsableByteArray(5);
- nalBuffer = new ParsableByteArray();
+ nalPrefix = new ParsableByteArray(6);
+ nalUnitWithoutHeaderBuffer = new ParsableByteArray();
scratchBytes = new byte[16];
scratch = new ParsableByteArray(scratchBytes);
containerAtoms = new ArrayDeque<>();
@@ -1533,12 +1541,10 @@ public class FragmentedMp4Extractor implements Extractor {
if (parserState == STATE_READING_SAMPLE_START) {
sampleSize = trackBundle.getCurrentSampleSize();
// We must check all NAL units in the Fragmented MP4 sample for dependencies.
- // When FLAG_READ_WITHIN_GOP_SAMPLE_DEPENDENCIES is unset, or codec is not supported,
+ // When reading sample dependencies is disabled, or codec is not supported,
// set isSampleDependedOn = true and skip parsing the payload bytes.
isSampleDependedOn =
- (flags & FLAG_READ_WITHIN_GOP_SAMPLE_DEPENDENCIES) == 0
- || !Objects.equals(
- trackBundle.moovSampleTable.track.format.sampleMimeType, MimeTypes.VIDEO_H264);
+ !canReadWithinGopSampleDependencies(trackBundle.moovSampleTable.track.format);
if (trackBundle.currentSampleIndex < trackBundle.firstSampleToOutputIndex) {
input.skipFully(sampleSize);
@@ -1585,57 +1591,74 @@ public class FragmentedMp4Extractor implements Extractor {
nalPrefixData[0] = 0;
nalPrefixData[1] = 0;
nalPrefixData[2] = 0;
- int nalUnitPrefixLength = track.nalUnitLengthFieldLength + 1;
int nalUnitLengthFieldLengthDiff = 4 - track.nalUnitLengthFieldLength;
// NAL units are length delimited, but the decoder requires start code delimited units.
// Loop until we've written the sample to the track output, replacing length delimiters with
// start codes as we encounter them.
while (sampleBytesWritten < sampleSize) {
if (sampleCurrentNalBytesRemaining == 0) {
+ int numberOfNalUnitHeaderBytesToRead = 0;
+ if (ceaTrackOutputs.length > 0 || !isSampleDependedOn) {
+ // Try to read the NAL unit header if we're parsing captions or sample dependencies.
+ int nalUnitHeaderSize = NalUnitUtil.numberOfBytesInNalUnitHeader(track.format);
+ if (track.nalUnitLengthFieldLength + nalUnitHeaderSize
+ <= sampleSize - sampleBytesWritten) {
+ // In some malformed videos with padding, the NAL unit may be empty.
+ // See b/383201567#comment20
+ // Only try to read the header if there are enough bytes in this sample.
+ numberOfNalUnitHeaderBytesToRead = nalUnitHeaderSize;
+ }
+ }
+ // Read numberOfNalUnitHeaderBytesToRead in the same readFully call that reads the NAL
+ // length. This ensures sampleBytesRead, sampleBytesWritten and isSampleDependedOn remain
+ // in a consistent state if we have read failures.
+ int nalUnitPrefixLength =
+ track.nalUnitLengthFieldLength + numberOfNalUnitHeaderBytesToRead;
// Read the NAL length so that we know where we find the next one, and its type.
input.readFully(nalPrefixData, nalUnitLengthFieldLengthDiff, nalUnitPrefixLength);
nalPrefix.setPosition(0);
int nalLengthInt = nalPrefix.readInt();
- if (nalLengthInt < 1) {
+ if (nalLengthInt < 0) {
throw ParserException.createForMalformedContainer(
"Invalid NAL length", /* cause= */ null);
}
- sampleCurrentNalBytesRemaining = nalLengthInt - 1;
+ sampleCurrentNalBytesRemaining = nalLengthInt - numberOfNalUnitHeaderBytesToRead;
// Write a start code for the current NAL unit.
nalStartCode.setPosition(0);
output.sampleData(nalStartCode, 4);
- // Write the NAL unit type byte.
- output.sampleData(nalPrefix, 1);
+ sampleBytesWritten += 4;
+ sampleSize += nalUnitLengthFieldLengthDiff;
processSeiNalUnitPayload =
ceaTrackOutputs.length > 0
+ && numberOfNalUnitHeaderBytesToRead > 0
&& NalUnitUtil.isNalUnitSei(track.format, nalPrefixData[4]);
- sampleBytesWritten += 5;
- sampleSize += nalUnitLengthFieldLengthDiff;
- if (!isSampleDependedOn
- && Objects.equals(
- trackBundle.moovSampleTable.track.format.sampleMimeType, MimeTypes.VIDEO_H264)
- && NalUnitUtil.isH264NalUnitDependedOn(nalPrefixData[4])) {
+ // Write the extra NAL unit bytes to the output.
+ output.sampleData(nalPrefix, numberOfNalUnitHeaderBytesToRead);
+ sampleBytesWritten += numberOfNalUnitHeaderBytesToRead;
+ if (numberOfNalUnitHeaderBytesToRead > 0
+ && !isSampleDependedOn
+ && NalUnitUtil.isDependedOn(
+ nalPrefixData,
+ /* offset= */ 4,
+ /* length= */ numberOfNalUnitHeaderBytesToRead,
+ track.format)) {
isSampleDependedOn = true;
}
} else {
int writtenBytes;
if (processSeiNalUnitPayload) {
- // Read and write the payload of the SEI NAL unit.
- nalBuffer.reset(sampleCurrentNalBytesRemaining);
- input.readFully(nalBuffer.getData(), 0, sampleCurrentNalBytesRemaining);
- output.sampleData(nalBuffer, sampleCurrentNalBytesRemaining);
+ // Read and write the remaining payload of the SEI NAL unit.
+ nalUnitWithoutHeaderBuffer.reset(sampleCurrentNalBytesRemaining);
+ input.readFully(
+ nalUnitWithoutHeaderBuffer.getData(), 0, sampleCurrentNalBytesRemaining);
+ output.sampleData(nalUnitWithoutHeaderBuffer, sampleCurrentNalBytesRemaining);
writtenBytes = sampleCurrentNalBytesRemaining;
// Unescape and process the SEI NAL unit.
int unescapedLength =
- NalUnitUtil.unescapeStream(nalBuffer.getData(), nalBuffer.limit());
- // If the format is H.265/HEVC the NAL unit header has two bytes so skip one more byte.
- nalBuffer.setPosition(
- Objects.equals(track.format.sampleMimeType, MimeTypes.VIDEO_H265)
- || MimeTypes.containsCodecsCorrespondingToMimeType(
- track.format.codecs, MimeTypes.VIDEO_H265)
- ? 1
- : 0);
- nalBuffer.setLimit(unescapedLength);
+ NalUnitUtil.unescapeStream(
+ nalUnitWithoutHeaderBuffer.getData(), nalUnitWithoutHeaderBuffer.limit());
+ nalUnitWithoutHeaderBuffer.setPosition(0);
+ nalUnitWithoutHeaderBuffer.setLimit(unescapedLength);
if (track.format.maxNumReorderSamples == Format.NO_VALUE) {
if (reorderingSeiMessageQueue.getMaxSize() != 0) {
@@ -1645,7 +1668,7 @@ public class FragmentedMp4Extractor implements Extractor {
!= track.format.maxNumReorderSamples) {
reorderingSeiMessageQueue.setMaxSize(track.format.maxNumReorderSamples);
}
- reorderingSeiMessageQueue.add(sampleTimeUs, nalBuffer);
+ reorderingSeiMessageQueue.add(sampleTimeUs, nalUnitWithoutHeaderBuffer);
if ((trackBundle.getCurrentSampleFlags() & C.BUFFER_FLAG_END_OF_STREAM) != 0) {
reorderingSeiMessageQueue.flush();
@@ -1666,7 +1689,7 @@ public class FragmentedMp4Extractor implements Extractor {
}
@C.BufferFlags int sampleFlags = trackBundle.getCurrentSampleFlags();
- if ((flags & FLAG_READ_WITHIN_GOP_SAMPLE_DEPENDENCIES) != 0 && !isSampleDependedOn) {
+ if (!isSampleDependedOn) {
sampleFlags |= C.BUFFER_FLAG_NOT_DEPENDED_ON;
}
@@ -1688,6 +1711,20 @@ public class FragmentedMp4Extractor implements Extractor {
return true;
}
+ /**
+ * Returns whether reading within GOP sample dependencies is enabled for the sample {@link
+ * Format}.
+ */
+ private boolean canReadWithinGopSampleDependencies(Format format) {
+ if (Objects.equals(format.sampleMimeType, MimeTypes.VIDEO_H264)) {
+ return (flags & FLAG_READ_WITHIN_GOP_SAMPLE_DEPENDENCIES) != 0;
+ }
+ if (Objects.equals(format.sampleMimeType, MimeTypes.VIDEO_H265)) {
+ return (flags & FLAG_READ_WITHIN_GOP_SAMPLE_DEPENDENCIES_H265) != 0;
+ }
+ return false;
+ }
+
/**
* Called immediately after outputting a non-metadata sample, to output any pending metadata
* samples.
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java
index 0f0e4be0e2..4b874be88c 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java
@@ -902,15 +902,14 @@ public final class Mp4Extractor implements Extractor, SeekMap {
int nalUnitPrefixLength = track.track.nalUnitLengthFieldLength;
int numberOfBytesToDetermineSampleDependencies = 0;
if (!isSampleDependedOn
- && nalUnitPrefixLength
- + NalUnitUtil.numberOfBytesToDetermineSampleDependencies(track.track.format)
+ && nalUnitPrefixLength + NalUnitUtil.numberOfBytesInNalUnitHeader(track.track.format)
<= track.sampleTable.sizes[sampleIndex] - sampleBytesRead) {
// Parsing sample dependencies needs the first few NAL unit bytes. Read them in the same
// readFully call that reads the NAL length. This ensures sampleBytesRead,
// sampleBytesWritten and isSampleDependedOn remain in a consistent state if we have
// read failures.
numberOfBytesToDetermineSampleDependencies =
- NalUnitUtil.numberOfBytesToDetermineSampleDependencies(track.track.format);
+ NalUnitUtil.numberOfBytesInNalUnitHeader(track.track.format);
nalUnitPrefixLength =
track.track.nalUnitLengthFieldLength + numberOfBytesToDetermineSampleDependencies;
}
diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/mp4/FragmentedMp4ExtractorParameterizedTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/mp4/FragmentedMp4ExtractorParameterizedTest.java
index c7bd6cd1ec..3e19c45378 100644
--- a/libraries/extractor/src/test/java/androidx/media3/extractor/mp4/FragmentedMp4ExtractorParameterizedTest.java
+++ b/libraries/extractor/src/test/java/androidx/media3/extractor/mp4/FragmentedMp4ExtractorParameterizedTest.java
@@ -200,6 +200,16 @@ public final class FragmentedMp4ExtractorParameterizedTest {
/* closedCaptionFormats= */ ImmutableList.of(), "media/mp4/sample_fragmented_iamf.mp4");
}
+ @Test
+ public void sampleWithNonReferenceH265FramesAndCaptions() throws Exception {
+ // Enabling the CEA-608 track enables SEI payload parsing.
+ List closedCaptions =
+ Collections.singletonList(
+ new Format.Builder().setSampleMimeType(MimeTypes.APPLICATION_CEA608).build());
+
+ assertExtractorBehavior(closedCaptions, "media/mp4/fragmented_captions_h265.mp4");
+ }
+
private void assertExtractorBehavior(List closedCaptionFormats, String file)
throws IOException {
ExtractorAsserts.AssertionConfig.Builder assertionConfigBuilder =
@@ -232,6 +242,7 @@ public final class FragmentedMp4ExtractorParameterizedTest {
}
if (readWithinGopSampleDependencies) {
flags |= FragmentedMp4Extractor.FLAG_READ_WITHIN_GOP_SAMPLE_DEPENDENCIES;
+ flags |= FragmentedMp4Extractor.FLAG_READ_WITHIN_GOP_SAMPLE_DEPENDENCIES_H265;
}
@FragmentedMp4Extractor.Flags int finalFlags = flags;
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.0.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.0.dump
new file mode 100644
index 0000000000..23c9d2aae3
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.0.dump
@@ -0,0 +1,283 @@
+seekMap:
+ isSeekable = false
+ duration = UNSET TIME
+ getPosition(0) = [[timeUs=0, position=3203]]
+numberOfTracks = 2
+track 0:
+ total output bytes = 24020
+ sample count = 61
+ format 0:
+ id = 1
+ containerMimeType = video/mp4
+ sampleMimeType = video/hevc
+ codecs = hvc1.1.6.L60.90
+ maxNumReorderSamples = 2
+ width = 416
+ height = 234
+ maxSubLayers = 1
+ colorInfo:
+ colorSpace = 2
+ colorRange = 2
+ colorTransfer = 3
+ lumaBitdepth = 8
+ chromaBitdepth = 8
+ initializationData:
+ data = length 2426, hash 25737613
+ sample 0:
+ time = 66733
+ flags = 1
+ data = length 5220, hash 5F0CCC08
+ sample 1:
+ time = 233566
+ flags = 0
+ data = length 615, hash CBFF4D3C
+ sample 2:
+ time = 166833
+ flags = 0
+ data = length 217, hash B600557D
+ sample 3:
+ time = 100100
+ flags = 0
+ data = length 177, hash 99B5D77F
+ sample 4:
+ time = 133466
+ flags = 0
+ data = length 190, hash AF7F85A2
+ sample 5:
+ time = 200200
+ flags = 0
+ data = length 225, hash BA8F2E19
+ sample 6:
+ time = 333666
+ flags = 0
+ data = length 330, hash DCBABAE8
+ sample 7:
+ time = 300300
+ flags = 0
+ data = length 183, hash 15A2DB25
+ sample 8:
+ time = 266933
+ flags = 0
+ data = length 154, hash 81018D0E
+ sample 9:
+ time = 400400
+ flags = 0
+ data = length 576, hash C9C5389F
+ sample 10:
+ time = 367033
+ flags = 0
+ data = length 173, hash 2D567C7E
+ sample 11:
+ time = 500500
+ flags = 0
+ data = length 356, hash A6D37B60
+ sample 12:
+ time = 467133
+ flags = 0
+ data = length 188, hash 11AB1597
+ sample 13:
+ time = 433766
+ flags = 0
+ data = length 174, hash 18C46868
+ sample 14:
+ time = 567233
+ flags = 0
+ data = length 331, hash EEA184AF
+ sample 15:
+ time = 533866
+ flags = 0
+ data = length 213, hash 3FAB17EE
+ sample 16:
+ time = 667333
+ flags = 0
+ data = length 371, hash D0ECAE97
+ sample 17:
+ time = 633966
+ flags = 0
+ data = length 179, hash 796F24AF
+ sample 18:
+ time = 600600
+ flags = 0
+ data = length 154, hash FD43F0C6
+ sample 19:
+ time = 734066
+ flags = 0
+ data = length 622, hash 8AD9409C
+ sample 20:
+ time = 700700
+ flags = 0
+ data = length 195, hash 4EFA8FFD
+ sample 21:
+ time = 900900
+ flags = 0
+ data = length 424, hash 5F9D809C
+ sample 22:
+ time = 834166
+ flags = 0
+ data = length 187, hash FB5137FD
+ sample 23:
+ time = 767433
+ flags = 0
+ data = length 162, hash 7B352EA
+ sample 24:
+ time = 800800
+ flags = 0
+ data = length 173, hash 42EBA5AC
+ sample 25:
+ time = 867533
+ flags = 0
+ data = length 205, hash 15D2186B
+ sample 26:
+ time = 1001000
+ flags = 0
+ data = length 342, hash 70E9AD53
+ sample 27:
+ time = 967633
+ flags = 0
+ data = length 200, hash 2C923950
+ sample 28:
+ time = 934266
+ flags = 0
+ data = length 150, hash 242935D6
+ sample 29:
+ time = 1167833
+ flags = 0
+ data = length 802, hash 60213433
+ sample 30:
+ time = 1101100
+ flags = 0
+ data = length 203, hash 21B384E5
+ sample 31:
+ time = 1034366
+ flags = 0
+ data = length 185, hash B2735872
+ sample 32:
+ time = 1067733
+ flags = 0
+ data = length 1310, hash 8D4F7E35
+ sample 33:
+ time = 1134466
+ flags = 0
+ data = length 176, hash D1D91C73
+ sample 34:
+ time = 1334666
+ flags = 0
+ data = length 258, hash 92056B3A
+ sample 35:
+ time = 1267933
+ flags = 0
+ data = length 214, hash 86C47DEC
+ sample 36:
+ time = 1201200
+ flags = 0
+ data = length 201, hash AFA1B6D4
+ sample 37:
+ time = 1234566
+ flags = 0
+ data = length 146, hash 3E1B1C72
+ sample 38:
+ time = 1301300
+ flags = 0
+ data = length 173, hash FC0B911D
+ sample 39:
+ time = 1401400
+ flags = 0
+ data = length 617, hash 35AAFB9
+ sample 40:
+ time = 1368033
+ flags = 0
+ data = length 179, hash E9838582
+ sample 41:
+ time = 1468133
+ flags = 0
+ data = length 395, hash 8F5373AC
+ sample 42:
+ time = 1434766
+ flags = 0
+ data = length 184, hash 10ED9B63
+ sample 43:
+ time = 1634966
+ flags = 0
+ data = length 460, hash ED8ECDE7
+ sample 44:
+ time = 1568233
+ flags = 0
+ data = length 184, hash BF273413
+ sample 45:
+ time = 1501500
+ flags = 0
+ data = length 162, hash FE32A96B
+ sample 46:
+ time = 1534866
+ flags = 0
+ data = length 201, hash BBB627F2
+ sample 47:
+ time = 1601600
+ flags = 0
+ data = length 169, hash AFD9804F
+ sample 48:
+ time = 1735066
+ flags = 0
+ data = length 668, hash 4B9FB50F
+ sample 49:
+ time = 1701700
+ flags = 0
+ data = length 219, hash E2A651A2
+ sample 50:
+ time = 1668333
+ flags = 0
+ data = length 162, hash ECB52FD2
+ sample 51:
+ time = 1901900
+ flags = 0
+ data = length 405, hash 7DA640B4
+ sample 52:
+ time = 1835166
+ flags = 0
+ data = length 188, hash 46D5E9D4
+ sample 53:
+ time = 1768433
+ flags = 0
+ data = length 167, hash 65F87E6A
+ sample 54:
+ time = 1801800
+ flags = 0
+ data = length 161, hash 2E37B5E0
+ sample 55:
+ time = 1868533
+ flags = 0
+ data = length 199, hash D63CF14E
+ sample 56:
+ time = 2002000
+ flags = 0
+ data = length 282, hash E748555D
+ sample 57:
+ time = 1968633
+ flags = 0
+ data = length 199, hash F341EE39
+ sample 58:
+ time = 1935266
+ flags = 0
+ data = length 166, hash 32F07BFF
+ sample 59:
+ time = 2068733
+ flags = 0
+ data = length 2246, hash E07DFCAE
+ sample 60:
+ time = 2035366
+ flags = 0
+ data = length 53, hash D9F70BD5
+track 100:
+ total output bytes = 45
+ sample count = 2
+ format 0:
+ sampleMimeType = application/cea-608
+ sample 0:
+ time = 66733
+ flags = 1
+ data = length 36, hash 6E15DFAD
+ sample 1:
+ time = 1935266
+ flags = 1
+ data = length 9, hash 604EC6AA
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.reading_within_gop_sample_dependencies.0.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.reading_within_gop_sample_dependencies.0.dump
new file mode 100644
index 0000000000..fbf4d20816
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.reading_within_gop_sample_dependencies.0.dump
@@ -0,0 +1,283 @@
+seekMap:
+ isSeekable = false
+ duration = UNSET TIME
+ getPosition(0) = [[timeUs=0, position=3203]]
+numberOfTracks = 2
+track 0:
+ total output bytes = 24020
+ sample count = 61
+ format 0:
+ id = 1
+ containerMimeType = video/mp4
+ sampleMimeType = video/hevc
+ codecs = hvc1.1.6.L60.90
+ maxNumReorderSamples = 2
+ width = 416
+ height = 234
+ maxSubLayers = 1
+ colorInfo:
+ colorSpace = 2
+ colorRange = 2
+ colorTransfer = 3
+ lumaBitdepth = 8
+ chromaBitdepth = 8
+ initializationData:
+ data = length 2426, hash 25737613
+ sample 0:
+ time = 66733
+ flags = 1
+ data = length 5220, hash 5F0CCC08
+ sample 1:
+ time = 233566
+ flags = 0
+ data = length 615, hash CBFF4D3C
+ sample 2:
+ time = 166833
+ flags = 0
+ data = length 217, hash B600557D
+ sample 3:
+ time = 100100
+ flags = 67108864
+ data = length 177, hash 99B5D77F
+ sample 4:
+ time = 133466
+ flags = 67108864
+ data = length 190, hash AF7F85A2
+ sample 5:
+ time = 200200
+ flags = 67108864
+ data = length 225, hash BA8F2E19
+ sample 6:
+ time = 333666
+ flags = 0
+ data = length 330, hash DCBABAE8
+ sample 7:
+ time = 300300
+ flags = 0
+ data = length 183, hash 15A2DB25
+ sample 8:
+ time = 266933
+ flags = 67108864
+ data = length 154, hash 81018D0E
+ sample 9:
+ time = 400400
+ flags = 0
+ data = length 576, hash C9C5389F
+ sample 10:
+ time = 367033
+ flags = 67108864
+ data = length 173, hash 2D567C7E
+ sample 11:
+ time = 500500
+ flags = 0
+ data = length 356, hash A6D37B60
+ sample 12:
+ time = 467133
+ flags = 0
+ data = length 188, hash 11AB1597
+ sample 13:
+ time = 433766
+ flags = 67108864
+ data = length 174, hash 18C46868
+ sample 14:
+ time = 567233
+ flags = 0
+ data = length 331, hash EEA184AF
+ sample 15:
+ time = 533866
+ flags = 67108864
+ data = length 213, hash 3FAB17EE
+ sample 16:
+ time = 667333
+ flags = 0
+ data = length 371, hash D0ECAE97
+ sample 17:
+ time = 633966
+ flags = 0
+ data = length 179, hash 796F24AF
+ sample 18:
+ time = 600600
+ flags = 67108864
+ data = length 154, hash FD43F0C6
+ sample 19:
+ time = 734066
+ flags = 0
+ data = length 622, hash 8AD9409C
+ sample 20:
+ time = 700700
+ flags = 67108864
+ data = length 195, hash 4EFA8FFD
+ sample 21:
+ time = 900900
+ flags = 0
+ data = length 424, hash 5F9D809C
+ sample 22:
+ time = 834166
+ flags = 0
+ data = length 187, hash FB5137FD
+ sample 23:
+ time = 767433
+ flags = 67108864
+ data = length 162, hash 7B352EA
+ sample 24:
+ time = 800800
+ flags = 67108864
+ data = length 173, hash 42EBA5AC
+ sample 25:
+ time = 867533
+ flags = 67108864
+ data = length 205, hash 15D2186B
+ sample 26:
+ time = 1001000
+ flags = 0
+ data = length 342, hash 70E9AD53
+ sample 27:
+ time = 967633
+ flags = 0
+ data = length 200, hash 2C923950
+ sample 28:
+ time = 934266
+ flags = 67108864
+ data = length 150, hash 242935D6
+ sample 29:
+ time = 1167833
+ flags = 0
+ data = length 802, hash 60213433
+ sample 30:
+ time = 1101100
+ flags = 0
+ data = length 203, hash 21B384E5
+ sample 31:
+ time = 1034366
+ flags = 67108864
+ data = length 185, hash B2735872
+ sample 32:
+ time = 1067733
+ flags = 67108864
+ data = length 1310, hash 8D4F7E35
+ sample 33:
+ time = 1134466
+ flags = 67108864
+ data = length 176, hash D1D91C73
+ sample 34:
+ time = 1334666
+ flags = 0
+ data = length 258, hash 92056B3A
+ sample 35:
+ time = 1267933
+ flags = 0
+ data = length 214, hash 86C47DEC
+ sample 36:
+ time = 1201200
+ flags = 67108864
+ data = length 201, hash AFA1B6D4
+ sample 37:
+ time = 1234566
+ flags = 67108864
+ data = length 146, hash 3E1B1C72
+ sample 38:
+ time = 1301300
+ flags = 67108864
+ data = length 173, hash FC0B911D
+ sample 39:
+ time = 1401400
+ flags = 0
+ data = length 617, hash 35AAFB9
+ sample 40:
+ time = 1368033
+ flags = 67108864
+ data = length 179, hash E9838582
+ sample 41:
+ time = 1468133
+ flags = 0
+ data = length 395, hash 8F5373AC
+ sample 42:
+ time = 1434766
+ flags = 67108864
+ data = length 184, hash 10ED9B63
+ sample 43:
+ time = 1634966
+ flags = 0
+ data = length 460, hash ED8ECDE7
+ sample 44:
+ time = 1568233
+ flags = 0
+ data = length 184, hash BF273413
+ sample 45:
+ time = 1501500
+ flags = 67108864
+ data = length 162, hash FE32A96B
+ sample 46:
+ time = 1534866
+ flags = 67108864
+ data = length 201, hash BBB627F2
+ sample 47:
+ time = 1601600
+ flags = 67108864
+ data = length 169, hash AFD9804F
+ sample 48:
+ time = 1735066
+ flags = 0
+ data = length 668, hash 4B9FB50F
+ sample 49:
+ time = 1701700
+ flags = 0
+ data = length 219, hash E2A651A2
+ sample 50:
+ time = 1668333
+ flags = 67108864
+ data = length 162, hash ECB52FD2
+ sample 51:
+ time = 1901900
+ flags = 0
+ data = length 405, hash 7DA640B4
+ sample 52:
+ time = 1835166
+ flags = 0
+ data = length 188, hash 46D5E9D4
+ sample 53:
+ time = 1768433
+ flags = 67108864
+ data = length 167, hash 65F87E6A
+ sample 54:
+ time = 1801800
+ flags = 67108864
+ data = length 161, hash 2E37B5E0
+ sample 55:
+ time = 1868533
+ flags = 67108864
+ data = length 199, hash D63CF14E
+ sample 56:
+ time = 2002000
+ flags = 0
+ data = length 282, hash E748555D
+ sample 57:
+ time = 1968633
+ flags = 0
+ data = length 199, hash F341EE39
+ sample 58:
+ time = 1935266
+ flags = 0
+ data = length 166, hash 32F07BFF
+ sample 59:
+ time = 2068733
+ flags = 0
+ data = length 2246, hash E07DFCAE
+ sample 60:
+ time = 2035366
+ flags = 67108864
+ data = length 53, hash D9F70BD5
+track 100:
+ total output bytes = 45
+ sample count = 2
+ format 0:
+ sampleMimeType = application/cea-608
+ sample 0:
+ time = 66733
+ flags = 1
+ data = length 36, hash 6E15DFAD
+ sample 1:
+ time = 1935266
+ flags = 1
+ data = length 9, hash 604EC6AA
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.reading_within_gop_sample_dependencies.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.reading_within_gop_sample_dependencies.unknown_length.dump
new file mode 100644
index 0000000000..fbf4d20816
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.reading_within_gop_sample_dependencies.unknown_length.dump
@@ -0,0 +1,283 @@
+seekMap:
+ isSeekable = false
+ duration = UNSET TIME
+ getPosition(0) = [[timeUs=0, position=3203]]
+numberOfTracks = 2
+track 0:
+ total output bytes = 24020
+ sample count = 61
+ format 0:
+ id = 1
+ containerMimeType = video/mp4
+ sampleMimeType = video/hevc
+ codecs = hvc1.1.6.L60.90
+ maxNumReorderSamples = 2
+ width = 416
+ height = 234
+ maxSubLayers = 1
+ colorInfo:
+ colorSpace = 2
+ colorRange = 2
+ colorTransfer = 3
+ lumaBitdepth = 8
+ chromaBitdepth = 8
+ initializationData:
+ data = length 2426, hash 25737613
+ sample 0:
+ time = 66733
+ flags = 1
+ data = length 5220, hash 5F0CCC08
+ sample 1:
+ time = 233566
+ flags = 0
+ data = length 615, hash CBFF4D3C
+ sample 2:
+ time = 166833
+ flags = 0
+ data = length 217, hash B600557D
+ sample 3:
+ time = 100100
+ flags = 67108864
+ data = length 177, hash 99B5D77F
+ sample 4:
+ time = 133466
+ flags = 67108864
+ data = length 190, hash AF7F85A2
+ sample 5:
+ time = 200200
+ flags = 67108864
+ data = length 225, hash BA8F2E19
+ sample 6:
+ time = 333666
+ flags = 0
+ data = length 330, hash DCBABAE8
+ sample 7:
+ time = 300300
+ flags = 0
+ data = length 183, hash 15A2DB25
+ sample 8:
+ time = 266933
+ flags = 67108864
+ data = length 154, hash 81018D0E
+ sample 9:
+ time = 400400
+ flags = 0
+ data = length 576, hash C9C5389F
+ sample 10:
+ time = 367033
+ flags = 67108864
+ data = length 173, hash 2D567C7E
+ sample 11:
+ time = 500500
+ flags = 0
+ data = length 356, hash A6D37B60
+ sample 12:
+ time = 467133
+ flags = 0
+ data = length 188, hash 11AB1597
+ sample 13:
+ time = 433766
+ flags = 67108864
+ data = length 174, hash 18C46868
+ sample 14:
+ time = 567233
+ flags = 0
+ data = length 331, hash EEA184AF
+ sample 15:
+ time = 533866
+ flags = 67108864
+ data = length 213, hash 3FAB17EE
+ sample 16:
+ time = 667333
+ flags = 0
+ data = length 371, hash D0ECAE97
+ sample 17:
+ time = 633966
+ flags = 0
+ data = length 179, hash 796F24AF
+ sample 18:
+ time = 600600
+ flags = 67108864
+ data = length 154, hash FD43F0C6
+ sample 19:
+ time = 734066
+ flags = 0
+ data = length 622, hash 8AD9409C
+ sample 20:
+ time = 700700
+ flags = 67108864
+ data = length 195, hash 4EFA8FFD
+ sample 21:
+ time = 900900
+ flags = 0
+ data = length 424, hash 5F9D809C
+ sample 22:
+ time = 834166
+ flags = 0
+ data = length 187, hash FB5137FD
+ sample 23:
+ time = 767433
+ flags = 67108864
+ data = length 162, hash 7B352EA
+ sample 24:
+ time = 800800
+ flags = 67108864
+ data = length 173, hash 42EBA5AC
+ sample 25:
+ time = 867533
+ flags = 67108864
+ data = length 205, hash 15D2186B
+ sample 26:
+ time = 1001000
+ flags = 0
+ data = length 342, hash 70E9AD53
+ sample 27:
+ time = 967633
+ flags = 0
+ data = length 200, hash 2C923950
+ sample 28:
+ time = 934266
+ flags = 67108864
+ data = length 150, hash 242935D6
+ sample 29:
+ time = 1167833
+ flags = 0
+ data = length 802, hash 60213433
+ sample 30:
+ time = 1101100
+ flags = 0
+ data = length 203, hash 21B384E5
+ sample 31:
+ time = 1034366
+ flags = 67108864
+ data = length 185, hash B2735872
+ sample 32:
+ time = 1067733
+ flags = 67108864
+ data = length 1310, hash 8D4F7E35
+ sample 33:
+ time = 1134466
+ flags = 67108864
+ data = length 176, hash D1D91C73
+ sample 34:
+ time = 1334666
+ flags = 0
+ data = length 258, hash 92056B3A
+ sample 35:
+ time = 1267933
+ flags = 0
+ data = length 214, hash 86C47DEC
+ sample 36:
+ time = 1201200
+ flags = 67108864
+ data = length 201, hash AFA1B6D4
+ sample 37:
+ time = 1234566
+ flags = 67108864
+ data = length 146, hash 3E1B1C72
+ sample 38:
+ time = 1301300
+ flags = 67108864
+ data = length 173, hash FC0B911D
+ sample 39:
+ time = 1401400
+ flags = 0
+ data = length 617, hash 35AAFB9
+ sample 40:
+ time = 1368033
+ flags = 67108864
+ data = length 179, hash E9838582
+ sample 41:
+ time = 1468133
+ flags = 0
+ data = length 395, hash 8F5373AC
+ sample 42:
+ time = 1434766
+ flags = 67108864
+ data = length 184, hash 10ED9B63
+ sample 43:
+ time = 1634966
+ flags = 0
+ data = length 460, hash ED8ECDE7
+ sample 44:
+ time = 1568233
+ flags = 0
+ data = length 184, hash BF273413
+ sample 45:
+ time = 1501500
+ flags = 67108864
+ data = length 162, hash FE32A96B
+ sample 46:
+ time = 1534866
+ flags = 67108864
+ data = length 201, hash BBB627F2
+ sample 47:
+ time = 1601600
+ flags = 67108864
+ data = length 169, hash AFD9804F
+ sample 48:
+ time = 1735066
+ flags = 0
+ data = length 668, hash 4B9FB50F
+ sample 49:
+ time = 1701700
+ flags = 0
+ data = length 219, hash E2A651A2
+ sample 50:
+ time = 1668333
+ flags = 67108864
+ data = length 162, hash ECB52FD2
+ sample 51:
+ time = 1901900
+ flags = 0
+ data = length 405, hash 7DA640B4
+ sample 52:
+ time = 1835166
+ flags = 0
+ data = length 188, hash 46D5E9D4
+ sample 53:
+ time = 1768433
+ flags = 67108864
+ data = length 167, hash 65F87E6A
+ sample 54:
+ time = 1801800
+ flags = 67108864
+ data = length 161, hash 2E37B5E0
+ sample 55:
+ time = 1868533
+ flags = 67108864
+ data = length 199, hash D63CF14E
+ sample 56:
+ time = 2002000
+ flags = 0
+ data = length 282, hash E748555D
+ sample 57:
+ time = 1968633
+ flags = 0
+ data = length 199, hash F341EE39
+ sample 58:
+ time = 1935266
+ flags = 0
+ data = length 166, hash 32F07BFF
+ sample 59:
+ time = 2068733
+ flags = 0
+ data = length 2246, hash E07DFCAE
+ sample 60:
+ time = 2035366
+ flags = 67108864
+ data = length 53, hash D9F70BD5
+track 100:
+ total output bytes = 45
+ sample count = 2
+ format 0:
+ sampleMimeType = application/cea-608
+ sample 0:
+ time = 66733
+ flags = 1
+ data = length 36, hash 6E15DFAD
+ sample 1:
+ time = 1935266
+ flags = 1
+ data = length 9, hash 604EC6AA
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.unknown_length.dump
new file mode 100644
index 0000000000..23c9d2aae3
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.unknown_length.dump
@@ -0,0 +1,283 @@
+seekMap:
+ isSeekable = false
+ duration = UNSET TIME
+ getPosition(0) = [[timeUs=0, position=3203]]
+numberOfTracks = 2
+track 0:
+ total output bytes = 24020
+ sample count = 61
+ format 0:
+ id = 1
+ containerMimeType = video/mp4
+ sampleMimeType = video/hevc
+ codecs = hvc1.1.6.L60.90
+ maxNumReorderSamples = 2
+ width = 416
+ height = 234
+ maxSubLayers = 1
+ colorInfo:
+ colorSpace = 2
+ colorRange = 2
+ colorTransfer = 3
+ lumaBitdepth = 8
+ chromaBitdepth = 8
+ initializationData:
+ data = length 2426, hash 25737613
+ sample 0:
+ time = 66733
+ flags = 1
+ data = length 5220, hash 5F0CCC08
+ sample 1:
+ time = 233566
+ flags = 0
+ data = length 615, hash CBFF4D3C
+ sample 2:
+ time = 166833
+ flags = 0
+ data = length 217, hash B600557D
+ sample 3:
+ time = 100100
+ flags = 0
+ data = length 177, hash 99B5D77F
+ sample 4:
+ time = 133466
+ flags = 0
+ data = length 190, hash AF7F85A2
+ sample 5:
+ time = 200200
+ flags = 0
+ data = length 225, hash BA8F2E19
+ sample 6:
+ time = 333666
+ flags = 0
+ data = length 330, hash DCBABAE8
+ sample 7:
+ time = 300300
+ flags = 0
+ data = length 183, hash 15A2DB25
+ sample 8:
+ time = 266933
+ flags = 0
+ data = length 154, hash 81018D0E
+ sample 9:
+ time = 400400
+ flags = 0
+ data = length 576, hash C9C5389F
+ sample 10:
+ time = 367033
+ flags = 0
+ data = length 173, hash 2D567C7E
+ sample 11:
+ time = 500500
+ flags = 0
+ data = length 356, hash A6D37B60
+ sample 12:
+ time = 467133
+ flags = 0
+ data = length 188, hash 11AB1597
+ sample 13:
+ time = 433766
+ flags = 0
+ data = length 174, hash 18C46868
+ sample 14:
+ time = 567233
+ flags = 0
+ data = length 331, hash EEA184AF
+ sample 15:
+ time = 533866
+ flags = 0
+ data = length 213, hash 3FAB17EE
+ sample 16:
+ time = 667333
+ flags = 0
+ data = length 371, hash D0ECAE97
+ sample 17:
+ time = 633966
+ flags = 0
+ data = length 179, hash 796F24AF
+ sample 18:
+ time = 600600
+ flags = 0
+ data = length 154, hash FD43F0C6
+ sample 19:
+ time = 734066
+ flags = 0
+ data = length 622, hash 8AD9409C
+ sample 20:
+ time = 700700
+ flags = 0
+ data = length 195, hash 4EFA8FFD
+ sample 21:
+ time = 900900
+ flags = 0
+ data = length 424, hash 5F9D809C
+ sample 22:
+ time = 834166
+ flags = 0
+ data = length 187, hash FB5137FD
+ sample 23:
+ time = 767433
+ flags = 0
+ data = length 162, hash 7B352EA
+ sample 24:
+ time = 800800
+ flags = 0
+ data = length 173, hash 42EBA5AC
+ sample 25:
+ time = 867533
+ flags = 0
+ data = length 205, hash 15D2186B
+ sample 26:
+ time = 1001000
+ flags = 0
+ data = length 342, hash 70E9AD53
+ sample 27:
+ time = 967633
+ flags = 0
+ data = length 200, hash 2C923950
+ sample 28:
+ time = 934266
+ flags = 0
+ data = length 150, hash 242935D6
+ sample 29:
+ time = 1167833
+ flags = 0
+ data = length 802, hash 60213433
+ sample 30:
+ time = 1101100
+ flags = 0
+ data = length 203, hash 21B384E5
+ sample 31:
+ time = 1034366
+ flags = 0
+ data = length 185, hash B2735872
+ sample 32:
+ time = 1067733
+ flags = 0
+ data = length 1310, hash 8D4F7E35
+ sample 33:
+ time = 1134466
+ flags = 0
+ data = length 176, hash D1D91C73
+ sample 34:
+ time = 1334666
+ flags = 0
+ data = length 258, hash 92056B3A
+ sample 35:
+ time = 1267933
+ flags = 0
+ data = length 214, hash 86C47DEC
+ sample 36:
+ time = 1201200
+ flags = 0
+ data = length 201, hash AFA1B6D4
+ sample 37:
+ time = 1234566
+ flags = 0
+ data = length 146, hash 3E1B1C72
+ sample 38:
+ time = 1301300
+ flags = 0
+ data = length 173, hash FC0B911D
+ sample 39:
+ time = 1401400
+ flags = 0
+ data = length 617, hash 35AAFB9
+ sample 40:
+ time = 1368033
+ flags = 0
+ data = length 179, hash E9838582
+ sample 41:
+ time = 1468133
+ flags = 0
+ data = length 395, hash 8F5373AC
+ sample 42:
+ time = 1434766
+ flags = 0
+ data = length 184, hash 10ED9B63
+ sample 43:
+ time = 1634966
+ flags = 0
+ data = length 460, hash ED8ECDE7
+ sample 44:
+ time = 1568233
+ flags = 0
+ data = length 184, hash BF273413
+ sample 45:
+ time = 1501500
+ flags = 0
+ data = length 162, hash FE32A96B
+ sample 46:
+ time = 1534866
+ flags = 0
+ data = length 201, hash BBB627F2
+ sample 47:
+ time = 1601600
+ flags = 0
+ data = length 169, hash AFD9804F
+ sample 48:
+ time = 1735066
+ flags = 0
+ data = length 668, hash 4B9FB50F
+ sample 49:
+ time = 1701700
+ flags = 0
+ data = length 219, hash E2A651A2
+ sample 50:
+ time = 1668333
+ flags = 0
+ data = length 162, hash ECB52FD2
+ sample 51:
+ time = 1901900
+ flags = 0
+ data = length 405, hash 7DA640B4
+ sample 52:
+ time = 1835166
+ flags = 0
+ data = length 188, hash 46D5E9D4
+ sample 53:
+ time = 1768433
+ flags = 0
+ data = length 167, hash 65F87E6A
+ sample 54:
+ time = 1801800
+ flags = 0
+ data = length 161, hash 2E37B5E0
+ sample 55:
+ time = 1868533
+ flags = 0
+ data = length 199, hash D63CF14E
+ sample 56:
+ time = 2002000
+ flags = 0
+ data = length 282, hash E748555D
+ sample 57:
+ time = 1968633
+ flags = 0
+ data = length 199, hash F341EE39
+ sample 58:
+ time = 1935266
+ flags = 0
+ data = length 166, hash 32F07BFF
+ sample 59:
+ time = 2068733
+ flags = 0
+ data = length 2246, hash E07DFCAE
+ sample 60:
+ time = 2035366
+ flags = 0
+ data = length 53, hash D9F70BD5
+track 100:
+ total output bytes = 45
+ sample count = 2
+ format 0:
+ sampleMimeType = application/cea-608
+ sample 0:
+ time = 66733
+ flags = 1
+ data = length 36, hash 6E15DFAD
+ sample 1:
+ time = 1935266
+ flags = 1
+ data = length 9, hash 604EC6AA
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/media/mp4/fragmented_captions_h265.mp4 b/libraries/test_data/src/test/assets/media/mp4/fragmented_captions_h265.mp4
new file mode 100644
index 0000000000..901e165ce2
Binary files /dev/null and b/libraries/test_data/src/test/assets/media/mp4/fragmented_captions_h265.mp4 differ