From 95aa5db14829183519867de95b76c1ed8717c338 Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 10 Sep 2018 03:53:22 -0700 Subject: [PATCH] Reenable edit lists without keyframe again but remove samples before keyframe. Ignoring all edit lists if they don't start with a keyframe causes A/V sync issues when valid edit lists are applied at the beginning. This change allows such edit lists again but removes all samples before the first keyframe (these samples would be ignored by the renderer anyway if at the beginning OR cause visible distortions when appended to an unrelated keyframe). Issue:#4774 Issue:#4348 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=212244407 --- RELEASENOTES.md | 2 + .../exoplayer2/extractor/mp4/AtomParsers.java | 67 +++++++++---------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 794aac95c1e..0f1c314637f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -120,6 +120,8 @@ [#4634](https://github.com/google/ExoPlayer/issues/4634)). * Fix issue where errors of upcoming playlist items are thrown too early ([#4661](https://github.com/google/ExoPlayer/issues/4661)). +* Allow edit lists which do not start with a sync sample. + ([#4774](https://github.com/google/ExoPlayer/issues/4774)). * IMA extension: * Refine the previous fix for empty ad groups to avoid discarding ad breaks unnecessarily ([#4030](https://github.com/google/ExoPlayer/issues/4030)), diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index d150d65ae0d..7197321bf03 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -382,19 +382,29 @@ public static TrackSampleTable parseStbl( int editedSampleCount = 0; int nextSampleIndex = 0; boolean copyMetadata = false; + int[] startIndices = new int[track.editListDurations.length]; + int[] endIndices = new int[track.editListDurations.length]; for (int i = 0; i < track.editListDurations.length; i++) { long editMediaTime = track.editListMediaTimes[i]; if (editMediaTime != -1) { long editDuration = Util.scaleLargeTimestamp( track.editListDurations[i], track.timescale, track.movieTimescale); - int startIndex = Util.binarySearchCeil(timestamps, editMediaTime, true, true); - int endIndex = + startIndices[i] = Util.binarySearchCeil(timestamps, editMediaTime, true, true); + endIndices[i] = Util.binarySearchCeil( timestamps, editMediaTime + editDuration, omitClippedSample, false); - editedSampleCount += endIndex - startIndex; - copyMetadata |= nextSampleIndex != startIndex; - nextSampleIndex = endIndex; + while (startIndices[i] < endIndices[i] + && (flags[startIndices[i]] & C.BUFFER_FLAG_KEY_FRAME) == 0) { + // Applying the edit correctly would require prerolling from the previous sync sample. In + // the current implementation we advance to the next sync sample instead. Only other + // tracks (i.e. audio) will be rendered until the time of the first sync sample. + // See https://github.com/google/ExoPlayer/issues/1659. + startIndices[i]++; + } + editedSampleCount += endIndices[i] - startIndices[i]; + copyMetadata |= nextSampleIndex != startIndices[i]; + nextSampleIndex = endIndices[i]; } } copyMetadata |= editedSampleCount != sampleCount; @@ -409,37 +419,26 @@ public static TrackSampleTable parseStbl( int sampleIndex = 0; for (int i = 0; i < track.editListDurations.length; i++) { long editMediaTime = track.editListMediaTimes[i]; - long editDuration = track.editListDurations[i]; - if (editMediaTime != -1) { - long endMediaTime = - editMediaTime - + Util.scaleLargeTimestamp(editDuration, track.timescale, track.movieTimescale); - int startIndex = Util.binarySearchCeil(timestamps, editMediaTime, true, true); - int endIndex = Util.binarySearchCeil(timestamps, endMediaTime, omitClippedSample, false); - if (copyMetadata) { - int count = endIndex - startIndex; - System.arraycopy(offsets, startIndex, editedOffsets, sampleIndex, count); - System.arraycopy(sizes, startIndex, editedSizes, sampleIndex, count); - System.arraycopy(flags, startIndex, editedFlags, sampleIndex, count); - } - if (startIndex < endIndex && (editedFlags[sampleIndex] & C.BUFFER_FLAG_KEY_FRAME) == 0) { - // Applying the edit list would require prerolling from a sync sample. - Log.w(TAG, "Ignoring edit list: edit does not start with a sync sample."); - throw new UnhandledEditListException(); - } - for (int j = startIndex; j < endIndex; j++) { - long ptsUs = Util.scaleLargeTimestamp(pts, C.MICROS_PER_SECOND, track.movieTimescale); - long timeInSegmentUs = - Util.scaleLargeTimestamp( - timestamps[j] - editMediaTime, C.MICROS_PER_SECOND, track.timescale); - editedTimestamps[sampleIndex] = ptsUs + timeInSegmentUs; - if (copyMetadata && editedSizes[sampleIndex] > editedMaximumSize) { - editedMaximumSize = sizes[j]; - } - sampleIndex++; + int startIndex = startIndices[i]; + int endIndex = endIndices[i]; + if (copyMetadata) { + int count = endIndex - startIndex; + System.arraycopy(offsets, startIndex, editedOffsets, sampleIndex, count); + System.arraycopy(sizes, startIndex, editedSizes, sampleIndex, count); + System.arraycopy(flags, startIndex, editedFlags, sampleIndex, count); + } + for (int j = startIndex; j < endIndex; j++) { + long ptsUs = Util.scaleLargeTimestamp(pts, C.MICROS_PER_SECOND, track.movieTimescale); + long timeInSegmentUs = + Util.scaleLargeTimestamp( + timestamps[j] - editMediaTime, C.MICROS_PER_SECOND, track.timescale); + editedTimestamps[sampleIndex] = ptsUs + timeInSegmentUs; + if (copyMetadata && editedSizes[sampleIndex] > editedMaximumSize) { + editedMaximumSize = sizes[j]; } + sampleIndex++; } - pts += editDuration; + pts += track.editListDurations[i]; } long editedDurationUs = Util.scaleLargeTimestamp(pts, C.MICROS_PER_SECOND, track.timescale); return new TrackSampleTable(