From 27c021fd7bb827491568dca04b11a9a396d8aee9 Mon Sep 17 00:00:00 2001
From: rahulnmohan <94949796+rahulnmohan@users.noreply.github.com>
Date: Tue, 16 Jan 2024 08:51:36 -0800
Subject: [PATCH] Merge Issue: androidx/media#275: MPEG2-TS: Support DTS,
DTS-LBR and DTS:X Profile2
Imported from GitHub PR https://github.com/androidx/media/pull/275
Added below mentioned features.
- Support for extracting DTS LBR(DTS Express) and DTS UHD Profile 2(DTS:X) descriptor ID from PSI PMT
- The DTSReader class is updated for extracting a DTS LBR.
- Newly added DtsUhdReader class for extracting DTS UHD frame.
- The DTSUtil class is updated to parse the DTS LBR or DTS UHD frame and report the format information.
Feature request for ExoPlayer: https://github.com/google/ExoPlayer/issues/11075
Merge 21efa0810db31550d6b215639f9ca2af6a32139a into 104cfc322c095b40f88e705eb4a6c2f029bacdd6
COPYBARA_INTEGRATE_REVIEW=https://github.com/androidx/media/pull/275 from rahulnmohan:dts-mpeg2ts-update 21efa0810db31550d6b215639f9ca2af6a32139a
PiperOrigin-RevId: 598854998
---
RELEASENOTES.md | 2 +
.../androidx/media3/common/util/Util.java | 55 +
.../androidx/media3/extractor/DtsUtil.java | 567 ++++++++++-
.../ts/DefaultTsPayloadReaderFactory.java | 5 +-
.../media3/extractor/ts/DtsReader.java | 180 +++-
.../media3/extractor/ts/TsExtractor.java | 10 +
.../media3/extractor/ts/TsExtractorTest.java | 24 +
.../extractordumps/ts/sample_dts.ts.0.dump | 194 ++++
.../extractordumps/ts/sample_dts.ts.1.dump | 138 +++
.../extractordumps/ts/sample_dts.ts.2.dump | 82 ++
.../extractordumps/ts/sample_dts.ts.3.dump | 26 +
.../ts/sample_dts.ts.unknown_length.dump | 191 ++++
.../extractordumps/ts/sample_dts_hd.ts.0.dump | 61 ++
.../extractordumps/ts/sample_dts_hd.ts.1.dump | 49 +
.../extractordumps/ts/sample_dts_hd.ts.2.dump | 37 +
.../extractordumps/ts/sample_dts_hd.ts.3.dump | 17 +
.../ts/sample_dts_hd.ts.unknown_length.dump | 58 ++
.../ts/sample_dts_uhd.ts.0.dump | 953 ++++++++++++++++++
.../ts/sample_dts_uhd.ts.1.dump | 661 ++++++++++++
.../ts/sample_dts_uhd.ts.2.dump | 349 +++++++
.../ts/sample_dts_uhd.ts.3.dump | 37 +
.../ts/sample_dts_uhd.ts.unknown_length.dump | 950 +++++++++++++++++
.../src/test/assets/media/ts/sample_dts.ts | Bin 0 -> 50008 bytes
.../src/test/assets/media/ts/sample_dts_hd.ts | Bin 0 -> 47940 bytes
.../test/assets/media/ts/sample_dts_uhd.ts | Bin 0 -> 215448 bytes
25 files changed, 4582 insertions(+), 64 deletions(-)
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.0.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.1.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.2.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.3.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.unknown_length.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.0.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.1.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.2.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.3.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.unknown_length.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.0.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.1.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.2.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.3.dump
create mode 100644 libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.unknown_length.dump
create mode 100644 libraries/test_data/src/test/assets/media/ts/sample_dts.ts
create mode 100644 libraries/test_data/src/test/assets/media/ts/sample_dts_hd.ts
create mode 100644 libraries/test_data/src/test/assets/media/ts/sample_dts_uhd.ts
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 3304423334..8f8dd5756c 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -17,6 +17,8 @@
(the CBR equivalent of the `Xing` header). Previously we used the seek
table from the `Info` header, but this results in less precise seeking
than if we ignore it and assume the file is CBR.
+ * MPEG2-TS: Add DTS, DTS-LBR and DTS:X Profile2 support
+ ([#275](https://github.com/androidx/media/pull/275)).
* Audio:
* Video:
* Text:
diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java
index 561d878043..8ef7669962 100644
--- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java
+++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java
@@ -94,6 +94,7 @@ import com.google.common.base.Ascii;
import com.google.common.base.Charsets;
import com.google.common.math.DoubleMath;
import com.google.common.math.LongMath;
+import com.google.common.primitives.UnsignedBytes;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -2825,6 +2826,50 @@ public final class Util {
return initialValue;
}
+ /**
+ * Returns the result of updating a CRC-16 with the specified bytes in a "most significant bit
+ * first" order.
+ *
+ * @param bytes Array containing the bytes to update the crc value with.
+ * @param start The start index (inclusive) of the byte range to update the crc with.
+ * @param end The end index (exclusive) of the byte range to update the crc with.
+ * @param initialValue The initial value for the crc calculation. The lower 16 bits of this 32-bit
+ * integer are used for the CRC computation.
+ * @return The result of updating the initial value with the specified bytes.
+ */
+ @UnstableApi
+ public static int crc16(byte[] bytes, int start, int end, int initialValue) {
+ for (int i = start; i < end; i++) {
+ int value = UnsignedBytes.toInt(bytes[i]);
+ // Process one message byte to update the current CRC-16 value.
+ initialValue = crc16UpdateFourBits(value >> 4, initialValue); // High nibble first.
+ initialValue = crc16UpdateFourBits(value & 0x0F, initialValue); // Low nibble.
+ }
+ return initialValue;
+ }
+
+ /**
+ * Process 4 bits of the message to update the CRC Value. Note that the data will be in the low
+ * nibble of value.
+ *
+ * @param value The 4-bit message data to be processed.
+ * @param crc16Register The current CRC-16 register to be updated. Only the lower 16 bits of this
+ * 32-bit integer are used for the CRC computation.
+ * @return The result of updating the CRC-16 register with the specified 4-bit message data.
+ */
+ private static int crc16UpdateFourBits(int value, int crc16Register) {
+ // Step one, extract the most significant 4 bits of the CRC register.
+ int mostSignificant4Bits = (crc16Register >> 12) & 0xFF;
+ // XOR in the Message Data into the extracted bits.
+ mostSignificant4Bits = (mostSignificant4Bits ^ value) & 0xFF;
+ // Shift the CRC register left 4 bits.
+ crc16Register = (crc16Register << 4) & 0xFFFF; // Handle as 16 bit, discard any sign extension.
+ // Do the table look-ups and XOR the result into the CRC tables.
+ crc16Register = (crc16Register ^ CRC16_BYTES_MSBF[mostSignificant4Bits]) & 0xFFFF;
+
+ return crc16Register;
+ }
+
/**
* Returns the result of updating a CRC-8 with the specified bytes in a "most significant bit
* first" order.
@@ -3810,6 +3855,16 @@ public final class Util {
0XBCB4666D, 0XB8757BDA, 0XB5365D03, 0XB1F740B4
};
+ /**
+ * Allows the CRC-16 calculation to be done byte by byte instead of bit per bit in the order "most
+ * significant bit first".
+ */
+ private static final int[] CRC16_BYTES_MSBF =
+ new int[] {
+ 0x0000, 0x01021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+ 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF
+ };
+
/**
* Allows the CRC-8 calculation to be done byte by byte instead of bit per bit in the order "most
* significant bit first".
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java b/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java
index 9bda1c9f30..2c2b8cf0bd 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java
@@ -15,19 +15,124 @@
*/
package androidx.media3.extractor;
+import static java.lang.annotation.ElementType.TYPE_USE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
+import androidx.annotation.StringDef;
+import androidx.media3.common.C;
import androidx.media3.common.DrmInitData;
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
+import androidx.media3.common.ParserException;
import androidx.media3.common.util.ParsableBitArray;
import androidx.media3.common.util.UnstableApi;
+import androidx.media3.common.util.Util;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicInteger;
/** Utility methods for parsing DTS frames. */
@UnstableApi
public final class DtsUtil {
+ /** Information parsed from a DTS frame header. */
+ public static final class DtsHeader {
+ /** The mime type of the DTS bitstream. */
+ public final @DtsAudioMimeType String mimeType;
+
+ /** The audio sampling rate in Hertz, or {@link C#RATE_UNSET_INT} if unknown. */
+ public final int sampleRate;
+
+ /** The number of channels, or {@link C#LENGTH_UNSET} if unknown. */
+ public final int channelCount;
+
+ /** The size of the DTS frame (compressed), in bytes. */
+ public final int frameSize;
+
+ /** The duration of the DTS frame in microseconds, or {@link C#TIME_UNSET} if unknown. */
+ public final long frameDurationUs;
+
+ /** The bitrate of compressed stream. */
+ public final int bitrate;
+
+ private DtsHeader(
+ String mimeType,
+ int channelCount,
+ int sampleRate,
+ int frameSize,
+ long frameDurationUs,
+ int bitrate) {
+ this.mimeType = mimeType;
+ this.channelCount = channelCount;
+ this.sampleRate = sampleRate;
+ this.frameSize = frameSize;
+ this.frameDurationUs = frameDurationUs;
+ this.bitrate = bitrate;
+ }
+ }
+
+ /**
+ * The possible MIME types for DTS that can be used.
+ *
+ *
One of:
+ *
+ *
+ * - {@link MimeTypes#AUDIO_DTS}
+ *
- {@link MimeTypes#AUDIO_DTS_EXPRESS}
+ *
- {@link MimeTypes#AUDIO_DTS_X}
+ *
+ */
+ @Documented
+ @Retention(SOURCE)
+ @Target(TYPE_USE)
+ @StringDef({MimeTypes.AUDIO_DTS, MimeTypes.AUDIO_DTS_EXPRESS, MimeTypes.AUDIO_DTS_X})
+ public @interface DtsAudioMimeType {}
+
+ /**
+ * Frame types for a DTS stream.
+ *
+ * One of:
+ *
+ *
+ * - {@link #FRAME_TYPE_UNKNOWN}
+ *
- {@link #FRAME_TYPE_CORE}
+ *
- {@link #FRAME_TYPE_EXTENSION_SUBSTREAM}
+ *
- {@link #FRAME_TYPE_UHD_SYNC}
+ *
- {@link #FRAME_TYPE_UHD_NON_SYNC}
+ *
+ */
+ @Documented
+ @Retention(SOURCE)
+ @Target(TYPE_USE)
+ @IntDef({
+ FRAME_TYPE_UNKNOWN,
+ FRAME_TYPE_CORE,
+ FRAME_TYPE_EXTENSION_SUBSTREAM,
+ FRAME_TYPE_UHD_SYNC,
+ FRAME_TYPE_UHD_NON_SYNC
+ })
+ public @interface FrameType {}
+
+ /** Represents a DTS frame for which type is unknown. */
+ public static final int FRAME_TYPE_UNKNOWN = 0;
+
+ /** Represents a DTS core frame. */
+ public static final int FRAME_TYPE_CORE = 1;
+
+ /** Represents a DTS extension substream frame. */
+ public static final int FRAME_TYPE_EXTENSION_SUBSTREAM = 2;
+
+ /** Represents a DTS UHD sync frame. */
+ public static final int FRAME_TYPE_UHD_SYNC = 3;
+
+ /** Represents a DTS UHD non-sync frame. */
+ public static final int FRAME_TYPE_UHD_NON_SYNC = 4;
+
/**
* Maximum rate for a DTS audio stream, in bytes per second.
*
@@ -38,7 +143,11 @@ public final class DtsUtil {
/** Maximum rate for a DTS-HD audio stream, in bytes per second. */
public static final int DTS_HD_MAX_RATE_BYTES_PER_SECOND = 18000 * 1000 / 8;
+ /**
+ * DTS Core Syncword (in different Endianness). See ETSI TS 102 114 V1.6.1 (2019-08), Section 5.3.
+ */
private static final int SYNC_VALUE_BE = 0x7FFE8001;
+
private static final int SYNC_VALUE_14B_BE = 0x1FFFE800;
private static final int SYNC_VALUE_LE = 0xFE7F0180;
private static final int SYNC_VALUE_14B_LE = 0xFF1F00E8;
@@ -47,18 +156,34 @@ public final class DtsUtil {
* DTS Extension Substream Syncword (in different Endianness). See ETSI TS 102 114 (V1.6.1)
* Section 7.4.1.
*/
- private static final int SYNC_EXT_SUB_LE = 0x25205864;
+ private static final int SYNC_VALUE_EXTSS_BE = 0x64582025;
+
+ private static final int SYNC_VALUE_EXTSS_LE = 0x25205864;
/**
- * DTS FTOC Sync words (in different Endianness). See ETSI TS 103 491 (V1.2.1) Section 6.4.4.1.
+ * DTS UHD FTOC Sync words (in different Endianness). See ETSI TS 103 491 (V1.2.1) Section
+ * 6.4.4.1.
*/
- private static final int SYNC_FTOC_LE = 0xF21B4140;
+ private static final int SYNC_VALUE_UHD_FTOC_SYNC_BE = 0x40411BF2;
+
+ private static final int SYNC_VALUE_UHD_FTOC_SYNC_LE = 0xF21B4140;
+ private static final int SYNC_VALUE_UHD_FTOC_NONSYNC_BE = 0x71C442E8;
+ private static final int SYNC_VALUE_UHD_FTOC_NONSYNC_LE = 0xE842C471;
- private static final int SYNC_FTOC_NON_SYNC_LE = 0xE842C471;
private static final byte FIRST_BYTE_BE = (byte) (SYNC_VALUE_BE >>> 24);
private static final byte FIRST_BYTE_14B_BE = (byte) (SYNC_VALUE_14B_BE >>> 24);
private static final byte FIRST_BYTE_LE = (byte) (SYNC_VALUE_LE >>> 24);
private static final byte FIRST_BYTE_14B_LE = (byte) (SYNC_VALUE_14B_LE >>> 24);
+ private static final byte FIRST_BYTE_EXTSS_BE = (byte) (SYNC_VALUE_EXTSS_BE >>> 24);
+ private static final byte FIRST_BYTE_EXTSS_LE = (byte) (SYNC_VALUE_EXTSS_LE >>> 24);
+ private static final byte FIRST_BYTE_UHD_FTOC_SYNC_BE =
+ (byte) (SYNC_VALUE_UHD_FTOC_SYNC_BE >>> 24);
+ private static final byte FIRST_BYTE_UHD_FTOC_SYNC_LE =
+ (byte) (SYNC_VALUE_UHD_FTOC_SYNC_LE >>> 24);
+ private static final byte FIRST_BYTE_UHD_FTOC_NONSYNC_BE =
+ (byte) (SYNC_VALUE_UHD_FTOC_NONSYNC_BE >>> 24);
+ private static final byte FIRST_BYTE_UHD_FTOC_NONSYNC_LE =
+ (byte) (SYNC_VALUE_UHD_FTOC_NONSYNC_LE >>> 24);
/** Maps AMODE to the number of channels. See ETSI TS 102 114 table 5-4. */
private static final int[] CHANNELS_BY_AMODE =
@@ -79,24 +204,58 @@ public final class DtsUtil {
};
/**
- * Returns whether a given integer matches a DTS sync word. Synchronization and storage modes are
- * defined in ETSI TS 102 114 V1.1.1 (2002-08), Section 5.3.
- *
- * @param word An integer.
- * @return Whether a given integer matches a DTS sync word.
+ * Maps MaxSampleRate index to sampling frequency in Hz. See ETSI TS 102 114 V1.6.1 (2019-08)
+ * Table 7-9.
*/
- public static boolean isSyncWord(int word) {
- return word == SYNC_VALUE_BE
+ private static final int[] SAMPLE_RATE_BY_INDEX =
+ new int[] {
+ 8_000, 16_000, 32_000, 64_000, 128_000, 22_050, 44_100, 88_200, 176_400, 352_800, 12_000,
+ 24_000, 48_000, 96_000, 192_000, 384_000
+ };
+
+ /**
+ * Payload length table for DTS UHD FTOC messages. See ETSI TS 103 491 V1.2.1 (2019-05), Section
+ * 6.4.3.
+ */
+ private static final int[] UHD_FTOC_PAYLOAD_LENGTH_TABLE = new int[] {5, 8, 10, 12};
+
+ /** Metadata chunk size length table for DTS UHD. See ETSI TS 103 491 V1.2.1, Table 6-20. */
+ private static final int[] UHD_METADATA_CHUNK_SIZE_LENGTH_TABLE = new int[] {6, 9, 12, 15};
+
+ /** Audio chunk ID length table for DTS UHD. See ETSI TS 103 491 V1.2.1, Section 6.4.14.4. */
+ private static final int[] UHD_AUDIO_CHUNK_ID_LENGTH_TABLE = new int[] {2, 4, 6, 8};
+
+ /** Audio chunk size length table for DTS UHD. See ETSI TS 103 491 V1.2.1, Section 6.4.14.4. */
+ private static final int[] UHD_AUDIO_CHUNK_SIZE_LENGTH_TABLE = new int[] {9, 11, 13, 16};
+
+ /** Header size length table for DTS UHD. See ETSI TS 103 491 V1.2.1 (2019-05), Section 6.4.3. */
+ private static final int[] UHD_HEADER_SIZE_LENGTH_TABLE = new int[] {5, 8, 10, 12};
+
+ /**
+ * Returns the {@link FrameType} if {@code word} is a DTS sync word, otherwise {@link
+ * #FRAME_TYPE_UNKNOWN}.
+ */
+ public static @FrameType int getFrameType(int word) {
+ if (word == SYNC_VALUE_BE
|| word == SYNC_VALUE_LE
|| word == SYNC_VALUE_14B_BE
- || word == SYNC_VALUE_14B_LE;
+ || word == SYNC_VALUE_14B_LE) {
+ return FRAME_TYPE_CORE;
+ } else if (word == SYNC_VALUE_EXTSS_BE || word == SYNC_VALUE_EXTSS_LE) {
+ return FRAME_TYPE_EXTENSION_SUBSTREAM;
+ } else if (word == SYNC_VALUE_UHD_FTOC_SYNC_BE || word == SYNC_VALUE_UHD_FTOC_SYNC_LE) {
+ return FRAME_TYPE_UHD_SYNC;
+ } else if (word == SYNC_VALUE_UHD_FTOC_NONSYNC_BE || word == SYNC_VALUE_UHD_FTOC_NONSYNC_LE) {
+ return FRAME_TYPE_UHD_NON_SYNC;
+ }
+ return FRAME_TYPE_UNKNOWN;
}
/**
- * Returns the DTS format given {@code data} containing the DTS frame according to ETSI TS 102 114
- * subsections 5.3/5.4.
+ * Returns the DTS format given {@code data} containing the DTS Core frame according to ETSI TS
+ * 102 114 V1.6.1 (2019-08) subsections 5.3/5.4.
*
- * @param frame The DTS frame to parse.
+ * @param frame The DTS Core frame to parse.
* @param trackId The track identifier to set on the format.
* @param language The language to set on the format.
* @param drmInitData {@link DrmInitData} to be included in the format.
@@ -107,7 +266,7 @@ public final class DtsUtil {
@Nullable String trackId,
@Nullable String language,
@Nullable DrmInitData drmInitData) {
- ParsableBitArray frameBits = getNormalizedFrameHeader(frame);
+ ParsableBitArray frameBits = getNormalizedFrame(frame);
frameBits.skipBits(32 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE
int amode = frameBits.readBits(6);
int channelCount = CHANNELS_BY_AMODE[amode];
@@ -132,7 +291,7 @@ public final class DtsUtil {
}
/**
- * Returns the number of audio samples represented by the given DTS frame.
+ * Returns the number of audio samples represented by the given DTS Core frame.
*
* @param data The frame to parse.
* @return The number of audio samples represented by the frame.
@@ -164,11 +323,12 @@ public final class DtsUtil {
* @return The number of audio samples represented by the syncframe.
*/
public static int parseDtsAudioSampleCount(ByteBuffer buffer) {
- if ((buffer.getInt(0) == SYNC_FTOC_LE) || (buffer.getInt(0) == SYNC_FTOC_NON_SYNC_LE)) {
+ if ((buffer.getInt(0) == SYNC_VALUE_UHD_FTOC_SYNC_LE)
+ || (buffer.getInt(0) == SYNC_VALUE_UHD_FTOC_NONSYNC_LE)) {
// Check for DTS:X Profile 2 sync or non sync word and return 1024 if found. This is the only
// audio sample count that is used by DTS:X Streaming Encoder.
return 1024;
- } else if (buffer.getInt(0) == SYNC_EXT_SUB_LE) {
+ } else if (buffer.getInt(0) == SYNC_VALUE_EXTSS_LE) {
// Check for DTS Express sync word and return 4096 if found. This is the only audio sample
// count that is used by DTS Streaming Encoder.
return 4096;
@@ -195,7 +355,7 @@ public final class DtsUtil {
}
/**
- * Returns the size in bytes of the given DTS frame.
+ * Returns the size in bytes of the given DTS Core frame.
*
* @param data The frame to parse.
* @return The frame's size in bytes.
@@ -224,36 +384,369 @@ public final class DtsUtil {
return uses14BitPerWord ? fsize * 16 / 14 : fsize;
}
- private static ParsableBitArray getNormalizedFrameHeader(byte[] frameHeader) {
- if (frameHeader[0] == FIRST_BYTE_BE) {
- // The frame is already 16-bit mode, big endian.
- return new ParsableBitArray(frameHeader);
+ /**
+ * Parses the {@link DtsHeader} data from the extension substream header of a DTS-HD frame
+ * according to ETSI TS 102 114 V1.6.1 (2019-08), Section 7.5.2.
+ *
+ * @param header The DTS-HD extension substream header to parse.
+ * @return The {@link DtsHeader} data extracted from the header.
+ */
+ public static DtsHeader parseDtsHdHeader(byte[] header) throws ParserException {
+ ParsableBitArray headerBits = getNormalizedFrame(header);
+ headerBits.skipBits(32 + 8); // SYNCEXTSSH, UserDefinedBits
+
+ int extensionSubstreamIndex = headerBits.readBits(2); // nExtSSIndex
+ int headerSizeInBits; // nuBits4Header
+ int extensionSubstreamFrameSizeBits; // nuBits4ExSSFsize
+ if (!headerBits.readBit()) { // bHeaderSizeType
+ headerSizeInBits = 8;
+ extensionSubstreamFrameSizeBits = 16;
+ } else {
+ headerSizeInBits = 12;
+ extensionSubstreamFrameSizeBits = 20;
}
- // Data is not normalized, but we don't want to modify frameHeader.
- frameHeader = Arrays.copyOf(frameHeader, frameHeader.length);
- if (isLittleEndianFrameHeader(frameHeader)) {
- // Change endianness.
- for (int i = 0; i < frameHeader.length - 1; i += 2) {
- byte temp = frameHeader[i];
- frameHeader[i] = frameHeader[i + 1];
- frameHeader[i + 1] = temp;
+ headerBits.skipBits(headerSizeInBits); // nuExtSSHeaderSize
+ int extensionSubstreamFrameSize =
+ headerBits.readBits(extensionSubstreamFrameSizeBits) + 1; // nuExtSSFsize
+
+ int assetsCount; // nuNumAssets
+ int referenceClockCode; // nuRefClockCode
+ int extensionSubstreamFrameDurationCode; // nuExSSFrameDurationCode
+
+ boolean staticFieldsPresent = headerBits.readBit(); // bStaticFieldsPresent
+ if (staticFieldsPresent) {
+ referenceClockCode = headerBits.readBits(2);
+ extensionSubstreamFrameDurationCode = 512 * (headerBits.readBits(3) + 1);
+
+ if (headerBits.readBit()) { // bTimeStampFlag
+ headerBits.skipBits(32 + 4); // nuTimeStamp, nLSB
+ }
+
+ int audioPresentationsCount = headerBits.readBits(3) + 1; // nuNumAudioPresnt
+ assetsCount = headerBits.readBits(3) + 1;
+ if (audioPresentationsCount != 1 || assetsCount != 1) {
+ throw ParserException.createForUnsupportedContainerFeature(
+ /* message= */ "Multiple audio presentations or assets not supported");
+ }
+
+ // We've already asserted audioPresentationsCount = 1.
+ int activeExtensionSubstreamMask =
+ headerBits.readBits(extensionSubstreamIndex + 1); // nuActiveExSSMask
+
+ for (int i = 0; i < extensionSubstreamIndex + 1; i++) {
+ if (((activeExtensionSubstreamMask >> i) & 0x1) == 1) {
+ headerBits.skipBits(8); // nuActiveAssetMask
+ }
+ }
+
+ if (headerBits.readBit()) { // bMixMetadataEnbl
+ headerBits.skipBits(2); // nuMixMetadataAdjLevel
+ int mixerOutputMaskBits = (headerBits.readBits(2) + 1) << 2; // nuBits4MixOutMask
+ int mixerOutputConfigurationCount = headerBits.readBits(2) + 1; // nuNumMixOutConfigs
+ // Output Mixing Configuration Loop
+ for (int i = 0; i < mixerOutputConfigurationCount; i++) {
+ headerBits.skipBits(mixerOutputMaskBits); // nuMixOutChMask
+ }
+ }
+ } else {
+ // Assignments below are placeholders and will never be used as they are only relevant when
+ // staticFieldsPresent == true. Initialised here to keep the compiler happy.
+ referenceClockCode = C.INDEX_UNSET;
+ extensionSubstreamFrameDurationCode = 0;
+ }
+
+ // We've already asserted assetsCount = 1.
+ headerBits.skipBits(extensionSubstreamFrameSizeBits); // nuAssetFsize
+ int sampleRate = C.RATE_UNSET_INT;
+ int channelCount = C.LENGTH_UNSET; // nuTotalNumChs
+
+ // Asset descriptor, see ETSI TS 102 114 V1.6.1 (2019-08) Table 7-5.
+ headerBits.skipBits(9 + 3); // nuAssetDescriptFsize, nuAssetIndex
+ if (staticFieldsPresent) {
+ if (headerBits.readBit()) { // bAssetTypeDescrPresent
+ headerBits.skipBits(4); // nuAssetTypeDescriptor
+ }
+ if (headerBits.readBit()) { // bLanguageDescrPresent
+ headerBits.skipBits(24); // LanguageDescriptor
+ }
+ if (headerBits.readBit()) { // bInfoTextPresent
+ int infoTextByteSize = headerBits.readBits(10) + 1; // nuInfoTextByteSize
+ headerBits.skipBytes(infoTextByteSize); // InfoTextString
+ }
+ headerBits.skipBits(5); // nuBitResolution
+ sampleRate = SAMPLE_RATE_BY_INDEX[headerBits.readBits(4)]; // nuMaxSampleRate
+ channelCount = headerBits.readBits(8) + 1;
+ // Done reading necessary bits, ignoring the rest.
+ }
+
+ long frameDurationUs = C.TIME_UNSET;
+ if (staticFieldsPresent) {
+ int referenceClockFrequency;
+ // ETSI TS 102 114 V1.6.1 (2019-08) Table 7-3.
+ switch (referenceClockCode) {
+ case 0:
+ referenceClockFrequency = 32_000;
+ break;
+ case 1:
+ referenceClockFrequency = 44_100;
+ break;
+ case 2:
+ referenceClockFrequency = 48_000;
+ break;
+ default:
+ throw ParserException.createForMalformedContainer(
+ /* message= */ "Unsupported reference clock code in DTS HD header: "
+ + referenceClockCode,
+ /* cause= */ null);
+ }
+ frameDurationUs =
+ Util.scaleLargeTimestamp(
+ extensionSubstreamFrameDurationCode, C.MICROS_PER_SECOND, referenceClockFrequency);
+ }
+ return new DtsHeader(
+ MimeTypes.AUDIO_DTS_EXPRESS,
+ channelCount,
+ sampleRate,
+ extensionSubstreamFrameSize,
+ frameDurationUs,
+ /* bitrate= */ 0);
+ }
+
+ /**
+ * Returns the size of the extension substream header in a DTS-HD frame according to ETSI TS 102
+ * 114 V1.6.1 (2019-08), Section 7.5.2.
+ *
+ * @param headerPrefix A byte array containing at least the first 55 bits of a DTS-HD frame.
+ * @return Size of the DTS-HD frame header in bytes.
+ */
+ public static int parseDtsHdHeaderSize(byte[] headerPrefix) {
+ ParsableBitArray headerPrefixBits = getNormalizedFrame(headerPrefix);
+ headerPrefixBits.skipBits(32 + 8 + 2); // SYNCEXTSSH, UserDefinedBits, nExtSSIndex
+ // Unpack the num of bits to be used to read header size
+ int headerBits = headerPrefixBits.readBit() ? 12 : 8; // bHeaderSizeType
+ // Unpack the substream header size
+ return headerPrefixBits.readBits(headerBits) + 1; // nuExtSSHeaderSize
+ }
+
+ /**
+ * Parses the {@link DtsHeader} data from the headers of a DTS-UHD(Profile 2) frame according to
+ * ETSI TS 103 491 V1.2.1 (2019-05), Section 6.4.3.
+ *
+ * @param header The DTS-UHD header to parse.
+ * @param uhdAudioChunkId An {@link AtomicInteger} containing the last read UHD audio chunk ID
+ * from a synchronized frame, or zero if unset. This parameter is both an input and output
+ * parameter. In synchronized frames, the input value is not used; instead, the parameter is
+ * set to the current UHD audio chunk ID, which becomes the output value. For non-synchronized
+ * frames, it is used without any modification.
+ * @return The {@link DtsHeader} data extracted from the header.
+ */
+ public static DtsHeader parseDtsUhdHeader(byte[] header, AtomicInteger uhdAudioChunkId)
+ throws ParserException {
+ ParsableBitArray headerBits = getNormalizedFrame(header);
+ int syncWord = headerBits.readBits(32);
+ boolean syncFrameFlag = syncWord == SYNC_VALUE_UHD_FTOC_SYNC_BE;
+
+ int ftocPayloadInBytes =
+ parseUnsignedVarInt(
+ headerBits, UHD_FTOC_PAYLOAD_LENGTH_TABLE, /* extractAndAddFlag= */ true)
+ + 1;
+
+ // ETSI TS 103 491 V1.2.1, Section 6.4.5.
+ int sampleRate = C.RATE_UNSET_INT; // m_unAudioSamplRate
+ long frameDurationUs = C.TIME_UNSET;
+ if (syncFrameFlag) {
+ // ETSI TS 103 491 V1.2.1, Section 6.4.6.1.
+ if (!headerBits.readBit()) { // m_bFullChannelBasedMixFlag
+ throw ParserException.createForUnsupportedContainerFeature(
+ /* message= */ "Only supports full channel mask-based audio presentation");
+ }
+
+ // ETSI TS 103 491 V1.2.1, Section 6.4.6.2.
+ checkCrc(header, ftocPayloadInBytes);
+
+ int baseDurationIndex = headerBits.readBits(2);
+ int baseDuration; // m_unBaseDuration
+ // ETSI TS 103 491 V1.2.1 (2019-05) Table 6-13.
+ switch (baseDurationIndex) {
+ case 0:
+ baseDuration = 512;
+ break;
+ case 1:
+ baseDuration = 480;
+ break;
+ case 2:
+ baseDuration = 384;
+ break;
+ default:
+ throw ParserException.createForMalformedContainer(
+ /* message= */ "Unsupported base duration index in DTS UHD header: "
+ + baseDurationIndex,
+ /* cause= */ null);
+ }
+ int frameDurationInClockPeriods =
+ baseDuration * (headerBits.readBits(3) + 1); // m_unFrameDuration
+ int clockRateIndex = headerBits.readBits(2);
+ int clockRateHertz; // m_unClockRateInHz
+ switch (clockRateIndex) {
+ case 0:
+ clockRateHertz = 32_000;
+ break;
+ case 1:
+ clockRateHertz = 44_100;
+ break;
+ case 2:
+ clockRateHertz = 48_000;
+ break;
+ default:
+ throw ParserException.createForMalformedContainer(
+ /* message= */ "Unsupported clock rate index in DTS UHD header: " + clockRateIndex,
+ /* cause= */ null);
+ }
+ // Skip time stamp information if present, see section 5.2.3.2.
+ if (headerBits.readBit()) { // m_bParamPresent
+ // m_bUpdateFlag == true as m_bSyncFramePredefValueExists is set to false in the encoder.
+ headerBits.skipBits(32 + 4); // m_TimeStamp
+ }
+ int sampleRateMultiplier = (1 << headerBits.readBits(2));
+ sampleRate = clockRateHertz * sampleRateMultiplier;
+ frameDurationUs =
+ Util.scaleLargeTimestamp(
+ frameDurationInClockPeriods, C.MICROS_PER_SECOND, clockRateHertz);
+ }
+
+ // ETSI TS 103 491 V1.2.1, Table 6-20.
+ // m_bFullChannelBasedMixFlag == true as we throw unsupported container feature otherwise.
+ int chunkPayloadBytes = 0;
+ int numOfMetadataChunks = syncFrameFlag ? 1 : 0; // Metadata chunks
+ for (int i = 0; i < numOfMetadataChunks; i++) {
+ int metadataChunkSize =
+ parseUnsignedVarInt(
+ headerBits, UHD_METADATA_CHUNK_SIZE_LENGTH_TABLE, /* extractAndAddFlag= */ true);
+ chunkPayloadBytes += metadataChunkSize;
+ }
+
+ // See ETSI TS 103 491 V1.2.1, Section 6.4.14.4.
+ // m_bFullChannelBasedMixFlag == true as we throw unsupported container feature otherwise.
+ int numAudioChunks = 1;
+ for (int i = 0; i < numAudioChunks; i++) {
+ // If syncFrameFlag is true the audio chunk ID will be present.
+ if (syncFrameFlag) {
+ uhdAudioChunkId.set(
+ parseUnsignedVarInt(
+ headerBits, UHD_AUDIO_CHUNK_ID_LENGTH_TABLE, /* extractAndAddFlag= */ true));
+ }
+ int audioChunkSize =
+ uhdAudioChunkId.get() != 0
+ ? parseUnsignedVarInt(
+ headerBits, UHD_AUDIO_CHUNK_SIZE_LENGTH_TABLE, /* extractAndAddFlag= */ true)
+ : 0;
+ chunkPayloadBytes += audioChunkSize;
+ }
+
+ int frameSize = ftocPayloadInBytes + chunkPayloadBytes;
+ return new DtsHeader(
+ MimeTypes.AUDIO_DTS_X,
+ // To determine the actual number of channels from a bit stream, we need to read the
+ // metadata chunk bytes. If defining a constant channel count causes problems, we can
+ // consider adding additional parsing logic for UHD frames.
+ // For now, using the estimated number of channels for DTS UHD bitstreams as 2.
+ /* channelCount= */ 2,
+ sampleRate,
+ frameSize,
+ frameDurationUs,
+ /* bitrate= */ 0);
+ }
+
+ /**
+ * Returns the size of frame header in a DTS-UHD(Profile 2) frame according to ETSI TS 103 491
+ * V1.2.1 (2019-05), Section 6.4.3.
+ *
+ * @param headerPrefix A byte array containing at least the first 47 bits of a DTS-UHD frame.
+ * @return Size of the DTS-UHD frame header in bytes.
+ */
+ public static int parseDtsUhdHeaderSize(byte[] headerPrefix) {
+ ParsableBitArray headerPrefixBits = getNormalizedFrame(headerPrefix);
+ headerPrefixBits.skipBits(32); // SYNC
+ return parseUnsignedVarInt(
+ headerPrefixBits, UHD_HEADER_SIZE_LENGTH_TABLE, /* extractAndAddFlag= */ true)
+ + 1;
+ }
+
+ /**
+ * Check if calculated and extracted CRC-16 words match. See ETSI TS 103 491 V1.2.1, Table 6-8.
+ */
+ private static void checkCrc(byte[] frame, int sizeInBytes) throws ParserException {
+ int initialValue = 0xFFFF;
+ int extractedCrc =
+ (((frame[sizeInBytes - 2] << 8) & initialValue) | (frame[sizeInBytes - 1] & 0xFF));
+ int calculatedCrc = Util.crc16(frame, /* start= */ 0, /* end= */ sizeInBytes - 2, initialValue);
+ if (extractedCrc != calculatedCrc) {
+ throw ParserException.createForMalformedContainer(
+ /* message= */ "CRC check failed", /* cause= */ null);
+ }
+ }
+
+ /**
+ * Helper function for the DTS UHD header parsing. Used to extract a field of variable length. See
+ * ETSI TS 103 491 V1.2.1, Section 5.2.3.1.
+ */
+ private static int parseUnsignedVarInt(
+ ParsableBitArray frameBits, int[] lengths, boolean extractAndAddFlag) {
+ int index = 0;
+ for (int i = 0; i < 3; i++) {
+ if (frameBits.readBit()) {
+ index++;
+ } else {
+ break;
}
}
- ParsableBitArray frameBits = new ParsableBitArray(frameHeader);
- if (frameHeader[0] == (byte) (SYNC_VALUE_14B_BE >> 24)) {
+
+ int value = 0;
+ if (extractAndAddFlag) {
+ for (int i = 0; i < index; i++) {
+ value += (1 << lengths[i]);
+ }
+ }
+ return value + frameBits.readBits(lengths[index]);
+ }
+
+ private static ParsableBitArray getNormalizedFrame(byte[] frame) {
+ if (frame[0] == FIRST_BYTE_BE
+ || frame[0] == FIRST_BYTE_EXTSS_BE
+ || frame[0] == FIRST_BYTE_UHD_FTOC_SYNC_BE
+ || frame[0] == FIRST_BYTE_UHD_FTOC_NONSYNC_BE) {
+ // The frame is already 16-bit mode, big endian.
+ return new ParsableBitArray(frame);
+ }
+ // Data is not normalized, but we don't want to modify frame.
+ frame = Arrays.copyOf(frame, frame.length);
+ if (isLittleEndianFrameHeader(frame)) {
+ // Change endianness.
+ for (int i = 0; i < frame.length - 1; i += 2) {
+ byte temp = frame[i];
+ frame[i] = frame[i + 1];
+ frame[i + 1] = temp;
+ }
+ }
+ ParsableBitArray frameBits = new ParsableBitArray(frame);
+ if (frame[0] == (byte) (SYNC_VALUE_14B_BE >> 24)) {
// Discard the 2 most significant bits of each 16 bit word.
- ParsableBitArray scratchBits = new ParsableBitArray(frameHeader);
+ ParsableBitArray scratchBits = new ParsableBitArray(frame);
while (scratchBits.bitsLeft() >= 16) {
scratchBits.skipBits(2);
frameBits.putInt(scratchBits.readBits(14), 14);
}
}
- frameBits.reset(frameHeader);
+ frameBits.reset(frame);
return frameBits;
}
private static boolean isLittleEndianFrameHeader(byte[] frameHeader) {
- return frameHeader[0] == FIRST_BYTE_LE || frameHeader[0] == FIRST_BYTE_14B_LE;
+ return frameHeader[0] == FIRST_BYTE_LE
+ || frameHeader[0] == FIRST_BYTE_14B_LE
+ || frameHeader[0] == FIRST_BYTE_EXTSS_LE
+ || frameHeader[0] == FIRST_BYTE_UHD_FTOC_SYNC_LE
+ || frameHeader[0] == FIRST_BYTE_UHD_FTOC_NONSYNC_LE;
}
private DtsUtil() {}
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java
index b55157081e..73454eb07e 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DefaultTsPayloadReaderFactory.java
@@ -171,7 +171,10 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
}
// Fall through.
case TsExtractor.TS_STREAM_TYPE_DTS:
- return new PesReader(new DtsReader(esInfo.language));
+ case TsExtractor.TS_STREAM_TYPE_DTS_HD:
+ return new PesReader(new DtsReader(esInfo.language, DtsReader.EXTSS_HEADER_SIZE_MAX));
+ case TsExtractor.TS_STREAM_TYPE_DTS_UHD:
+ return new PesReader(new DtsReader(esInfo.language, DtsReader.FTOC_MAX_HEADER_SIZE));
case TsExtractor.TS_STREAM_TYPE_H262:
case TsExtractor.TS_STREAM_TYPE_DC2_H262:
return new PesReader(new H262Reader(buildUserDataReader(esInfo)));
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DtsReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DtsReader.java
index 7dd515ce66..9c2237fb93 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DtsReader.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/DtsReader.java
@@ -16,32 +16,57 @@
package androidx.media3.extractor.ts;
import static androidx.media3.common.util.Assertions.checkState;
+import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static java.lang.Math.min;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Format;
-import androidx.media3.common.util.Assertions;
+import androidx.media3.common.ParserException;
import androidx.media3.common.util.ParsableByteArray;
import androidx.media3.common.util.UnstableApi;
+import androidx.media3.common.util.Util;
import androidx.media3.extractor.DtsUtil;
import androidx.media3.extractor.ExtractorOutput;
import androidx.media3.extractor.TrackOutput;
import androidx.media3.extractor.ts.TsPayloadReader.TrackIdGenerator;
+import com.google.common.primitives.Ints;
+import java.util.concurrent.atomic.AtomicInteger;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
-/** Parses a continuous DTS byte stream and extracts individual samples. */
+/** Parses a continuous DTS or DTS UHD byte stream and extracts individual samples. */
@UnstableApi
public final class DtsReader implements ElementaryStreamReader {
private static final int STATE_FINDING_SYNC = 0;
- private static final int STATE_READING_HEADER = 1;
- private static final int STATE_READING_SAMPLE = 2;
+ private static final int STATE_READING_CORE_HEADER = 1;
+ private static final int STATE_FINDING_EXTSS_HEADER_SIZE = 2;
+ private static final int STATE_READING_EXTSS_HEADER = 3;
+ private static final int STATE_FINDING_UHD_HEADER_SIZE = 4;
+ private static final int STATE_READING_UHD_HEADER = 5;
+ private static final int STATE_READING_SAMPLE = 6;
- private static final int HEADER_SIZE = 18;
+ /** Size of core header, in bytes. */
+ private static final int CORE_HEADER_SIZE = 18;
+
+ /**
+ * Maximum possible size of extension sub-stream header, in bytes. See See ETSI TS 102 114 V1.6.1
+ * (2019-08) Section 7.5.2.
+ */
+ /* package */ static final int EXTSS_HEADER_SIZE_MAX = 4096;
+
+ /**
+ * Maximum size of DTS UHD(DTS:X) frame header, in bytes. See ETSI TS 103 491 V1.2.1 (2019-05)
+ * Section 6.4.4.3.
+ */
+ /* package */ static final int FTOC_MAX_HEADER_SIZE = 5408;
private final ParsableByteArray headerScratchBytes;
+
+ /** The chunk ID is read in synchronized frames and re-used in non-synchronized frames. */
+ private final AtomicInteger uhdAudioChunkId;
+
@Nullable private final String language;
private @MonotonicNonNull String formatId;
@@ -50,13 +75,16 @@ public final class DtsReader implements ElementaryStreamReader {
private int state;
private int bytesRead;
- // Used to find the header.
+ /** Used to find the header. */
private int syncBytes;
// Used when parsing the header.
private long sampleDurationUs;
private @MonotonicNonNull Format format;
private int sampleSize;
+ private @DtsUtil.FrameType int frameType;
+ private int extensionSubstreamHeaderSize;
+ private int uhdHeaderSize;
// Used when reading the samples.
private long timeUs;
@@ -65,11 +93,15 @@ public final class DtsReader implements ElementaryStreamReader {
* Constructs a new reader for DTS elementary streams.
*
* @param language Track language.
+ * @param maxHeaderSize Maximum size of the header in a frame.
*/
- public DtsReader(@Nullable String language) {
- headerScratchBytes = new ParsableByteArray(new byte[HEADER_SIZE]);
+ public DtsReader(@Nullable String language, int maxHeaderSize) {
+ headerScratchBytes = new ParsableByteArray(new byte[maxHeaderSize]);
state = STATE_FINDING_SYNC;
timeUs = C.TIME_UNSET;
+ uhdAudioChunkId = new AtomicInteger();
+ extensionSubstreamHeaderSize = C.LENGTH_UNSET;
+ uhdHeaderSize = C.LENGTH_UNSET;
this.language = language;
}
@@ -79,6 +111,7 @@ public final class DtsReader implements ElementaryStreamReader {
bytesRead = 0;
syncBytes = 0;
timeUs = C.TIME_UNSET;
+ uhdAudioChunkId.set(0);
}
@Override
@@ -94,20 +127,64 @@ public final class DtsReader implements ElementaryStreamReader {
}
@Override
- public void consume(ParsableByteArray data) {
- Assertions.checkStateNotNull(output); // Asserts that createTracks has been called.
+ public void consume(ParsableByteArray data) throws ParserException {
+ checkStateNotNull(output); // Asserts that createTracks has been called.
while (data.bytesLeft() > 0) {
switch (state) {
case STATE_FINDING_SYNC:
- if (skipToNextSync(data)) {
- state = STATE_READING_HEADER;
+ if (skipToNextSyncWord(data)) {
+ if (frameType == DtsUtil.FRAME_TYPE_UHD_SYNC
+ || frameType == DtsUtil.FRAME_TYPE_UHD_NON_SYNC) {
+ state = STATE_FINDING_UHD_HEADER_SIZE;
+ } else if (frameType == DtsUtil.FRAME_TYPE_CORE) {
+ state = STATE_READING_CORE_HEADER;
+ } else {
+ state = STATE_FINDING_EXTSS_HEADER_SIZE;
+ }
}
break;
- case STATE_READING_HEADER:
- if (continueRead(data, headerScratchBytes.getData(), HEADER_SIZE)) {
- parseHeader();
+ case STATE_READING_CORE_HEADER:
+ if (continueRead(data, headerScratchBytes.getData(), CORE_HEADER_SIZE)) {
+ parseCoreHeader();
headerScratchBytes.setPosition(0);
- output.sampleData(headerScratchBytes, HEADER_SIZE);
+ output.sampleData(headerScratchBytes, CORE_HEADER_SIZE);
+ state = STATE_READING_SAMPLE;
+ }
+ break;
+ case STATE_FINDING_EXTSS_HEADER_SIZE:
+ // Read enough bytes to parse the header size information.
+ if (continueRead(data, headerScratchBytes.getData(), /* targetLength= */ 7)) {
+ extensionSubstreamHeaderSize =
+ DtsUtil.parseDtsHdHeaderSize(headerScratchBytes.getData());
+ state = STATE_READING_EXTSS_HEADER;
+ }
+ break;
+ case STATE_READING_EXTSS_HEADER:
+ if (continueRead(data, headerScratchBytes.getData(), extensionSubstreamHeaderSize)) {
+ parseExtensionSubstreamHeader();
+ headerScratchBytes.setPosition(0);
+ output.sampleData(headerScratchBytes, extensionSubstreamHeaderSize);
+ state = STATE_READING_SAMPLE;
+ }
+ break;
+ case STATE_FINDING_UHD_HEADER_SIZE:
+ // Read enough bytes to parse the header size information.
+ if (continueRead(data, headerScratchBytes.getData(), /* targetLength= */ 6)) {
+ uhdHeaderSize = DtsUtil.parseDtsUhdHeaderSize(headerScratchBytes.getData());
+ // Adjust the array read position if data read is more than the actual header size.
+ if (bytesRead > uhdHeaderSize) {
+ int extraBytes = bytesRead - uhdHeaderSize;
+ bytesRead -= extraBytes;
+ data.setPosition(data.getPosition() - extraBytes);
+ }
+ state = STATE_READING_UHD_HEADER;
+ }
+ break;
+ case STATE_READING_UHD_HEADER:
+ if (continueRead(data, headerScratchBytes.getData(), uhdHeaderSize)) {
+ parseUhdHeader();
+ headerScratchBytes.setPosition(0);
+ output.sampleData(headerScratchBytes, uhdHeaderSize);
state = STATE_READING_SAMPLE;
}
break;
@@ -118,7 +195,12 @@ public final class DtsReader implements ElementaryStreamReader {
if (bytesRead == sampleSize) {
// packetStarted method must be called before consuming samples.
checkState(timeUs != C.TIME_UNSET);
- output.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null);
+ output.sampleMetadata(
+ timeUs,
+ frameType == DtsUtil.FRAME_TYPE_UHD_NON_SYNC ? 0 : C.BUFFER_FLAG_KEY_FRAME,
+ sampleSize,
+ 0,
+ null);
timeUs += sampleDurationUs;
state = STATE_FINDING_SYNC;
}
@@ -151,17 +233,18 @@ public final class DtsReader implements ElementaryStreamReader {
}
/**
- * Locates the next SYNC value in the buffer, advancing the position to the byte that immediately
- * follows it. If SYNC was not located, the position is advanced to the limit.
+ * Locates the next SYNC word value in the buffer, advancing the position to the byte that
+ * immediately follows it. If SYNC was not located, the position is advanced to the limit.
*
* @param pesBuffer The buffer whose position should be advanced.
- * @return Whether SYNC was found.
+ * @return Whether SYNC word was found.
*/
- private boolean skipToNextSync(ParsableByteArray pesBuffer) {
+ private boolean skipToNextSyncWord(ParsableByteArray pesBuffer) {
while (pesBuffer.bytesLeft() > 0) {
syncBytes <<= 8;
syncBytes |= pesBuffer.readUnsignedByte();
- if (DtsUtil.isSyncWord(syncBytes)) {
+ frameType = DtsUtil.getFrameType(syncBytes);
+ if (frameType != DtsUtil.FRAME_TYPE_UNKNOWN) {
byte[] headerData = headerScratchBytes.getData();
headerData[0] = (byte) ((syncBytes >> 24) & 0xFF);
headerData[1] = (byte) ((syncBytes >> 16) & 0xFF);
@@ -175,9 +258,9 @@ public final class DtsReader implements ElementaryStreamReader {
return false;
}
- /** Parses the sample header. */
+ /** Parses the DTS Core Sub-stream header. */
@RequiresNonNull("output")
- private void parseHeader() {
+ private void parseCoreHeader() {
byte[] frameData = headerScratchBytes.getData();
if (format == null) {
format = DtsUtil.parseDtsFormat(frameData, formatId, language, null);
@@ -187,7 +270,52 @@ public final class DtsReader implements ElementaryStreamReader {
// In this class a sample is an access unit (frame in DTS), but the format's sample rate
// specifies the number of PCM audio samples per second.
sampleDurationUs =
- (int)
- (C.MICROS_PER_SECOND * DtsUtil.parseDtsAudioSampleCount(frameData) / format.sampleRate);
+ Ints.checkedCast(
+ Util.sampleCountToDurationUs(
+ DtsUtil.parseDtsAudioSampleCount(frameData), format.sampleRate));
+ }
+
+ /** Parses the DTS Extension Sub-stream header. */
+ @RequiresNonNull("output")
+ private void parseExtensionSubstreamHeader() throws ParserException {
+ DtsUtil.DtsHeader dtsHeader = DtsUtil.parseDtsHdHeader(headerScratchBytes.getData());
+ updateFormatWithDtsHeaderInfo(dtsHeader);
+ sampleSize = dtsHeader.frameSize;
+ sampleDurationUs = dtsHeader.frameDurationUs == C.TIME_UNSET ? 0 : dtsHeader.frameDurationUs;
+ }
+
+ /** Parses the UHD frame header. */
+ @RequiresNonNull({"output"})
+ private void parseUhdHeader() throws ParserException {
+ DtsUtil.DtsHeader dtsHeader =
+ DtsUtil.parseDtsUhdHeader(headerScratchBytes.getData(), uhdAudioChunkId);
+ // Format updates will happen only in FTOC sync frames.
+ if (frameType == DtsUtil.FRAME_TYPE_UHD_SYNC) {
+ updateFormatWithDtsHeaderInfo(dtsHeader);
+ }
+ sampleSize = dtsHeader.frameSize;
+ sampleDurationUs = dtsHeader.frameDurationUs == C.TIME_UNSET ? 0 : dtsHeader.frameDurationUs;
+ }
+
+ @RequiresNonNull({"output"})
+ private void updateFormatWithDtsHeaderInfo(DtsUtil.DtsHeader dtsHeader) {
+ if (dtsHeader.sampleRate == C.RATE_UNSET_INT || dtsHeader.channelCount == C.LENGTH_UNSET) {
+ return;
+ }
+ if (format == null
+ || dtsHeader.channelCount != format.channelCount
+ || dtsHeader.sampleRate != format.sampleRate
+ || !Util.areEqual(dtsHeader.mimeType, format.sampleMimeType)) {
+ Format.Builder formatBuilder = format == null ? new Format.Builder() : format.buildUpon();
+ format =
+ formatBuilder
+ .setId(formatId)
+ .setSampleMimeType(dtsHeader.mimeType)
+ .setChannelCount(dtsHeader.channelCount)
+ .setSampleRate(dtsHeader.sampleRate)
+ .setLanguage(language)
+ .build();
+ output.format(format);
+ }
}
}
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java
index 7ff5fa4a60..fd994de56d 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java
@@ -133,6 +133,8 @@ public final class TsExtractor implements Extractor {
public static final int TS_STREAM_TYPE_ID3 = 0x15;
public static final int TS_STREAM_TYPE_SPLICE_INFO = 0x86;
public static final int TS_STREAM_TYPE_DVBSUBS = 0x59;
+ public static final int TS_STREAM_TYPE_DTS_HD = 0x88; // As per ATSC Code Point Registry
+ public static final int TS_STREAM_TYPE_DTS_UHD = 0x8B;
// Stream types that aren't defined by the MPEG-2 TS specification.
public static final int TS_STREAM_TYPE_DC2_H262 = 0x80;
@@ -679,6 +681,8 @@ public final class TsExtractor implements Extractor {
private static final int TS_PMT_DESC_DVBSUBS = 0x59;
private static final int TS_PMT_DESC_DVB_EXT_AC4 = 0x15;
+ private static final int TS_PMT_DESC_DVB_EXT_DTS_HD = 0x0E;
+ private static final int TS_PMT_DESC_DVB_EXT_DTS_UHD = 0x21;
private final ParsableBitArray pmtScratch;
private final SparseArray<@NullableType TsPayloadReader> trackIdToReaderScratch;
@@ -868,6 +872,12 @@ public final class TsExtractor implements Extractor {
if (descriptorTagExt == TS_PMT_DESC_DVB_EXT_AC4) {
// AC-4_descriptor in DVB (ETSI EN 300 468).
streamType = TS_STREAM_TYPE_AC4;
+ } else if (descriptorTagExt == TS_PMT_DESC_DVB_EXT_DTS_HD) {
+ // DTS-HD descriptor in DVB (ETSI EN 300 468).
+ streamType = TS_STREAM_TYPE_DTS_HD;
+ } else if (descriptorTagExt == TS_PMT_DESC_DVB_EXT_DTS_UHD) {
+ // DTS-UHD descriptor in DVB (ETSI EN 300 468).
+ streamType = TS_STREAM_TYPE_DTS_UHD;
}
} else if (descriptorTag == TS_PMT_DESC_DTS) { // DTS_descriptor
streamType = TS_STREAM_TYPE_DTS;
diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/ts/TsExtractorTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/ts/TsExtractorTest.java
index 61e2633446..5658523dca 100644
--- a/libraries/extractor/src/test/java/androidx/media3/extractor/ts/TsExtractorTest.java
+++ b/libraries/extractor/src/test/java/androidx/media3/extractor/ts/TsExtractorTest.java
@@ -165,6 +165,30 @@ public final class TsExtractorTest {
simulationConfig);
}
+ @Test
+ public void sampleWithDts() throws Exception {
+ ExtractorAsserts.assertBehavior(
+ getExtractorFactory(subtitlesParsedDuringExtraction),
+ "media/ts/sample_dts.ts",
+ simulationConfig);
+ }
+
+ @Test
+ public void sampleWithDtsHd() throws Exception {
+ ExtractorAsserts.assertBehavior(
+ getExtractorFactory(subtitlesParsedDuringExtraction),
+ "media/ts/sample_dts_hd.ts",
+ simulationConfig);
+ }
+
+ @Test
+ public void sampleWithDtsUhd() throws Exception {
+ ExtractorAsserts.assertBehavior(
+ getExtractorFactory(subtitlesParsedDuringExtraction),
+ "media/ts/sample_dts_uhd.ts",
+ simulationConfig);
+ }
+
@Test
public void sampleWithAc3() throws Exception {
ExtractorAsserts.assertBehavior(
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.0.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.0.dump
new file mode 100644
index 0000000000..c365cd4e31
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.0.dump
@@ -0,0 +1,194 @@
+seekMap:
+ isSeekable = true
+ duration = 458666
+ getPosition(0) = [[timeUs=0, position=0]]
+ getPosition(1) = [[timeUs=1, position=0]]
+ getPosition(229333) = [[timeUs=229333, position=23565]]
+ getPosition(458666) = [[timeUs=458666, position=47319]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 45056
+ sample count = 44
+ format 0:
+ averageBitrate = 768000
+ id = 1/257
+ sampleMimeType = audio/vnd.dts
+ channelCount = 6
+ sampleRate = 48000
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 1024, hash 76A741FC
+ sample 1:
+ time = 10666
+ flags = 1
+ data = length 1024, hash 2DB2026F
+ sample 2:
+ time = 21333
+ flags = 1
+ data = length 1024, hash AB0CA0C0
+ sample 3:
+ time = 32000
+ flags = 1
+ data = length 1024, hash 5E448BFF
+ sample 4:
+ time = 42666
+ flags = 1
+ data = length 1024, hash 64919D7
+ sample 5:
+ time = 53333
+ flags = 1
+ data = length 1024, hash 50BF95B1
+ sample 6:
+ time = 64000
+ flags = 1
+ data = length 1024, hash AFF14A6A
+ sample 7:
+ time = 74666
+ flags = 1
+ data = length 1024, hash B0905902
+ sample 8:
+ time = 85333
+ flags = 1
+ data = length 1024, hash C09014B3
+ sample 9:
+ time = 96000
+ flags = 1
+ data = length 1024, hash 7D918717
+ sample 10:
+ time = 106666
+ flags = 1
+ data = length 1024, hash DEE7A9AA
+ sample 11:
+ time = 117333
+ flags = 1
+ data = length 1024, hash 2905D4DB
+ sample 12:
+ time = 128000
+ flags = 1
+ data = length 1024, hash 80615D30
+ sample 13:
+ time = 138666
+ flags = 1
+ data = length 1024, hash 9949F1C
+ sample 14:
+ time = 149333
+ flags = 1
+ data = length 1024, hash 322284E0
+ sample 15:
+ time = 160000
+ flags = 1
+ data = length 1024, hash F2D0A402
+ sample 16:
+ time = 170666
+ flags = 1
+ data = length 1024, hash FF8E5254
+ sample 17:
+ time = 181333
+ flags = 1
+ data = length 1024, hash 1D0A5685
+ sample 18:
+ time = 192000
+ flags = 1
+ data = length 1024, hash A7B5783E
+ sample 19:
+ time = 202666
+ flags = 1
+ data = length 1024, hash FFFA8F6A
+ sample 20:
+ time = 213333
+ flags = 1
+ data = length 1024, hash 7EEF1A48
+ sample 21:
+ time = 224000
+ flags = 1
+ data = length 1024, hash 465956B4
+ sample 22:
+ time = 234666
+ flags = 1
+ data = length 1024, hash D1987449
+ sample 23:
+ time = 245333
+ flags = 1
+ data = length 1024, hash 33001B39
+ sample 24:
+ time = 256000
+ flags = 1
+ data = length 1024, hash F1B2E5B2
+ sample 25:
+ time = 266666
+ flags = 1
+ data = length 1024, hash F3F55C7C
+ sample 26:
+ time = 277333
+ flags = 1
+ data = length 1024, hash C965813F
+ sample 27:
+ time = 288000
+ flags = 1
+ data = length 1024, hash 611AD82C
+ sample 28:
+ time = 298666
+ flags = 1
+ data = length 1024, hash 6A630F21
+ sample 29:
+ time = 309333
+ flags = 1
+ data = length 1024, hash 7B52C0BF
+ sample 30:
+ time = 320000
+ flags = 1
+ data = length 1024, hash F9105AD9
+ sample 31:
+ time = 330666
+ flags = 1
+ data = length 1024, hash 14046870
+ sample 32:
+ time = 341333
+ flags = 1
+ data = length 1024, hash 17365B01
+ sample 33:
+ time = 352000
+ flags = 1
+ data = length 1024, hash 71BE4CC6
+ sample 34:
+ time = 362666
+ flags = 1
+ data = length 1024, hash 9C9B421C
+ sample 35:
+ time = 373333
+ flags = 1
+ data = length 1024, hash 52A90B15
+ sample 36:
+ time = 384000
+ flags = 1
+ data = length 1024, hash D4722243
+ sample 37:
+ time = 394666
+ flags = 1
+ data = length 1024, hash 50819265
+ sample 38:
+ time = 405333
+ flags = 1
+ data = length 1024, hash E80B3D7B
+ sample 39:
+ time = 416000
+ flags = 1
+ data = length 1024, hash 5E48841A
+ sample 40:
+ time = 426666
+ flags = 1
+ data = length 1024, hash C52E7744
+ sample 41:
+ time = 437333
+ flags = 1
+ data = length 1024, hash 1B1EADC1
+ sample 42:
+ time = 448000
+ flags = 1
+ data = length 1024, hash 7F8F62BB
+ sample 43:
+ time = 458666
+ flags = 1
+ data = length 1024, hash 4DF51924
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.1.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.1.dump
new file mode 100644
index 0000000000..3971920a3f
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.1.dump
@@ -0,0 +1,138 @@
+seekMap:
+ isSeekable = true
+ duration = 458666
+ getPosition(0) = [[timeUs=0, position=0]]
+ getPosition(1) = [[timeUs=1, position=0]]
+ getPosition(229333) = [[timeUs=229333, position=23565]]
+ getPosition(458666) = [[timeUs=458666, position=47319]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 30720
+ sample count = 30
+ format 0:
+ averageBitrate = 768000
+ id = 1/257
+ sampleMimeType = audio/vnd.dts
+ channelCount = 6
+ sampleRate = 48000
+ sample 0:
+ time = 149333
+ flags = 1
+ data = length 1024, hash 322284E0
+ sample 1:
+ time = 160000
+ flags = 1
+ data = length 1024, hash F2D0A402
+ sample 2:
+ time = 170666
+ flags = 1
+ data = length 1024, hash FF8E5254
+ sample 3:
+ time = 181333
+ flags = 1
+ data = length 1024, hash 1D0A5685
+ sample 4:
+ time = 192000
+ flags = 1
+ data = length 1024, hash A7B5783E
+ sample 5:
+ time = 202666
+ flags = 1
+ data = length 1024, hash FFFA8F6A
+ sample 6:
+ time = 213333
+ flags = 1
+ data = length 1024, hash 7EEF1A48
+ sample 7:
+ time = 224000
+ flags = 1
+ data = length 1024, hash 465956B4
+ sample 8:
+ time = 234666
+ flags = 1
+ data = length 1024, hash D1987449
+ sample 9:
+ time = 245333
+ flags = 1
+ data = length 1024, hash 33001B39
+ sample 10:
+ time = 256000
+ flags = 1
+ data = length 1024, hash F1B2E5B2
+ sample 11:
+ time = 266666
+ flags = 1
+ data = length 1024, hash F3F55C7C
+ sample 12:
+ time = 277333
+ flags = 1
+ data = length 1024, hash C965813F
+ sample 13:
+ time = 288000
+ flags = 1
+ data = length 1024, hash 611AD82C
+ sample 14:
+ time = 298666
+ flags = 1
+ data = length 1024, hash 6A630F21
+ sample 15:
+ time = 309333
+ flags = 1
+ data = length 1024, hash 7B52C0BF
+ sample 16:
+ time = 320000
+ flags = 1
+ data = length 1024, hash F9105AD9
+ sample 17:
+ time = 330666
+ flags = 1
+ data = length 1024, hash 14046870
+ sample 18:
+ time = 341333
+ flags = 1
+ data = length 1024, hash 17365B01
+ sample 19:
+ time = 352000
+ flags = 1
+ data = length 1024, hash 71BE4CC6
+ sample 20:
+ time = 362666
+ flags = 1
+ data = length 1024, hash 9C9B421C
+ sample 21:
+ time = 373333
+ flags = 1
+ data = length 1024, hash 52A90B15
+ sample 22:
+ time = 384000
+ flags = 1
+ data = length 1024, hash D4722243
+ sample 23:
+ time = 394666
+ flags = 1
+ data = length 1024, hash 50819265
+ sample 24:
+ time = 405333
+ flags = 1
+ data = length 1024, hash E80B3D7B
+ sample 25:
+ time = 416000
+ flags = 1
+ data = length 1024, hash 5E48841A
+ sample 26:
+ time = 426666
+ flags = 1
+ data = length 1024, hash C52E7744
+ sample 27:
+ time = 437333
+ flags = 1
+ data = length 1024, hash 1B1EADC1
+ sample 28:
+ time = 448000
+ flags = 1
+ data = length 1024, hash 7F8F62BB
+ sample 29:
+ time = 458666
+ flags = 1
+ data = length 1024, hash 4DF51924
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.2.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.2.dump
new file mode 100644
index 0000000000..96942653e0
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.2.dump
@@ -0,0 +1,82 @@
+seekMap:
+ isSeekable = true
+ duration = 458666
+ getPosition(0) = [[timeUs=0, position=0]]
+ getPosition(1) = [[timeUs=1, position=0]]
+ getPosition(229333) = [[timeUs=229333, position=23565]]
+ getPosition(458666) = [[timeUs=458666, position=47319]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 16384
+ sample count = 16
+ format 0:
+ averageBitrate = 768000
+ id = 1/257
+ sampleMimeType = audio/vnd.dts
+ channelCount = 6
+ sampleRate = 48000
+ sample 0:
+ time = 298666
+ flags = 1
+ data = length 1024, hash 6A630F21
+ sample 1:
+ time = 309333
+ flags = 1
+ data = length 1024, hash 7B52C0BF
+ sample 2:
+ time = 320000
+ flags = 1
+ data = length 1024, hash F9105AD9
+ sample 3:
+ time = 330666
+ flags = 1
+ data = length 1024, hash 14046870
+ sample 4:
+ time = 341333
+ flags = 1
+ data = length 1024, hash 17365B01
+ sample 5:
+ time = 352000
+ flags = 1
+ data = length 1024, hash 71BE4CC6
+ sample 6:
+ time = 362666
+ flags = 1
+ data = length 1024, hash 9C9B421C
+ sample 7:
+ time = 373333
+ flags = 1
+ data = length 1024, hash 52A90B15
+ sample 8:
+ time = 384000
+ flags = 1
+ data = length 1024, hash D4722243
+ sample 9:
+ time = 394666
+ flags = 1
+ data = length 1024, hash 50819265
+ sample 10:
+ time = 405333
+ flags = 1
+ data = length 1024, hash E80B3D7B
+ sample 11:
+ time = 416000
+ flags = 1
+ data = length 1024, hash 5E48841A
+ sample 12:
+ time = 426666
+ flags = 1
+ data = length 1024, hash C52E7744
+ sample 13:
+ time = 437333
+ flags = 1
+ data = length 1024, hash 1B1EADC1
+ sample 14:
+ time = 448000
+ flags = 1
+ data = length 1024, hash 7F8F62BB
+ sample 15:
+ time = 458666
+ flags = 1
+ data = length 1024, hash 4DF51924
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.3.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.3.dump
new file mode 100644
index 0000000000..ab45b1bd00
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.3.dump
@@ -0,0 +1,26 @@
+seekMap:
+ isSeekable = true
+ duration = 458666
+ getPosition(0) = [[timeUs=0, position=0]]
+ getPosition(1) = [[timeUs=1, position=0]]
+ getPosition(229333) = [[timeUs=229333, position=23565]]
+ getPosition(458666) = [[timeUs=458666, position=47319]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 2048
+ sample count = 2
+ format 0:
+ averageBitrate = 768000
+ id = 1/257
+ sampleMimeType = audio/vnd.dts
+ channelCount = 6
+ sampleRate = 48000
+ sample 0:
+ time = 448000
+ flags = 1
+ data = length 1024, hash 7F8F62BB
+ sample 1:
+ time = 458666
+ flags = 1
+ data = length 1024, hash 4DF51924
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.unknown_length.dump
new file mode 100644
index 0000000000..1150cbacfb
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts.ts.unknown_length.dump
@@ -0,0 +1,191 @@
+seekMap:
+ isSeekable = false
+ duration = UNSET TIME
+ getPosition(0) = [[timeUs=0, position=0]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 45056
+ sample count = 44
+ format 0:
+ averageBitrate = 768000
+ id = 1/257
+ sampleMimeType = audio/vnd.dts
+ channelCount = 6
+ sampleRate = 48000
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 1024, hash 76A741FC
+ sample 1:
+ time = 10666
+ flags = 1
+ data = length 1024, hash 2DB2026F
+ sample 2:
+ time = 21333
+ flags = 1
+ data = length 1024, hash AB0CA0C0
+ sample 3:
+ time = 32000
+ flags = 1
+ data = length 1024, hash 5E448BFF
+ sample 4:
+ time = 42666
+ flags = 1
+ data = length 1024, hash 64919D7
+ sample 5:
+ time = 53333
+ flags = 1
+ data = length 1024, hash 50BF95B1
+ sample 6:
+ time = 64000
+ flags = 1
+ data = length 1024, hash AFF14A6A
+ sample 7:
+ time = 74666
+ flags = 1
+ data = length 1024, hash B0905902
+ sample 8:
+ time = 85333
+ flags = 1
+ data = length 1024, hash C09014B3
+ sample 9:
+ time = 96000
+ flags = 1
+ data = length 1024, hash 7D918717
+ sample 10:
+ time = 106666
+ flags = 1
+ data = length 1024, hash DEE7A9AA
+ sample 11:
+ time = 117333
+ flags = 1
+ data = length 1024, hash 2905D4DB
+ sample 12:
+ time = 128000
+ flags = 1
+ data = length 1024, hash 80615D30
+ sample 13:
+ time = 138666
+ flags = 1
+ data = length 1024, hash 9949F1C
+ sample 14:
+ time = 149333
+ flags = 1
+ data = length 1024, hash 322284E0
+ sample 15:
+ time = 160000
+ flags = 1
+ data = length 1024, hash F2D0A402
+ sample 16:
+ time = 170666
+ flags = 1
+ data = length 1024, hash FF8E5254
+ sample 17:
+ time = 181333
+ flags = 1
+ data = length 1024, hash 1D0A5685
+ sample 18:
+ time = 192000
+ flags = 1
+ data = length 1024, hash A7B5783E
+ sample 19:
+ time = 202666
+ flags = 1
+ data = length 1024, hash FFFA8F6A
+ sample 20:
+ time = 213333
+ flags = 1
+ data = length 1024, hash 7EEF1A48
+ sample 21:
+ time = 224000
+ flags = 1
+ data = length 1024, hash 465956B4
+ sample 22:
+ time = 234666
+ flags = 1
+ data = length 1024, hash D1987449
+ sample 23:
+ time = 245333
+ flags = 1
+ data = length 1024, hash 33001B39
+ sample 24:
+ time = 256000
+ flags = 1
+ data = length 1024, hash F1B2E5B2
+ sample 25:
+ time = 266666
+ flags = 1
+ data = length 1024, hash F3F55C7C
+ sample 26:
+ time = 277333
+ flags = 1
+ data = length 1024, hash C965813F
+ sample 27:
+ time = 288000
+ flags = 1
+ data = length 1024, hash 611AD82C
+ sample 28:
+ time = 298666
+ flags = 1
+ data = length 1024, hash 6A630F21
+ sample 29:
+ time = 309333
+ flags = 1
+ data = length 1024, hash 7B52C0BF
+ sample 30:
+ time = 320000
+ flags = 1
+ data = length 1024, hash F9105AD9
+ sample 31:
+ time = 330666
+ flags = 1
+ data = length 1024, hash 14046870
+ sample 32:
+ time = 341333
+ flags = 1
+ data = length 1024, hash 17365B01
+ sample 33:
+ time = 352000
+ flags = 1
+ data = length 1024, hash 71BE4CC6
+ sample 34:
+ time = 362666
+ flags = 1
+ data = length 1024, hash 9C9B421C
+ sample 35:
+ time = 373333
+ flags = 1
+ data = length 1024, hash 52A90B15
+ sample 36:
+ time = 384000
+ flags = 1
+ data = length 1024, hash D4722243
+ sample 37:
+ time = 394666
+ flags = 1
+ data = length 1024, hash 50819265
+ sample 38:
+ time = 405333
+ flags = 1
+ data = length 1024, hash E80B3D7B
+ sample 39:
+ time = 416000
+ flags = 1
+ data = length 1024, hash 5E48841A
+ sample 40:
+ time = 426666
+ flags = 1
+ data = length 1024, hash C52E7744
+ sample 41:
+ time = 437333
+ flags = 1
+ data = length 1024, hash 1B1EADC1
+ sample 42:
+ time = 448000
+ flags = 1
+ data = length 1024, hash 7F8F62BB
+ sample 43:
+ time = 458666
+ flags = 1
+ data = length 1024, hash 4DF51924
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.0.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.0.dump
new file mode 100644
index 0000000000..7d6271f027
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.0.dump
@@ -0,0 +1,61 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=0]]
+ getPosition(1) = [[timeUs=1, position=0]]
+ getPosition(426666) = [[timeUs=426666, position=22583]]
+ getPosition(853333) = [[timeUs=853333, position=45355]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 45056
+ sample count = 11
+ format 0:
+ id = 1/257
+ sampleMimeType = audio/vnd.dts.hd;profile=lbr
+ channelCount = 6
+ sampleRate = 48000
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 4096, hash 1F0B79C5
+ sample 1:
+ time = 85333
+ flags = 1
+ data = length 4096, hash 2EC282A1
+ sample 2:
+ time = 170666
+ flags = 1
+ data = length 4096, hash 6B7902F0
+ sample 3:
+ time = 256000
+ flags = 1
+ data = length 4096, hash 8FC4EE2C
+ sample 4:
+ time = 341333
+ flags = 1
+ data = length 4096, hash 67899547
+ sample 5:
+ time = 426666
+ flags = 1
+ data = length 4096, hash 1BE4CF1C
+ sample 6:
+ time = 512000
+ flags = 1
+ data = length 4096, hash 620F5E51
+ sample 7:
+ time = 597333
+ flags = 1
+ data = length 4096, hash 4D3E0644
+ sample 8:
+ time = 682666
+ flags = 1
+ data = length 4096, hash F69B5FED
+ sample 9:
+ time = 768000
+ flags = 1
+ data = length 4096, hash 93D31EA7
+ sample 10:
+ time = 853333
+ flags = 1
+ data = length 4096, hash 41F1D921
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.1.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.1.dump
new file mode 100644
index 0000000000..affd4f713b
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.1.dump
@@ -0,0 +1,49 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=0]]
+ getPosition(1) = [[timeUs=1, position=0]]
+ getPosition(426666) = [[timeUs=426666, position=22583]]
+ getPosition(853333) = [[timeUs=853333, position=45355]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 32768
+ sample count = 8
+ format 0:
+ id = 1/257
+ sampleMimeType = audio/vnd.dts.hd;profile=lbr
+ channelCount = 6
+ sampleRate = 48000
+ sample 0:
+ time = 256000
+ flags = 1
+ data = length 4096, hash 8FC4EE2C
+ sample 1:
+ time = 341333
+ flags = 1
+ data = length 4096, hash 67899547
+ sample 2:
+ time = 426666
+ flags = 1
+ data = length 4096, hash 1BE4CF1C
+ sample 3:
+ time = 512000
+ flags = 1
+ data = length 4096, hash 620F5E51
+ sample 4:
+ time = 597333
+ flags = 1
+ data = length 4096, hash 4D3E0644
+ sample 5:
+ time = 682666
+ flags = 1
+ data = length 4096, hash F69B5FED
+ sample 6:
+ time = 768000
+ flags = 1
+ data = length 4096, hash 93D31EA7
+ sample 7:
+ time = 853333
+ flags = 1
+ data = length 4096, hash 41F1D921
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.2.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.2.dump
new file mode 100644
index 0000000000..d9803a8904
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.2.dump
@@ -0,0 +1,37 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=0]]
+ getPosition(1) = [[timeUs=1, position=0]]
+ getPosition(426666) = [[timeUs=426666, position=22583]]
+ getPosition(853333) = [[timeUs=853333, position=45355]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 20480
+ sample count = 5
+ format 0:
+ id = 1/257
+ sampleMimeType = audio/vnd.dts.hd;profile=lbr
+ channelCount = 6
+ sampleRate = 48000
+ sample 0:
+ time = 512000
+ flags = 1
+ data = length 4096, hash 620F5E51
+ sample 1:
+ time = 597333
+ flags = 1
+ data = length 4096, hash 4D3E0644
+ sample 2:
+ time = 682666
+ flags = 1
+ data = length 4096, hash F69B5FED
+ sample 3:
+ time = 768000
+ flags = 1
+ data = length 4096, hash 93D31EA7
+ sample 4:
+ time = 853333
+ flags = 1
+ data = length 4096, hash 41F1D921
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.3.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.3.dump
new file mode 100644
index 0000000000..7c2f06ab6b
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.3.dump
@@ -0,0 +1,17 @@
+seekMap:
+ isSeekable = true
+ duration = 853333
+ getPosition(0) = [[timeUs=0, position=0]]
+ getPosition(1) = [[timeUs=1, position=0]]
+ getPosition(426666) = [[timeUs=426666, position=22583]]
+ getPosition(853333) = [[timeUs=853333, position=45355]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 0
+ sample count = 0
+ format 0:
+ id = 1/257
+ sampleMimeType = audio/vnd.dts.hd;profile=lbr
+ channelCount = 6
+ sampleRate = 48000
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.unknown_length.dump
new file mode 100644
index 0000000000..00eb0c5d24
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_hd.ts.unknown_length.dump
@@ -0,0 +1,58 @@
+seekMap:
+ isSeekable = false
+ duration = UNSET TIME
+ getPosition(0) = [[timeUs=0, position=0]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 45056
+ sample count = 11
+ format 0:
+ id = 1/257
+ sampleMimeType = audio/vnd.dts.hd;profile=lbr
+ channelCount = 6
+ sampleRate = 48000
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 4096, hash 1F0B79C5
+ sample 1:
+ time = 85333
+ flags = 1
+ data = length 4096, hash 2EC282A1
+ sample 2:
+ time = 170666
+ flags = 1
+ data = length 4096, hash 6B7902F0
+ sample 3:
+ time = 256000
+ flags = 1
+ data = length 4096, hash 8FC4EE2C
+ sample 4:
+ time = 341333
+ flags = 1
+ data = length 4096, hash 67899547
+ sample 5:
+ time = 426666
+ flags = 1
+ data = length 4096, hash 1BE4CF1C
+ sample 6:
+ time = 512000
+ flags = 1
+ data = length 4096, hash 620F5E51
+ sample 7:
+ time = 597333
+ flags = 1
+ data = length 4096, hash 4D3E0644
+ sample 8:
+ time = 682666
+ flags = 1
+ data = length 4096, hash F69B5FED
+ sample 9:
+ time = 768000
+ flags = 1
+ data = length 4096, hash 93D31EA7
+ sample 10:
+ time = 853333
+ flags = 1
+ data = length 4096, hash 41F1D921
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.0.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.0.dump
new file mode 100644
index 0000000000..a87dc4b897
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.0.dump
@@ -0,0 +1,953 @@
+seekMap:
+ isSeekable = true
+ duration = 4970666
+ getPosition(0) = [[timeUs=0, position=0]]
+ getPosition(1) = [[timeUs=1, position=0]]
+ getPosition(2485333) = [[timeUs=2485333, position=102149]]
+ getPosition(4970666) = [[timeUs=4970666, position=204487]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 179682
+ sample count = 234
+ format 0:
+ id = 1/257
+ sampleMimeType = audio/vnd.dts.uhd;profile=p2
+ channelCount = 2
+ sampleRate = 48000
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 776, hash E68E7565
+ sample 1:
+ time = 21333
+ flags = 0
+ data = length 765, hash 713DA83C
+ sample 2:
+ time = 42666
+ flags = 0
+ data = length 771, hash E672DC
+ sample 3:
+ time = 64000
+ flags = 0
+ data = length 769, hash 83F90436
+ sample 4:
+ time = 85333
+ flags = 0
+ data = length 765, hash 5C8A17D1
+ sample 5:
+ time = 106666
+ flags = 0
+ data = length 771, hash 5528EC74
+ sample 6:
+ time = 128000
+ flags = 0
+ data = length 767, hash 954FC4B5
+ sample 7:
+ time = 149333
+ flags = 0
+ data = length 767, hash 5C2089BB
+ sample 8:
+ time = 170666
+ flags = 0
+ data = length 771, hash 21AF360B
+ sample 9:
+ time = 192000
+ flags = 0
+ data = length 766, hash 2C363E9B
+ sample 10:
+ time = 213333
+ flags = 0
+ data = length 767, hash 812E9BF7
+ sample 11:
+ time = 234666
+ flags = 0
+ data = length 770, hash ED9D5378
+ sample 12:
+ time = 256000
+ flags = 0
+ data = length 769, hash FBF9DD0E
+ sample 13:
+ time = 277333
+ flags = 0
+ data = length 767, hash 5E7822E9
+ sample 14:
+ time = 298666
+ flags = 0
+ data = length 770, hash 97EDBA77
+ sample 15:
+ time = 320000
+ flags = 0
+ data = length 767, hash 1739FF77
+ sample 16:
+ time = 341333
+ flags = 0
+ data = length 766, hash 99225978
+ sample 17:
+ time = 362666
+ flags = 0
+ data = length 772, hash 1971320D
+ sample 18:
+ time = 384000
+ flags = 0
+ data = length 768, hash F8C4F6A2
+ sample 19:
+ time = 405333
+ flags = 0
+ data = length 765, hash 225434F4
+ sample 20:
+ time = 426666
+ flags = 0
+ data = length 770, hash E232FA0D
+ sample 21:
+ time = 448000
+ flags = 0
+ data = length 767, hash 721418FA
+ sample 22:
+ time = 469333
+ flags = 0
+ data = length 768, hash E52E3AD0
+ sample 23:
+ time = 490666
+ flags = 0
+ data = length 772, hash 9D0F9C8C
+ sample 24:
+ time = 512000
+ flags = 0
+ data = length 767, hash EAFEDC
+ sample 25:
+ time = 533333
+ flags = 0
+ data = length 766, hash CAFCB522
+ sample 26:
+ time = 554666
+ flags = 0
+ data = length 770, hash EB1806AF
+ sample 27:
+ time = 576000
+ flags = 0
+ data = length 768, hash F022F870
+ sample 28:
+ time = 597333
+ flags = 0
+ data = length 767, hash 2DB31DD6
+ sample 29:
+ time = 618666
+ flags = 0
+ data = length 770, hash 3168DE65
+ sample 30:
+ time = 640000
+ flags = 0
+ data = length 767, hash 1D25591B
+ sample 31:
+ time = 661333
+ flags = 0
+ data = length 766, hash 2DB4AF54
+ sample 32:
+ time = 682666
+ flags = 0
+ data = length 772, hash 9B9176A8
+ sample 33:
+ time = 704000
+ flags = 0
+ data = length 768, hash F49BD7C2
+ sample 34:
+ time = 725333
+ flags = 0
+ data = length 765, hash 4D1A1A5B
+ sample 35:
+ time = 746666
+ flags = 0
+ data = length 771, hash EED28144
+ sample 36:
+ time = 768000
+ flags = 0
+ data = length 767, hash DE09C384
+ sample 37:
+ time = 789333
+ flags = 0
+ data = length 767, hash 5B154F22
+ sample 38:
+ time = 810666
+ flags = 0
+ data = length 772, hash 7621AB2B
+ sample 39:
+ time = 832000
+ flags = 0
+ data = length 767, hash 3B503329
+ sample 40:
+ time = 853333
+ flags = 0
+ data = length 766, hash 6D5F86F0
+ sample 41:
+ time = 874666
+ flags = 0
+ data = length 770, hash 122B33CF
+ sample 42:
+ time = 896000
+ flags = 0
+ data = length 769, hash 701BED85
+ sample 43:
+ time = 917333
+ flags = 0
+ data = length 767, hash 66F75279
+ sample 44:
+ time = 938666
+ flags = 0
+ data = length 769, hash 8EB616E3
+ sample 45:
+ time = 960000
+ flags = 0
+ data = length 767, hash C04E8FCB
+ sample 46:
+ time = 981333
+ flags = 0
+ data = length 765, hash 568E8728
+ sample 47:
+ time = 1002666
+ flags = 0
+ data = length 772, hash C23993F0
+ sample 48:
+ time = 1024000
+ flags = 0
+ data = length 768, hash 518ACD0D
+ sample 49:
+ time = 1045333
+ flags = 0
+ data = length 765, hash 297ACB8E
+ sample 50:
+ time = 1066666
+ flags = 0
+ data = length 770, hash 676ED3C1
+ sample 51:
+ time = 1088000
+ flags = 0
+ data = length 767, hash 836598AA
+ sample 52:
+ time = 1109333
+ flags = 0
+ data = length 767, hash F1536245
+ sample 53:
+ time = 1130666
+ flags = 0
+ data = length 772, hash 12AC3BCE
+ sample 54:
+ time = 1152000
+ flags = 0
+ data = length 766, hash 9948C9C
+ sample 55:
+ time = 1173333
+ flags = 0
+ data = length 766, hash B42ADF4E
+ sample 56:
+ time = 1194666
+ flags = 0
+ data = length 770, hash 67AC576F
+ sample 57:
+ time = 1216000
+ flags = 0
+ data = length 769, hash B843E663
+ sample 58:
+ time = 1237333
+ flags = 0
+ data = length 767, hash 432AD19
+ sample 59:
+ time = 1258666
+ flags = 0
+ data = length 770, hash 14BA5FE6
+ sample 60:
+ time = 1280000
+ flags = 0
+ data = length 768, hash A0D32F30
+ sample 61:
+ time = 1301333
+ flags = 0
+ data = length 765, hash 4BF180A9
+ sample 62:
+ time = 1322666
+ flags = 0
+ data = length 772, hash 1C613DF7
+ sample 63:
+ time = 1344000
+ flags = 0
+ data = length 768, hash F30F6A09
+ sample 64:
+ time = 1365333
+ flags = 0
+ data = length 765, hash D40AC240
+ sample 65:
+ time = 1386666
+ flags = 0
+ data = length 770, hash 1E64D8FE
+ sample 66:
+ time = 1408000
+ flags = 0
+ data = length 767, hash 5DFE9BC3
+ sample 67:
+ time = 1429333
+ flags = 0
+ data = length 767, hash 19A018BD
+ sample 68:
+ time = 1450666
+ flags = 0
+ data = length 772, hash 363BF537
+ sample 69:
+ time = 1472000
+ flags = 0
+ data = length 766, hash 4832AF5B
+ sample 70:
+ time = 1493333
+ flags = 0
+ data = length 766, hash 3CBED44A
+ sample 71:
+ time = 1514666
+ flags = 0
+ data = length 771, hash C5E800AD
+ sample 72:
+ time = 1536000
+ flags = 0
+ data = length 786, hash DA64F1EE
+ sample 73:
+ time = 1557333
+ flags = 0
+ data = length 785, hash 4771990A
+ sample 74:
+ time = 1578666
+ flags = 0
+ data = length 788, hash CBB53C84
+ sample 75:
+ time = 1600000
+ flags = 0
+ data = length 786, hash AF107053
+ sample 76:
+ time = 1621333
+ flags = 0
+ data = length 784, hash 7418BBD7
+ sample 77:
+ time = 1642666
+ flags = 0
+ data = length 790, hash 35A5E570
+ sample 78:
+ time = 1664000
+ flags = 0
+ data = length 787, hash 4F662158
+ sample 79:
+ time = 1685333
+ flags = 0
+ data = length 783, hash A574B045
+ sample 80:
+ time = 1706666
+ flags = 0
+ data = length 789, hash 211311A9
+ sample 81:
+ time = 1728000
+ flags = 0
+ data = length 785, hash 4A7E6F21
+ sample 82:
+ time = 1749333
+ flags = 0
+ data = length 786, hash DE8047C6
+ sample 83:
+ time = 1770666
+ flags = 0
+ data = length 789, hash E778E75A
+ sample 84:
+ time = 1792000
+ flags = 0
+ data = length 785, hash AD3A50F8
+ sample 85:
+ time = 1813333
+ flags = 0
+ data = length 783, hash 6DCEAA5C
+ sample 86:
+ time = 1834666
+ flags = 0
+ data = length 787, hash D5E2A255
+ sample 87:
+ time = 1856000
+ flags = 0
+ data = length 787, hash E9138642
+ sample 88:
+ time = 1877333
+ flags = 0
+ data = length 785, hash B2D94BAF
+ sample 89:
+ time = 1898666
+ flags = 0
+ data = length 789, hash F0F3BBDB
+ sample 90:
+ time = 1920000
+ flags = 0
+ data = length 786, hash B0D62A20
+ sample 91:
+ time = 1941333
+ flags = 0
+ data = length 767, hash 710CB0E4
+ sample 92:
+ time = 1962666
+ flags = 0
+ data = length 400, hash 8A0182CB
+ sample 93:
+ time = 1984000
+ flags = 1
+ data = length 402, hash E60D79A1
+ sample 94:
+ time = 2005333
+ flags = 0
+ data = length 394, hash 585703B6
+ sample 95:
+ time = 2026666
+ flags = 0
+ data = length 394, hash 50CA225B
+ sample 96:
+ time = 2048000
+ flags = 0
+ data = length 395, hash E0229BF0
+ sample 97:
+ time = 2069333
+ flags = 0
+ data = length 501, hash F8930C9C
+ sample 98:
+ time = 2090666
+ flags = 0
+ data = length 667, hash C05CCFA2
+ sample 99:
+ time = 2112000
+ flags = 0
+ data = length 918, hash 5832371D
+ sample 100:
+ time = 2133333
+ flags = 0
+ data = length 1101, hash 1F97D86E
+ sample 101:
+ time = 2154666
+ flags = 0
+ data = length 1006, hash 974AC450
+ sample 102:
+ time = 2176000
+ flags = 0
+ data = length 1153, hash 22CA9C79
+ sample 103:
+ time = 2197333
+ flags = 0
+ data = length 959, hash 2011C476
+ sample 104:
+ time = 2218666
+ flags = 0
+ data = length 735, hash C2F6C1F9
+ sample 105:
+ time = 2240000
+ flags = 0
+ data = length 865, hash 394C4481
+ sample 106:
+ time = 2261333
+ flags = 0
+ data = length 825, hash 611B513C
+ sample 107:
+ time = 2282666
+ flags = 0
+ data = length 952, hash 5B24AEC2
+ sample 108:
+ time = 2304000
+ flags = 0
+ data = length 984, hash 172B469
+ sample 109:
+ time = 2325333
+ flags = 0
+ data = length 1010, hash D3BBA580
+ sample 110:
+ time = 2346666
+ flags = 0
+ data = length 1067, hash E09AD57F
+ sample 111:
+ time = 2368000
+ flags = 0
+ data = length 1060, hash 5332E90B
+ sample 112:
+ time = 2389333
+ flags = 0
+ data = length 991, hash 48B80B00
+ sample 113:
+ time = 2410666
+ flags = 0
+ data = length 873, hash 696FBD43
+ sample 114:
+ time = 2432000
+ flags = 0
+ data = length 810, hash 7D3D7B29
+ sample 115:
+ time = 2453333
+ flags = 0
+ data = length 897, hash D41D4CB7
+ sample 116:
+ time = 2474666
+ flags = 0
+ data = length 781, hash A589A312
+ sample 117:
+ time = 2496000
+ flags = 0
+ data = length 705, hash FDC5698B
+ sample 118:
+ time = 2517333
+ flags = 0
+ data = length 608, hash F929EDA6
+ sample 119:
+ time = 2538666
+ flags = 0
+ data = length 546, hash A141CD5E
+ sample 120:
+ time = 2560000
+ flags = 0
+ data = length 639, hash BABD5C9F
+ sample 121:
+ time = 2581333
+ flags = 0
+ data = length 561, hash B1AF4D92
+ sample 122:
+ time = 2602666
+ flags = 0
+ data = length 529, hash D0E0EF42
+ sample 123:
+ time = 2624000
+ flags = 0
+ data = length 441, hash E0B1DB1
+ sample 124:
+ time = 2645333
+ flags = 0
+ data = length 393, hash 5A34D1B0
+ sample 125:
+ time = 2666666
+ flags = 0
+ data = length 393, hash B792EA44
+ sample 126:
+ time = 2688000
+ flags = 0
+ data = length 393, hash 7CCE0C94
+ sample 127:
+ time = 2709333
+ flags = 0
+ data = length 393, hash C80E41E0
+ sample 128:
+ time = 2730666
+ flags = 0
+ data = length 393, hash 2C5B4860
+ sample 129:
+ time = 2752000
+ flags = 0
+ data = length 393, hash 9930B5F0
+ sample 130:
+ time = 2773333
+ flags = 0
+ data = length 393, hash B78D1FDA
+ sample 131:
+ time = 2794666
+ flags = 0
+ data = length 393, hash 817AB352
+ sample 132:
+ time = 2816000
+ flags = 0
+ data = length 393, hash EA7D83DF
+ sample 133:
+ time = 2837333
+ flags = 0
+ data = length 393, hash E76873B0
+ sample 134:
+ time = 2858666
+ flags = 0
+ data = length 393, hash 2F48A520
+ sample 135:
+ time = 2880000
+ flags = 0
+ data = length 393, hash C0CBB3EF
+ sample 136:
+ time = 2901333
+ flags = 0
+ data = length 393, hash 255B0E50
+ sample 137:
+ time = 2922666
+ flags = 0
+ data = length 393, hash 9A49C63D
+ sample 138:
+ time = 2944000
+ flags = 0
+ data = length 1516, hash 4994C5B
+ sample 139:
+ time = 2965333
+ flags = 0
+ data = length 1519, hash 5514C06F
+ sample 140:
+ time = 2986666
+ flags = 0
+ data = length 1007, hash F994B3FC
+ sample 141:
+ time = 3008000
+ flags = 0
+ data = length 946, hash 7714E7B1
+ sample 142:
+ time = 3029333
+ flags = 0
+ data = length 985, hash C0E4D841
+ sample 143:
+ time = 3050666
+ flags = 0
+ data = length 1018, hash A18F6981
+ sample 144:
+ time = 3072000
+ flags = 0
+ data = length 790, hash 9F884211
+ sample 145:
+ time = 3093333
+ flags = 0
+ data = length 809, hash 1E0978C9
+ sample 146:
+ time = 3114666
+ flags = 0
+ data = length 791, hash 9BC34342
+ sample 147:
+ time = 3136000
+ flags = 0
+ data = length 806, hash C64EDB12
+ sample 148:
+ time = 3157333
+ flags = 0
+ data = length 757, hash 6C971B85
+ sample 149:
+ time = 3178666
+ flags = 0
+ data = length 950, hash 5509778D
+ sample 150:
+ time = 3200000
+ flags = 0
+ data = length 713, hash 2BD954C8
+ sample 151:
+ time = 3221333
+ flags = 0
+ data = length 800, hash 1C71F2EF
+ sample 152:
+ time = 3242666
+ flags = 0
+ data = length 804, hash BFCCE7C
+ sample 153:
+ time = 3264000
+ flags = 0
+ data = length 876, hash AB1137D9
+ sample 154:
+ time = 3285333
+ flags = 0
+ data = length 957, hash 1CC9B8CA
+ sample 155:
+ time = 3306666
+ flags = 0
+ data = length 563, hash A6FF9D2B
+ sample 156:
+ time = 3328000
+ flags = 0
+ data = length 796, hash B0110276
+ sample 157:
+ time = 3349333
+ flags = 0
+ data = length 863, hash 55EDF24F
+ sample 158:
+ time = 3370666
+ flags = 0
+ data = length 701, hash 3AE3C48F
+ sample 159:
+ time = 3392000
+ flags = 0
+ data = length 934, hash 2CC1C6E9
+ sample 160:
+ time = 3413333
+ flags = 0
+ data = length 967, hash 34E61D20
+ sample 161:
+ time = 3434666
+ flags = 0
+ data = length 1518, hash C0EC5B50
+ sample 162:
+ time = 3456000
+ flags = 0
+ data = length 1474, hash 6EC36C24
+ sample 163:
+ time = 3477333
+ flags = 0
+ data = length 1475, hash 6F1FD2AB
+ sample 164:
+ time = 3498666
+ flags = 0
+ data = length 1451, hash B58F4D31
+ sample 165:
+ time = 3520000
+ flags = 0
+ data = length 526, hash FC424B78
+ sample 166:
+ time = 3541333
+ flags = 0
+ data = length 598, hash 4BDB2753
+ sample 167:
+ time = 3562666
+ flags = 0
+ data = length 627, hash 48327C83
+ sample 168:
+ time = 3584000
+ flags = 0
+ data = length 551, hash 691D6CB2
+ sample 169:
+ time = 3605333
+ flags = 0
+ data = length 594, hash 86D1E249
+ sample 170:
+ time = 3626666
+ flags = 0
+ data = length 760, hash AF4B774F
+ sample 171:
+ time = 3648000
+ flags = 0
+ data = length 568, hash F4765276
+ sample 172:
+ time = 3669333
+ flags = 0
+ data = length 722, hash 2A72C44B
+ sample 173:
+ time = 3690666
+ flags = 0
+ data = length 602, hash 5D7135BB
+ sample 174:
+ time = 3712000
+ flags = 0
+ data = length 694, hash 8C5CE9CF
+ sample 175:
+ time = 3733333
+ flags = 0
+ data = length 725, hash 4EBFA7A8
+ sample 176:
+ time = 3754666
+ flags = 0
+ data = length 666, hash 2B1379B
+ sample 177:
+ time = 3776000
+ flags = 0
+ data = length 846, hash 57FC9178
+ sample 178:
+ time = 3797333
+ flags = 0
+ data = length 881, hash 19FDF109
+ sample 179:
+ time = 3818666
+ flags = 0
+ data = length 512, hash 49FD56DF
+ sample 180:
+ time = 3840000
+ flags = 0
+ data = length 840, hash FC78237B
+ sample 181:
+ time = 3861333
+ flags = 0
+ data = length 1075, hash 5031D568
+ sample 182:
+ time = 3882666
+ flags = 0
+ data = length 716, hash A509900D
+ sample 183:
+ time = 3904000
+ flags = 0
+ data = length 798, hash 386EB6B5
+ sample 184:
+ time = 3925333
+ flags = 0
+ data = length 969, hash 1E781925
+ sample 185:
+ time = 3946666
+ flags = 0
+ data = length 1444, hash F2DD1E7C
+ sample 186:
+ time = 3968000
+ flags = 0
+ data = length 399, hash 3FE1EAB6
+ sample 187:
+ time = 3989333
+ flags = 1
+ data = length 776, hash BEA73ED6
+ sample 188:
+ time = 4010666
+ flags = 0
+ data = length 875, hash 173C290C
+ sample 189:
+ time = 4032000
+ flags = 0
+ data = length 787, hash 531B9CB3
+ sample 190:
+ time = 4053333
+ flags = 0
+ data = length 737, hash 75CB4A48
+ sample 191:
+ time = 4074666
+ flags = 0
+ data = length 603, hash DDE525EE
+ sample 192:
+ time = 4096000
+ flags = 0
+ data = length 645, hash F4666418
+ sample 193:
+ time = 4117333
+ flags = 0
+ data = length 704, hash F7E18921
+ sample 194:
+ time = 4138666
+ flags = 0
+ data = length 802, hash 822433B5
+ sample 195:
+ time = 4160000
+ flags = 0
+ data = length 990, hash E4712448
+ sample 196:
+ time = 4181333
+ flags = 0
+ data = length 602, hash 3265AF8E
+ sample 197:
+ time = 4202666
+ flags = 0
+ data = length 790, hash 6EF432BA
+ sample 198:
+ time = 4224000
+ flags = 0
+ data = length 746, hash E20E7B36
+ sample 199:
+ time = 4245333
+ flags = 0
+ data = length 439, hash 8C32DDA0
+ sample 200:
+ time = 4266666
+ flags = 0
+ data = length 932, hash AF323520
+ sample 201:
+ time = 4288000
+ flags = 0
+ data = length 1003, hash 3D70121B
+ sample 202:
+ time = 4309333
+ flags = 0
+ data = length 1158, hash 1215442E
+ sample 203:
+ time = 4330666
+ flags = 0
+ data = length 1047, hash 2A517450
+ sample 204:
+ time = 4352000
+ flags = 0
+ data = length 1123, hash 33387B5F
+ sample 205:
+ time = 4373333
+ flags = 0
+ data = length 1033, hash D82CF575
+ sample 206:
+ time = 4394666
+ flags = 0
+ data = length 1009, hash 30DC6A02
+ sample 207:
+ time = 4416000
+ flags = 0
+ data = length 1033, hash 82127054
+ sample 208:
+ time = 4437333
+ flags = 0
+ data = length 761, hash 4A5EA11F
+ sample 209:
+ time = 4458666
+ flags = 0
+ data = length 708, hash 53FA04E4
+ sample 210:
+ time = 4480000
+ flags = 0
+ data = length 750, hash 44F541A6
+ sample 211:
+ time = 4501333
+ flags = 0
+ data = length 902, hash 6688117A
+ sample 212:
+ time = 4522666
+ flags = 0
+ data = length 716, hash A19D938D
+ sample 213:
+ time = 4544000
+ flags = 0
+ data = length 395, hash 4EF7E6DE
+ sample 214:
+ time = 4565333
+ flags = 0
+ data = length 677, hash 57DF6F41
+ sample 215:
+ time = 4586666
+ flags = 0
+ data = length 566, hash 515BD3F0
+ sample 216:
+ time = 4608000
+ flags = 0
+ data = length 772, hash 957617C9
+ sample 217:
+ time = 4629333
+ flags = 0
+ data = length 777, hash 6BB742A3
+ sample 218:
+ time = 4650666
+ flags = 0
+ data = length 672, hash 97BA98A3
+ sample 219:
+ time = 4672000
+ flags = 0
+ data = length 695, hash F8190CDE
+ sample 220:
+ time = 4693333
+ flags = 0
+ data = length 668, hash 6D62BE54
+ sample 221:
+ time = 4714666
+ flags = 0
+ data = length 662, hash 36D886CD
+ sample 222:
+ time = 4736000
+ flags = 0
+ data = length 678, hash 87772FCA
+ sample 223:
+ time = 4757333
+ flags = 0
+ data = length 946, hash BBFB4C98
+ sample 224:
+ time = 4778666
+ flags = 0
+ data = length 594, hash CEDCD36C
+ sample 225:
+ time = 4800000
+ flags = 0
+ data = length 499, hash 3B933D5B
+ sample 226:
+ time = 4821333
+ flags = 0
+ data = length 784, hash 97BC8486
+ sample 227:
+ time = 4842666
+ flags = 0
+ data = length 510, hash 9A082552
+ sample 228:
+ time = 4864000
+ flags = 0
+ data = length 741, hash E17DAE88
+ sample 229:
+ time = 4885333
+ flags = 0
+ data = length 656, hash C8CC5738
+ sample 230:
+ time = 4906666
+ flags = 0
+ data = length 672, hash 84C28F77
+ sample 231:
+ time = 4928000
+ flags = 0
+ data = length 559, hash B563D0ED
+ sample 232:
+ time = 4949333
+ flags = 0
+ data = length 1503, hash 183821B0
+ sample 233:
+ time = 4970666
+ flags = 0
+ data = length 393, hash D807573D
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.1.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.1.dump
new file mode 100644
index 0000000000..1c4379b259
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.1.dump
@@ -0,0 +1,661 @@
+seekMap:
+ isSeekable = true
+ duration = 4970666
+ getPosition(0) = [[timeUs=0, position=0]]
+ getPosition(1) = [[timeUs=1, position=0]]
+ getPosition(2485333) = [[timeUs=2485333, position=102149]]
+ getPosition(4970666) = [[timeUs=4970666, position=204487]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 108399
+ sample count = 161
+ format 0:
+ id = 1/257
+ sampleMimeType = audio/vnd.dts.uhd;profile=p2
+ channelCount = 2
+ sampleRate = 48000
+ sample 0:
+ time = 1557333
+ flags = 0
+ data = length 7, hash 5EB61B7F
+ sample 1:
+ time = 1578666
+ flags = 0
+ data = length 7, hash 5EB61ADF
+ sample 2:
+ time = 1600000
+ flags = 0
+ data = length 7, hash 5EB61B9F
+ sample 3:
+ time = 1621333
+ flags = 0
+ data = length 7, hash 5EB61B5F
+ sample 4:
+ time = 1642666
+ flags = 0
+ data = length 7, hash 5EB61B1F
+ sample 5:
+ time = 1664000
+ flags = 0
+ data = length 7, hash 5EB61ABF
+ sample 6:
+ time = 1685333
+ flags = 0
+ data = length 7, hash 5EB61B3F
+ sample 7:
+ time = 1706666
+ flags = 0
+ data = length 7, hash 5EB61AFF
+ sample 8:
+ time = 1728000
+ flags = 0
+ data = length 7, hash 5EB61B7F
+ sample 9:
+ time = 1749333
+ flags = 0
+ data = length 7, hash 5EB61B9F
+ sample 10:
+ time = 1770666
+ flags = 0
+ data = length 7, hash 5EB61AFF
+ sample 11:
+ time = 1792000
+ flags = 0
+ data = length 7, hash 5EB61B7F
+ sample 12:
+ time = 1813333
+ flags = 0
+ data = length 7, hash 5EB61B3F
+ sample 13:
+ time = 1834666
+ flags = 0
+ data = length 7, hash 5EB61ABF
+ sample 14:
+ time = 1856000
+ flags = 0
+ data = length 7, hash 5EB61ABF
+ sample 15:
+ time = 1877333
+ flags = 0
+ data = length 7, hash 5EB61B7F
+ sample 16:
+ time = 1898666
+ flags = 0
+ data = length 7, hash 5EB61AFF
+ sample 17:
+ time = 1920000
+ flags = 0
+ data = length 7, hash 5EB61B9F
+ sample 18:
+ time = 1941333
+ flags = 0
+ data = length 7, hash 5EB61B01
+ sample 19:
+ time = 1962666
+ flags = 0
+ data = length 6, hash F289FFAF
+ sample 20:
+ time = 1984000
+ flags = 1
+ data = length 402, hash E60D79A1
+ sample 21:
+ time = 2005333
+ flags = 0
+ data = length 394, hash 585703B6
+ sample 22:
+ time = 2026666
+ flags = 0
+ data = length 394, hash 50CA225B
+ sample 23:
+ time = 2048000
+ flags = 0
+ data = length 395, hash E0229BF0
+ sample 24:
+ time = 2069333
+ flags = 0
+ data = length 501, hash F8930C9C
+ sample 25:
+ time = 2090666
+ flags = 0
+ data = length 667, hash C05CCFA2
+ sample 26:
+ time = 2112000
+ flags = 0
+ data = length 918, hash 5832371D
+ sample 27:
+ time = 2133333
+ flags = 0
+ data = length 1101, hash 1F97D86E
+ sample 28:
+ time = 2154666
+ flags = 0
+ data = length 1006, hash 974AC450
+ sample 29:
+ time = 2176000
+ flags = 0
+ data = length 1153, hash 22CA9C79
+ sample 30:
+ time = 2197333
+ flags = 0
+ data = length 959, hash 2011C476
+ sample 31:
+ time = 2218666
+ flags = 0
+ data = length 735, hash C2F6C1F9
+ sample 32:
+ time = 2240000
+ flags = 0
+ data = length 865, hash 394C4481
+ sample 33:
+ time = 2261333
+ flags = 0
+ data = length 825, hash 611B513C
+ sample 34:
+ time = 2282666
+ flags = 0
+ data = length 952, hash 5B24AEC2
+ sample 35:
+ time = 2304000
+ flags = 0
+ data = length 984, hash 172B469
+ sample 36:
+ time = 2325333
+ flags = 0
+ data = length 1010, hash D3BBA580
+ sample 37:
+ time = 2346666
+ flags = 0
+ data = length 1067, hash E09AD57F
+ sample 38:
+ time = 2368000
+ flags = 0
+ data = length 1060, hash 5332E90B
+ sample 39:
+ time = 2389333
+ flags = 0
+ data = length 991, hash 48B80B00
+ sample 40:
+ time = 2410666
+ flags = 0
+ data = length 873, hash 696FBD43
+ sample 41:
+ time = 2432000
+ flags = 0
+ data = length 810, hash 7D3D7B29
+ sample 42:
+ time = 2453333
+ flags = 0
+ data = length 897, hash D41D4CB7
+ sample 43:
+ time = 2474666
+ flags = 0
+ data = length 781, hash A589A312
+ sample 44:
+ time = 2496000
+ flags = 0
+ data = length 705, hash FDC5698B
+ sample 45:
+ time = 2517333
+ flags = 0
+ data = length 608, hash F929EDA6
+ sample 46:
+ time = 2538666
+ flags = 0
+ data = length 546, hash A141CD5E
+ sample 47:
+ time = 2560000
+ flags = 0
+ data = length 639, hash BABD5C9F
+ sample 48:
+ time = 2581333
+ flags = 0
+ data = length 561, hash B1AF4D92
+ sample 49:
+ time = 2602666
+ flags = 0
+ data = length 529, hash D0E0EF42
+ sample 50:
+ time = 2624000
+ flags = 0
+ data = length 441, hash E0B1DB1
+ sample 51:
+ time = 2645333
+ flags = 0
+ data = length 393, hash 5A34D1B0
+ sample 52:
+ time = 2666666
+ flags = 0
+ data = length 393, hash B792EA44
+ sample 53:
+ time = 2688000
+ flags = 0
+ data = length 393, hash 7CCE0C94
+ sample 54:
+ time = 2709333
+ flags = 0
+ data = length 393, hash C80E41E0
+ sample 55:
+ time = 2730666
+ flags = 0
+ data = length 393, hash 2C5B4860
+ sample 56:
+ time = 2752000
+ flags = 0
+ data = length 393, hash 9930B5F0
+ sample 57:
+ time = 2773333
+ flags = 0
+ data = length 393, hash B78D1FDA
+ sample 58:
+ time = 2794666
+ flags = 0
+ data = length 393, hash 817AB352
+ sample 59:
+ time = 2816000
+ flags = 0
+ data = length 393, hash EA7D83DF
+ sample 60:
+ time = 2837333
+ flags = 0
+ data = length 393, hash E76873B0
+ sample 61:
+ time = 2858666
+ flags = 0
+ data = length 393, hash 2F48A520
+ sample 62:
+ time = 2880000
+ flags = 0
+ data = length 393, hash C0CBB3EF
+ sample 63:
+ time = 2901333
+ flags = 0
+ data = length 393, hash 255B0E50
+ sample 64:
+ time = 2922666
+ flags = 0
+ data = length 393, hash 9A49C63D
+ sample 65:
+ time = 2944000
+ flags = 0
+ data = length 1516, hash 4994C5B
+ sample 66:
+ time = 2965333
+ flags = 0
+ data = length 1519, hash 5514C06F
+ sample 67:
+ time = 2986666
+ flags = 0
+ data = length 1007, hash F994B3FC
+ sample 68:
+ time = 3008000
+ flags = 0
+ data = length 946, hash 7714E7B1
+ sample 69:
+ time = 3029333
+ flags = 0
+ data = length 985, hash C0E4D841
+ sample 70:
+ time = 3050666
+ flags = 0
+ data = length 1018, hash A18F6981
+ sample 71:
+ time = 3072000
+ flags = 0
+ data = length 790, hash 9F884211
+ sample 72:
+ time = 3093333
+ flags = 0
+ data = length 809, hash 1E0978C9
+ sample 73:
+ time = 3114666
+ flags = 0
+ data = length 791, hash 9BC34342
+ sample 74:
+ time = 3136000
+ flags = 0
+ data = length 806, hash C64EDB12
+ sample 75:
+ time = 3157333
+ flags = 0
+ data = length 757, hash 6C971B85
+ sample 76:
+ time = 3178666
+ flags = 0
+ data = length 950, hash 5509778D
+ sample 77:
+ time = 3200000
+ flags = 0
+ data = length 713, hash 2BD954C8
+ sample 78:
+ time = 3221333
+ flags = 0
+ data = length 800, hash 1C71F2EF
+ sample 79:
+ time = 3242666
+ flags = 0
+ data = length 804, hash BFCCE7C
+ sample 80:
+ time = 3264000
+ flags = 0
+ data = length 876, hash AB1137D9
+ sample 81:
+ time = 3285333
+ flags = 0
+ data = length 957, hash 1CC9B8CA
+ sample 82:
+ time = 3306666
+ flags = 0
+ data = length 563, hash A6FF9D2B
+ sample 83:
+ time = 3328000
+ flags = 0
+ data = length 796, hash B0110276
+ sample 84:
+ time = 3349333
+ flags = 0
+ data = length 863, hash 55EDF24F
+ sample 85:
+ time = 3370666
+ flags = 0
+ data = length 701, hash 3AE3C48F
+ sample 86:
+ time = 3392000
+ flags = 0
+ data = length 934, hash 2CC1C6E9
+ sample 87:
+ time = 3413333
+ flags = 0
+ data = length 967, hash 34E61D20
+ sample 88:
+ time = 3434666
+ flags = 0
+ data = length 1518, hash C0EC5B50
+ sample 89:
+ time = 3456000
+ flags = 0
+ data = length 1474, hash 6EC36C24
+ sample 90:
+ time = 3477333
+ flags = 0
+ data = length 1475, hash 6F1FD2AB
+ sample 91:
+ time = 3498666
+ flags = 0
+ data = length 1451, hash B58F4D31
+ sample 92:
+ time = 3520000
+ flags = 0
+ data = length 526, hash FC424B78
+ sample 93:
+ time = 3541333
+ flags = 0
+ data = length 598, hash 4BDB2753
+ sample 94:
+ time = 3562666
+ flags = 0
+ data = length 627, hash 48327C83
+ sample 95:
+ time = 3584000
+ flags = 0
+ data = length 551, hash 691D6CB2
+ sample 96:
+ time = 3605333
+ flags = 0
+ data = length 594, hash 86D1E249
+ sample 97:
+ time = 3626666
+ flags = 0
+ data = length 760, hash AF4B774F
+ sample 98:
+ time = 3648000
+ flags = 0
+ data = length 568, hash F4765276
+ sample 99:
+ time = 3669333
+ flags = 0
+ data = length 722, hash 2A72C44B
+ sample 100:
+ time = 3690666
+ flags = 0
+ data = length 602, hash 5D7135BB
+ sample 101:
+ time = 3712000
+ flags = 0
+ data = length 694, hash 8C5CE9CF
+ sample 102:
+ time = 3733333
+ flags = 0
+ data = length 725, hash 4EBFA7A8
+ sample 103:
+ time = 3754666
+ flags = 0
+ data = length 666, hash 2B1379B
+ sample 104:
+ time = 3776000
+ flags = 0
+ data = length 846, hash 57FC9178
+ sample 105:
+ time = 3797333
+ flags = 0
+ data = length 881, hash 19FDF109
+ sample 106:
+ time = 3818666
+ flags = 0
+ data = length 512, hash 49FD56DF
+ sample 107:
+ time = 3840000
+ flags = 0
+ data = length 840, hash FC78237B
+ sample 108:
+ time = 3861333
+ flags = 0
+ data = length 1075, hash 5031D568
+ sample 109:
+ time = 3882666
+ flags = 0
+ data = length 716, hash A509900D
+ sample 110:
+ time = 3904000
+ flags = 0
+ data = length 798, hash 386EB6B5
+ sample 111:
+ time = 3925333
+ flags = 0
+ data = length 969, hash 1E781925
+ sample 112:
+ time = 3946666
+ flags = 0
+ data = length 1444, hash F2DD1E7C
+ sample 113:
+ time = 3968000
+ flags = 0
+ data = length 399, hash 3FE1EAB6
+ sample 114:
+ time = 3989333
+ flags = 1
+ data = length 776, hash BEA73ED6
+ sample 115:
+ time = 4010666
+ flags = 0
+ data = length 875, hash 173C290C
+ sample 116:
+ time = 4032000
+ flags = 0
+ data = length 787, hash 531B9CB3
+ sample 117:
+ time = 4053333
+ flags = 0
+ data = length 737, hash 75CB4A48
+ sample 118:
+ time = 4074666
+ flags = 0
+ data = length 603, hash DDE525EE
+ sample 119:
+ time = 4096000
+ flags = 0
+ data = length 645, hash F4666418
+ sample 120:
+ time = 4117333
+ flags = 0
+ data = length 704, hash F7E18921
+ sample 121:
+ time = 4138666
+ flags = 0
+ data = length 802, hash 822433B5
+ sample 122:
+ time = 4160000
+ flags = 0
+ data = length 990, hash E4712448
+ sample 123:
+ time = 4181333
+ flags = 0
+ data = length 602, hash 3265AF8E
+ sample 124:
+ time = 4202666
+ flags = 0
+ data = length 790, hash 6EF432BA
+ sample 125:
+ time = 4224000
+ flags = 0
+ data = length 746, hash E20E7B36
+ sample 126:
+ time = 4245333
+ flags = 0
+ data = length 439, hash 8C32DDA0
+ sample 127:
+ time = 4266666
+ flags = 0
+ data = length 932, hash AF323520
+ sample 128:
+ time = 4288000
+ flags = 0
+ data = length 1003, hash 3D70121B
+ sample 129:
+ time = 4309333
+ flags = 0
+ data = length 1158, hash 1215442E
+ sample 130:
+ time = 4330666
+ flags = 0
+ data = length 1047, hash 2A517450
+ sample 131:
+ time = 4352000
+ flags = 0
+ data = length 1123, hash 33387B5F
+ sample 132:
+ time = 4373333
+ flags = 0
+ data = length 1033, hash D82CF575
+ sample 133:
+ time = 4394666
+ flags = 0
+ data = length 1009, hash 30DC6A02
+ sample 134:
+ time = 4416000
+ flags = 0
+ data = length 1033, hash 82127054
+ sample 135:
+ time = 4437333
+ flags = 0
+ data = length 761, hash 4A5EA11F
+ sample 136:
+ time = 4458666
+ flags = 0
+ data = length 708, hash 53FA04E4
+ sample 137:
+ time = 4480000
+ flags = 0
+ data = length 750, hash 44F541A6
+ sample 138:
+ time = 4501333
+ flags = 0
+ data = length 902, hash 6688117A
+ sample 139:
+ time = 4522666
+ flags = 0
+ data = length 716, hash A19D938D
+ sample 140:
+ time = 4544000
+ flags = 0
+ data = length 395, hash 4EF7E6DE
+ sample 141:
+ time = 4565333
+ flags = 0
+ data = length 677, hash 57DF6F41
+ sample 142:
+ time = 4586666
+ flags = 0
+ data = length 566, hash 515BD3F0
+ sample 143:
+ time = 4608000
+ flags = 0
+ data = length 772, hash 957617C9
+ sample 144:
+ time = 4629333
+ flags = 0
+ data = length 777, hash 6BB742A3
+ sample 145:
+ time = 4650666
+ flags = 0
+ data = length 672, hash 97BA98A3
+ sample 146:
+ time = 4672000
+ flags = 0
+ data = length 695, hash F8190CDE
+ sample 147:
+ time = 4693333
+ flags = 0
+ data = length 668, hash 6D62BE54
+ sample 148:
+ time = 4714666
+ flags = 0
+ data = length 662, hash 36D886CD
+ sample 149:
+ time = 4736000
+ flags = 0
+ data = length 678, hash 87772FCA
+ sample 150:
+ time = 4757333
+ flags = 0
+ data = length 946, hash BBFB4C98
+ sample 151:
+ time = 4778666
+ flags = 0
+ data = length 594, hash CEDCD36C
+ sample 152:
+ time = 4800000
+ flags = 0
+ data = length 499, hash 3B933D5B
+ sample 153:
+ time = 4821333
+ flags = 0
+ data = length 784, hash 97BC8486
+ sample 154:
+ time = 4842666
+ flags = 0
+ data = length 510, hash 9A082552
+ sample 155:
+ time = 4864000
+ flags = 0
+ data = length 741, hash E17DAE88
+ sample 156:
+ time = 4885333
+ flags = 0
+ data = length 656, hash C8CC5738
+ sample 157:
+ time = 4906666
+ flags = 0
+ data = length 672, hash 84C28F77
+ sample 158:
+ time = 4928000
+ flags = 0
+ data = length 559, hash B563D0ED
+ sample 159:
+ time = 4949333
+ flags = 0
+ data = length 1503, hash 183821B0
+ sample 160:
+ time = 4970666
+ flags = 0
+ data = length 393, hash D807573D
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.2.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.2.dump
new file mode 100644
index 0000000000..d4f5dcac85
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.2.dump
@@ -0,0 +1,349 @@
+seekMap:
+ isSeekable = true
+ duration = 4970666
+ getPosition(0) = [[timeUs=0, position=0]]
+ getPosition(1) = [[timeUs=1, position=0]]
+ getPosition(2485333) = [[timeUs=2485333, position=102149]]
+ getPosition(4970666) = [[timeUs=4970666, position=204487]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 36340
+ sample count = 83
+ format 0:
+ id = 1/257
+ sampleMimeType = audio/vnd.dts.uhd;profile=p2
+ channelCount = 2
+ sampleRate = 48000
+ sample 0:
+ time = 3221333
+ flags = 0
+ data = length 7, hash 5EB61B9D
+ sample 1:
+ time = 3242666
+ flags = 0
+ data = length 7, hash 5EB61B1D
+ sample 2:
+ time = 3264000
+ flags = 0
+ data = length 7, hash 5EB61C34
+ sample 3:
+ time = 3285333
+ flags = 0
+ data = length 7, hash 5EB61D8A
+ sample 4:
+ time = 3306666
+ flags = 0
+ data = length 7, hash 5EB6175B
+ sample 5:
+ time = 3328000
+ flags = 0
+ data = length 7, hash 5EB61AFE
+ sample 6:
+ time = 3349333
+ flags = 0
+ data = length 7, hash 5EB61C75
+ sample 7:
+ time = 3370666
+ flags = 0
+ data = length 7, hash 5EB619AA
+ sample 8:
+ time = 3392000
+ flags = 0
+ data = length 7, hash 5EB61D4D
+ sample 9:
+ time = 3413333
+ flags = 0
+ data = length 7, hash 5EB61E08
+ sample 10:
+ time = 3434666
+ flags = 0
+ data = length 7, hash 5EB62624
+ sample 11:
+ time = 3456000
+ flags = 0
+ data = length 7, hash 5EB62609
+ sample 12:
+ time = 3477333
+ flags = 0
+ data = length 7, hash 5EB62529
+ sample 13:
+ time = 3498666
+ flags = 0
+ data = length 7, hash 5EB624CC
+ sample 14:
+ time = 3520000
+ flags = 0
+ data = length 7, hash 5EB61720
+ sample 15:
+ time = 3541333
+ flags = 0
+ data = length 7, hash 5EB61837
+ sample 16:
+ time = 3562666
+ flags = 0
+ data = length 7, hash 5EB61853
+ sample 17:
+ time = 3584000
+ flags = 0
+ data = length 7, hash 5EB617BC
+ sample 18:
+ time = 3605333
+ flags = 0
+ data = length 7, hash 5EB618B7
+ sample 19:
+ time = 3626666
+ flags = 0
+ data = length 7, hash 5EB61B02
+ sample 20:
+ time = 3648000
+ flags = 0
+ data = length 7, hash 5EB6181A
+ sample 21:
+ time = 3669333
+ flags = 0
+ data = length 7, hash 5EB61AA7
+ sample 22:
+ time = 3690666
+ flags = 0
+ data = length 7, hash 5EB618D6
+ sample 23:
+ time = 3712000
+ flags = 0
+ data = length 7, hash 5EB619AB
+ sample 24:
+ time = 3733333
+ flags = 0
+ data = length 7, hash 5EB61A07
+ sample 25:
+ time = 3754666
+ flags = 0
+ data = length 7, hash 5EB619CE
+ sample 26:
+ time = 3776000
+ flags = 0
+ data = length 7, hash 5EB61BF8
+ sample 27:
+ time = 3797333
+ flags = 0
+ data = length 7, hash 5EB61CF3
+ sample 28:
+ time = 3818666
+ flags = 0
+ data = length 6, hash F28A001F
+ sample 29:
+ time = 3840000
+ flags = 0
+ data = length 7, hash 5EB61C38
+ sample 30:
+ time = 3861333
+ flags = 0
+ data = length 7, hash 5EB61F1B
+ sample 31:
+ time = 3882666
+ flags = 0
+ data = length 7, hash 5EB619C8
+ sample 32:
+ time = 3904000
+ flags = 0
+ data = length 7, hash 5EB61B3E
+ sample 33:
+ time = 3925333
+ flags = 0
+ data = length 7, hash 5EB61E48
+ sample 34:
+ time = 3946666
+ flags = 0
+ data = length 7, hash 5EB624CD
+ sample 35:
+ time = 3968000
+ flags = 0
+ data = length 6, hash F289FFAE
+ sample 36:
+ time = 3989333
+ flags = 1
+ data = length 776, hash BEA73ED6
+ sample 37:
+ time = 4010666
+ flags = 0
+ data = length 875, hash 173C290C
+ sample 38:
+ time = 4032000
+ flags = 0
+ data = length 787, hash 531B9CB3
+ sample 39:
+ time = 4053333
+ flags = 0
+ data = length 737, hash 75CB4A48
+ sample 40:
+ time = 4074666
+ flags = 0
+ data = length 603, hash DDE525EE
+ sample 41:
+ time = 4096000
+ flags = 0
+ data = length 645, hash F4666418
+ sample 42:
+ time = 4117333
+ flags = 0
+ data = length 704, hash F7E18921
+ sample 43:
+ time = 4138666
+ flags = 0
+ data = length 802, hash 822433B5
+ sample 44:
+ time = 4160000
+ flags = 0
+ data = length 990, hash E4712448
+ sample 45:
+ time = 4181333
+ flags = 0
+ data = length 602, hash 3265AF8E
+ sample 46:
+ time = 4202666
+ flags = 0
+ data = length 790, hash 6EF432BA
+ sample 47:
+ time = 4224000
+ flags = 0
+ data = length 746, hash E20E7B36
+ sample 48:
+ time = 4245333
+ flags = 0
+ data = length 439, hash 8C32DDA0
+ sample 49:
+ time = 4266666
+ flags = 0
+ data = length 932, hash AF323520
+ sample 50:
+ time = 4288000
+ flags = 0
+ data = length 1003, hash 3D70121B
+ sample 51:
+ time = 4309333
+ flags = 0
+ data = length 1158, hash 1215442E
+ sample 52:
+ time = 4330666
+ flags = 0
+ data = length 1047, hash 2A517450
+ sample 53:
+ time = 4352000
+ flags = 0
+ data = length 1123, hash 33387B5F
+ sample 54:
+ time = 4373333
+ flags = 0
+ data = length 1033, hash D82CF575
+ sample 55:
+ time = 4394666
+ flags = 0
+ data = length 1009, hash 30DC6A02
+ sample 56:
+ time = 4416000
+ flags = 0
+ data = length 1033, hash 82127054
+ sample 57:
+ time = 4437333
+ flags = 0
+ data = length 761, hash 4A5EA11F
+ sample 58:
+ time = 4458666
+ flags = 0
+ data = length 708, hash 53FA04E4
+ sample 59:
+ time = 4480000
+ flags = 0
+ data = length 750, hash 44F541A6
+ sample 60:
+ time = 4501333
+ flags = 0
+ data = length 902, hash 6688117A
+ sample 61:
+ time = 4522666
+ flags = 0
+ data = length 716, hash A19D938D
+ sample 62:
+ time = 4544000
+ flags = 0
+ data = length 395, hash 4EF7E6DE
+ sample 63:
+ time = 4565333
+ flags = 0
+ data = length 677, hash 57DF6F41
+ sample 64:
+ time = 4586666
+ flags = 0
+ data = length 566, hash 515BD3F0
+ sample 65:
+ time = 4608000
+ flags = 0
+ data = length 772, hash 957617C9
+ sample 66:
+ time = 4629333
+ flags = 0
+ data = length 777, hash 6BB742A3
+ sample 67:
+ time = 4650666
+ flags = 0
+ data = length 672, hash 97BA98A3
+ sample 68:
+ time = 4672000
+ flags = 0
+ data = length 695, hash F8190CDE
+ sample 69:
+ time = 4693333
+ flags = 0
+ data = length 668, hash 6D62BE54
+ sample 70:
+ time = 4714666
+ flags = 0
+ data = length 662, hash 36D886CD
+ sample 71:
+ time = 4736000
+ flags = 0
+ data = length 678, hash 87772FCA
+ sample 72:
+ time = 4757333
+ flags = 0
+ data = length 946, hash BBFB4C98
+ sample 73:
+ time = 4778666
+ flags = 0
+ data = length 594, hash CEDCD36C
+ sample 74:
+ time = 4800000
+ flags = 0
+ data = length 499, hash 3B933D5B
+ sample 75:
+ time = 4821333
+ flags = 0
+ data = length 784, hash 97BC8486
+ sample 76:
+ time = 4842666
+ flags = 0
+ data = length 510, hash 9A082552
+ sample 77:
+ time = 4864000
+ flags = 0
+ data = length 741, hash E17DAE88
+ sample 78:
+ time = 4885333
+ flags = 0
+ data = length 656, hash C8CC5738
+ sample 79:
+ time = 4906666
+ flags = 0
+ data = length 672, hash 84C28F77
+ sample 80:
+ time = 4928000
+ flags = 0
+ data = length 559, hash B563D0ED
+ sample 81:
+ time = 4949333
+ flags = 0
+ data = length 1503, hash 183821B0
+ sample 82:
+ time = 4970666
+ flags = 0
+ data = length 393, hash D807573D
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.3.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.3.dump
new file mode 100644
index 0000000000..7250b07952
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.3.dump
@@ -0,0 +1,37 @@
+seekMap:
+ isSeekable = true
+ duration = 4970666
+ getPosition(0) = [[timeUs=0, position=0]]
+ getPosition(1) = [[timeUs=1, position=0]]
+ getPosition(2485333) = [[timeUs=2485333, position=102149]]
+ getPosition(4970666) = [[timeUs=4970666, position=204487]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 34
+ sample count = 5
+ format 0:
+ id = 1/257
+ sampleMimeType = audio/vnd.dts.uhd;profile=p2
+ channelCount = 2
+ sampleRate = 48000
+ sample 0:
+ time = 4885333
+ flags = 0
+ data = length 7, hash 5EB6196F
+ sample 1:
+ time = 4906666
+ flags = 0
+ data = length 7, hash 5EB619AD
+ sample 2:
+ time = 4928000
+ flags = 0
+ data = length 7, hash 5EB617DB
+ sample 3:
+ time = 4949333
+ flags = 0
+ data = length 7, hash 5EB62625
+ sample 4:
+ time = 4970666
+ flags = 0
+ data = length 6, hash F289FFA8
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.unknown_length.dump
new file mode 100644
index 0000000000..0900c65dc1
--- /dev/null
+++ b/libraries/test_data/src/test/assets/extractordumps/ts/sample_dts_uhd.ts.unknown_length.dump
@@ -0,0 +1,950 @@
+seekMap:
+ isSeekable = false
+ duration = UNSET TIME
+ getPosition(0) = [[timeUs=0, position=0]]
+numberOfTracks = 1
+track 257:
+ total output bytes = 179682
+ sample count = 234
+ format 0:
+ id = 1/257
+ sampleMimeType = audio/vnd.dts.uhd;profile=p2
+ channelCount = 2
+ sampleRate = 48000
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 776, hash E68E7565
+ sample 1:
+ time = 21333
+ flags = 0
+ data = length 765, hash 713DA83C
+ sample 2:
+ time = 42666
+ flags = 0
+ data = length 771, hash E672DC
+ sample 3:
+ time = 64000
+ flags = 0
+ data = length 769, hash 83F90436
+ sample 4:
+ time = 85333
+ flags = 0
+ data = length 765, hash 5C8A17D1
+ sample 5:
+ time = 106666
+ flags = 0
+ data = length 771, hash 5528EC74
+ sample 6:
+ time = 128000
+ flags = 0
+ data = length 767, hash 954FC4B5
+ sample 7:
+ time = 149333
+ flags = 0
+ data = length 767, hash 5C2089BB
+ sample 8:
+ time = 170666
+ flags = 0
+ data = length 771, hash 21AF360B
+ sample 9:
+ time = 192000
+ flags = 0
+ data = length 766, hash 2C363E9B
+ sample 10:
+ time = 213333
+ flags = 0
+ data = length 767, hash 812E9BF7
+ sample 11:
+ time = 234666
+ flags = 0
+ data = length 770, hash ED9D5378
+ sample 12:
+ time = 256000
+ flags = 0
+ data = length 769, hash FBF9DD0E
+ sample 13:
+ time = 277333
+ flags = 0
+ data = length 767, hash 5E7822E9
+ sample 14:
+ time = 298666
+ flags = 0
+ data = length 770, hash 97EDBA77
+ sample 15:
+ time = 320000
+ flags = 0
+ data = length 767, hash 1739FF77
+ sample 16:
+ time = 341333
+ flags = 0
+ data = length 766, hash 99225978
+ sample 17:
+ time = 362666
+ flags = 0
+ data = length 772, hash 1971320D
+ sample 18:
+ time = 384000
+ flags = 0
+ data = length 768, hash F8C4F6A2
+ sample 19:
+ time = 405333
+ flags = 0
+ data = length 765, hash 225434F4
+ sample 20:
+ time = 426666
+ flags = 0
+ data = length 770, hash E232FA0D
+ sample 21:
+ time = 448000
+ flags = 0
+ data = length 767, hash 721418FA
+ sample 22:
+ time = 469333
+ flags = 0
+ data = length 768, hash E52E3AD0
+ sample 23:
+ time = 490666
+ flags = 0
+ data = length 772, hash 9D0F9C8C
+ sample 24:
+ time = 512000
+ flags = 0
+ data = length 767, hash EAFEDC
+ sample 25:
+ time = 533333
+ flags = 0
+ data = length 766, hash CAFCB522
+ sample 26:
+ time = 554666
+ flags = 0
+ data = length 770, hash EB1806AF
+ sample 27:
+ time = 576000
+ flags = 0
+ data = length 768, hash F022F870
+ sample 28:
+ time = 597333
+ flags = 0
+ data = length 767, hash 2DB31DD6
+ sample 29:
+ time = 618666
+ flags = 0
+ data = length 770, hash 3168DE65
+ sample 30:
+ time = 640000
+ flags = 0
+ data = length 767, hash 1D25591B
+ sample 31:
+ time = 661333
+ flags = 0
+ data = length 766, hash 2DB4AF54
+ sample 32:
+ time = 682666
+ flags = 0
+ data = length 772, hash 9B9176A8
+ sample 33:
+ time = 704000
+ flags = 0
+ data = length 768, hash F49BD7C2
+ sample 34:
+ time = 725333
+ flags = 0
+ data = length 765, hash 4D1A1A5B
+ sample 35:
+ time = 746666
+ flags = 0
+ data = length 771, hash EED28144
+ sample 36:
+ time = 768000
+ flags = 0
+ data = length 767, hash DE09C384
+ sample 37:
+ time = 789333
+ flags = 0
+ data = length 767, hash 5B154F22
+ sample 38:
+ time = 810666
+ flags = 0
+ data = length 772, hash 7621AB2B
+ sample 39:
+ time = 832000
+ flags = 0
+ data = length 767, hash 3B503329
+ sample 40:
+ time = 853333
+ flags = 0
+ data = length 766, hash 6D5F86F0
+ sample 41:
+ time = 874666
+ flags = 0
+ data = length 770, hash 122B33CF
+ sample 42:
+ time = 896000
+ flags = 0
+ data = length 769, hash 701BED85
+ sample 43:
+ time = 917333
+ flags = 0
+ data = length 767, hash 66F75279
+ sample 44:
+ time = 938666
+ flags = 0
+ data = length 769, hash 8EB616E3
+ sample 45:
+ time = 960000
+ flags = 0
+ data = length 767, hash C04E8FCB
+ sample 46:
+ time = 981333
+ flags = 0
+ data = length 765, hash 568E8728
+ sample 47:
+ time = 1002666
+ flags = 0
+ data = length 772, hash C23993F0
+ sample 48:
+ time = 1024000
+ flags = 0
+ data = length 768, hash 518ACD0D
+ sample 49:
+ time = 1045333
+ flags = 0
+ data = length 765, hash 297ACB8E
+ sample 50:
+ time = 1066666
+ flags = 0
+ data = length 770, hash 676ED3C1
+ sample 51:
+ time = 1088000
+ flags = 0
+ data = length 767, hash 836598AA
+ sample 52:
+ time = 1109333
+ flags = 0
+ data = length 767, hash F1536245
+ sample 53:
+ time = 1130666
+ flags = 0
+ data = length 772, hash 12AC3BCE
+ sample 54:
+ time = 1152000
+ flags = 0
+ data = length 766, hash 9948C9C
+ sample 55:
+ time = 1173333
+ flags = 0
+ data = length 766, hash B42ADF4E
+ sample 56:
+ time = 1194666
+ flags = 0
+ data = length 770, hash 67AC576F
+ sample 57:
+ time = 1216000
+ flags = 0
+ data = length 769, hash B843E663
+ sample 58:
+ time = 1237333
+ flags = 0
+ data = length 767, hash 432AD19
+ sample 59:
+ time = 1258666
+ flags = 0
+ data = length 770, hash 14BA5FE6
+ sample 60:
+ time = 1280000
+ flags = 0
+ data = length 768, hash A0D32F30
+ sample 61:
+ time = 1301333
+ flags = 0
+ data = length 765, hash 4BF180A9
+ sample 62:
+ time = 1322666
+ flags = 0
+ data = length 772, hash 1C613DF7
+ sample 63:
+ time = 1344000
+ flags = 0
+ data = length 768, hash F30F6A09
+ sample 64:
+ time = 1365333
+ flags = 0
+ data = length 765, hash D40AC240
+ sample 65:
+ time = 1386666
+ flags = 0
+ data = length 770, hash 1E64D8FE
+ sample 66:
+ time = 1408000
+ flags = 0
+ data = length 767, hash 5DFE9BC3
+ sample 67:
+ time = 1429333
+ flags = 0
+ data = length 767, hash 19A018BD
+ sample 68:
+ time = 1450666
+ flags = 0
+ data = length 772, hash 363BF537
+ sample 69:
+ time = 1472000
+ flags = 0
+ data = length 766, hash 4832AF5B
+ sample 70:
+ time = 1493333
+ flags = 0
+ data = length 766, hash 3CBED44A
+ sample 71:
+ time = 1514666
+ flags = 0
+ data = length 771, hash C5E800AD
+ sample 72:
+ time = 1536000
+ flags = 0
+ data = length 786, hash DA64F1EE
+ sample 73:
+ time = 1557333
+ flags = 0
+ data = length 785, hash 4771990A
+ sample 74:
+ time = 1578666
+ flags = 0
+ data = length 788, hash CBB53C84
+ sample 75:
+ time = 1600000
+ flags = 0
+ data = length 786, hash AF107053
+ sample 76:
+ time = 1621333
+ flags = 0
+ data = length 784, hash 7418BBD7
+ sample 77:
+ time = 1642666
+ flags = 0
+ data = length 790, hash 35A5E570
+ sample 78:
+ time = 1664000
+ flags = 0
+ data = length 787, hash 4F662158
+ sample 79:
+ time = 1685333
+ flags = 0
+ data = length 783, hash A574B045
+ sample 80:
+ time = 1706666
+ flags = 0
+ data = length 789, hash 211311A9
+ sample 81:
+ time = 1728000
+ flags = 0
+ data = length 785, hash 4A7E6F21
+ sample 82:
+ time = 1749333
+ flags = 0
+ data = length 786, hash DE8047C6
+ sample 83:
+ time = 1770666
+ flags = 0
+ data = length 789, hash E778E75A
+ sample 84:
+ time = 1792000
+ flags = 0
+ data = length 785, hash AD3A50F8
+ sample 85:
+ time = 1813333
+ flags = 0
+ data = length 783, hash 6DCEAA5C
+ sample 86:
+ time = 1834666
+ flags = 0
+ data = length 787, hash D5E2A255
+ sample 87:
+ time = 1856000
+ flags = 0
+ data = length 787, hash E9138642
+ sample 88:
+ time = 1877333
+ flags = 0
+ data = length 785, hash B2D94BAF
+ sample 89:
+ time = 1898666
+ flags = 0
+ data = length 789, hash F0F3BBDB
+ sample 90:
+ time = 1920000
+ flags = 0
+ data = length 786, hash B0D62A20
+ sample 91:
+ time = 1941333
+ flags = 0
+ data = length 767, hash 710CB0E4
+ sample 92:
+ time = 1962666
+ flags = 0
+ data = length 400, hash 8A0182CB
+ sample 93:
+ time = 1984000
+ flags = 1
+ data = length 402, hash E60D79A1
+ sample 94:
+ time = 2005333
+ flags = 0
+ data = length 394, hash 585703B6
+ sample 95:
+ time = 2026666
+ flags = 0
+ data = length 394, hash 50CA225B
+ sample 96:
+ time = 2048000
+ flags = 0
+ data = length 395, hash E0229BF0
+ sample 97:
+ time = 2069333
+ flags = 0
+ data = length 501, hash F8930C9C
+ sample 98:
+ time = 2090666
+ flags = 0
+ data = length 667, hash C05CCFA2
+ sample 99:
+ time = 2112000
+ flags = 0
+ data = length 918, hash 5832371D
+ sample 100:
+ time = 2133333
+ flags = 0
+ data = length 1101, hash 1F97D86E
+ sample 101:
+ time = 2154666
+ flags = 0
+ data = length 1006, hash 974AC450
+ sample 102:
+ time = 2176000
+ flags = 0
+ data = length 1153, hash 22CA9C79
+ sample 103:
+ time = 2197333
+ flags = 0
+ data = length 959, hash 2011C476
+ sample 104:
+ time = 2218666
+ flags = 0
+ data = length 735, hash C2F6C1F9
+ sample 105:
+ time = 2240000
+ flags = 0
+ data = length 865, hash 394C4481
+ sample 106:
+ time = 2261333
+ flags = 0
+ data = length 825, hash 611B513C
+ sample 107:
+ time = 2282666
+ flags = 0
+ data = length 952, hash 5B24AEC2
+ sample 108:
+ time = 2304000
+ flags = 0
+ data = length 984, hash 172B469
+ sample 109:
+ time = 2325333
+ flags = 0
+ data = length 1010, hash D3BBA580
+ sample 110:
+ time = 2346666
+ flags = 0
+ data = length 1067, hash E09AD57F
+ sample 111:
+ time = 2368000
+ flags = 0
+ data = length 1060, hash 5332E90B
+ sample 112:
+ time = 2389333
+ flags = 0
+ data = length 991, hash 48B80B00
+ sample 113:
+ time = 2410666
+ flags = 0
+ data = length 873, hash 696FBD43
+ sample 114:
+ time = 2432000
+ flags = 0
+ data = length 810, hash 7D3D7B29
+ sample 115:
+ time = 2453333
+ flags = 0
+ data = length 897, hash D41D4CB7
+ sample 116:
+ time = 2474666
+ flags = 0
+ data = length 781, hash A589A312
+ sample 117:
+ time = 2496000
+ flags = 0
+ data = length 705, hash FDC5698B
+ sample 118:
+ time = 2517333
+ flags = 0
+ data = length 608, hash F929EDA6
+ sample 119:
+ time = 2538666
+ flags = 0
+ data = length 546, hash A141CD5E
+ sample 120:
+ time = 2560000
+ flags = 0
+ data = length 639, hash BABD5C9F
+ sample 121:
+ time = 2581333
+ flags = 0
+ data = length 561, hash B1AF4D92
+ sample 122:
+ time = 2602666
+ flags = 0
+ data = length 529, hash D0E0EF42
+ sample 123:
+ time = 2624000
+ flags = 0
+ data = length 441, hash E0B1DB1
+ sample 124:
+ time = 2645333
+ flags = 0
+ data = length 393, hash 5A34D1B0
+ sample 125:
+ time = 2666666
+ flags = 0
+ data = length 393, hash B792EA44
+ sample 126:
+ time = 2688000
+ flags = 0
+ data = length 393, hash 7CCE0C94
+ sample 127:
+ time = 2709333
+ flags = 0
+ data = length 393, hash C80E41E0
+ sample 128:
+ time = 2730666
+ flags = 0
+ data = length 393, hash 2C5B4860
+ sample 129:
+ time = 2752000
+ flags = 0
+ data = length 393, hash 9930B5F0
+ sample 130:
+ time = 2773333
+ flags = 0
+ data = length 393, hash B78D1FDA
+ sample 131:
+ time = 2794666
+ flags = 0
+ data = length 393, hash 817AB352
+ sample 132:
+ time = 2816000
+ flags = 0
+ data = length 393, hash EA7D83DF
+ sample 133:
+ time = 2837333
+ flags = 0
+ data = length 393, hash E76873B0
+ sample 134:
+ time = 2858666
+ flags = 0
+ data = length 393, hash 2F48A520
+ sample 135:
+ time = 2880000
+ flags = 0
+ data = length 393, hash C0CBB3EF
+ sample 136:
+ time = 2901333
+ flags = 0
+ data = length 393, hash 255B0E50
+ sample 137:
+ time = 2922666
+ flags = 0
+ data = length 393, hash 9A49C63D
+ sample 138:
+ time = 2944000
+ flags = 0
+ data = length 1516, hash 4994C5B
+ sample 139:
+ time = 2965333
+ flags = 0
+ data = length 1519, hash 5514C06F
+ sample 140:
+ time = 2986666
+ flags = 0
+ data = length 1007, hash F994B3FC
+ sample 141:
+ time = 3008000
+ flags = 0
+ data = length 946, hash 7714E7B1
+ sample 142:
+ time = 3029333
+ flags = 0
+ data = length 985, hash C0E4D841
+ sample 143:
+ time = 3050666
+ flags = 0
+ data = length 1018, hash A18F6981
+ sample 144:
+ time = 3072000
+ flags = 0
+ data = length 790, hash 9F884211
+ sample 145:
+ time = 3093333
+ flags = 0
+ data = length 809, hash 1E0978C9
+ sample 146:
+ time = 3114666
+ flags = 0
+ data = length 791, hash 9BC34342
+ sample 147:
+ time = 3136000
+ flags = 0
+ data = length 806, hash C64EDB12
+ sample 148:
+ time = 3157333
+ flags = 0
+ data = length 757, hash 6C971B85
+ sample 149:
+ time = 3178666
+ flags = 0
+ data = length 950, hash 5509778D
+ sample 150:
+ time = 3200000
+ flags = 0
+ data = length 713, hash 2BD954C8
+ sample 151:
+ time = 3221333
+ flags = 0
+ data = length 800, hash 1C71F2EF
+ sample 152:
+ time = 3242666
+ flags = 0
+ data = length 804, hash BFCCE7C
+ sample 153:
+ time = 3264000
+ flags = 0
+ data = length 876, hash AB1137D9
+ sample 154:
+ time = 3285333
+ flags = 0
+ data = length 957, hash 1CC9B8CA
+ sample 155:
+ time = 3306666
+ flags = 0
+ data = length 563, hash A6FF9D2B
+ sample 156:
+ time = 3328000
+ flags = 0
+ data = length 796, hash B0110276
+ sample 157:
+ time = 3349333
+ flags = 0
+ data = length 863, hash 55EDF24F
+ sample 158:
+ time = 3370666
+ flags = 0
+ data = length 701, hash 3AE3C48F
+ sample 159:
+ time = 3392000
+ flags = 0
+ data = length 934, hash 2CC1C6E9
+ sample 160:
+ time = 3413333
+ flags = 0
+ data = length 967, hash 34E61D20
+ sample 161:
+ time = 3434666
+ flags = 0
+ data = length 1518, hash C0EC5B50
+ sample 162:
+ time = 3456000
+ flags = 0
+ data = length 1474, hash 6EC36C24
+ sample 163:
+ time = 3477333
+ flags = 0
+ data = length 1475, hash 6F1FD2AB
+ sample 164:
+ time = 3498666
+ flags = 0
+ data = length 1451, hash B58F4D31
+ sample 165:
+ time = 3520000
+ flags = 0
+ data = length 526, hash FC424B78
+ sample 166:
+ time = 3541333
+ flags = 0
+ data = length 598, hash 4BDB2753
+ sample 167:
+ time = 3562666
+ flags = 0
+ data = length 627, hash 48327C83
+ sample 168:
+ time = 3584000
+ flags = 0
+ data = length 551, hash 691D6CB2
+ sample 169:
+ time = 3605333
+ flags = 0
+ data = length 594, hash 86D1E249
+ sample 170:
+ time = 3626666
+ flags = 0
+ data = length 760, hash AF4B774F
+ sample 171:
+ time = 3648000
+ flags = 0
+ data = length 568, hash F4765276
+ sample 172:
+ time = 3669333
+ flags = 0
+ data = length 722, hash 2A72C44B
+ sample 173:
+ time = 3690666
+ flags = 0
+ data = length 602, hash 5D7135BB
+ sample 174:
+ time = 3712000
+ flags = 0
+ data = length 694, hash 8C5CE9CF
+ sample 175:
+ time = 3733333
+ flags = 0
+ data = length 725, hash 4EBFA7A8
+ sample 176:
+ time = 3754666
+ flags = 0
+ data = length 666, hash 2B1379B
+ sample 177:
+ time = 3776000
+ flags = 0
+ data = length 846, hash 57FC9178
+ sample 178:
+ time = 3797333
+ flags = 0
+ data = length 881, hash 19FDF109
+ sample 179:
+ time = 3818666
+ flags = 0
+ data = length 512, hash 49FD56DF
+ sample 180:
+ time = 3840000
+ flags = 0
+ data = length 840, hash FC78237B
+ sample 181:
+ time = 3861333
+ flags = 0
+ data = length 1075, hash 5031D568
+ sample 182:
+ time = 3882666
+ flags = 0
+ data = length 716, hash A509900D
+ sample 183:
+ time = 3904000
+ flags = 0
+ data = length 798, hash 386EB6B5
+ sample 184:
+ time = 3925333
+ flags = 0
+ data = length 969, hash 1E781925
+ sample 185:
+ time = 3946666
+ flags = 0
+ data = length 1444, hash F2DD1E7C
+ sample 186:
+ time = 3968000
+ flags = 0
+ data = length 399, hash 3FE1EAB6
+ sample 187:
+ time = 3989333
+ flags = 1
+ data = length 776, hash BEA73ED6
+ sample 188:
+ time = 4010666
+ flags = 0
+ data = length 875, hash 173C290C
+ sample 189:
+ time = 4032000
+ flags = 0
+ data = length 787, hash 531B9CB3
+ sample 190:
+ time = 4053333
+ flags = 0
+ data = length 737, hash 75CB4A48
+ sample 191:
+ time = 4074666
+ flags = 0
+ data = length 603, hash DDE525EE
+ sample 192:
+ time = 4096000
+ flags = 0
+ data = length 645, hash F4666418
+ sample 193:
+ time = 4117333
+ flags = 0
+ data = length 704, hash F7E18921
+ sample 194:
+ time = 4138666
+ flags = 0
+ data = length 802, hash 822433B5
+ sample 195:
+ time = 4160000
+ flags = 0
+ data = length 990, hash E4712448
+ sample 196:
+ time = 4181333
+ flags = 0
+ data = length 602, hash 3265AF8E
+ sample 197:
+ time = 4202666
+ flags = 0
+ data = length 790, hash 6EF432BA
+ sample 198:
+ time = 4224000
+ flags = 0
+ data = length 746, hash E20E7B36
+ sample 199:
+ time = 4245333
+ flags = 0
+ data = length 439, hash 8C32DDA0
+ sample 200:
+ time = 4266666
+ flags = 0
+ data = length 932, hash AF323520
+ sample 201:
+ time = 4288000
+ flags = 0
+ data = length 1003, hash 3D70121B
+ sample 202:
+ time = 4309333
+ flags = 0
+ data = length 1158, hash 1215442E
+ sample 203:
+ time = 4330666
+ flags = 0
+ data = length 1047, hash 2A517450
+ sample 204:
+ time = 4352000
+ flags = 0
+ data = length 1123, hash 33387B5F
+ sample 205:
+ time = 4373333
+ flags = 0
+ data = length 1033, hash D82CF575
+ sample 206:
+ time = 4394666
+ flags = 0
+ data = length 1009, hash 30DC6A02
+ sample 207:
+ time = 4416000
+ flags = 0
+ data = length 1033, hash 82127054
+ sample 208:
+ time = 4437333
+ flags = 0
+ data = length 761, hash 4A5EA11F
+ sample 209:
+ time = 4458666
+ flags = 0
+ data = length 708, hash 53FA04E4
+ sample 210:
+ time = 4480000
+ flags = 0
+ data = length 750, hash 44F541A6
+ sample 211:
+ time = 4501333
+ flags = 0
+ data = length 902, hash 6688117A
+ sample 212:
+ time = 4522666
+ flags = 0
+ data = length 716, hash A19D938D
+ sample 213:
+ time = 4544000
+ flags = 0
+ data = length 395, hash 4EF7E6DE
+ sample 214:
+ time = 4565333
+ flags = 0
+ data = length 677, hash 57DF6F41
+ sample 215:
+ time = 4586666
+ flags = 0
+ data = length 566, hash 515BD3F0
+ sample 216:
+ time = 4608000
+ flags = 0
+ data = length 772, hash 957617C9
+ sample 217:
+ time = 4629333
+ flags = 0
+ data = length 777, hash 6BB742A3
+ sample 218:
+ time = 4650666
+ flags = 0
+ data = length 672, hash 97BA98A3
+ sample 219:
+ time = 4672000
+ flags = 0
+ data = length 695, hash F8190CDE
+ sample 220:
+ time = 4693333
+ flags = 0
+ data = length 668, hash 6D62BE54
+ sample 221:
+ time = 4714666
+ flags = 0
+ data = length 662, hash 36D886CD
+ sample 222:
+ time = 4736000
+ flags = 0
+ data = length 678, hash 87772FCA
+ sample 223:
+ time = 4757333
+ flags = 0
+ data = length 946, hash BBFB4C98
+ sample 224:
+ time = 4778666
+ flags = 0
+ data = length 594, hash CEDCD36C
+ sample 225:
+ time = 4800000
+ flags = 0
+ data = length 499, hash 3B933D5B
+ sample 226:
+ time = 4821333
+ flags = 0
+ data = length 784, hash 97BC8486
+ sample 227:
+ time = 4842666
+ flags = 0
+ data = length 510, hash 9A082552
+ sample 228:
+ time = 4864000
+ flags = 0
+ data = length 741, hash E17DAE88
+ sample 229:
+ time = 4885333
+ flags = 0
+ data = length 656, hash C8CC5738
+ sample 230:
+ time = 4906666
+ flags = 0
+ data = length 672, hash 84C28F77
+ sample 231:
+ time = 4928000
+ flags = 0
+ data = length 559, hash B563D0ED
+ sample 232:
+ time = 4949333
+ flags = 0
+ data = length 1503, hash 183821B0
+ sample 233:
+ time = 4970666
+ flags = 0
+ data = length 393, hash D807573D
+tracksEnded = true
diff --git a/libraries/test_data/src/test/assets/media/ts/sample_dts.ts b/libraries/test_data/src/test/assets/media/ts/sample_dts.ts
new file mode 100644
index 0000000000000000000000000000000000000000..48ca8263eaf6302dfc9b4212a5d8849f73dbfd4a
GIT binary patch
literal 50008
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