Commit e88e889c by ibaker Committed by Oliver Woodman

Extract MediaSourceEventDispatcher from MediaSourceEventListener

Update it to allow any listener class to be registered (and thus
require the caller of dispatch() to provide the type of listener
to call).

Maintain MediaSourceEventListener.EventDispatcher as a sub-class
for now so that all existing references continue to work. This
avoids creating a huge diff in one CL. The intention is to in-line
these incrementally.

This is pre-work for issue:#6765

PiperOrigin-RevId: 298818198
parent f204e4df
...@@ -16,9 +16,6 @@ ...@@ -16,9 +16,6 @@
package com.google.android.exoplayer2.source; package com.google.android.exoplayer2.source;
import android.net.Uri; import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.CheckResult;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
...@@ -26,6 +23,7 @@ import com.google.android.exoplayer2.Player; ...@@ -26,6 +23,7 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
...@@ -167,101 +165,43 @@ public interface MediaSourceEventListener { ...@@ -167,101 +165,43 @@ public interface MediaSourceEventListener {
default void onDownstreamFormatChanged( default void onDownstreamFormatChanged(
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {} int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {}
/** Dispatches events to {@link MediaSourceEventListener}s. */ /** @deprecated Use {@link MediaSourceEventDispatcher} directly instead. */
final class EventDispatcher { @Deprecated
final class EventDispatcher extends MediaSourceEventDispatcher {
/** The timeline window index reported with the events. */
public final int windowIndex;
/** The {@link MediaPeriodId} reported with the events. */
@Nullable public final MediaPeriodId mediaPeriodId;
private final CopyOnWriteArrayList<ListenerAndHandler> listenerAndHandlers;
private final long mediaTimeOffsetMs;
/** Creates an event dispatcher. */
public EventDispatcher() { public EventDispatcher() {
this( super();
/* listenerAndHandlers= */ new CopyOnWriteArrayList<>(),
/* windowIndex= */ 0,
/* mediaPeriodId= */ null,
/* mediaTimeOffsetMs= */ 0);
} }
private EventDispatcher( public EventDispatcher(
CopyOnWriteArrayList<ListenerAndHandler> listenerAndHandlers, CopyOnWriteArrayList<ListenerAndHandler> listeners,
int windowIndex, int windowIndex,
@Nullable MediaPeriodId mediaPeriodId, @Nullable MediaPeriodId mediaPeriodId,
long mediaTimeOffsetMs) { long mediaTimeOffsetMs) {
this.listenerAndHandlers = listenerAndHandlers; super(listeners, windowIndex, mediaPeriodId, mediaTimeOffsetMs);
this.windowIndex = windowIndex;
this.mediaPeriodId = mediaPeriodId;
this.mediaTimeOffsetMs = mediaTimeOffsetMs;
} }
/** @Override
* Creates a view of the event dispatcher with pre-configured window index, media period id, and
* media time offset.
*
* @param windowIndex The timeline window index to be reported with the events.
* @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events.
* @param mediaTimeOffsetMs The offset to be added to all media times, in milliseconds.
* @return A view of the event dispatcher with the pre-configured parameters.
*/
@CheckResult
public EventDispatcher withParameters( public EventDispatcher withParameters(
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) { int windowIndex, @Nullable MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) {
return new EventDispatcher( return new EventDispatcher(
listenerAndHandlers, windowIndex, mediaPeriodId, mediaTimeOffsetMs); listenerAndHandlers, windowIndex, mediaPeriodId, mediaTimeOffsetMs);
} }
/**
* Adds a listener to the event dispatcher.
*
* @param handler A handler on the which listener events will be posted.
* @param eventListener The listener to be added.
*/
public void addEventListener(Handler handler, MediaSourceEventListener eventListener) {
Assertions.checkNotNull(handler);
Assertions.checkNotNull(eventListener);
listenerAndHandlers.add(new ListenerAndHandler(handler, eventListener));
}
/**
* Removes a listener from the event dispatcher.
*
* @param eventListener The listener to be removed.
*/
public void removeEventListener(MediaSourceEventListener eventListener) {
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
if (listenerAndHandler.listener == eventListener) {
listenerAndHandlers.remove(listenerAndHandler);
}
}
}
/** Dispatches {@link #onMediaPeriodCreated(int, MediaPeriodId)}. */
public void mediaPeriodCreated() { public void mediaPeriodCreated() {
MediaPeriodId mediaPeriodId = Assertions.checkNotNull(this.mediaPeriodId); dispatch(
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { (listener, windowIndex, mediaPeriodId) ->
final MediaSourceEventListener listener = listenerAndHandler.listener; listener.onMediaPeriodCreated(windowIndex, Assertions.checkNotNull(mediaPeriodId)),
postOrRun( MediaSourceEventListener.class);
listenerAndHandler.handler,
() -> listener.onMediaPeriodCreated(windowIndex, mediaPeriodId));
}
} }
/** Dispatches {@link #onMediaPeriodReleased(int, MediaPeriodId)}. */
public void mediaPeriodReleased() { public void mediaPeriodReleased() {
MediaPeriodId mediaPeriodId = Assertions.checkNotNull(this.mediaPeriodId); dispatch(
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { (listener, windowIndex, mediaPeriodId) ->
final MediaSourceEventListener listener = listenerAndHandler.listener; listener.onMediaPeriodReleased(windowIndex, Assertions.checkNotNull(mediaPeriodId)),
postOrRun( MediaSourceEventListener.class);
listenerAndHandler.handler,
() -> listener.onMediaPeriodReleased(windowIndex, mediaPeriodId));
}
} }
/** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
public void loadStarted(DataSpec dataSpec, int dataType, long elapsedRealtimeMs) { public void loadStarted(DataSpec dataSpec, int dataType, long elapsedRealtimeMs) {
loadStarted( loadStarted(
dataSpec, dataSpec,
...@@ -275,7 +215,6 @@ public interface MediaSourceEventListener { ...@@ -275,7 +215,6 @@ public interface MediaSourceEventListener {
elapsedRealtimeMs); elapsedRealtimeMs);
} }
/** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
public void loadStarted( public void loadStarted(
DataSpec dataSpec, DataSpec dataSpec,
int dataType, int dataType,
...@@ -304,17 +243,13 @@ public interface MediaSourceEventListener { ...@@ -304,17 +243,13 @@ public interface MediaSourceEventListener {
adjustMediaTime(mediaEndTimeUs))); adjustMediaTime(mediaEndTimeUs)));
} }
/** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
public void loadStarted(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) { public void loadStarted(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { dispatch(
final MediaSourceEventListener listener = listenerAndHandler.listener; (listener, windowIndex, mediaPeriodId) ->
postOrRun( listener.onLoadStarted(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData),
listenerAndHandler.handler, MediaSourceEventListener.class);
() -> listener.onLoadStarted(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData));
}
} }
/** Dispatches {@link #onLoadCompleted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
public void loadCompleted( public void loadCompleted(
DataSpec dataSpec, DataSpec dataSpec,
Uri uri, Uri uri,
...@@ -339,7 +274,6 @@ public interface MediaSourceEventListener { ...@@ -339,7 +274,6 @@ public interface MediaSourceEventListener {
bytesLoaded); bytesLoaded);
} }
/** Dispatches {@link #onLoadCompleted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
public void loadCompleted( public void loadCompleted(
DataSpec dataSpec, DataSpec dataSpec,
Uri uri, Uri uri,
...@@ -367,18 +301,13 @@ public interface MediaSourceEventListener { ...@@ -367,18 +301,13 @@ public interface MediaSourceEventListener {
adjustMediaTime(mediaEndTimeUs))); adjustMediaTime(mediaEndTimeUs)));
} }
/** Dispatches {@link #onLoadCompleted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
public void loadCompleted(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) { public void loadCompleted(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { dispatch(
final MediaSourceEventListener listener = listenerAndHandler.listener; (listener, windowIndex, mediaPeriodId) ->
postOrRun( listener.onLoadCompleted(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData),
listenerAndHandler.handler, MediaSourceEventListener.class);
() ->
listener.onLoadCompleted(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData));
}
} }
/** Dispatches {@link #onLoadCanceled(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
public void loadCanceled( public void loadCanceled(
DataSpec dataSpec, DataSpec dataSpec,
Uri uri, Uri uri,
...@@ -403,7 +332,6 @@ public interface MediaSourceEventListener { ...@@ -403,7 +332,6 @@ public interface MediaSourceEventListener {
bytesLoaded); bytesLoaded);
} }
/** Dispatches {@link #onLoadCanceled(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
public void loadCanceled( public void loadCanceled(
DataSpec dataSpec, DataSpec dataSpec,
Uri uri, Uri uri,
...@@ -431,21 +359,13 @@ public interface MediaSourceEventListener { ...@@ -431,21 +359,13 @@ public interface MediaSourceEventListener {
adjustMediaTime(mediaEndTimeUs))); adjustMediaTime(mediaEndTimeUs)));
} }
/** Dispatches {@link #onLoadCanceled(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */
public void loadCanceled(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) { public void loadCanceled(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) {
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { dispatch(
MediaSourceEventListener listener = listenerAndHandler.listener; (listener, windowIndex, mediaPeriodId) ->
postOrRun( listener.onLoadCanceled(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData),
listenerAndHandler.handler, MediaSourceEventListener.class);
() ->
listener.onLoadCanceled(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData));
}
} }
/**
* Dispatches {@link #onLoadError(int, MediaPeriodId, LoadEventInfo, MediaLoadData, IOException,
* boolean)}.
*/
public void loadError( public void loadError(
DataSpec dataSpec, DataSpec dataSpec,
Uri uri, Uri uri,
...@@ -474,10 +394,6 @@ public interface MediaSourceEventListener { ...@@ -474,10 +394,6 @@ public interface MediaSourceEventListener {
wasCanceled); wasCanceled);
} }
/**
* Dispatches {@link #onLoadError(int, MediaPeriodId, LoadEventInfo, MediaLoadData, IOException,
* boolean)}.
*/
public void loadError( public void loadError(
DataSpec dataSpec, DataSpec dataSpec,
Uri uri, Uri uri,
...@@ -509,37 +425,25 @@ public interface MediaSourceEventListener { ...@@ -509,37 +425,25 @@ public interface MediaSourceEventListener {
wasCanceled); wasCanceled);
} }
/**
* Dispatches {@link #onLoadError(int, MediaPeriodId, LoadEventInfo, MediaLoadData, IOException,
* boolean)}.
*/
public void loadError( public void loadError(
LoadEventInfo loadEventInfo, LoadEventInfo loadEventInfo,
MediaLoadData mediaLoadData, MediaLoadData mediaLoadData,
IOException error, IOException error,
boolean wasCanceled) { boolean wasCanceled) {
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { dispatch(
final MediaSourceEventListener listener = listenerAndHandler.listener; (listener, windowIndex, mediaPeriodId) ->
postOrRun(
listenerAndHandler.handler,
() ->
listener.onLoadError( listener.onLoadError(
windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData, error, wasCanceled)); windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData, error, wasCanceled),
} MediaSourceEventListener.class);
} }
/** Dispatches {@link #onReadingStarted(int, MediaPeriodId)}. */
public void readingStarted() { public void readingStarted() {
MediaPeriodId mediaPeriodId = Assertions.checkNotNull(this.mediaPeriodId); dispatch(
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { (listener, windowIndex, mediaPeriodId) ->
final MediaSourceEventListener listener = listenerAndHandler.listener; listener.onReadingStarted(windowIndex, Assertions.checkNotNull(mediaPeriodId)),
postOrRun( MediaSourceEventListener.class);
listenerAndHandler.handler,
() -> listener.onReadingStarted(windowIndex, mediaPeriodId));
}
} }
/** Dispatches {@link #onUpstreamDiscarded(int, MediaPeriodId, MediaLoadData)}. */
public void upstreamDiscarded(int trackType, long mediaStartTimeUs, long mediaEndTimeUs) { public void upstreamDiscarded(int trackType, long mediaStartTimeUs, long mediaEndTimeUs) {
upstreamDiscarded( upstreamDiscarded(
new MediaLoadData( new MediaLoadData(
...@@ -552,18 +456,14 @@ public interface MediaSourceEventListener { ...@@ -552,18 +456,14 @@ public interface MediaSourceEventListener {
adjustMediaTime(mediaEndTimeUs))); adjustMediaTime(mediaEndTimeUs)));
} }
/** Dispatches {@link #onUpstreamDiscarded(int, MediaPeriodId, MediaLoadData)}. */
public void upstreamDiscarded(MediaLoadData mediaLoadData) { public void upstreamDiscarded(MediaLoadData mediaLoadData) {
MediaPeriodId mediaPeriodId = Assertions.checkNotNull(this.mediaPeriodId); dispatch(
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { (listener, windowIndex, mediaPeriodId) ->
final MediaSourceEventListener listener = listenerAndHandler.listener; listener.onUpstreamDiscarded(
postOrRun( windowIndex, Assertions.checkNotNull(mediaPeriodId), mediaLoadData),
listenerAndHandler.handler, MediaSourceEventListener.class);
() -> listener.onUpstreamDiscarded(windowIndex, mediaPeriodId, mediaLoadData));
}
} }
/** Dispatches {@link #onDownstreamFormatChanged(int, MediaPeriodId, MediaLoadData)}. */
public void downstreamFormatChanged( public void downstreamFormatChanged(
int trackType, int trackType,
@Nullable Format trackFormat, @Nullable Format trackFormat,
...@@ -581,38 +481,15 @@ public interface MediaSourceEventListener { ...@@ -581,38 +481,15 @@ public interface MediaSourceEventListener {
/* mediaEndTimeMs= */ C.TIME_UNSET)); /* mediaEndTimeMs= */ C.TIME_UNSET));
} }
/** Dispatches {@link #onDownstreamFormatChanged(int, MediaPeriodId, MediaLoadData)}. */
public void downstreamFormatChanged(MediaLoadData mediaLoadData) { public void downstreamFormatChanged(MediaLoadData mediaLoadData) {
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { dispatch(
final MediaSourceEventListener listener = listenerAndHandler.listener; (listener, windowIndex, mediaPeriodId) ->
postOrRun( listener.onDownstreamFormatChanged(windowIndex, mediaPeriodId, mediaLoadData),
listenerAndHandler.handler, MediaSourceEventListener.class);
() -> listener.onDownstreamFormatChanged(windowIndex, mediaPeriodId, mediaLoadData));
}
} }
private long adjustMediaTime(long mediaTimeUs) { private long adjustMediaTime(long mediaTimeUs) {
long mediaTimeMs = C.usToMs(mediaTimeUs); return adjustMediaTime(mediaTimeUs, mediaTimeOffsetMs);
return mediaTimeMs == C.TIME_UNSET ? C.TIME_UNSET : mediaTimeOffsetMs + mediaTimeMs;
}
private void postOrRun(Handler handler, Runnable runnable) {
if (handler.getLooper() == Looper.myLooper()) {
runnable.run();
} else {
handler.post(runnable);
}
}
private static final class ListenerAndHandler {
public final Handler handler;
public final MediaSourceEventListener listener;
public ListenerAndHandler(Handler handler, MediaSourceEventListener listener) {
this.handler = handler;
this.listener = listener;
}
} }
} }
} }
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.util;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.CheckResult;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Event dispatcher which forwards events to a list of registered listeners.
*
* <p>Adds the correct {@code windowIndex} and {@code mediaPeriodId} values (and {@code
* mediaTimeOffsetMs} if needed).
*
* <p>Allows listeners of any type to be registered, calls to {@link #dispatch} then provide the
* type of listener to forward to, which is used to filter the registered listeners.
*/
// TODO: Make this final when MediaSourceEventListener.EventDispatcher is deleted.
public class MediaSourceEventDispatcher {
/**
* Functional interface to send an event with {@code windowIndex} and {@code mediaPeriodId}
* attached.
*/
public interface EventWithPeriodId<T> {
/** Sends the event to a listener. */
void sendTo(T listener, int windowIndex, @Nullable MediaPeriodId mediaPeriodId);
}
/** The timeline window index reported with the events. */
public final int windowIndex;
/** The {@link MediaPeriodId} reported with the events. */
@Nullable public final MediaPeriodId mediaPeriodId;
// TODO: Make these private when MediaSourceEventListener.EventDispatcher is deleted.
protected final CopyOnWriteArrayList<ListenerAndHandler> listenerAndHandlers;
// TODO: Define exactly what this means, and check it's always set correctly.
protected final long mediaTimeOffsetMs;
/** Creates an event dispatcher. */
public MediaSourceEventDispatcher() {
this(
/* listenerAndHandlers= */ new CopyOnWriteArrayList<>(),
/* windowIndex= */ 0,
/* mediaPeriodId= */ null,
/* mediaTimeOffsetMs= */ 0);
}
protected MediaSourceEventDispatcher(
CopyOnWriteArrayList<ListenerAndHandler> listenerAndHandlers,
int windowIndex,
@Nullable MediaPeriodId mediaPeriodId,
long mediaTimeOffsetMs) {
this.listenerAndHandlers = listenerAndHandlers;
this.windowIndex = windowIndex;
this.mediaPeriodId = mediaPeriodId;
this.mediaTimeOffsetMs = mediaTimeOffsetMs;
}
/**
* Creates a view of the event dispatcher with pre-configured window index, media period id, and
* media time offset.
*
* @param windowIndex The timeline window index to be reported with the events.
* @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events.
* @param mediaTimeOffsetMs The offset to be added to all media times, in milliseconds.
* @return A view of the event dispatcher with the pre-configured parameters.
*/
@CheckResult
public MediaSourceEventDispatcher withParameters(
int windowIndex, @Nullable MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) {
return new MediaSourceEventDispatcher(
listenerAndHandlers, windowIndex, mediaPeriodId, mediaTimeOffsetMs);
}
/**
* Adds a listener to the event dispatcher.
*
* @param handler A handler on the which listener events will be posted.
* @param eventListener The listener to be added.
*/
public void addEventListener(Handler handler, Object eventListener) {
Assertions.checkNotNull(handler);
Assertions.checkNotNull(eventListener);
listenerAndHandlers.add(new ListenerAndHandler(handler, eventListener));
}
/**
* Removes a listener from the event dispatcher.
*
* @param eventListener The listener to be removed.
*/
public void removeEventListener(Object eventListener) {
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
if (listenerAndHandler.listener == eventListener) {
listenerAndHandlers.remove(listenerAndHandler);
}
}
}
/** Dispatches {@code event} to all registered listeners of type {@code listenerClass}. */
@SuppressWarnings("unchecked") // The cast is gated with listenerClass.isInstance()
public <T> void dispatch(EventWithPeriodId<T> event, Class<T> listenerClass) {
for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) {
if (listenerClass.isInstance(listenerAndHandler.listener)) {
postOrRun(
listenerAndHandler.handler,
() -> event.sendTo((T) listenerAndHandler.listener, windowIndex, mediaPeriodId));
}
}
}
private static void postOrRun(Handler handler, Runnable runnable) {
if (handler.getLooper() == Looper.myLooper()) {
runnable.run();
} else {
handler.post(runnable);
}
}
public static long adjustMediaTime(long mediaTimeUs, long mediaTimeOffsetMs) {
long mediaTimeMs = C.usToMs(mediaTimeUs);
return mediaTimeMs == C.TIME_UNSET ? C.TIME_UNSET : mediaTimeOffsetMs + mediaTimeMs;
}
/** Container class for a {@link Handler} and {@code listener} object. */
protected static final class ListenerAndHandler {
public final Handler handler;
public final Object listener;
public ListenerAndHandler(Handler handler, Object listener) {
this.handler = handler;
this.listener = listener;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment