diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index bbe1147a3b..25436be3a7 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -58,6 +58,8 @@
* RTMP Extension:
* HLS Extension:
* DASH Extension:
+ * Add AC-4 Level-4 format support for DASH
+ ([#1898](https://github.com/androidx/media/pull/1898)).
* Smooth Streaming Extension:
* RTSP Extension:
* Decoder Extensions (FFmpeg, VP9, AV1, etc.):
diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java
index 3b02cdd75c..11eddae3d8 100644
--- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java
+++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/manifest/DashManifestParser.java
@@ -47,6 +47,7 @@ import androidx.media3.exoplayer.upstream.ParsingLoadable;
import androidx.media3.extractor.metadata.emsg.EventMessage;
import androidx.media3.extractor.mp4.PsshAtomUtil;
import com.google.common.base.Ascii;
+import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.io.ByteArrayOutputStream;
@@ -77,6 +78,20 @@ public class DashManifestParser extends DefaultHandler
private static final Pattern CEA_708_ACCESSIBILITY_PATTERN =
Pattern.compile("([1-9]|[1-5][0-9]|6[0-3])=.*");
+ /**
+ * Maps the value attribute of an AudioChannelConfiguration with schemeIdUri
+ * "tag:dolby.com,2015:dash:audio_channel_configuration:2015" to the corresponding number of
+ * channels for each speaker group, as defined by ETSI TS 103 190-2 v1.2.1 clause G.3.
+ *
+ *
Table A.27 in ETSI TS 103 190-2 v1.2.1 defines the speaker counts for each speaker group
+ * index. The channel count is calculated by summing the speaker counts for the present indexes.
+ *
+ *
For example, a value of "0x3" (binary "11") indicates the presence of speaker groups 0 and
+ * 1. This maps to 2 + 1 = 3 channels.
+ */
+ private static final int[] DOLBY_AC4_CHANNEL_CONFIGURATION_MAPPING =
+ new int[] {2, 1, 2, 2, 2, 2, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2};
+
/**
* Maps the value attribute of an AudioChannelConfiguration with schemeIdUri
* "urn:mpeg:mpegB:cicp:ChannelConfiguration", as defined by ISO 23091-3:2018 clause 6.2, to a
@@ -441,7 +456,7 @@ public class DashManifestParser extends DefaultHandler
} else if (XmlPullParserUtil.isStartTag(xpp, "Role")) {
roleDescriptors.add(parseDescriptor(xpp, "Role"));
} else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
- audioChannels = parseAudioChannelConfiguration(xpp);
+ audioChannels = parseAudioChannelConfiguration(xpp, codecs);
} else if (XmlPullParserUtil.isStartTag(xpp, "Accessibility")) {
accessibilityDescriptors.add(parseDescriptor(xpp, "Accessibility"));
} else if (XmlPullParserUtil.isStartTag(xpp, "EssentialProperty")) {
@@ -720,7 +735,7 @@ public class DashManifestParser extends DefaultHandler
}
baseUrls.addAll(parseBaseUrl(xpp, parentBaseUrls, dvbProfileDeclared));
} else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
- audioChannels = parseAudioChannelConfiguration(xpp);
+ audioChannels = parseAudioChannelConfiguration(xpp, codecs);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
segmentBase = parseSegmentBase(xpp, (SingleSegmentBase) segmentBase);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) {
@@ -1487,7 +1502,7 @@ public class DashManifestParser extends DefaultHandler
// AudioChannelConfiguration parsing.
- protected int parseAudioChannelConfiguration(XmlPullParser xpp)
+ protected int parseAudioChannelConfiguration(XmlPullParser xpp, String codecs)
throws XmlPullParserException, IOException {
String schemeIdUri = parseString(xpp, "schemeIdUri", null);
int audioChannels;
@@ -1509,6 +1524,9 @@ public class DashManifestParser extends DefaultHandler
case "urn:dolby:dash:audio_channel_configuration:2011":
audioChannels = parseDolbyChannelConfiguration(xpp);
break;
+ case "tag:dolby.com,2015:dash:audio_channel_configuration:2015":
+ audioChannels = parseDolbyAC4ChannelConfiguration(xpp, codecs);
+ break;
default:
audioChannels = Format.NO_VALUE;
break;
@@ -2044,6 +2062,65 @@ public class DashManifestParser extends DefaultHandler
}
}
+ /**
+ * Parses the number of channels from the value attribute of an AudioChannelConfiguration with
+ * schemeIdUri "tag:dolby.com,2015:dash:audio_channel_configuration:2015" as defined by table A.27
+ * in ETSI TS 103 190-2 v1.2.1.
+ *
+ * @param xpp The parser from which to read.
+ * @param codecs The codecs string from the current element being parsed.
+ * @return The parsed number of channels, or {@link Format#NO_VALUE} if the channel count could
+ * not be parsed.
+ */
+ protected static int parseDolbyAC4ChannelConfiguration(XmlPullParser xpp, String codecs) {
+ @Nullable String value = xpp.getAttributeValue(null, "value");
+ // The value attribute must be set to a six-digit uppercase hexadecimal string.
+ if (value == null || value.length() != 6) {
+ return Format.NO_VALUE;
+ }
+
+ // Mask for bit 23, indicating object-based audio (See E.10.14 presentation_channel_mask_v1).
+ int objectBasedAudioFlag = 1 << 23;
+ int ac4ChannelMask = Integer.parseInt(value, /* radix= */ 16);
+ if ((ac4ChannelMask & objectBasedAudioFlag) != 0) {
+ // object-based audio content.
+ return parseDolbyAc4ObjectBasedChannelConfiguration(codecs);
+ }
+ // channel-based audio content.
+ return parseDolbyAc4ChannelBasedChannelConfiguration(ac4ChannelMask);
+ }
+
+ private static int parseDolbyAc4ObjectBasedChannelConfiguration(String codecs) {
+ String[] codecList = Util.splitCodecs(codecs);
+ if (codecList.length == 0) {
+ return Format.NO_VALUE;
+ }
+
+ // The AC-4 codec string format is "ac-4.xx.yy.zz", where zz is presentation level.
+ List parts = Splitter.on('.').splitToList(Ascii.toLowerCase(codecList[0].trim()));
+ if (parts.size() != 4 || !parts.get(0).equals("ac-4")) {
+ return Format.NO_VALUE;
+ }
+
+ switch (parts.get(3)) {
+ case "03":
+ return 18; // AC-4 Level 3 object-based content is mapped to 17.1 channels.
+ case "04":
+ return 21; // AC-4 Level 4 object-based content is mapped to 20.1 channels.
+ default:
+ return Format.NO_VALUE;
+ }
+ }
+
+ private static int parseDolbyAc4ChannelBasedChannelConfiguration(int ac4ChannelMask) {
+ // Bits 0...18 indicate the presence of individual channel groups.
+ int channelCount = 0;
+ for (int i = 0; i < DOLBY_AC4_CHANNEL_CONFIGURATION_MAPPING.length; i++) {
+ channelCount += ((ac4ChannelMask >> i) & 0x1) * DOLBY_AC4_CHANNEL_CONFIGURATION_MAPPING[i];
+ }
+ return channelCount == 0 ? Format.NO_VALUE : channelCount;
+ }
+
protected static long parseLastSegmentNumberSupplementalProperty(
List supplementalProperties) {
for (int i = 0; i < supplementalProperties.size(); i++) {
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 9d324eff24..5af9894930 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
@@ -110,6 +110,13 @@ public final class FragmentedMp4ExtractorParameterizedTest {
/* closedCaptionFormats= */ ImmutableList.of(), "media/mp4/sample_ac4_fragmented.mp4");
}
+ @Test
+ public void sampleWithAc4Level4Track() throws Exception {
+ assertExtractorBehavior(
+ /* closedCaptionFormats= */ ImmutableList.of(),
+ "media/mp4/sample_ac4_level4_fragmented.mp4");
+ }
+
@Test
public void sampleWithProtectedAc4Track() throws Exception {
assertExtractorBehavior(
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.0.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.0.dump
new file mode 100644
index 0000000000..5d322b9d8b
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.0.dump
@@ -0,0 +1,100 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=669]]
+ getPosition(1) = [[timeUs=0, position=669]]
+ getPosition(426666) = [[timeUs=0, position=669]]
+ getPosition(853333) = [[timeUs=0, position=669]]
+numberOfTracks = 1
+track 0:
+ total output bytes = 162700
+ sample count = 20
+ track duration = 853000
+ format 0:
+ id = 1
+ containerMimeType = audio/mp4
+ sampleMimeType = audio/ac4
+ channelCount = 21
+ sampleRate = 48000
+ language = und
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 8135, hash B524F88E
+ sample 1:
+ time = 42666
+ flags = 0
+ data = length 8135, hash FB80C2FB
+ sample 2:
+ time = 85333
+ flags = 0
+ data = length 8135, hash 907C0C31
+ sample 3:
+ time = 128000
+ flags = 0
+ data = length 8135, hash FDFBD32B
+ sample 4:
+ time = 170666
+ flags = 0
+ data = length 8135, hash 6CAF0549
+ sample 5:
+ time = 213333
+ flags = 0
+ data = length 8135, hash F5CA1C9A
+ sample 6:
+ time = 256000
+ flags = 0
+ data = length 8135, hash B1B5160D
+ sample 7:
+ time = 298666
+ flags = 0
+ data = length 8135, hash 9E923B3F
+ sample 8:
+ time = 341333
+ flags = 0
+ data = length 8135, hash B1C0BB1F
+ sample 9:
+ time = 384000
+ flags = 0
+ data = length 8135, hash 56F65A03
+ sample 10:
+ time = 426666
+ flags = 1
+ data = length 8135, hash D07FA9A1
+ sample 11:
+ time = 469333
+ flags = 0
+ data = length 8135, hash EF26FDDE
+ sample 12:
+ time = 512000
+ flags = 0
+ data = length 8135, hash 8946EEEB
+ sample 13:
+ time = 554666
+ flags = 0
+ data = length 8135, hash AC2E4C99
+ sample 14:
+ time = 597333
+ flags = 0
+ data = length 8135, hash B63A1D8
+ sample 15:
+ time = 640000
+ flags = 0
+ data = length 8135, hash 23119F0F
+ sample 16:
+ time = 682666
+ flags = 0
+ data = length 8135, hash 507972CA
+ sample 17:
+ time = 725333
+ flags = 0
+ data = length 8135, hash E574BC00
+ sample 18:
+ time = 768000
+ flags = 0
+ data = length 8135, hash 52F482FA
+ sample 19:
+ time = 810666
+ flags = 0
+ data = length 8135, hash C1A7B518
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.1.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.1.dump
new file mode 100644
index 0000000000..5d322b9d8b
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.1.dump
@@ -0,0 +1,100 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=669]]
+ getPosition(1) = [[timeUs=0, position=669]]
+ getPosition(426666) = [[timeUs=0, position=669]]
+ getPosition(853333) = [[timeUs=0, position=669]]
+numberOfTracks = 1
+track 0:
+ total output bytes = 162700
+ sample count = 20
+ track duration = 853000
+ format 0:
+ id = 1
+ containerMimeType = audio/mp4
+ sampleMimeType = audio/ac4
+ channelCount = 21
+ sampleRate = 48000
+ language = und
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 8135, hash B524F88E
+ sample 1:
+ time = 42666
+ flags = 0
+ data = length 8135, hash FB80C2FB
+ sample 2:
+ time = 85333
+ flags = 0
+ data = length 8135, hash 907C0C31
+ sample 3:
+ time = 128000
+ flags = 0
+ data = length 8135, hash FDFBD32B
+ sample 4:
+ time = 170666
+ flags = 0
+ data = length 8135, hash 6CAF0549
+ sample 5:
+ time = 213333
+ flags = 0
+ data = length 8135, hash F5CA1C9A
+ sample 6:
+ time = 256000
+ flags = 0
+ data = length 8135, hash B1B5160D
+ sample 7:
+ time = 298666
+ flags = 0
+ data = length 8135, hash 9E923B3F
+ sample 8:
+ time = 341333
+ flags = 0
+ data = length 8135, hash B1C0BB1F
+ sample 9:
+ time = 384000
+ flags = 0
+ data = length 8135, hash 56F65A03
+ sample 10:
+ time = 426666
+ flags = 1
+ data = length 8135, hash D07FA9A1
+ sample 11:
+ time = 469333
+ flags = 0
+ data = length 8135, hash EF26FDDE
+ sample 12:
+ time = 512000
+ flags = 0
+ data = length 8135, hash 8946EEEB
+ sample 13:
+ time = 554666
+ flags = 0
+ data = length 8135, hash AC2E4C99
+ sample 14:
+ time = 597333
+ flags = 0
+ data = length 8135, hash B63A1D8
+ sample 15:
+ time = 640000
+ flags = 0
+ data = length 8135, hash 23119F0F
+ sample 16:
+ time = 682666
+ flags = 0
+ data = length 8135, hash 507972CA
+ sample 17:
+ time = 725333
+ flags = 0
+ data = length 8135, hash E574BC00
+ sample 18:
+ time = 768000
+ flags = 0
+ data = length 8135, hash 52F482FA
+ sample 19:
+ time = 810666
+ flags = 0
+ data = length 8135, hash C1A7B518
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.2.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.2.dump
new file mode 100644
index 0000000000..e4c4280ebd
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.2.dump
@@ -0,0 +1,60 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=669]]
+ getPosition(1) = [[timeUs=0, position=669]]
+ getPosition(426666) = [[timeUs=0, position=669]]
+ getPosition(853333) = [[timeUs=0, position=669]]
+numberOfTracks = 1
+track 0:
+ total output bytes = 81350
+ sample count = 10
+ track duration = 853000
+ format 0:
+ id = 1
+ containerMimeType = audio/mp4
+ sampleMimeType = audio/ac4
+ channelCount = 21
+ sampleRate = 48000
+ language = und
+ sample 0:
+ time = 426666
+ flags = 1
+ data = length 8135, hash D07FA9A1
+ sample 1:
+ time = 469333
+ flags = 0
+ data = length 8135, hash EF26FDDE
+ sample 2:
+ time = 512000
+ flags = 0
+ data = length 8135, hash 8946EEEB
+ sample 3:
+ time = 554666
+ flags = 0
+ data = length 8135, hash AC2E4C99
+ sample 4:
+ time = 597333
+ flags = 0
+ data = length 8135, hash B63A1D8
+ sample 5:
+ time = 640000
+ flags = 0
+ data = length 8135, hash 23119F0F
+ sample 6:
+ time = 682666
+ flags = 0
+ data = length 8135, hash 507972CA
+ sample 7:
+ time = 725333
+ flags = 0
+ data = length 8135, hash E574BC00
+ sample 8:
+ time = 768000
+ flags = 0
+ data = length 8135, hash 52F482FA
+ sample 9:
+ time = 810666
+ flags = 0
+ data = length 8135, hash C1A7B518
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.3.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.3.dump
new file mode 100644
index 0000000000..e4c4280ebd
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.3.dump
@@ -0,0 +1,60 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=669]]
+ getPosition(1) = [[timeUs=0, position=669]]
+ getPosition(426666) = [[timeUs=0, position=669]]
+ getPosition(853333) = [[timeUs=0, position=669]]
+numberOfTracks = 1
+track 0:
+ total output bytes = 81350
+ sample count = 10
+ track duration = 853000
+ format 0:
+ id = 1
+ containerMimeType = audio/mp4
+ sampleMimeType = audio/ac4
+ channelCount = 21
+ sampleRate = 48000
+ language = und
+ sample 0:
+ time = 426666
+ flags = 1
+ data = length 8135, hash D07FA9A1
+ sample 1:
+ time = 469333
+ flags = 0
+ data = length 8135, hash EF26FDDE
+ sample 2:
+ time = 512000
+ flags = 0
+ data = length 8135, hash 8946EEEB
+ sample 3:
+ time = 554666
+ flags = 0
+ data = length 8135, hash AC2E4C99
+ sample 4:
+ time = 597333
+ flags = 0
+ data = length 8135, hash B63A1D8
+ sample 5:
+ time = 640000
+ flags = 0
+ data = length 8135, hash 23119F0F
+ sample 6:
+ time = 682666
+ flags = 0
+ data = length 8135, hash 507972CA
+ sample 7:
+ time = 725333
+ flags = 0
+ data = length 8135, hash E574BC00
+ sample 8:
+ time = 768000
+ flags = 0
+ data = length 8135, hash 52F482FA
+ sample 9:
+ time = 810666
+ flags = 0
+ data = length 8135, hash C1A7B518
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.0.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.0.dump
new file mode 100644
index 0000000000..5d322b9d8b
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.0.dump
@@ -0,0 +1,100 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=669]]
+ getPosition(1) = [[timeUs=0, position=669]]
+ getPosition(426666) = [[timeUs=0, position=669]]
+ getPosition(853333) = [[timeUs=0, position=669]]
+numberOfTracks = 1
+track 0:
+ total output bytes = 162700
+ sample count = 20
+ track duration = 853000
+ format 0:
+ id = 1
+ containerMimeType = audio/mp4
+ sampleMimeType = audio/ac4
+ channelCount = 21
+ sampleRate = 48000
+ language = und
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 8135, hash B524F88E
+ sample 1:
+ time = 42666
+ flags = 0
+ data = length 8135, hash FB80C2FB
+ sample 2:
+ time = 85333
+ flags = 0
+ data = length 8135, hash 907C0C31
+ sample 3:
+ time = 128000
+ flags = 0
+ data = length 8135, hash FDFBD32B
+ sample 4:
+ time = 170666
+ flags = 0
+ data = length 8135, hash 6CAF0549
+ sample 5:
+ time = 213333
+ flags = 0
+ data = length 8135, hash F5CA1C9A
+ sample 6:
+ time = 256000
+ flags = 0
+ data = length 8135, hash B1B5160D
+ sample 7:
+ time = 298666
+ flags = 0
+ data = length 8135, hash 9E923B3F
+ sample 8:
+ time = 341333
+ flags = 0
+ data = length 8135, hash B1C0BB1F
+ sample 9:
+ time = 384000
+ flags = 0
+ data = length 8135, hash 56F65A03
+ sample 10:
+ time = 426666
+ flags = 1
+ data = length 8135, hash D07FA9A1
+ sample 11:
+ time = 469333
+ flags = 0
+ data = length 8135, hash EF26FDDE
+ sample 12:
+ time = 512000
+ flags = 0
+ data = length 8135, hash 8946EEEB
+ sample 13:
+ time = 554666
+ flags = 0
+ data = length 8135, hash AC2E4C99
+ sample 14:
+ time = 597333
+ flags = 0
+ data = length 8135, hash B63A1D8
+ sample 15:
+ time = 640000
+ flags = 0
+ data = length 8135, hash 23119F0F
+ sample 16:
+ time = 682666
+ flags = 0
+ data = length 8135, hash 507972CA
+ sample 17:
+ time = 725333
+ flags = 0
+ data = length 8135, hash E574BC00
+ sample 18:
+ time = 768000
+ flags = 0
+ data = length 8135, hash 52F482FA
+ sample 19:
+ time = 810666
+ flags = 0
+ data = length 8135, hash C1A7B518
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.1.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.1.dump
new file mode 100644
index 0000000000..5d322b9d8b
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.1.dump
@@ -0,0 +1,100 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=669]]
+ getPosition(1) = [[timeUs=0, position=669]]
+ getPosition(426666) = [[timeUs=0, position=669]]
+ getPosition(853333) = [[timeUs=0, position=669]]
+numberOfTracks = 1
+track 0:
+ total output bytes = 162700
+ sample count = 20
+ track duration = 853000
+ format 0:
+ id = 1
+ containerMimeType = audio/mp4
+ sampleMimeType = audio/ac4
+ channelCount = 21
+ sampleRate = 48000
+ language = und
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 8135, hash B524F88E
+ sample 1:
+ time = 42666
+ flags = 0
+ data = length 8135, hash FB80C2FB
+ sample 2:
+ time = 85333
+ flags = 0
+ data = length 8135, hash 907C0C31
+ sample 3:
+ time = 128000
+ flags = 0
+ data = length 8135, hash FDFBD32B
+ sample 4:
+ time = 170666
+ flags = 0
+ data = length 8135, hash 6CAF0549
+ sample 5:
+ time = 213333
+ flags = 0
+ data = length 8135, hash F5CA1C9A
+ sample 6:
+ time = 256000
+ flags = 0
+ data = length 8135, hash B1B5160D
+ sample 7:
+ time = 298666
+ flags = 0
+ data = length 8135, hash 9E923B3F
+ sample 8:
+ time = 341333
+ flags = 0
+ data = length 8135, hash B1C0BB1F
+ sample 9:
+ time = 384000
+ flags = 0
+ data = length 8135, hash 56F65A03
+ sample 10:
+ time = 426666
+ flags = 1
+ data = length 8135, hash D07FA9A1
+ sample 11:
+ time = 469333
+ flags = 0
+ data = length 8135, hash EF26FDDE
+ sample 12:
+ time = 512000
+ flags = 0
+ data = length 8135, hash 8946EEEB
+ sample 13:
+ time = 554666
+ flags = 0
+ data = length 8135, hash AC2E4C99
+ sample 14:
+ time = 597333
+ flags = 0
+ data = length 8135, hash B63A1D8
+ sample 15:
+ time = 640000
+ flags = 0
+ data = length 8135, hash 23119F0F
+ sample 16:
+ time = 682666
+ flags = 0
+ data = length 8135, hash 507972CA
+ sample 17:
+ time = 725333
+ flags = 0
+ data = length 8135, hash E574BC00
+ sample 18:
+ time = 768000
+ flags = 0
+ data = length 8135, hash 52F482FA
+ sample 19:
+ time = 810666
+ flags = 0
+ data = length 8135, hash C1A7B518
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.2.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.2.dump
new file mode 100644
index 0000000000..e4c4280ebd
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.2.dump
@@ -0,0 +1,60 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=669]]
+ getPosition(1) = [[timeUs=0, position=669]]
+ getPosition(426666) = [[timeUs=0, position=669]]
+ getPosition(853333) = [[timeUs=0, position=669]]
+numberOfTracks = 1
+track 0:
+ total output bytes = 81350
+ sample count = 10
+ track duration = 853000
+ format 0:
+ id = 1
+ containerMimeType = audio/mp4
+ sampleMimeType = audio/ac4
+ channelCount = 21
+ sampleRate = 48000
+ language = und
+ sample 0:
+ time = 426666
+ flags = 1
+ data = length 8135, hash D07FA9A1
+ sample 1:
+ time = 469333
+ flags = 0
+ data = length 8135, hash EF26FDDE
+ sample 2:
+ time = 512000
+ flags = 0
+ data = length 8135, hash 8946EEEB
+ sample 3:
+ time = 554666
+ flags = 0
+ data = length 8135, hash AC2E4C99
+ sample 4:
+ time = 597333
+ flags = 0
+ data = length 8135, hash B63A1D8
+ sample 5:
+ time = 640000
+ flags = 0
+ data = length 8135, hash 23119F0F
+ sample 6:
+ time = 682666
+ flags = 0
+ data = length 8135, hash 507972CA
+ sample 7:
+ time = 725333
+ flags = 0
+ data = length 8135, hash E574BC00
+ sample 8:
+ time = 768000
+ flags = 0
+ data = length 8135, hash 52F482FA
+ sample 9:
+ time = 810666
+ flags = 0
+ data = length 8135, hash C1A7B518
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.3.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.3.dump
new file mode 100644
index 0000000000..e4c4280ebd
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.3.dump
@@ -0,0 +1,60 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=669]]
+ getPosition(1) = [[timeUs=0, position=669]]
+ getPosition(426666) = [[timeUs=0, position=669]]
+ getPosition(853333) = [[timeUs=0, position=669]]
+numberOfTracks = 1
+track 0:
+ total output bytes = 81350
+ sample count = 10
+ track duration = 853000
+ format 0:
+ id = 1
+ containerMimeType = audio/mp4
+ sampleMimeType = audio/ac4
+ channelCount = 21
+ sampleRate = 48000
+ language = und
+ sample 0:
+ time = 426666
+ flags = 1
+ data = length 8135, hash D07FA9A1
+ sample 1:
+ time = 469333
+ flags = 0
+ data = length 8135, hash EF26FDDE
+ sample 2:
+ time = 512000
+ flags = 0
+ data = length 8135, hash 8946EEEB
+ sample 3:
+ time = 554666
+ flags = 0
+ data = length 8135, hash AC2E4C99
+ sample 4:
+ time = 597333
+ flags = 0
+ data = length 8135, hash B63A1D8
+ sample 5:
+ time = 640000
+ flags = 0
+ data = length 8135, hash 23119F0F
+ sample 6:
+ time = 682666
+ flags = 0
+ data = length 8135, hash 507972CA
+ sample 7:
+ time = 725333
+ flags = 0
+ data = length 8135, hash E574BC00
+ sample 8:
+ time = 768000
+ flags = 0
+ data = length 8135, hash 52F482FA
+ sample 9:
+ time = 810666
+ flags = 0
+ data = length 8135, hash C1A7B518
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.unknown_length.dump
new file mode 100644
index 0000000000..5d322b9d8b
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.reading_within_gop_sample_dependencies.unknown_length.dump
@@ -0,0 +1,100 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=669]]
+ getPosition(1) = [[timeUs=0, position=669]]
+ getPosition(426666) = [[timeUs=0, position=669]]
+ getPosition(853333) = [[timeUs=0, position=669]]
+numberOfTracks = 1
+track 0:
+ total output bytes = 162700
+ sample count = 20
+ track duration = 853000
+ format 0:
+ id = 1
+ containerMimeType = audio/mp4
+ sampleMimeType = audio/ac4
+ channelCount = 21
+ sampleRate = 48000
+ language = und
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 8135, hash B524F88E
+ sample 1:
+ time = 42666
+ flags = 0
+ data = length 8135, hash FB80C2FB
+ sample 2:
+ time = 85333
+ flags = 0
+ data = length 8135, hash 907C0C31
+ sample 3:
+ time = 128000
+ flags = 0
+ data = length 8135, hash FDFBD32B
+ sample 4:
+ time = 170666
+ flags = 0
+ data = length 8135, hash 6CAF0549
+ sample 5:
+ time = 213333
+ flags = 0
+ data = length 8135, hash F5CA1C9A
+ sample 6:
+ time = 256000
+ flags = 0
+ data = length 8135, hash B1B5160D
+ sample 7:
+ time = 298666
+ flags = 0
+ data = length 8135, hash 9E923B3F
+ sample 8:
+ time = 341333
+ flags = 0
+ data = length 8135, hash B1C0BB1F
+ sample 9:
+ time = 384000
+ flags = 0
+ data = length 8135, hash 56F65A03
+ sample 10:
+ time = 426666
+ flags = 1
+ data = length 8135, hash D07FA9A1
+ sample 11:
+ time = 469333
+ flags = 0
+ data = length 8135, hash EF26FDDE
+ sample 12:
+ time = 512000
+ flags = 0
+ data = length 8135, hash 8946EEEB
+ sample 13:
+ time = 554666
+ flags = 0
+ data = length 8135, hash AC2E4C99
+ sample 14:
+ time = 597333
+ flags = 0
+ data = length 8135, hash B63A1D8
+ sample 15:
+ time = 640000
+ flags = 0
+ data = length 8135, hash 23119F0F
+ sample 16:
+ time = 682666
+ flags = 0
+ data = length 8135, hash 507972CA
+ sample 17:
+ time = 725333
+ flags = 0
+ data = length 8135, hash E574BC00
+ sample 18:
+ time = 768000
+ flags = 0
+ data = length 8135, hash 52F482FA
+ sample 19:
+ time = 810666
+ flags = 0
+ data = length 8135, hash C1A7B518
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.unknown_length.dump
new file mode 100644
index 0000000000..5d322b9d8b
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_ac4_level4_fragmented.mp4.unknown_length.dump
@@ -0,0 +1,100 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=669]]
+ getPosition(1) = [[timeUs=0, position=669]]
+ getPosition(426666) = [[timeUs=0, position=669]]
+ getPosition(853333) = [[timeUs=0, position=669]]
+numberOfTracks = 1
+track 0:
+ total output bytes = 162700
+ sample count = 20
+ track duration = 853000
+ format 0:
+ id = 1
+ containerMimeType = audio/mp4
+ sampleMimeType = audio/ac4
+ channelCount = 21
+ sampleRate = 48000
+ language = und
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 8135, hash B524F88E
+ sample 1:
+ time = 42666
+ flags = 0
+ data = length 8135, hash FB80C2FB
+ sample 2:
+ time = 85333
+ flags = 0
+ data = length 8135, hash 907C0C31
+ sample 3:
+ time = 128000
+ flags = 0
+ data = length 8135, hash FDFBD32B
+ sample 4:
+ time = 170666
+ flags = 0
+ data = length 8135, hash 6CAF0549
+ sample 5:
+ time = 213333
+ flags = 0
+ data = length 8135, hash F5CA1C9A
+ sample 6:
+ time = 256000
+ flags = 0
+ data = length 8135, hash B1B5160D
+ sample 7:
+ time = 298666
+ flags = 0
+ data = length 8135, hash 9E923B3F
+ sample 8:
+ time = 341333
+ flags = 0
+ data = length 8135, hash B1C0BB1F
+ sample 9:
+ time = 384000
+ flags = 0
+ data = length 8135, hash 56F65A03
+ sample 10:
+ time = 426666
+ flags = 1
+ data = length 8135, hash D07FA9A1
+ sample 11:
+ time = 469333
+ flags = 0
+ data = length 8135, hash EF26FDDE
+ sample 12:
+ time = 512000
+ flags = 0
+ data = length 8135, hash 8946EEEB
+ sample 13:
+ time = 554666
+ flags = 0
+ data = length 8135, hash AC2E4C99
+ sample 14:
+ time = 597333
+ flags = 0
+ data = length 8135, hash B63A1D8
+ sample 15:
+ time = 640000
+ flags = 0
+ data = length 8135, hash 23119F0F
+ sample 16:
+ time = 682666
+ flags = 0
+ data = length 8135, hash 507972CA
+ sample 17:
+ time = 725333
+ flags = 0
+ data = length 8135, hash E574BC00
+ sample 18:
+ time = 768000
+ flags = 0
+ data = length 8135, hash 52F482FA
+ sample 19:
+ time = 810666
+ flags = 0
+ data = length 8135, hash C1A7B518
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/media/mp4/sample_ac4_level4_fragmented.mp4 b/libraries/test_data/src/test/assets/media/mp4/sample_ac4_level4_fragmented.mp4
new file mode 100644
index 0000000000..e0e505a43a
Binary files /dev/null and b/libraries/test_data/src/test/assets/media/mp4/sample_ac4_level4_fragmented.mp4 differ