diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e6874dbe69..f8381ce4e2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -36,9 +36,11 @@ even though the class was not used ([#8058](https://github.com/google/ExoPlayer/issues/8058)). * Extractors: - * Add support for .mp2 boxes in the `AtomParsers` + * Add support for `_mp2` boxes in `Mp4Extractor` ([#7967](https://github.com/google/ExoPlayer/issues/7967)). - * Use TLEN ID3 tag to compute the duration in Mp3Extractor + * Fix playback of MP4 and MOV files containing `pcm_alaw` or `pcm_mulaw` + audio tracks, by enabling sample rechunking of such tracks + * Use TLEN ID3 tag to compute the duration in `Mp3Extractor` ([#7949](https://github.com/google/ExoPlayer/issues/7949)). * Fix regression for Ogg files with packets that span multiple pages ([#7992](https://github.com/google/ExoPlayer/issues/7992)). diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index 573451ef6a..58cb57f261 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -384,9 +384,13 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; } // Fixed sample size raw audio may need to be rechunked. - boolean isFixedSampleSizeRawAudio = - sampleSizeBox.isFixedSampleSize() - && MimeTypes.AUDIO_RAW.equals(track.format.sampleMimeType) + int fixedSampleSize = sampleSizeBox.getFixedSampleSize(); + @Nullable String sampleMimeType = track.format.sampleMimeType; + boolean rechunkFixedSizeSamples = + fixedSampleSize != C.LENGTH_UNSET + && (MimeTypes.AUDIO_RAW.equals(sampleMimeType) + || MimeTypes.AUDIO_MLAW.equals(sampleMimeType) + || MimeTypes.AUDIO_ALAW.equals(sampleMimeType)) && remainingTimestampDeltaChanges == 0 && remainingTimestampOffsetChanges == 0 && remainingSynchronizationSamples == 0; @@ -399,15 +403,13 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; long timestampTimeUnits = 0; long duration; - if (isFixedSampleSizeRawAudio) { + if (rechunkFixedSizeSamples) { long[] chunkOffsetsBytes = new long[chunkIterator.length]; int[] chunkSampleCounts = new int[chunkIterator.length]; while (chunkIterator.moveNext()) { chunkOffsetsBytes[chunkIterator.index] = chunkIterator.offset; chunkSampleCounts[chunkIterator.index] = chunkIterator.numSamples; } - int fixedSampleSize = - Util.getPcmFrameSize(track.format.pcmEncoding, track.format.channelCount); FixedSampleSizeRechunker.Results rechunkedResults = FixedSampleSizeRechunker.rechunk( fixedSampleSize, chunkOffsetsBytes, chunkSampleCounts, timestampDeltaInTimeUnits); @@ -1661,16 +1663,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; */ int getSampleCount(); - /** - * Returns the size for the next sample. - */ + /** Returns the size of each sample if fixed, or {@link C#LENGTH_UNSET} otherwise. */ + int getFixedSampleSize(); + + /** Returns the size for the next sample. */ int readNextSampleSize(); - - /** - * Returns whether samples have a fixed size. - */ - boolean isFixedSampleSize(); - } /** @@ -1685,7 +1682,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; public StszSampleSizeBox(Atom.LeafAtom stszAtom) { data = stszAtom.data; data.setPosition(Atom.FULL_HEADER_SIZE); - fixedSampleSize = data.readUnsignedIntToInt(); + int fixedSampleSize = data.readUnsignedIntToInt(); + this.fixedSampleSize = fixedSampleSize == 0 ? C.LENGTH_UNSET : fixedSampleSize; sampleCount = data.readUnsignedIntToInt(); } @@ -1695,15 +1693,14 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; } @Override - public int readNextSampleSize() { - return fixedSampleSize == 0 ? data.readUnsignedIntToInt() : fixedSampleSize; + public int getFixedSampleSize() { + return fixedSampleSize; } @Override - public boolean isFixedSampleSize() { - return fixedSampleSize != 0; + public int readNextSampleSize() { + return fixedSampleSize == C.LENGTH_UNSET ? data.readUnsignedIntToInt() : fixedSampleSize; } - } /** @@ -1731,6 +1728,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; return sampleCount; } + @Override + public int getFixedSampleSize() { + return C.LENGTH_UNSET; + } + @Override public int readNextSampleSize() { if (fieldSize == 8) { @@ -1750,12 +1752,6 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; } } } - - @Override - public boolean isFixedSampleSize() { - return false; - } - } } diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp4/AtomParsersTest.java b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp4/AtomParsersTest.java index 9ca974afb8..651384094f 100644 --- a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp4/AtomParsersTest.java +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp4/AtomParsersTest.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.mp4; import static com.google.common.truth.Truth.assertThat; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; import org.junit.Test; @@ -64,7 +65,7 @@ public final class AtomParsersTest { private static void verifyStz2Parsing(Atom.LeafAtom stz2Atom) { AtomParsers.Stz2SampleSizeBox box = new AtomParsers.Stz2SampleSizeBox(stz2Atom); assertThat(box.getSampleCount()).isEqualTo(4); - assertThat(box.isFixedSampleSize()).isFalse(); + assertThat(box.getFixedSampleSize()).isEqualTo(C.LENGTH_UNSET); for (int i = 0; i < box.getSampleCount(); i++) { assertThat(box.readNextSampleSize()).isEqualTo(i + 1); }