Skip to content

Commit

Permalink
Move TimeOffsetMediaPeriod to its own class
Browse files Browse the repository at this point in the history
This makes it reusable for other MediaSource/Periods in the same
package.

Issue: google/ExoPlayer#11226
PiperOrigin-RevId: 563687935
  • Loading branch information
tonihei authored and microkatz committed Sep 21, 2023
1 parent 7ff999f commit 5c5944b
Show file tree
Hide file tree
Showing 4 changed files with 503 additions and 179 deletions.
Expand Up @@ -18,8 +18,10 @@
import static androidx.media3.common.util.Assertions.checkArgument;

import android.os.SystemClock;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.util.UnstableApi;
import com.google.common.base.Objects;
import com.google.errorprone.annotations.CanIgnoreReturnValue;

/** Information about the player state when loading is started or continued. */
Expand Down Expand Up @@ -133,4 +135,23 @@ public boolean rebufferedSince(long realtimeMs) {
&& realtimeMs != C.TIME_UNSET
&& lastRebufferRealtimeMs >= realtimeMs;
}

@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof LoadingInfo)) {
return false;
}
LoadingInfo that = (LoadingInfo) o;
return playbackPositionUs == that.playbackPositionUs
&& playbackSpeed == that.playbackSpeed
&& lastRebufferRealtimeMs == that.lastRebufferRealtimeMs;
}

@Override
public int hashCode() {
return Objects.hashCode(playbackPositionUs, playbackSpeed, lastRebufferRealtimeMs);
}
}
Expand Up @@ -20,12 +20,9 @@
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.StreamKey;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.NullableType;
import androidx.media3.decoder.DecoderInputBuffer;
import androidx.media3.exoplayer.FormatHolder;
import androidx.media3.exoplayer.LoadingInfo;
import androidx.media3.exoplayer.SeekParameters;
import androidx.media3.exoplayer.source.chunk.Chunk;
Expand All @@ -38,7 +35,6 @@
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;

/** Merges multiple {@link MediaPeriod}s. */
/* package */ final class MergingMediaPeriod implements MediaPeriod, MediaPeriod.Callback {
Expand Down Expand Up @@ -80,7 +76,7 @@ public MergingMediaPeriod(
*/
public MediaPeriod getChildPeriod(int index) {
return periods[index] instanceof TimeOffsetMediaPeriod
? ((TimeOffsetMediaPeriod) periods[index]).mediaPeriod
? ((TimeOffsetMediaPeriod) periods[index]).getWrappedMediaPeriod()
: periods[index];
}

Expand Down Expand Up @@ -302,180 +298,6 @@ public void onContinueLoadingRequested(MediaPeriod ignored) {
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
}

private static final class TimeOffsetMediaPeriod implements MediaPeriod, MediaPeriod.Callback {

private final MediaPeriod mediaPeriod;
private final long timeOffsetUs;

private @MonotonicNonNull Callback callback;

public TimeOffsetMediaPeriod(MediaPeriod mediaPeriod, long timeOffsetUs) {
this.mediaPeriod = mediaPeriod;
this.timeOffsetUs = timeOffsetUs;
}

@Override
public void prepare(Callback callback, long positionUs) {
this.callback = callback;
mediaPeriod.prepare(/* callback= */ this, positionUs - timeOffsetUs);
}

@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) {
@NullableType SampleStream[] childStreams = new SampleStream[streams.length];
for (int i = 0; i < streams.length; i++) {
TimeOffsetSampleStream sampleStream = (TimeOffsetSampleStream) streams[i];
childStreams[i] = sampleStream != null ? sampleStream.getChildStream() : null;
}
long startPositionUs =
mediaPeriod.selectTracks(
selections,
mayRetainStreamFlags,
childStreams,
streamResetFlags,
positionUs - timeOffsetUs);
for (int i = 0; i < streams.length; i++) {
@Nullable SampleStream childStream = childStreams[i];
if (childStream == null) {
streams[i] = null;
} else if (streams[i] == null
|| ((TimeOffsetSampleStream) streams[i]).getChildStream() != childStream) {
streams[i] = new TimeOffsetSampleStream(childStream, timeOffsetUs);
}
}
return startPositionUs + timeOffsetUs;
}

@Override
public void discardBuffer(long positionUs, boolean toKeyframe) {
mediaPeriod.discardBuffer(positionUs - timeOffsetUs, toKeyframe);
}

@Override
public long readDiscontinuity() {
long discontinuityPositionUs = mediaPeriod.readDiscontinuity();
return discontinuityPositionUs == C.TIME_UNSET
? C.TIME_UNSET
: discontinuityPositionUs + timeOffsetUs;
}

@Override
public long seekToUs(long positionUs) {
return mediaPeriod.seekToUs(positionUs - timeOffsetUs) + timeOffsetUs;
}

@Override
public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) {
return mediaPeriod.getAdjustedSeekPositionUs(positionUs - timeOffsetUs, seekParameters)
+ timeOffsetUs;
}

@Override
public long getBufferedPositionUs() {
long bufferedPositionUs = mediaPeriod.getBufferedPositionUs();
return bufferedPositionUs == C.TIME_END_OF_SOURCE
? C.TIME_END_OF_SOURCE
: bufferedPositionUs + timeOffsetUs;
}

@Override
public long getNextLoadPositionUs() {
long nextLoadPositionUs = mediaPeriod.getNextLoadPositionUs();
return nextLoadPositionUs == C.TIME_END_OF_SOURCE
? C.TIME_END_OF_SOURCE
: nextLoadPositionUs + timeOffsetUs;
}

@Override
public boolean continueLoading(LoadingInfo loadingInfo) {
return mediaPeriod.continueLoading(
loadingInfo
.buildUpon()
.setPlaybackPositionUs(loadingInfo.playbackPositionUs - timeOffsetUs)
.build());
}

@Override
public boolean isLoading() {
return mediaPeriod.isLoading();
}

@Override
public void reevaluateBuffer(long positionUs) {
mediaPeriod.reevaluateBuffer(positionUs - timeOffsetUs);
}

@Override
public void onPrepared(MediaPeriod mediaPeriod) {
Assertions.checkNotNull(callback).onPrepared(/* mediaPeriod= */ this);
}

@Override
public void onContinueLoadingRequested(MediaPeriod source) {
Assertions.checkNotNull(callback).onContinueLoadingRequested(/* source= */ this);
}
}

private static final class TimeOffsetSampleStream implements SampleStream {

private final SampleStream sampleStream;
private final long timeOffsetUs;

public TimeOffsetSampleStream(SampleStream sampleStream, long timeOffsetUs) {
this.sampleStream = sampleStream;
this.timeOffsetUs = timeOffsetUs;
}

public SampleStream getChildStream() {
return sampleStream;
}

@Override
public boolean isReady() {
return sampleStream.isReady();
}

@Override
public void maybeThrowError() throws IOException {
sampleStream.maybeThrowError();
}

@Override
public int readData(
FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {
int readResult = sampleStream.readData(formatHolder, buffer, readFlags);
if (readResult == C.RESULT_BUFFER_READ) {
buffer.timeUs = buffer.timeUs + timeOffsetUs;
}
return readResult;
}

@Override
public int skipData(long positionUs) {
return sampleStream.skipData(positionUs - timeOffsetUs);
}
}

private static final class ForwardingTrackSelection implements ExoTrackSelection {

private final ExoTrackSelection trackSelection;
Expand Down

0 comments on commit 5c5944b

Please sign in to comment.