Skip to content

Commit ac8d138

Browse files
rohitjoinscopybara-github
authored andcommitted
Fix gapless playback for tracks with timescale rounding differences
When parsing an MP4 file with a simple edit list used for gapless trimming, the extractor calculates the track's duration in two different ways: 1. `duration` is calculated by summing the integer durations of all samples in the `stts` box. 2. `editEndTime` is calculated by scaling the single duration value from the `elst` box (which is in the movie's timescale) to the track's timescale. This two-step scaling process can introduce small rounding errors. In some cases, the calculated `editEndTime` can be slightly larger than the `duration` (e.g., by 1-2 timescale units), even if the edit is valid and intended to end at the same time as the media. This caused the sanity check `editEndTime <= duration` to fail, preventing gapless playback info from being applied. This change introduces a small tolerance of 2 timescale units to this check, making it resilient to these rounding artifacts. PiperOrigin-RevId: 789749132
1 parent 579cae8 commit ac8d138

File tree

1 file changed

+11
-2
lines changed
  • libraries/extractor/src/main/java/androidx/media3/extractor/mp4

1 file changed

+11
-2
lines changed

libraries/extractor/src/main/java/androidx/media3/extractor/mp4/BoxParser.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@ public final class BoxParser {
117117
*/
118118
private static final int MAX_GAPLESS_TRIM_SIZE_SAMPLES = 4;
119119

120+
/**
121+
* A small tolerance for comparing track durations, in timescale units. This is to account for
122+
* small rounding errors that can be introduced when scaling edit list durations from the movie
123+
* timescale to the track timescale.
124+
*/
125+
private static final int EDIT_LIST_DURATION_TOLERANCE_TIMESCALE_UNITS = 2;
126+
120127
/** The magic signature for an Opus Identification header, as defined in RFC-7845. */
121128
private static final byte[] opusMagic = Util.getUtf8Bytes("OpusHead");
122129

@@ -758,7 +765,9 @@ public static TrackSampleTable parseStbl(
758765
+ Util.scaleLargeTimestamp(
759766
track.editListDurations[0], track.timescale, track.movieTimescale);
760767
if (canApplyEditWithGaplessInfo(timestamps, duration, editStartTime, editEndTime)) {
761-
long paddingTimeUnits = duration - editEndTime;
768+
// Clamp padding to 0 to account for rounding errors where editEndTime is slightly
769+
// greater than duration.
770+
long paddingTimeUnits = max(0, duration - editEndTime);
762771
long encoderDelay =
763772
Util.scaleLargeTimestamp(
764773
editStartTime - timestamps[0], track.format.sampleRate, track.timescale);
@@ -2669,7 +2678,7 @@ private static boolean canApplyEditWithGaplessInfo(
26692678
return timestamps[0] <= editStartTime
26702679
&& editStartTime < timestamps[latestDelayIndex]
26712680
&& timestamps[earliestPaddingIndex] < editEndTime
2672-
&& editEndTime <= duration;
2681+
&& editEndTime <= duration + EDIT_LIST_DURATION_TOLERANCE_TIMESCALE_UNITS;
26732682
}
26742683

26752684
private BoxParser() {

0 commit comments

Comments
 (0)