mirror of
https://github.com/samsonjs/media.git
synced 2026-03-25 09:25:53 +00:00
Use playing period TrackSelectorResult in track reselection update
If the reading period has already advanced and a track reselection procs that only affects the reading period media, then ExoPlayer may try and apply the reading period's track selection incorrectly unto the playing period. ExoPlayer should apply the playing period's track selection to the playing period instead. PiperOrigin-RevId: 609375077
This commit is contained in:
parent
0d1b35477d
commit
4192924622
3 changed files with 192 additions and 1 deletions
|
|
@ -6,6 +6,8 @@
|
|||
* ExoPlayer:
|
||||
* Fix issue where `PreloadMediaPeriod` cannot retain the streams when it
|
||||
is preloaded again.
|
||||
* Apply the correct corresponding `TrackSelectionResult` to the playing
|
||||
period in track reselection.
|
||||
* Transformer:
|
||||
* Add `audioConversionProcess` and `videoConversionProcess` to
|
||||
`ExportResult` indicating how the respective track in the output file
|
||||
|
|
|
|||
|
|
@ -1801,12 +1801,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
|
||||
boolean selectionsChangedForReadPeriod = true;
|
||||
TrackSelectorResult newTrackSelectorResult;
|
||||
// Keep playing period result in case of track selection change for reading period only.
|
||||
TrackSelectorResult newPlayingPeriodTrackSelectorResult = null;
|
||||
while (true) {
|
||||
if (periodHolder == null || !periodHolder.prepared) {
|
||||
// The reselection did not change any prepared periods.
|
||||
return;
|
||||
}
|
||||
newTrackSelectorResult = periodHolder.selectTracks(playbackSpeed, playbackInfo.timeline);
|
||||
if (periodHolder == queue.getPlayingPeriod()) {
|
||||
newPlayingPeriodTrackSelectorResult = newTrackSelectorResult;
|
||||
}
|
||||
if (!newTrackSelectorResult.isEquivalent(periodHolder.getTrackSelectorResult())) {
|
||||
// Selected tracks have changed for this period.
|
||||
break;
|
||||
|
|
@ -1826,7 +1831,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
boolean[] streamResetFlags = new boolean[renderers.length];
|
||||
long periodPositionUs =
|
||||
playingPeriodHolder.applyTrackSelection(
|
||||
newTrackSelectorResult, playbackInfo.positionUs, recreateStreams, streamResetFlags);
|
||||
checkNotNull(newPlayingPeriodTrackSelectorResult),
|
||||
playbackInfo.positionUs,
|
||||
recreateStreams,
|
||||
streamResetFlags);
|
||||
boolean hasDiscontinuity =
|
||||
playbackInfo.playbackState != Player.STATE_ENDED
|
||||
&& periodPositionUs != playbackInfo.positionUs;
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ import androidx.media3.common.Player.DiscontinuityReason;
|
|||
import androidx.media3.common.Player.Listener;
|
||||
import androidx.media3.common.Player.PlayWhenReadyChangeReason;
|
||||
import androidx.media3.common.Player.PositionInfo;
|
||||
import androidx.media3.common.StreamKey;
|
||||
import androidx.media3.common.Timeline;
|
||||
import androidx.media3.common.Timeline.Window;
|
||||
import androidx.media3.common.TrackGroup;
|
||||
|
|
@ -129,6 +130,7 @@ import androidx.media3.common.VideoSize;
|
|||
import androidx.media3.common.util.Assertions;
|
||||
import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.common.util.HandlerWrapper;
|
||||
import androidx.media3.common.util.NullableType;
|
||||
import androidx.media3.common.util.SystemClock;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.datasource.TransferListener;
|
||||
|
|
@ -146,12 +148,15 @@ import androidx.media3.exoplayer.source.MediaPeriod;
|
|||
import androidx.media3.exoplayer.source.MediaSource;
|
||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||
import androidx.media3.exoplayer.source.MediaSourceEventListener;
|
||||
import androidx.media3.exoplayer.source.SampleStream;
|
||||
import androidx.media3.exoplayer.source.ShuffleOrder;
|
||||
import androidx.media3.exoplayer.source.SinglePeriodTimeline;
|
||||
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||
import androidx.media3.exoplayer.source.WrappingMediaSource;
|
||||
import androidx.media3.exoplayer.source.ads.ServerSideAdInsertionMediaSource;
|
||||
import androidx.media3.exoplayer.text.TextOutput;
|
||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
|
||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||
import androidx.media3.exoplayer.upstream.Allocation;
|
||||
import androidx.media3.exoplayer.upstream.Allocator;
|
||||
import androidx.media3.exoplayer.upstream.Loader;
|
||||
|
|
@ -1410,6 +1415,88 @@ public final class ExoPlayerTest {
|
|||
assertThat(numSelectionsEnabled).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTrackSelectionParameters_onlyAffectingReadingPeriodMediaItem_selectsCorrectTracks()
|
||||
throws Exception {
|
||||
Format audioFormat1 =
|
||||
ExoPlayerTestRunner.AUDIO_FORMAT.buildUpon().setAverageBitrate(50_000).build();
|
||||
Format audioFormat2 = audioFormat1.buildUpon().setAverageBitrate(100_000).build();
|
||||
Format audioFormat3 = audioFormat1.buildUpon().setAverageBitrate(60_000).build();
|
||||
DefaultTrackSelector defaultTrackSelector = new DefaultTrackSelector(context);
|
||||
defaultTrackSelector.setParameters(
|
||||
defaultTrackSelector
|
||||
.buildUponParameters()
|
||||
.setExceedAudioConstraintsIfNecessary(false)
|
||||
.build());
|
||||
Timeline timeline = new FakeTimeline();
|
||||
AtomicInteger createMediaPeriodCounter = new AtomicInteger();
|
||||
AtomicReference<ExoTrackSelection[]> exoTrackSelectionAtomicReferenceMediaItem1 =
|
||||
new AtomicReference<>();
|
||||
AtomicReference<ExoTrackSelection[]> exoTrackSelectionAtomicReferenceMediaItem2 =
|
||||
new AtomicReference<>();
|
||||
ExoPlayer player =
|
||||
new TestExoPlayerBuilder(context).setTrackSelector(defaultTrackSelector).build();
|
||||
player.addMediaSources(
|
||||
ImmutableList.of(
|
||||
new FakeMediaSource(timeline, audioFormat1) {
|
||||
@Override
|
||||
public MediaPeriod createPeriod(
|
||||
MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
return new ForwardingMediaPeriod(
|
||||
super.createPeriod(id, allocator, startPositionUs),
|
||||
exoTrackSelectionAtomicReferenceMediaItem1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releasePeriod(MediaPeriod mediaPeriod) {
|
||||
if (mediaPeriod instanceof ForwardingMediaPeriod) {
|
||||
super.releasePeriod(((ForwardingMediaPeriod) mediaPeriod).mediaPeriod);
|
||||
} else {
|
||||
super.releasePeriod(mediaPeriod);
|
||||
}
|
||||
}
|
||||
},
|
||||
new FakeMediaSource(timeline, audioFormat2, audioFormat3) {
|
||||
@Override
|
||||
public MediaPeriod createPeriod(
|
||||
MediaPeriodId id, Allocator allocator, long startPositionUs) {
|
||||
createMediaPeriodCounter.getAndIncrement();
|
||||
return new ForwardingMediaPeriod(
|
||||
super.createPeriod(id, allocator, startPositionUs),
|
||||
exoTrackSelectionAtomicReferenceMediaItem2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releasePeriod(MediaPeriod mediaPeriod) {
|
||||
if (mediaPeriod instanceof ForwardingMediaPeriod) {
|
||||
super.releasePeriod(((ForwardingMediaPeriod) mediaPeriod).mediaPeriod);
|
||||
} else {
|
||||
super.releasePeriod(mediaPeriod);
|
||||
}
|
||||
}
|
||||
}));
|
||||
player.prepare();
|
||||
|
||||
TestPlayerRunHelper.playUntilPosition(
|
||||
player, /* mediaItemIndex= */ 0, /* positionMs= */ 5 * C.MICROS_PER_SECOND);
|
||||
assertThat(exoTrackSelectionAtomicReferenceMediaItem2.get()[1]).isNotNull();
|
||||
assertThat(exoTrackSelectionAtomicReferenceMediaItem2.get()[1].getFormat(0))
|
||||
.isEqualTo(audioFormat2);
|
||||
// Alter track selection parameters to invalidate track selection on second media item only.
|
||||
player.setTrackSelectionParameters(
|
||||
player.getTrackSelectionParameters().buildUpon().setMaxAudioBitrate(70_000).build());
|
||||
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||
player.release();
|
||||
|
||||
assertThat(createMediaPeriodCounter.get()).isEqualTo(2);
|
||||
assertThat(exoTrackSelectionAtomicReferenceMediaItem1.get()[1]).isNotNull();
|
||||
assertThat(exoTrackSelectionAtomicReferenceMediaItem1.get()[1].getFormat(0))
|
||||
.isEqualTo(audioFormat1);
|
||||
assertThat(exoTrackSelectionAtomicReferenceMediaItem2.get()[1]).isNotNull();
|
||||
assertThat(exoTrackSelectionAtomicReferenceMediaItem2.get()[1].getFormat(0))
|
||||
.isEqualTo(audioFormat3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dynamicTimelineChangeReason() throws Exception {
|
||||
Timeline timeline = new FakeTimeline(new TimelineWindowDefinition(false, false, 100000));
|
||||
|
|
@ -14563,6 +14650,100 @@ public final class ExoPlayerTest {
|
|||
}
|
||||
}
|
||||
|
||||
/** Forwarding {@link MediaPeriod} class with tracked reference for {@link #selectTracks}. */
|
||||
private static final class ForwardingMediaPeriod implements MediaPeriod {
|
||||
|
||||
/** The wrapped {@link MediaPeriod} that method calls are forwarded to. */
|
||||
public final MediaPeriod mediaPeriod;
|
||||
|
||||
/** Reference to last tracks selected through {@linkplain #selectTracks}. */
|
||||
public final AtomicReference<ExoTrackSelection[]> exoTrackSelectionReferenceList;
|
||||
|
||||
public ForwardingMediaPeriod(
|
||||
MediaPeriod mediaPeriod,
|
||||
AtomicReference<ExoTrackSelection[]> exoTrackSelectionReferenceList) {
|
||||
this.mediaPeriod = mediaPeriod;
|
||||
this.exoTrackSelectionReferenceList = exoTrackSelectionReferenceList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(Callback callback, long positionUs) {
|
||||
mediaPeriod.prepare(callback, positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeThrowPrepareError() throws IOException {
|
||||
mediaPeriod.maybeThrowPrepareError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackGroupArray getTrackGroups() {
|
||||
return mediaPeriod.getTrackGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StreamKey> getStreamKeys(List<ExoTrackSelection> trackSelections) {
|
||||
return mediaPeriod.getStreamKeys(trackSelections);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long selectTracks(
|
||||
@NullableType ExoTrackSelection[] selections,
|
||||
boolean[] mayRetainStreamFlags,
|
||||
@NullableType SampleStream[] streams,
|
||||
boolean[] streamResetFlags,
|
||||
long positionUs) {
|
||||
exoTrackSelectionReferenceList.set(selections);
|
||||
return mediaPeriod.selectTracks(
|
||||
selections, mayRetainStreamFlags, streams, streamResetFlags, positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void discardBuffer(long positionUs, boolean toKeyframe) {
|
||||
mediaPeriod.discardBuffer(positionUs, toKeyframe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readDiscontinuity() {
|
||||
return mediaPeriod.readDiscontinuity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long seekToUs(long positionUs) {
|
||||
return mediaPeriod.seekToUs(positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) {
|
||||
return mediaPeriod.getAdjustedSeekPositionUs(positionUs, seekParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getBufferedPositionUs() {
|
||||
return mediaPeriod.getBufferedPositionUs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextLoadPositionUs() {
|
||||
return mediaPeriod.getNextLoadPositionUs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean continueLoading(LoadingInfo loadingInfo) {
|
||||
return mediaPeriod.continueLoading(loadingInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoading() {
|
||||
return mediaPeriod.isLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reevaluateBuffer(long positionUs) {
|
||||
mediaPeriod.reevaluateBuffer(positionUs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an argument matcher for {@link Timeline} instances that ignores period and window uids.
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue