Add callbacks for media period life cycle.
This adds callbacks for creating, releasing, and starting to read from media periods. Such events allow listeners to keep a list of active media periods. This is useful to determine when no further events for a certain media period are expected. It also allows listeners to associate renderer events unambigiously with a reading media period. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190462717
This commit is contained in:
parent
2a85e792f8
commit
6c0d676050
@ -24,6 +24,8 @@
|
||||
* Allow adding and removing `MediaSourceEventListener`s to MediaSources after
|
||||
they have been created. Listening to events is now supported for all
|
||||
media sources including composite sources.
|
||||
* Added callbacks to `MediaSourceEventListener` to get notified when media
|
||||
periods are created, released and being read from.
|
||||
* Audio:
|
||||
* Factor out `AudioTrack` position tracking from `DefaultAudioSink`.
|
||||
* Fix an issue where the playback position would pause just after playback
|
||||
|
@ -185,6 +185,20 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
||||
eventDispatcher.mediaPeriodCreated();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaPeriodReleased(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
||||
eventDispatcher.mediaPeriodReleased();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadStarted(
|
||||
int windowIndex,
|
||||
@ -232,6 +246,13 @@ public abstract class CompositeMediaSource<T> extends BaseMediaSource {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadingStarted(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) {
|
||||
eventDispatcher.readingStarted();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpstreamDiscarded(
|
||||
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {
|
||||
|
@ -25,6 +25,16 @@ import java.io.IOException;
|
||||
*/
|
||||
public abstract class DefaultMediaSourceEventListener implements MediaSourceEventListener {
|
||||
|
||||
@Override
|
||||
public void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaPeriodReleased(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadStarted(
|
||||
int windowIndex,
|
||||
@ -63,6 +73,11 @@ public abstract class DefaultMediaSourceEventListener implements MediaSourceEven
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadingStarted(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpstreamDiscarded(
|
||||
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {
|
||||
|
@ -100,6 +100,7 @@ import java.util.Arrays;
|
||||
|
||||
private boolean seenFirstTrackSelection;
|
||||
private boolean notifyDiscontinuity;
|
||||
private boolean notifiedReadingStarted;
|
||||
private int enabledTrackCount;
|
||||
private TrackGroupArray tracks;
|
||||
private long durationUs;
|
||||
@ -176,6 +177,7 @@ import java.util.Arrays;
|
||||
minLoadableRetryCount == ExtractorMediaSource.MIN_RETRY_COUNT_DEFAULT_FOR_MEDIA
|
||||
? ExtractorMediaSource.DEFAULT_MIN_LOADABLE_RETRY_COUNT_ON_DEMAND
|
||||
: minLoadableRetryCount;
|
||||
eventDispatcher.mediaPeriodCreated();
|
||||
}
|
||||
|
||||
public void release() {
|
||||
@ -189,6 +191,7 @@ import java.util.Arrays;
|
||||
loader.release(this);
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
released = true;
|
||||
eventDispatcher.mediaPeriodReleased();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -319,6 +322,10 @@ import java.util.Arrays;
|
||||
|
||||
@Override
|
||||
public long readDiscontinuity() {
|
||||
if (!notifiedReadingStarted) {
|
||||
eventDispatcher.readingStarted();
|
||||
notifiedReadingStarted = true;
|
||||
}
|
||||
if (notifyDiscontinuity
|
||||
&& (loadingFinished || getExtractedSamplesCount() > extractedSamplesCountAtStartOfLoad)) {
|
||||
notifyDiscontinuity = false;
|
||||
|
@ -131,7 +131,8 @@ public interface MediaPeriod extends SequenceableLoader {
|
||||
* <p>After this method has returned a value other than {@link C#TIME_UNSET}, all {@link
|
||||
* SampleStream}s provided by the period are guaranteed to start from a key frame.
|
||||
*
|
||||
* <p>This method should only be called after the period has been prepared.
|
||||
* <p>This method should only be called after the period has been prepared. It must be called
|
||||
* before attempting to read from any {@link SampleStream}s provided by the period.
|
||||
*
|
||||
* @return If a discontinuity was read then the playback position in microseconds after the
|
||||
* discontinuity. Else {@link C#TIME_UNSET}.
|
||||
|
@ -133,6 +133,22 @@ public interface MediaSourceEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a media period is created by the media source.
|
||||
*
|
||||
* @param windowIndex The window index in the timeline this media period belongs to.
|
||||
* @param mediaPeriodId The {@link MediaPeriodId} of the created media period.
|
||||
*/
|
||||
void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId);
|
||||
|
||||
/**
|
||||
* Called when a media period is released by the media source.
|
||||
*
|
||||
* @param windowIndex The window index in the timeline this media period belongs to.
|
||||
* @param mediaPeriodId The {@link MediaPeriodId} of the released media period.
|
||||
*/
|
||||
void onMediaPeriodReleased(int windowIndex, MediaPeriodId mediaPeriodId);
|
||||
|
||||
/**
|
||||
* Called when a load begins.
|
||||
*
|
||||
@ -208,6 +224,14 @@ public interface MediaSourceEventListener {
|
||||
IOException error,
|
||||
boolean wasCanceled);
|
||||
|
||||
/**
|
||||
* Called when a media period is first being read from.
|
||||
*
|
||||
* @param windowIndex The window index in the timeline this media period belongs to.
|
||||
* @param mediaPeriodId The {@link MediaPeriodId} of the media period being read from.
|
||||
*/
|
||||
void onReadingStarted(int windowIndex, MediaPeriodId mediaPeriodId);
|
||||
|
||||
/**
|
||||
* Called when data is removed from the back of a media buffer, typically so that it can be
|
||||
* re-buffered in a different format.
|
||||
@ -301,6 +325,38 @@ public interface MediaSourceEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
/** Dispatches {@link #onMediaPeriodCreated(int, MediaPeriodId)}. */
|
||||
public void mediaPeriodCreated() {
|
||||
Assertions.checkState(mediaPeriodId != null);
|
||||
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
final MediaSourceEventListener listener = listenerAndHandler.listener;
|
||||
postOrRun(
|
||||
listenerAndHandler.handler,
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onMediaPeriodCreated(windowIndex, mediaPeriodId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Dispatches {@link #onMediaPeriodReleased(int, MediaPeriodId)}. */
|
||||
public void mediaPeriodReleased() {
|
||||
Assertions.checkState(mediaPeriodId != null);
|
||||
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
final MediaSourceEventListener listener = listenerAndHandler.listener;
|
||||
postOrRun(
|
||||
listenerAndHandler.handler,
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onMediaPeriodReleased(windowIndex, mediaPeriodId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
|
||||
public void loadStarted(DataSpec dataSpec, int dataType, long elapsedRealtimeMs) {
|
||||
loadStarted(
|
||||
@ -560,6 +616,22 @@ public interface MediaSourceEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
/** Dispatches {@link #onReadingStarted(int, MediaPeriodId)}. */
|
||||
public void readingStarted() {
|
||||
Assertions.checkState(mediaPeriodId != null);
|
||||
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
|
||||
final MediaSourceEventListener listener = listenerAndHandler.listener;
|
||||
postOrRun(
|
||||
listenerAndHandler.handler,
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onReadingStarted(windowIndex, mediaPeriodId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Dispatches {@link #onUpstreamDiscarded(int, MediaPeriodId, MediaLoadData)}. */
|
||||
public void upstreamDiscarded(int trackType, long mediaStartTimeUs, long mediaEndTimeUs) {
|
||||
upstreamDiscarded(
|
||||
|
@ -56,6 +56,7 @@ import java.util.Arrays;
|
||||
/* package */ final Format format;
|
||||
/* package */ final boolean treatLoadErrorsAsEndOfStream;
|
||||
|
||||
/* package */ boolean notifiedReadingStarted;
|
||||
/* package */ boolean loadingFinished;
|
||||
/* package */ boolean loadingSucceeded;
|
||||
/* package */ byte[] sampleData;
|
||||
@ -80,10 +81,12 @@ import java.util.Arrays;
|
||||
tracks = new TrackGroupArray(new TrackGroup(format));
|
||||
sampleStreams = new ArrayList<>();
|
||||
loader = new Loader("Loader:SingleSampleMediaPeriod");
|
||||
eventDispatcher.mediaPeriodCreated();
|
||||
}
|
||||
|
||||
public void release() {
|
||||
loader.release();
|
||||
eventDispatcher.mediaPeriodReleased();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -154,6 +157,10 @@ import java.util.Arrays;
|
||||
|
||||
@Override
|
||||
public long readDiscontinuity() {
|
||||
if (!notifiedReadingStarted) {
|
||||
eventDispatcher.readingStarted();
|
||||
notifiedReadingStarted = true;
|
||||
}
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
|
||||
|
@ -362,6 +362,16 @@ public class EventLogger
|
||||
|
||||
// MediaSourceEventListener
|
||||
|
||||
@Override
|
||||
public void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaPeriodReleased(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadStarted(
|
||||
int windowIndex,
|
||||
@ -400,6 +410,11 @@ public class EventLogger
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadingStarted(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpstreamDiscarded(
|
||||
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {
|
||||
|
@ -79,6 +79,7 @@ import java.util.List;
|
||||
private DashManifest manifest;
|
||||
private int periodIndex;
|
||||
private List<EventStream> eventStreams;
|
||||
private boolean notifiedReadingStarted;
|
||||
|
||||
public DashMediaPeriod(
|
||||
int id,
|
||||
@ -114,6 +115,7 @@ import java.util.List;
|
||||
eventStreams);
|
||||
trackGroups = result.first;
|
||||
trackGroupInfos = result.second;
|
||||
eventDispatcher.mediaPeriodCreated();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,6 +150,7 @@ import java.util.List;
|
||||
for (ChunkSampleStream<DashChunkSource> sampleStream : sampleStreams) {
|
||||
sampleStream.release(this);
|
||||
}
|
||||
eventDispatcher.mediaPeriodReleased();
|
||||
}
|
||||
|
||||
// ChunkSampleStream.ReleaseCallback implementation.
|
||||
@ -325,6 +328,10 @@ import java.util.List;
|
||||
|
||||
@Override
|
||||
public long readDiscontinuity() {
|
||||
if (!notifiedReadingStarted) {
|
||||
eventDispatcher.readingStarted();
|
||||
notifiedReadingStarted = true;
|
||||
}
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
||||
private HlsSampleStreamWrapper[] sampleStreamWrappers;
|
||||
private HlsSampleStreamWrapper[] enabledSampleStreamWrappers;
|
||||
private SequenceableLoader compositeSequenceableLoader;
|
||||
private boolean notifiedReadingStarted;
|
||||
|
||||
public HlsMediaPeriod(
|
||||
HlsExtractorFactory extractorFactory,
|
||||
@ -88,6 +89,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
||||
continueLoadingHandler = new Handler();
|
||||
sampleStreamWrappers = new HlsSampleStreamWrapper[0];
|
||||
enabledSampleStreamWrappers = new HlsSampleStreamWrapper[0];
|
||||
eventDispatcher.mediaPeriodCreated();
|
||||
}
|
||||
|
||||
public void release() {
|
||||
@ -96,6 +98,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
||||
for (HlsSampleStreamWrapper sampleStreamWrapper : sampleStreamWrappers) {
|
||||
sampleStreamWrapper.release();
|
||||
}
|
||||
eventDispatcher.mediaPeriodReleased();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -221,6 +224,10 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
|
||||
|
||||
@Override
|
||||
public long readDiscontinuity() {
|
||||
if (!notifiedReadingStarted) {
|
||||
eventDispatcher.readingStarted();
|
||||
notifiedReadingStarted = true;
|
||||
}
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,7 @@ import java.util.ArrayList;
|
||||
private SsManifest manifest;
|
||||
private ChunkSampleStream<SsChunkSource>[] sampleStreams;
|
||||
private SequenceableLoader compositeSequenceableLoader;
|
||||
private boolean notifiedReadingStarted;
|
||||
|
||||
public SsMediaPeriod(SsManifest manifest, SsChunkSource.Factory chunkSourceFactory,
|
||||
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
|
||||
@ -82,6 +83,7 @@ import java.util.ArrayList;
|
||||
sampleStreams = newSampleStreamArray(0);
|
||||
compositeSequenceableLoader =
|
||||
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams);
|
||||
eventDispatcher.mediaPeriodCreated();
|
||||
}
|
||||
|
||||
public void updateManifest(SsManifest manifest) {
|
||||
@ -96,6 +98,7 @@ import java.util.ArrayList;
|
||||
for (ChunkSampleStream<SsChunkSource> sampleStream : sampleStreams) {
|
||||
sampleStream.release();
|
||||
}
|
||||
eventDispatcher.mediaPeriodReleased();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -167,6 +170,10 @@ import java.util.ArrayList;
|
||||
|
||||
@Override
|
||||
public long readDiscontinuity() {
|
||||
if (!notifiedReadingStarted) {
|
||||
eventDispatcher.readingStarted();
|
||||
notifiedReadingStarted = true;
|
||||
}
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
|
||||
|
@ -58,10 +58,10 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
super.release();
|
||||
for (ChunkSampleStream<FakeChunkSource> sampleStream : sampleStreams) {
|
||||
sampleStream.release();
|
||||
}
|
||||
super.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -48,6 +48,7 @@ public class FakeMediaPeriod implements MediaPeriod {
|
||||
@Nullable private Callback prepareCallback;
|
||||
|
||||
private boolean deferOnPrepared;
|
||||
private boolean notifiedReadingStarted;
|
||||
private boolean prepared;
|
||||
private long seekOffsetUs;
|
||||
private long discontinuityPositionUs;
|
||||
@ -73,6 +74,7 @@ public class FakeMediaPeriod implements MediaPeriod {
|
||||
this.eventDispatcher = eventDispatcher;
|
||||
this.deferOnPrepared = deferOnPrepared;
|
||||
discontinuityPositionUs = C.TIME_UNSET;
|
||||
eventDispatcher.mediaPeriodCreated();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,6 +114,7 @@ public class FakeMediaPeriod implements MediaPeriod {
|
||||
|
||||
public void release() {
|
||||
prepared = false;
|
||||
eventDispatcher.mediaPeriodReleased();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -182,6 +185,10 @@ public class FakeMediaPeriod implements MediaPeriod {
|
||||
@Override
|
||||
public long readDiscontinuity() {
|
||||
assertThat(prepared).isTrue();
|
||||
if (!notifiedReadingStarted) {
|
||||
eventDispatcher.readingStarted();
|
||||
notifiedReadingStarted = true;
|
||||
}
|
||||
long positionDiscontinuityUs = this.discontinuityPositionUs;
|
||||
this.discontinuityPositionUs = C.TIME_UNSET;
|
||||
return positionDiscontinuityUs;
|
||||
|
@ -47,6 +47,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/** A runner for {@link MediaSource} tests. */
|
||||
public class MediaSourceTestRunner {
|
||||
@ -62,6 +63,9 @@ public class MediaSourceTestRunner {
|
||||
|
||||
private final LinkedBlockingDeque<Timeline> timelines;
|
||||
private final CopyOnWriteArrayList<Pair<Integer, MediaPeriodId>> completedLoads;
|
||||
private final AtomicReference<MediaPeriodId> lastCreatedMediaPeriod;
|
||||
private final AtomicReference<MediaPeriodId> lastReleasedMediaPeriod;
|
||||
|
||||
private Timeline timeline;
|
||||
|
||||
/**
|
||||
@ -79,6 +83,8 @@ public class MediaSourceTestRunner {
|
||||
mediaSourceListener = new MediaSourceListener();
|
||||
timelines = new LinkedBlockingDeque<>();
|
||||
completedLoads = new CopyOnWriteArrayList<>();
|
||||
lastCreatedMediaPeriod = new AtomicReference<>();
|
||||
lastReleasedMediaPeriod = new AtomicReference<>();
|
||||
mediaSource.addEventListener(playbackHandler, mediaSourceListener);
|
||||
}
|
||||
|
||||
@ -282,16 +288,20 @@ public class MediaSourceTestRunner {
|
||||
private void assertPrepareAndReleasePeriod(MediaPeriodId mediaPeriodId)
|
||||
throws InterruptedException {
|
||||
MediaPeriod mediaPeriod = createPeriod(mediaPeriodId);
|
||||
assertThat(lastCreatedMediaPeriod.getAndSet(/* newValue= */ null)).isEqualTo(mediaPeriodId);
|
||||
CountDownLatch preparedCondition = preparePeriod(mediaPeriod, 0);
|
||||
assertThat(preparedCondition.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
|
||||
// MediaSource is supposed to support multiple calls to createPeriod with the same id without an
|
||||
// intervening call to releasePeriod.
|
||||
MediaPeriod secondMediaPeriod = createPeriod(mediaPeriodId);
|
||||
assertThat(lastCreatedMediaPeriod.getAndSet(/* newValue= */ null)).isEqualTo(mediaPeriodId);
|
||||
CountDownLatch secondPreparedCondition = preparePeriod(secondMediaPeriod, 0);
|
||||
assertThat(secondPreparedCondition.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
|
||||
// Release the periods.
|
||||
releasePeriod(mediaPeriod);
|
||||
assertThat(lastReleasedMediaPeriod.getAndSet(/* newValue= */ null)).isEqualTo(mediaPeriodId);
|
||||
releasePeriod(secondMediaPeriod);
|
||||
assertThat(lastReleasedMediaPeriod.getAndSet(/* newValue= */ null)).isEqualTo(mediaPeriodId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -354,6 +364,18 @@ public class MediaSourceTestRunner {
|
||||
|
||||
// MediaSourceEventListener methods.
|
||||
|
||||
@Override
|
||||
public void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
|
||||
lastCreatedMediaPeriod.set(mediaPeriodId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaPeriodReleased(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
|
||||
lastReleasedMediaPeriod.set(mediaPeriodId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadStarted(
|
||||
int windowIndex,
|
||||
@ -393,6 +415,11 @@ public class MediaSourceTestRunner {
|
||||
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadingStarted(int windowIndex, MediaPeriodId mediaPeriodId) {
|
||||
Assertions.checkState(Looper.myLooper() == playbackThread.getLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpstreamDiscarded(
|
||||
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user