-
Notifications
You must be signed in to change notification settings - Fork 6k
Dev v2 truehd #9415
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dev v2 truehd #9415
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| /* | ||
| * Copyright (C) 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.audio; | ||
|
|
||
| import androidx.annotation.Nullable; | ||
| import com.google.android.exoplayer2.C; | ||
| import com.google.android.exoplayer2.Format; | ||
| import com.google.android.exoplayer2.drm.DrmInitData; | ||
| import com.google.android.exoplayer2.util.MimeTypes; | ||
| import com.google.android.exoplayer2.util.ParsableBitArray; | ||
| import com.google.android.exoplayer2.util.ParsableByteArray; | ||
| import java.nio.ByteBuffer; | ||
|
|
||
| /** Utility methods for parsing MLP frames, which are access units in MLP bitstreams. */ | ||
| public final class MlpUtil { | ||
|
|
||
| /** The channel count of MLP stream. */ | ||
| // TODO: Parse MLP stream channel count. | ||
| private static final int CHANNEL_COUNT_2 = 2; | ||
|
|
||
|
|
||
| /** | ||
| * Returns the MLP format given {@code data} containing the MLPSpecificBox according to | ||
| * dolbytruehdbitstreamswithintheisobasemediafileformat.pdf | ||
| * The reading position of {@code data} will be modified. | ||
| * | ||
| * @param data The MLPSpecificBox to parse. | ||
| * @param trackId The track identifier to set on the format. | ||
| * @param sampleRate The sample rate to be included in the format. | ||
| * @param language The language to set on the format. | ||
| * @param drmInitData {@link DrmInitData} to be included in the format. | ||
| * @return The MLP format parsed from data in the header. | ||
| */ | ||
| public static Format parseMlpFormat( | ||
| ParsableByteArray data, String trackId, int sampleRate, | ||
| String language, @Nullable DrmInitData drmInitData) { | ||
|
|
||
| return new Format.Builder() | ||
| .setId(trackId) | ||
| .setSampleMimeType(MimeTypes.AUDIO_TRUEHD) | ||
| .setChannelCount(CHANNEL_COUNT_2) | ||
| .setSampleRate(sampleRate) | ||
| .setDrmInitData(drmInitData) | ||
| .setLanguage(language) | ||
| .build(); | ||
| } | ||
|
|
||
| private MlpUtil() {} | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |
| */ | ||
| package com.google.android.exoplayer2.extractor.mp4; | ||
|
|
||
| import static com.google.android.exoplayer2.audio.Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT; | ||
| import static com.google.android.exoplayer2.extractor.mp4.AtomParsers.parseTraks; | ||
| import static com.google.android.exoplayer2.extractor.mp4.Sniffer.BRAND_HEIC; | ||
| import static com.google.android.exoplayer2.extractor.mp4.Sniffer.BRAND_QUICKTIME; | ||
|
|
@@ -44,6 +45,7 @@ | |
| import com.google.android.exoplayer2.metadata.mp4.MotionPhotoMetadata; | ||
| import com.google.android.exoplayer2.metadata.mp4.SlowMotionData; | ||
| import com.google.android.exoplayer2.util.Assertions; | ||
| import com.google.android.exoplayer2.util.Log; | ||
| import com.google.android.exoplayer2.util.MimeTypes; | ||
| import com.google.android.exoplayer2.util.NalUnitUtil; | ||
| import com.google.android.exoplayer2.util.ParsableByteArray; | ||
|
|
@@ -155,6 +157,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { | |
| private int sampleBytesRead; | ||
| private int sampleBytesWritten; | ||
| private int sampleCurrentNalBytesRemaining; | ||
| private int lastReadSampleSize; | ||
|
|
||
| // Extractor outputs. | ||
| private @MonotonicNonNull ExtractorOutput extractorOutput; | ||
|
|
@@ -506,6 +509,12 @@ private void processMoovAtom(ContainerAtom moov) throws ParserException { | |
| // 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. | ||
| int maxInputSize = trackSampleTable.maximumSize + 3 * 10; | ||
|
|
||
| if (track.format.sampleMimeType.equals(MimeTypes.AUDIO_TRUEHD)) { | ||
| // TrueHD collates 16 source samples per output | ||
| maxInputSize = trackSampleTable.maximumSize * TRUEHD_RECHUNK_SAMPLE_COUNT; | ||
| } | ||
|
|
||
| Format.Builder formatBuilder = track.format.buildUpon(); | ||
| formatBuilder.setMaxInputSize(maxInputSize); | ||
| if (track.type == C.TRACK_TYPE_VIDEO | ||
|
|
@@ -554,7 +563,7 @@ private void processMoovAtom(ContainerAtom moov) throws ParserException { | |
| * @return One of the {@code RESULT_*} flags in {@link Extractor}. | ||
| * @throws IOException If an error occurs reading from the input. | ||
| */ | ||
| private int readSample(ExtractorInput input, PositionHolder positionHolder) throws IOException { | ||
| private int readSample1(ExtractorInput input, PositionHolder positionHolder) throws IOException { | ||
| long inputPosition = input.getPosition(); | ||
| if (sampleTrackIndex == C.INDEX_UNSET) { | ||
| sampleTrackIndex = getTrackIndexOfNextReadSample(inputPosition); | ||
|
|
@@ -632,20 +641,75 @@ private int readSample(ExtractorInput input, PositionHolder positionHolder) thro | |
| sampleCurrentNalBytesRemaining -= writtenBytes; | ||
| } | ||
| } | ||
| trackOutput.sampleMetadata( | ||
| track.sampleTable.timestampsUs[sampleIndex], | ||
| track.sampleTable.flags[sampleIndex], | ||
| sampleSize, | ||
| 0, | ||
| null); | ||
|
|
||
| track.sampleIndex++; | ||
| sampleTrackIndex = C.INDEX_UNSET; | ||
| sampleBytesRead = 0; | ||
| sampleBytesWritten = 0; | ||
| sampleCurrentNalBytesRemaining = 0; | ||
| lastReadSampleSize = sampleSize; | ||
| return RESULT_CONTINUE; | ||
| } | ||
|
|
||
| /** | ||
| * Attempts to extract the next sample or the next 16 samples in case of Dolby TrueHD audio | ||
| * in the current mdat atom for the specified track. | ||
| * | ||
| * <p>Returns {@link #RESULT_SEEK} if the source should be reloaded from the position in {@code | ||
| * positionHolder}. | ||
| * | ||
| * <p>Returns {@link #RESULT_END_OF_INPUT} if no samples are left. Otherwise, returns {@link | ||
| * #RESULT_CONTINUE}. | ||
| * | ||
| * @param input The {@link ExtractorInput} from which to read data. | ||
| * @param positionHolder If {@link #RESULT_SEEK} is returned, this holder is updated to hold the | ||
| * position of the required data. | ||
| * @return One of the {@code RESULT_*} flags in {@link Extractor}. | ||
| * @throws IOException If an error occurs reading from the input. | ||
| */ | ||
| private int readSample(ExtractorInput input, PositionHolder positionHolder) throws IOException { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The method readSample should return as often as possible so it should ideally read only one sample before returning. To avoid that, you can use a rechunker as in the MatroskaExtractor that would check whether sampleMetadata should be called or not.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that I get it... you mean "don't change the readSample to read 16 samples a time but to issue the metadata only once every 16 samples for TrueHD" ... I'll try to implement this. |
||
| long inputPosition = input.getPosition(); | ||
| if (sampleTrackIndex == C.INDEX_UNSET) { | ||
| sampleTrackIndex = getTrackIndexOfNextReadSample(inputPosition); | ||
| if (sampleTrackIndex == C.INDEX_UNSET) { | ||
| return RESULT_END_OF_INPUT; | ||
| } | ||
| } | ||
|
|
||
| Mp4Track track = castNonNull(tracks)[sampleTrackIndex]; | ||
| TrackOutput trackOutput = track.trackOutput; | ||
| int sampleIndex = track.sampleIndex; | ||
| int result = readSample1(input, positionHolder); | ||
|
|
||
| if (result != RESULT_CONTINUE) { | ||
| return result; | ||
| } | ||
|
|
||
| int sampleSize = lastReadSampleSize; | ||
|
|
||
| if (MimeTypes.AUDIO_TRUEHD.equals(track.track.format.sampleMimeType)) { | ||
| int untilIdx = sampleIndex + TRUEHD_RECHUNK_SAMPLE_COUNT; | ||
| int lastIdx = track.sampleTable.sizes.length; | ||
|
|
||
| untilIdx = min(untilIdx, lastIdx); | ||
|
|
||
| while (result == RESULT_CONTINUE && track.sampleIndex < untilIdx) { | ||
| int i = track.sampleIndex; | ||
| result = readSample1(input, positionHolder); | ||
| sampleSize += result == RESULT_CONTINUE ? lastReadSampleSize : 0; | ||
| } | ||
| } | ||
|
|
||
| trackOutput.sampleMetadata( | ||
| track.sampleTable.timestampsUs[sampleIndex], | ||
| track.sampleTable.flags[sampleIndex], | ||
| sampleSize, | ||
| 0, | ||
| null); | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the index of the track that contains the next sample to be read, or {@link | ||
| * C#INDEX_UNSET} if no samples remain. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -102,4 +102,10 @@ public void mp4SampleWithColorInfo() throws Exception { | |
| ExtractorAsserts.assertBehavior( | ||
| Mp4Extractor::new, "media/mp4/sample_with_color_info.mp4", simulationConfig); | ||
| } | ||
|
|
||
| @Test | ||
| public void mp4SampleWithDolbyTrueHDTrack() throws Exception { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test doesn't pass. The changes also seem to have broken test mp4SampleWithAc4Track.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've messed up while rebasing. First a closing bracket } is missing on line 104. As of the regression on AC4, I have indeed broken it while moving most of the readSample method code into a readSample1 method.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The mp4SampleWithDolbyTrueHDTrack() test still fails when simulating i/o errors but I am not quite sure how to debug this because don't quite understand how i/o errors get injected. |
||
| ExtractorAsserts.assertBehavior( | ||
| Mp4Extractor::new, "media/mp4/sample_dthd.mp4", simulationConfig); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this intended that this TODO has not been implemented?