From 387cc2f2bd11521b2f61cc13868c8dc021f08a32 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 13 Apr 2018 01:58:59 -0700 Subject: [PATCH] Trim more than one sample for elst gapless Allow trimming an arbitrary small number of samples (needing to trim up to two samples actually seems to be common). For larger numbers of samples we do coarse trimming by applying the edit list in the normal path, and don't use gapless playback. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=192736956 --- RELEASENOTES.md | 2 + .../exoplayer2/extractor/mp4/AtomParsers.java | 37 +++++++++++++------ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 54537ce844..b717fd0830 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -43,6 +43,8 @@ ((#3845)[https://github.com/google/ExoPlayer/issues/3845]). * Handle non-empty end-of-stream buffers, to fix gapless playback of streams with encoder padding when the decoder returns a non-empty final buffer. + * Allow trimming more than one sample when applying an elst audio edit via + gapless playback info. * Caching: * Add release method to Cache interface. * Prevent multiple instances of SimpleCache in the same folder. 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 30358ff7c7..a6e2524f0b 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 @@ -53,6 +53,12 @@ import java.util.List; private static final int TYPE_clcp = Util.getIntegerCodeForString("clcp"); private static final int TYPE_meta = Util.getIntegerCodeForString("meta"); + /** + * The threshold number of samples to trim from the start/end of an audio track when applying an + * edit below which gapless info can be used (rather than removing samples from the sample table). + */ + private static final int MAX_GAPLESS_TRIM_SIZE_SAMPLES = 3; + /** * Parses a trak atom (defined in 14496-12). * @@ -311,22 +317,18 @@ import java.util.List; // See the BMFF spec (ISO 14496-12) subsection 8.6.6. Edit lists that require prerolling from a // sync sample after reordering are not supported. Partial audio sample truncation is only - // supported in edit lists with one edit that removes less than one sample from the start/end of - // the track, for gapless audio playback. This implementation handles simple discarding/delaying - // of samples. The extractor may place further restrictions on what edited streams are playable. + // supported in edit lists with one edit that removes less than MAX_GAPLESS_TRIM_SIZE_SAMPLES + // samples from the start/end of the track. This implementation handles simple + // discarding/delaying of samples. The extractor may place further restrictions on what edited + // streams are playable. - if (track.editListDurations.length == 1 && track.type == C.TRACK_TYPE_AUDIO + if (track.editListDurations.length == 1 + && track.type == C.TRACK_TYPE_AUDIO && timestamps.length >= 2) { - // Handle the edit by setting gapless playback metadata, if possible. This implementation - // assumes that only one "roll" sample is needed, which is the case for AAC, so the start/end - // points of the edit must lie within the first/last samples respectively. long editStartTime = track.editListMediaTimes[0]; long editEndTime = editStartTime + Util.scaleLargeTimestamp(track.editListDurations[0], track.timescale, track.movieTimescale); - if (timestamps[0] <= editStartTime - && editStartTime < timestamps[1] - && timestamps[timestamps.length - 1] < editEndTime - && editEndTime <= duration) { + if (canApplyEditWithGaplessInfo(timestamps, duration, editStartTime, editEndTime)) { long paddingTimeUnits = duration - editEndTime; long encoderDelay = Util.scaleLargeTimestamp(editStartTime - timestamps[0], track.format.sampleRate, track.timescale); @@ -1180,6 +1182,19 @@ import java.util.List; return size; } + /** Returns whether it's possible to apply the specified edit using gapless playback info. */ + private static boolean canApplyEditWithGaplessInfo( + long[] timestamps, long duration, long editStartTime, long editEndTime) { + int lastIndex = timestamps.length - 1; + int latestDelayIndex = Util.constrainValue(MAX_GAPLESS_TRIM_SIZE_SAMPLES, 0, lastIndex); + int earliestPaddingIndex = + Util.constrainValue(timestamps.length - MAX_GAPLESS_TRIM_SIZE_SAMPLES, 0, lastIndex); + return timestamps[0] <= editStartTime + && editStartTime < timestamps[latestDelayIndex] + && timestamps[earliestPaddingIndex] < editEndTime + && editEndTime <= duration; + } + private AtomParsers() { // Prevent instantiation. }