From aaaab36f2d65d7afdd4999f95688377cafdeeb5a Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Wed, 15 Apr 2020 14:33:01 +0100 Subject: [PATCH] Avoid throwing an exception for sample default values Allows playback of content when the default value is not valid, but not used for any samples. Issue: #7207 PiperOrigin-RevId: 306631376 --- RELEASENOTES.md | 6 ++ .../extractor/mp4/FragmentedMp4Extractor.java | 62 +++++++++++++------ 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 8820273a730..3ae572c5b24 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,11 @@ # Release notes # +### Next release ### + +* Avoid throwing an exception while parsing fragmented MP4 default sample + values where the most-significant bit is set + ([#7207](https://github.com/google/ExoPlayer/issues/7207)). + ### 2.11.4 (2020-04-08) ### * Add `SimpleExoPlayer.setWakeMode` to allow automatic `WifiLock` and `WakeLock` diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java index 35f85d0a08c..42aeab64b34 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java @@ -664,9 +664,9 @@ private void onEmsgLeafAtomRead(ParsableByteArray atom) { private static Pair parseTrex(ParsableByteArray trex) { trex.setPosition(Atom.FULL_HEADER_SIZE); int trackId = trex.readInt(); - int defaultSampleDescriptionIndex = trex.readUnsignedIntToInt() - 1; - int defaultSampleDuration = trex.readUnsignedIntToInt(); - int defaultSampleSize = trex.readUnsignedIntToInt(); + int defaultSampleDescriptionIndex = trex.readInt() - 1; + int defaultSampleDuration = trex.readInt(); + int defaultSampleSize = trex.readInt(); int defaultSampleFlags = trex.readInt(); return Pair.create(trackId, new DefaultSampleValues(defaultSampleDescriptionIndex, @@ -751,8 +751,9 @@ private static void parseTraf(ContainerAtom traf, SparseArray track } } - private static void parseTruns(ContainerAtom traf, TrackBundle trackBundle, long decodeTime, - @Flags int flags) { + private static void parseTruns( + ContainerAtom traf, TrackBundle trackBundle, long decodeTime, @Flags int flags) + throws ParserException { int trunCount = 0; int totalSampleCount = 0; List leafChildren = traf.leafChildren; @@ -871,13 +872,20 @@ private static TrackBundle parseTfhd( DefaultSampleValues defaultSampleValues = trackBundle.defaultSampleValues; int defaultSampleDescriptionIndex = ((atomFlags & 0x02 /* default_sample_description_index_present */) != 0) - ? tfhd.readUnsignedIntToInt() - 1 : defaultSampleValues.sampleDescriptionIndex; - int defaultSampleDuration = ((atomFlags & 0x08 /* default_sample_duration_present */) != 0) - ? tfhd.readUnsignedIntToInt() : defaultSampleValues.duration; - int defaultSampleSize = ((atomFlags & 0x10 /* default_sample_size_present */) != 0) - ? tfhd.readUnsignedIntToInt() : defaultSampleValues.size; - int defaultSampleFlags = ((atomFlags & 0x20 /* default_sample_flags_present */) != 0) - ? tfhd.readUnsignedIntToInt() : defaultSampleValues.flags; + ? tfhd.readInt() - 1 + : defaultSampleValues.sampleDescriptionIndex; + int defaultSampleDuration = + ((atomFlags & 0x08 /* default_sample_duration_present */) != 0) + ? tfhd.readInt() + : defaultSampleValues.duration; + int defaultSampleSize = + ((atomFlags & 0x10 /* default_sample_size_present */) != 0) + ? tfhd.readInt() + : defaultSampleValues.size; + int defaultSampleFlags = + ((atomFlags & 0x20 /* default_sample_flags_present */) != 0) + ? tfhd.readInt() + : defaultSampleValues.flags; trackBundle.fragment.header = new DefaultSampleValues(defaultSampleDescriptionIndex, defaultSampleDuration, defaultSampleSize, defaultSampleFlags); return trackBundle; @@ -910,16 +918,22 @@ private static long parseTfdt(ParsableByteArray tfdt) { /** * Parses a trun atom (defined in 14496-12). * - * @param trackBundle The {@link TrackBundle} that contains the {@link TrackFragment} into - * which parsed data should be placed. + * @param trackBundle The {@link TrackBundle} that contains the {@link TrackFragment} into which + * parsed data should be placed. * @param index Index of the track run in the fragment. * @param decodeTime The decode time of the first sample in the fragment run. * @param flags Flags to allow any required workaround to be executed. * @param trun The trun atom to decode. * @return The starting position of samples for the next run. */ - private static int parseTrun(TrackBundle trackBundle, int index, long decodeTime, - @Flags int flags, ParsableByteArray trun, int trackRunStart) { + private static int parseTrun( + TrackBundle trackBundle, + int index, + long decodeTime, + @Flags int flags, + ParsableByteArray trun, + int trackRunStart) + throws ParserException { trun.setPosition(Atom.HEADER_SIZE); int fullAtom = trun.readInt(); int atomFlags = Atom.parseFullAtomFlags(fullAtom); @@ -937,7 +951,7 @@ private static int parseTrun(TrackBundle trackBundle, int index, long decodeTime boolean firstSampleFlagsPresent = (atomFlags & 0x04 /* first_sample_flags_present */) != 0; int firstSampleFlags = defaultSampleValues.flags; if (firstSampleFlagsPresent) { - firstSampleFlags = trun.readUnsignedIntToInt(); + firstSampleFlags = trun.readInt(); } boolean sampleDurationsPresent = (atomFlags & 0x100 /* sample_duration_present */) != 0; @@ -972,9 +986,10 @@ private static int parseTrun(TrackBundle trackBundle, int index, long decodeTime long cumulativeTime = index > 0 ? fragment.nextFragmentDecodeTime : decodeTime; for (int i = trackRunStart; i < trackRunEnd; i++) { // Use trun values if present, otherwise tfhd, otherwise trex. - int sampleDuration = sampleDurationsPresent ? trun.readUnsignedIntToInt() - : defaultSampleValues.duration; - int sampleSize = sampleSizesPresent ? trun.readUnsignedIntToInt() : defaultSampleValues.size; + int sampleDuration = + checkNonNegative(sampleDurationsPresent ? trun.readInt() : defaultSampleValues.duration); + int sampleSize = + checkNonNegative(sampleSizesPresent ? trun.readInt() : defaultSampleValues.size); int sampleFlags = (i == 0 && firstSampleFlagsPresent) ? firstSampleFlags : sampleFlagsPresent ? trun.readInt() : defaultSampleValues.flags; if (sampleCompositionTimeOffsetsPresent) { @@ -1000,6 +1015,13 @@ private static int parseTrun(TrackBundle trackBundle, int index, long decodeTime return trackRunEnd; } + private static int checkNonNegative(int value) throws ParserException { + if (value < 0) { + throw new ParserException("Unexpected negtive value: " + value); + } + return value; + } + private static void parseUuid(ParsableByteArray uuid, TrackFragment out, byte[] extendedTypeScratch) throws ParserException { uuid.setPosition(Atom.HEADER_SIZE);