Merge pull request #9496 from DolbyLaboratories:dev-v2-truehd

PiperOrigin-RevId: 403081883
This commit is contained in:
kim-vde 2021-10-14 16:16:05 +01:00
commit 059dfaef7c
13 changed files with 724 additions and 93 deletions

View File

@ -8,8 +8,8 @@
`DefaultRenderersFactory` to force enable or force disable asynchronous `DefaultRenderersFactory` to force enable or force disable asynchronous
queueing ([6348](https://github.com/google/ExoPlayer/issues/6348)). queueing ([6348](https://github.com/google/ExoPlayer/issues/6348)).
* Add 12 public method headers to `ExoPlayer` that exist in * Add 12 public method headers to `ExoPlayer` that exist in
`SimpleExoPlayer`, such that all public methods in `SimpleExoPlayer` `SimpleExoPlayer`, such that all public methods in `SimpleExoPlayer` are
are overrides. overrides.
* Move `com.google.android.exoplayer2.device.DeviceInfo` to * Move `com.google.android.exoplayer2.device.DeviceInfo` to
`com.google.android.exoplayer2.DeviceInfo`. `com.google.android.exoplayer2.DeviceInfo`.
* Move `com.google.android.exoplayer2.drm.DecryptionException` to * Move `com.google.android.exoplayer2.drm.DecryptionException` to
@ -51,6 +51,8 @@
([#9452](https://github.com/google/ExoPlayer/issues/9452)). ([#9452](https://github.com/google/ExoPlayer/issues/9452)).
* Extractors: * Extractors:
* MP4: Correctly handle HEVC tracks with pixel aspect ratios other than 1. * MP4: Correctly handle HEVC tracks with pixel aspect ratios other than 1.
* MP4: Add support for Dolby TrueHD (only for unfragmented streams)
([#9496](https://github.com/google/ExoPlayer/issues/9496)).
* TS: Correctly handle HEVC tracks with pixel aspect ratios other than 1. * TS: Correctly handle HEVC tracks with pixel aspect ratios other than 1.
* TS: Map stream type 0x80 to H262 * TS: Map stream type 0x80 to H262
([#9472](https://github.com/google/ExoPlayer/issues/9472)). ([#9472](https://github.com/google/ExoPlayer/issues/9472)).

View File

@ -0,0 +1,93 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.extractor;
import static com.google.android.exoplayer2.util.Assertions.checkState;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import java.io.IOException;
/**
* Rechunks TrueHD sample data into groups of {@link Ac3Util#TRUEHD_RECHUNK_SAMPLE_COUNT} samples.
*/
public final class TrueHdSampleRechunker {
private final byte[] syncframePrefix;
private boolean foundSyncframe;
private int chunkSampleCount;
private long chunkTimeUs;
@C.BufferFlags private int chunkFlags;
private int chunkSize;
private int chunkOffset;
public TrueHdSampleRechunker() {
syncframePrefix = new byte[Ac3Util.TRUEHD_SYNCFRAME_PREFIX_LENGTH];
}
public void reset() {
foundSyncframe = false;
chunkSampleCount = 0;
}
public void startSample(ExtractorInput input) throws IOException {
if (foundSyncframe) {
return;
}
input.peekFully(syncframePrefix, 0, Ac3Util.TRUEHD_SYNCFRAME_PREFIX_LENGTH);
input.resetPeekPosition();
if (Ac3Util.parseTrueHdSyncframeAudioSampleCount(syncframePrefix) == 0) {
return;
}
foundSyncframe = true;
}
public void sampleMetadata(
TrackOutput trackOutput,
long timeUs,
@C.BufferFlags int flags,
int size,
int offset,
@Nullable TrackOutput.CryptoData cryptoData) {
checkState(
chunkOffset <= size + offset,
"TrueHD chunk samples must be contiguous in the sample queue.");
if (!foundSyncframe) {
return;
}
if (chunkSampleCount++ == 0) {
// This is the first sample in the chunk.
chunkTimeUs = timeUs;
chunkFlags = flags;
chunkSize = 0;
}
chunkSize += size;
chunkOffset = offset; // The offset is to the end of the sample.
if (chunkSampleCount >= Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT) {
outputPendingSampleMetadata(trackOutput, cryptoData);
}
}
public void outputPendingSampleMetadata(
TrackOutput trackOutput, @Nullable TrackOutput.CryptoData cryptoData) {
if (chunkSampleCount > 0) {
trackOutput.sampleMetadata(chunkTimeUs, chunkFlags, chunkSize, chunkOffset, cryptoData);
chunkSampleCount = 0;
}
}
}

View File

@ -31,7 +31,6 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.audio.AacUtil; import com.google.android.exoplayer2.audio.AacUtil;
import com.google.android.exoplayer2.audio.Ac3Util;
import com.google.android.exoplayer2.audio.MpegAudioUtil; import com.google.android.exoplayer2.audio.MpegAudioUtil;
import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
@ -43,6 +42,7 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.TrueHdSampleRechunker;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.LongArray; import com.google.android.exoplayer2.util.LongArray;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
@ -1334,7 +1334,8 @@ public class MatroskaExtractor implements Extractor {
private void commitSampleToOutput( private void commitSampleToOutput(
Track track, long timeUs, @C.BufferFlags int flags, int size, int offset) { Track track, long timeUs, @C.BufferFlags int flags, int size, int offset) {
if (track.trueHdSampleRechunker != null) { if (track.trueHdSampleRechunker != null) {
track.trueHdSampleRechunker.sampleMetadata(track, timeUs, flags, size, offset); track.trueHdSampleRechunker.sampleMetadata(
track.output, timeUs, flags, size, offset, track.cryptoData);
} else { } else {
if (CODEC_ID_SUBRIP.equals(track.codecId) || CODEC_ID_ASS.equals(track.codecId)) { if (CODEC_ID_SUBRIP.equals(track.codecId) || CODEC_ID_ASS.equals(track.codecId)) {
if (blockSampleCount > 1) { if (blockSampleCount > 1) {
@ -1899,70 +1900,6 @@ public class MatroskaExtractor implements Extractor {
} }
} }
/**
* Rechunks TrueHD sample data into groups of {@link Ac3Util#TRUEHD_RECHUNK_SAMPLE_COUNT} samples.
*/
private static final class TrueHdSampleRechunker {
private final byte[] syncframePrefix;
private boolean foundSyncframe;
private int chunkSampleCount;
private long chunkTimeUs;
private @C.BufferFlags int chunkFlags;
private int chunkSize;
private int chunkOffset;
public TrueHdSampleRechunker() {
syncframePrefix = new byte[Ac3Util.TRUEHD_SYNCFRAME_PREFIX_LENGTH];
}
public void reset() {
foundSyncframe = false;
chunkSampleCount = 0;
}
public void startSample(ExtractorInput input) throws IOException {
if (foundSyncframe) {
return;
}
input.peekFully(syncframePrefix, 0, Ac3Util.TRUEHD_SYNCFRAME_PREFIX_LENGTH);
input.resetPeekPosition();
if (Ac3Util.parseTrueHdSyncframeAudioSampleCount(syncframePrefix) == 0) {
return;
}
foundSyncframe = true;
}
@RequiresNonNull("#1.output")
public void sampleMetadata(
Track track, long timeUs, @C.BufferFlags int flags, int size, int offset) {
if (!foundSyncframe) {
return;
}
if (chunkSampleCount++ == 0) {
// This is the first sample in the chunk.
chunkTimeUs = timeUs;
chunkFlags = flags;
chunkSize = 0;
}
chunkSize += size;
chunkOffset = offset; // The offset is to the end of the sample.
if (chunkSampleCount >= Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT) {
outputPendingSampleMetadata(track);
}
}
@RequiresNonNull("#1.output")
public void outputPendingSampleMetadata(Track track) {
if (chunkSampleCount > 0) {
track.output.sampleMetadata(
chunkTimeUs, chunkFlags, chunkSize, chunkOffset, track.cryptoData);
chunkSampleCount = 0;
}
}
}
private static final class Track { private static final class Track {
private static final int DISPLAY_UNIT_PIXELS = 0; private static final int DISPLAY_UNIT_PIXELS = 0;
@ -2335,7 +2272,7 @@ public class MatroskaExtractor implements Extractor {
@RequiresNonNull("output") @RequiresNonNull("output")
public void outputPendingSampleMetadata() { public void outputPendingSampleMetadata() {
if (trueHdSampleRechunker != null) { if (trueHdSampleRechunker != null) {
trueHdSampleRechunker.outputPendingSampleMetadata(this); trueHdSampleRechunker.outputPendingSampleMetadata(output, cryptoData);
} }
} }

View File

@ -152,6 +152,12 @@ import java.util.List;
@SuppressWarnings("ConstantCaseForConstants") @SuppressWarnings("ConstantCaseForConstants")
public static final int TYPE_dac4 = 0x64616334; public static final int TYPE_dac4 = 0x64616334;
@SuppressWarnings("ConstantCaseForConstants")
public static final int TYPE_mlpa = 0x6d6c7061;
@SuppressWarnings("ConstantCaseForConstants")
public static final int TYPE_dmlp = 0x646d6c70;
@SuppressWarnings("ConstantCaseForConstants") @SuppressWarnings("ConstantCaseForConstants")
public static final int TYPE_dtsc = 0x64747363; public static final int TYPE_dtsc = 0x64747363;

View File

@ -962,6 +962,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|| childAtomType == Atom.TYPE_ac_3 || childAtomType == Atom.TYPE_ac_3
|| childAtomType == Atom.TYPE_ec_3 || childAtomType == Atom.TYPE_ec_3
|| childAtomType == Atom.TYPE_ac_4 || childAtomType == Atom.TYPE_ac_4
|| childAtomType == Atom.TYPE_mlpa
|| childAtomType == Atom.TYPE_dtsc || childAtomType == Atom.TYPE_dtsc
|| childAtomType == Atom.TYPE_dtse || childAtomType == Atom.TYPE_dtse
|| childAtomType == Atom.TYPE_dtsh || childAtomType == Atom.TYPE_dtsh
@ -1317,13 +1318,18 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
int channelCount; int channelCount;
int sampleRate; int sampleRate;
int sampleRateMlp = 0;
@C.PcmEncoding int pcmEncoding = Format.NO_VALUE; @C.PcmEncoding int pcmEncoding = Format.NO_VALUE;
@Nullable String codecs = null; @Nullable String codecs = null;
if (quickTimeSoundDescriptionVersion == 0 || quickTimeSoundDescriptionVersion == 1) { if (quickTimeSoundDescriptionVersion == 0 || quickTimeSoundDescriptionVersion == 1) {
channelCount = parent.readUnsignedShort(); channelCount = parent.readUnsignedShort();
parent.skipBytes(6); // sampleSize, compressionId, packetSize. parent.skipBytes(6); // sampleSize, compressionId, packetSize.
sampleRate = parent.readUnsignedFixedPoint1616(); sampleRate = parent.readUnsignedFixedPoint1616();
parent.skipBytes(-4);
// The sample rate has been redefined as a 32-bit value for Dolby TrueHD (MLP) streams.
sampleRateMlp = parent.readInt();
if (quickTimeSoundDescriptionVersion == 1) { if (quickTimeSoundDescriptionVersion == 1) {
parent.skipBytes(16); parent.skipBytes(16);
@ -1404,6 +1410,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
mimeType = MimeTypes.AUDIO_OPUS; mimeType = MimeTypes.AUDIO_OPUS;
} else if (atomType == Atom.TYPE_fLaC) { } else if (atomType == Atom.TYPE_fLaC) {
mimeType = MimeTypes.AUDIO_FLAC; mimeType = MimeTypes.AUDIO_FLAC;
} else if (atomType == Atom.TYPE_mlpa) {
mimeType = MimeTypes.AUDIO_TRUEHD;
} }
@Nullable List<byte[]> initializationData = null; @Nullable List<byte[]> initializationData = null;
@ -1457,6 +1465,17 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
parent.setPosition(Atom.HEADER_SIZE + childPosition); parent.setPosition(Atom.HEADER_SIZE + childPosition);
out.format = out.format =
Ac4Util.parseAc4AnnexEFormat(parent, Integer.toString(trackId), language, drmInitData); Ac4Util.parseAc4AnnexEFormat(parent, Integer.toString(trackId), language, drmInitData);
} else if (childAtomType == Atom.TYPE_dmlp) {
if (sampleRateMlp <= 0) {
throw ParserException.createForMalformedContainer(
"Invalid sample rate for Dolby TrueHD MLP stream: " + sampleRateMlp,
/* cause= */ null);
}
sampleRate = sampleRateMlp;
// The channel count from the sample entry must be ignored for Dolby TrueHD (MLP) streams
// because these streams can carry simultaneously multiple representations of the same
// audio. Use stereo by default.
channelCount = 2;
} else if (childAtomType == Atom.TYPE_ddts) { } else if (childAtomType == Atom.TYPE_ddts) {
out.format = out.format =
new Format.Builder() new Format.Builder()

View File

@ -29,6 +29,7 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.audio.Ac3Util;
import com.google.android.exoplayer2.audio.Ac4Util; import com.google.android.exoplayer2.audio.Ac4Util;
import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
@ -39,6 +40,7 @@ import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.SeekPoint; import com.google.android.exoplayer2.extractor.SeekPoint;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.TrueHdSampleRechunker;
import com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom; import com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom;
import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.mp4.MotionPhotoMetadata; import com.google.android.exoplayer2.metadata.mp4.MotionPhotoMetadata;
@ -56,7 +58,6 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType; import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** Extracts data from the MP4 container format. */ /** Extracts data from the MP4 container format. */
public final class Mp4Extractor implements Extractor, SeekMap { public final class Mp4Extractor implements Extractor, SeekMap {
@ -220,7 +221,12 @@ public final class Mp4Extractor implements Extractor, SeekMap {
slowMotionMetadataEntries.clear(); slowMotionMetadataEntries.clear();
} }
} else if (tracks != null) { } else if (tracks != null) {
updateSampleIndices(timeUs); for (Mp4Track track : tracks) {
updateSampleIndex(track, timeUs);
if (track.trueHdSampleRechunker != null) {
track.trueHdSampleRechunker.reset();
}
}
} }
} }
@ -503,9 +509,16 @@ public final class Mp4Extractor implements Extractor, SeekMap {
Mp4Track mp4Track = Mp4Track mp4Track =
new Mp4Track(track, trackSampleTable, extractorOutput.track(i, track.type)); new Mp4Track(track, trackSampleTable, extractorOutput.track(i, track.type));
// Each sample has up to three bytes of overhead for the start code that replaces its length. int maxInputSize;
// Allow ten source samples per output sample, like the platform extractor. if (MimeTypes.AUDIO_TRUEHD.equals(track.format.sampleMimeType)) {
int maxInputSize = trackSampleTable.maximumSize + 3 * 10; // TrueHD groups samples per chunks of TRUEHD_RECHUNK_SAMPLE_COUNT samples.
maxInputSize = trackSampleTable.maximumSize * Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT;
} else {
// Each sample has up to three bytes of overhead for the start code that replaces its
// length. Allow ten source samples per output sample, like the platform extractor.
maxInputSize = trackSampleTable.maximumSize + 3 * 10;
}
Format.Builder formatBuilder = track.format.buildUpon(); Format.Builder formatBuilder = track.format.buildUpon();
formatBuilder.setMaxInputSize(maxInputSize); formatBuilder.setMaxInputSize(maxInputSize);
if (track.type == C.TRACK_TYPE_VIDEO if (track.type == C.TRACK_TYPE_VIDEO
@ -567,6 +580,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
int sampleIndex = track.sampleIndex; int sampleIndex = track.sampleIndex;
long position = track.sampleTable.offsets[sampleIndex]; long position = track.sampleTable.offsets[sampleIndex];
int sampleSize = track.sampleTable.sizes[sampleIndex]; int sampleSize = track.sampleTable.sizes[sampleIndex];
@Nullable TrueHdSampleRechunker trueHdSampleRechunker = track.trueHdSampleRechunker;
long skipAmount = position - inputPosition + sampleBytesRead; long skipAmount = position - inputPosition + sampleBytesRead;
if (skipAmount < 0 || skipAmount >= RELOAD_MINIMUM_SEEK_DISTANCE) { if (skipAmount < 0 || skipAmount >= RELOAD_MINIMUM_SEEK_DISTANCE) {
positionHolder.position = position; positionHolder.position = position;
@ -624,7 +638,10 @@ public final class Mp4Extractor implements Extractor, SeekMap {
sampleBytesWritten += Ac4Util.SAMPLE_HEADER_SIZE; sampleBytesWritten += Ac4Util.SAMPLE_HEADER_SIZE;
} }
sampleSize += Ac4Util.SAMPLE_HEADER_SIZE; sampleSize += Ac4Util.SAMPLE_HEADER_SIZE;
} else if (trueHdSampleRechunker != null) {
trueHdSampleRechunker.startSample(input);
} }
while (sampleBytesWritten < sampleSize) { while (sampleBytesWritten < sampleSize) {
int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false); int writtenBytes = trackOutput.sampleData(input, sampleSize - sampleBytesWritten, false);
sampleBytesRead += writtenBytes; sampleBytesRead += writtenBytes;
@ -632,12 +649,20 @@ public final class Mp4Extractor implements Extractor, SeekMap {
sampleCurrentNalBytesRemaining -= writtenBytes; sampleCurrentNalBytesRemaining -= writtenBytes;
} }
} }
long timeUs = track.sampleTable.timestampsUs[sampleIndex];
@C.BufferFlags int flags = track.sampleTable.flags[sampleIndex];
if (trueHdSampleRechunker != null) {
trueHdSampleRechunker.sampleMetadata(
trackOutput, timeUs, flags, sampleSize, /* offset= */ 0, /* cryptoData= */ null);
if (sampleIndex + 1 == track.sampleTable.sampleCount) {
trueHdSampleRechunker.outputPendingSampleMetadata(trackOutput, /* cryptoData= */ null);
}
} else {
trackOutput.sampleMetadata( trackOutput.sampleMetadata(
track.sampleTable.timestampsUs[sampleIndex], timeUs, flags, sampleSize, /* offset= */ 0, /* cryptoData= */ null);
track.sampleTable.flags[sampleIndex], }
sampleSize,
0,
null);
track.sampleIndex++; track.sampleIndex++;
sampleTrackIndex = C.INDEX_UNSET; sampleTrackIndex = C.INDEX_UNSET;
sampleBytesRead = 0; sampleBytesRead = 0;
@ -697,12 +722,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
: minAccumulatedBytesTrackIndex; : minAccumulatedBytesTrackIndex;
} }
/** /** Updates a track's sample index to point its latest sync sample before/at {@code timeUs}. */
* Updates every track's sample index to point its latest sync sample before/at {@code timeUs}. private void updateSampleIndex(Mp4Track track, long timeUs) {
*/
@RequiresNonNull("tracks")
private void updateSampleIndices(long timeUs) {
for (Mp4Track track : tracks) {
TrackSampleTable sampleTable = track.sampleTable; TrackSampleTable sampleTable = track.sampleTable;
int sampleIndex = sampleTable.getIndexOfEarlierOrEqualSynchronizationSample(timeUs); int sampleIndex = sampleTable.getIndexOfEarlierOrEqualSynchronizationSample(timeUs);
if (sampleIndex == C.INDEX_UNSET) { if (sampleIndex == C.INDEX_UNSET) {
@ -711,7 +732,6 @@ public final class Mp4Extractor implements Extractor, SeekMap {
} }
track.sampleIndex = sampleIndex; track.sampleIndex = sampleIndex;
} }
}
/** Processes the end of stream in case there is not atom left to read. */ /** Processes the end of stream in case there is not atom left to read. */
private void processEndOfStreamReadingAtomHeader() { private void processEndOfStreamReadingAtomHeader() {
@ -902,6 +922,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
public final Track track; public final Track track;
public final TrackSampleTable sampleTable; public final TrackSampleTable sampleTable;
public final TrackOutput trackOutput; public final TrackOutput trackOutput;
@Nullable public final TrueHdSampleRechunker trueHdSampleRechunker;
public int sampleIndex; public int sampleIndex;
@ -909,6 +930,10 @@ public final class Mp4Extractor implements Extractor, SeekMap {
this.track = track; this.track = track;
this.sampleTable = sampleTable; this.sampleTable = sampleTable;
this.trackOutput = trackOutput; this.trackOutput = trackOutput;
trueHdSampleRechunker =
MimeTypes.AUDIO_TRUEHD.equals(track.format.sampleMimeType)
? new TrueHdSampleRechunker()
: null;
} }
} }
} }

View File

@ -102,4 +102,10 @@ public final class Mp4ExtractorTest {
ExtractorAsserts.assertBehavior( ExtractorAsserts.assertBehavior(
Mp4Extractor::new, "media/mp4/sample_with_color_info.mp4", simulationConfig); Mp4Extractor::new, "media/mp4/sample_with_color_info.mp4", simulationConfig);
} }
@Test
public void mp4SampleWithDolbyTrueHDTrack() throws Exception {
ExtractorAsserts.assertBehavior(
Mp4Extractor::new, "media/mp4/sample_dthd.mp4", simulationConfig);
}
} }

View File

@ -0,0 +1,147 @@
seekMap:
isSeekable = true
duration = 418333
getPosition(0) = [[timeUs=0, position=3447]]
getPosition(1) = [[timeUs=1, position=3447]]
getPosition(209166) = [[timeUs=209166, position=27035]]
getPosition(418333) = [[timeUs=418333, position=75365]]
numberOfTracks = 1
track 0:
total output bytes = 94656
sample count = 32
format 0:
id = 1
sampleMimeType = audio/true-hd
maxInputSize = 12480
channelCount = 2
sampleRate = 48000
language = und
sample 0:
time = 0
flags = 1
data = length 3512, hash B77F1117
sample 1:
time = 13333
flags = 0
data = length 2830, hash 4B19B7D5
sample 2:
time = 26666
flags = 0
data = length 2868, hash BC04A38E
sample 3:
time = 40000
flags = 0
data = length 2834, hash D2AF8AF9
sample 4:
time = 53333
flags = 0
data = length 2898, hash 5C9B3119
sample 5:
time = 66666
flags = 0
data = length 2800, hash 31B9C93F
sample 6:
time = 80000
flags = 0
data = length 2866, hash 7FCABDBC
sample 7:
time = 93333
flags = 0
data = length 2980, hash FC2CCBDA
sample 8:
time = 106666
flags = 1
data = length 3432, hash 17F43166
sample 9:
time = 120000
flags = 0
data = length 2974, hash 69EDFD38
sample 10:
time = 133333
flags = 0
data = length 2898, hash 60E09542
sample 11:
time = 146666
flags = 0
data = length 2896, hash 94A43D4A
sample 12:
time = 160000
flags = 0
data = length 3008, hash 82D706BB
sample 13:
time = 173333
flags = 0
data = length 2918, hash 22DE72A8
sample 14:
time = 186666
flags = 0
data = length 2990, hash E478A008
sample 15:
time = 200000
flags = 0
data = length 2860, hash B5C3DE40
sample 16:
time = 213333
flags = 1
data = length 3638, hash 3FCD885B
sample 17:
time = 226666
flags = 0
data = length 2968, hash A3692382
sample 18:
time = 240000
flags = 0
data = length 2940, hash 72A71C81
sample 19:
time = 253333
flags = 0
data = length 3010, hash A826B2C3
sample 20:
time = 266666
flags = 0
data = length 2952, hash BCEA8C02
sample 21:
time = 280000
flags = 0
data = length 3018, hash C313A53F
sample 22:
time = 293333
flags = 0
data = length 2930, hash 4AAB358
sample 23:
time = 306666
flags = 0
data = length 2898, hash C2C22662
sample 24:
time = 320000
flags = 1
data = length 3680, hash 354DF989
sample 25:
time = 333333
flags = 0
data = length 2970, hash 3191F764
sample 26:
time = 346666
flags = 0
data = length 3044, hash 9E115802
sample 27:
time = 360000
flags = 0
data = length 2946, hash B1341399
sample 28:
time = 373333
flags = 0
data = length 2992, hash 4DA27845
sample 29:
time = 386666
flags = 0
data = length 2930, hash 140DC44C
sample 30:
time = 400000
flags = 0
data = length 2960, hash 5287EBF8
sample 31:
time = 413333
flags = 0
data = length 1216, hash B83FE151
tracksEnded = true

View File

@ -0,0 +1,115 @@
seekMap:
isSeekable = true
duration = 418333
getPosition(0) = [[timeUs=0, position=3447]]
getPosition(1) = [[timeUs=1, position=3447]]
getPosition(209166) = [[timeUs=209166, position=27035]]
getPosition(418333) = [[timeUs=418333, position=75365]]
numberOfTracks = 1
track 0:
total output bytes = 71068
sample count = 24
format 0:
id = 1
sampleMimeType = audio/true-hd
maxInputSize = 12480
channelCount = 2
sampleRate = 48000
language = und
sample 0:
time = 106666
flags = 1
data = length 3432, hash 17F43166
sample 1:
time = 120000
flags = 0
data = length 2974, hash 69EDFD38
sample 2:
time = 133333
flags = 0
data = length 2898, hash 60E09542
sample 3:
time = 146666
flags = 0
data = length 2896, hash 94A43D4A
sample 4:
time = 160000
flags = 0
data = length 3008, hash 82D706BB
sample 5:
time = 173333
flags = 0
data = length 2918, hash 22DE72A8
sample 6:
time = 186666
flags = 0
data = length 2990, hash E478A008
sample 7:
time = 200000
flags = 0
data = length 2860, hash B5C3DE40
sample 8:
time = 213333
flags = 1
data = length 3638, hash 3FCD885B
sample 9:
time = 226666
flags = 0
data = length 2968, hash A3692382
sample 10:
time = 240000
flags = 0
data = length 2940, hash 72A71C81
sample 11:
time = 253333
flags = 0
data = length 3010, hash A826B2C3
sample 12:
time = 266666
flags = 0
data = length 2952, hash BCEA8C02
sample 13:
time = 280000
flags = 0
data = length 3018, hash C313A53F
sample 14:
time = 293333
flags = 0
data = length 2930, hash 4AAB358
sample 15:
time = 306666
flags = 0
data = length 2898, hash C2C22662
sample 16:
time = 320000
flags = 1
data = length 3680, hash 354DF989
sample 17:
time = 333333
flags = 0
data = length 2970, hash 3191F764
sample 18:
time = 346666
flags = 0
data = length 3044, hash 9E115802
sample 19:
time = 360000
flags = 0
data = length 2946, hash B1341399
sample 20:
time = 373333
flags = 0
data = length 2992, hash 4DA27845
sample 21:
time = 386666
flags = 0
data = length 2930, hash 140DC44C
sample 22:
time = 400000
flags = 0
data = length 2960, hash 5287EBF8
sample 23:
time = 413333
flags = 0
data = length 1216, hash B83FE151
tracksEnded = true

View File

@ -0,0 +1,83 @@
seekMap:
isSeekable = true
duration = 418333
getPosition(0) = [[timeUs=0, position=3447]]
getPosition(1) = [[timeUs=1, position=3447]]
getPosition(209166) = [[timeUs=209166, position=27035]]
getPosition(418333) = [[timeUs=418333, position=75365]]
numberOfTracks = 1
track 0:
total output bytes = 47092
sample count = 16
format 0:
id = 1
sampleMimeType = audio/true-hd
maxInputSize = 12480
channelCount = 2
sampleRate = 48000
language = und
sample 0:
time = 213333
flags = 1
data = length 3638, hash 3FCD885B
sample 1:
time = 226666
flags = 0
data = length 2968, hash A3692382
sample 2:
time = 240000
flags = 0
data = length 2940, hash 72A71C81
sample 3:
time = 253333
flags = 0
data = length 3010, hash A826B2C3
sample 4:
time = 266666
flags = 0
data = length 2952, hash BCEA8C02
sample 5:
time = 280000
flags = 0
data = length 3018, hash C313A53F
sample 6:
time = 293333
flags = 0
data = length 2930, hash 4AAB358
sample 7:
time = 306666
flags = 0
data = length 2898, hash C2C22662
sample 8:
time = 320000
flags = 1
data = length 3680, hash 354DF989
sample 9:
time = 333333
flags = 0
data = length 2970, hash 3191F764
sample 10:
time = 346666
flags = 0
data = length 3044, hash 9E115802
sample 11:
time = 360000
flags = 0
data = length 2946, hash B1341399
sample 12:
time = 373333
flags = 0
data = length 2992, hash 4DA27845
sample 13:
time = 386666
flags = 0
data = length 2930, hash 140DC44C
sample 14:
time = 400000
flags = 0
data = length 2960, hash 5287EBF8
sample 15:
time = 413333
flags = 0
data = length 1216, hash B83FE151
tracksEnded = true

View File

@ -0,0 +1,51 @@
seekMap:
isSeekable = true
duration = 418333
getPosition(0) = [[timeUs=0, position=3447]]
getPosition(1) = [[timeUs=1, position=3447]]
getPosition(209166) = [[timeUs=209166, position=27035]]
getPosition(418333) = [[timeUs=418333, position=75365]]
numberOfTracks = 1
track 0:
total output bytes = 22738
sample count = 8
format 0:
id = 1
sampleMimeType = audio/true-hd
maxInputSize = 12480
channelCount = 2
sampleRate = 48000
language = und
sample 0:
time = 320000
flags = 1
data = length 3680, hash 354DF989
sample 1:
time = 333333
flags = 0
data = length 2970, hash 3191F764
sample 2:
time = 346666
flags = 0
data = length 3044, hash 9E115802
sample 3:
time = 360000
flags = 0
data = length 2946, hash B1341399
sample 4:
time = 373333
flags = 0
data = length 2992, hash 4DA27845
sample 5:
time = 386666
flags = 0
data = length 2930, hash 140DC44C
sample 6:
time = 400000
flags = 0
data = length 2960, hash 5287EBF8
sample 7:
time = 413333
flags = 0
data = length 1216, hash B83FE151
tracksEnded = true

View File

@ -0,0 +1,147 @@
seekMap:
isSeekable = true
duration = 418333
getPosition(0) = [[timeUs=0, position=3447]]
getPosition(1) = [[timeUs=1, position=3447]]
getPosition(209166) = [[timeUs=209166, position=27035]]
getPosition(418333) = [[timeUs=418333, position=75365]]
numberOfTracks = 1
track 0:
total output bytes = 94656
sample count = 32
format 0:
id = 1
sampleMimeType = audio/true-hd
maxInputSize = 12480
channelCount = 2
sampleRate = 48000
language = und
sample 0:
time = 0
flags = 1
data = length 3512, hash B77F1117
sample 1:
time = 13333
flags = 0
data = length 2830, hash 4B19B7D5
sample 2:
time = 26666
flags = 0
data = length 2868, hash BC04A38E
sample 3:
time = 40000
flags = 0
data = length 2834, hash D2AF8AF9
sample 4:
time = 53333
flags = 0
data = length 2898, hash 5C9B3119
sample 5:
time = 66666
flags = 0
data = length 2800, hash 31B9C93F
sample 6:
time = 80000
flags = 0
data = length 2866, hash 7FCABDBC
sample 7:
time = 93333
flags = 0
data = length 2980, hash FC2CCBDA
sample 8:
time = 106666
flags = 1
data = length 3432, hash 17F43166
sample 9:
time = 120000
flags = 0
data = length 2974, hash 69EDFD38
sample 10:
time = 133333
flags = 0
data = length 2898, hash 60E09542
sample 11:
time = 146666
flags = 0
data = length 2896, hash 94A43D4A
sample 12:
time = 160000
flags = 0
data = length 3008, hash 82D706BB
sample 13:
time = 173333
flags = 0
data = length 2918, hash 22DE72A8
sample 14:
time = 186666
flags = 0
data = length 2990, hash E478A008
sample 15:
time = 200000
flags = 0
data = length 2860, hash B5C3DE40
sample 16:
time = 213333
flags = 1
data = length 3638, hash 3FCD885B
sample 17:
time = 226666
flags = 0
data = length 2968, hash A3692382
sample 18:
time = 240000
flags = 0
data = length 2940, hash 72A71C81
sample 19:
time = 253333
flags = 0
data = length 3010, hash A826B2C3
sample 20:
time = 266666
flags = 0
data = length 2952, hash BCEA8C02
sample 21:
time = 280000
flags = 0
data = length 3018, hash C313A53F
sample 22:
time = 293333
flags = 0
data = length 2930, hash 4AAB358
sample 23:
time = 306666
flags = 0
data = length 2898, hash C2C22662
sample 24:
time = 320000
flags = 1
data = length 3680, hash 354DF989
sample 25:
time = 333333
flags = 0
data = length 2970, hash 3191F764
sample 26:
time = 346666
flags = 0
data = length 3044, hash 9E115802
sample 27:
time = 360000
flags = 0
data = length 2946, hash B1341399
sample 28:
time = 373333
flags = 0
data = length 2992, hash 4DA27845
sample 29:
time = 386666
flags = 0
data = length 2930, hash 140DC44C
sample 30:
time = 400000
flags = 0
data = length 2960, hash 5287EBF8
sample 31:
time = 413333
flags = 0
data = length 1216, hash B83FE151
tracksEnded = true

Binary file not shown.