From df07fa35d835c1226a6bfeb4783f944c7f00e858 Mon Sep 17 00:00:00 2001 From: rohks Date: Wed, 30 Oct 2024 06:26:17 -0700 Subject: [PATCH] Populate `MediaFormat.KEY_DURATION` in `MediaExtractorCompat` Previously, `getTrackFormat()` in `MediaExtractorCompat` returned a `MediaFormat` without setting `MediaFormat.KEY_DURATION`. With this change: - `MediaFormat.KEY_DURATION` is set based on the track's duration, if available. - If the track duration is unset, the duration from the seek map is used as a fallback. - When neither duration is set, `MediaFormat.KEY_DURATION` remains unset. This ensures that `MediaFormat.KEY_DURATION` is populated when possible, enhancing duration information availability. PiperOrigin-RevId: 691395114 --- .../exoplayer/MediaExtractorCompat.java | 18 ++++- .../exoplayer/MediaExtractorCompatTest.java | 70 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaExtractorCompat.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaExtractorCompat.java index 8211f740dd..2f7ba8f3ca 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaExtractorCompat.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaExtractorCompat.java @@ -417,7 +417,15 @@ public final class MediaExtractorCompat { /** Returns the track {@link MediaFormat} at the specified {@code trackIndex}. */ public MediaFormat getTrackFormat(int trackIndex) { - return tracks.get(trackIndex).createDownstreamMediaFormat(formatHolder, noDataBuffer); + MediaExtractorTrack track = tracks.get(trackIndex); + MediaFormat mediaFormat = track.createDownstreamMediaFormat(formatHolder, noDataBuffer); + long trackDurationUs = track.sampleQueue.trackDurationUs; + if (trackDurationUs != C.TIME_UNSET) { + mediaFormat.setLong(MediaFormat.KEY_DURATION, trackDurationUs); + } else if (seekMap != null && seekMap.getDurationUs() != C.TIME_UNSET) { + mediaFormat.setLong(MediaFormat.KEY_DURATION, seekMap.getDurationUs()); + } + return mediaFormat; } /** @@ -865,6 +873,7 @@ public final class MediaExtractorCompat { private final class MediaExtractorSampleQueue extends SampleQueue { public final int trackId; + public long trackDurationUs; private int mainTrackIndex; private int compatibilityTrackIndex; @@ -873,6 +882,7 @@ public final class MediaExtractorCompat { // values for DRM-related arguments. super(allocator, /* drmSessionManager= */ null, /* drmEventDispatcher= */ null); this.trackId = trackId; + trackDurationUs = C.TIME_UNSET; mainTrackIndex = C.INDEX_UNSET; compatibilityTrackIndex = C.INDEX_UNSET; } @@ -887,6 +897,12 @@ public final class MediaExtractorCompat { // SampleQueue implementation. + @Override + public void durationUs(long durationUs) { + this.trackDurationUs = durationUs; + super.durationUs(durationUs); + } + @Override public Format getAdjustedUpstreamFormat(Format format) { if (getUpstreamFormat() == null) { diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaExtractorCompatTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaExtractorCompatTest.java index f810b1d26c..d4c3b80c4d 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaExtractorCompatTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaExtractorCompatTest.java @@ -616,6 +616,76 @@ public class MediaExtractorCompatTest { .isEqualTo(-1); } + @Test + public void getTrackFormat_withBothTrackAndSeekMapDurationsSet_prioritizesTrackDuration() + throws IOException { + TrackOutput[] outputs = new TrackOutput[1]; + fakeExtractor.addReadAction( + (input, seekPosition) -> { + outputs[0] = extractorOutput.track(/* id= */ 0, C.TRACK_TYPE_VIDEO); + extractorOutput.endTracks(); + extractorOutput.seekMap( + new FakeSeekMap( + /* durationUs= */ 1_000_000L, (timeUs) -> new SeekPoints(SeekPoint.START))); + outputs[0].format(PLACEHOLDER_FORMAT_VIDEO); + outputs[0].durationUs(2_000_000L); + return Extractor.RESULT_CONTINUE; + }); + mediaExtractorCompat.setDataSource(PLACEHOLDER_URI, /* offset= */ 0); + mediaExtractorCompat.selectTrack(/* trackIndex= */ 0); + + MediaFormat mediaFormat = mediaExtractorCompat.getTrackFormat(/* trackIndex= */ 0); + + assertThat(mediaFormat.containsKey(MediaFormat.KEY_DURATION)).isTrue(); + assertThat(mediaFormat.getLong(MediaFormat.KEY_DURATION)).isEqualTo(2_000_000L); + } + + @Test + public void getTrackFormat_withOnlySeekMapDurationSet_returnsSeekMapDuration() + throws IOException { + TrackOutput[] outputs = new TrackOutput[1]; + fakeExtractor.addReadAction( + (input, seekPosition) -> { + outputs[0] = extractorOutput.track(/* id= */ 0, C.TRACK_TYPE_VIDEO); + extractorOutput.endTracks(); + extractorOutput.seekMap( + new FakeSeekMap( + /* durationUs= */ 1_000_000L, (timeUs) -> new SeekPoints(SeekPoint.START))); + outputs[0].format(PLACEHOLDER_FORMAT_VIDEO); + return Extractor.RESULT_CONTINUE; + }); + mediaExtractorCompat.setDataSource(PLACEHOLDER_URI, /* offset= */ 0); + mediaExtractorCompat.selectTrack(/* trackIndex= */ 0); + + MediaFormat mediaFormat = mediaExtractorCompat.getTrackFormat(/* trackIndex= */ 0); + + assertThat(mediaFormat.containsKey(MediaFormat.KEY_DURATION)).isTrue(); + assertThat(mediaFormat.getLong(MediaFormat.KEY_DURATION)).isEqualTo(1_000_000L); + } + + @Test + public void getTrackFormat_withNoTrackOrSeekMapDurationSet_returnsNoDuration() + throws IOException { + TrackOutput[] outputs = new TrackOutput[1]; + fakeExtractor.addReadAction( + (input, seekPosition) -> { + outputs[0] = extractorOutput.track(/* id= */ 0, C.TRACK_TYPE_VIDEO); + extractorOutput.endTracks(); + outputs[0].format( + new Format.Builder() + .setSampleMimeType(MimeTypes.VIDEO_H264) + .setCodecs("avc.123") + .build()); + return Extractor.RESULT_CONTINUE; + }); + mediaExtractorCompat.setDataSource(PLACEHOLDER_URI, /* offset= */ 0); + mediaExtractorCompat.selectTrack(/* trackIndex= */ 0); + + MediaFormat mediaFormat = mediaExtractorCompat.getTrackFormat(/* trackIndex= */ 0); + + assertThat(mediaFormat.containsKey(MediaFormat.KEY_DURATION)).isFalse(); + } + // Internal methods. private void assertReadSample(int trackIndex, long timeUs, int size, byte... sampleData) {