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) {