Support multiple video/text/metadata outputs

We've seen more than one issue filed where a developer has
registered a video listener and been confused by the fact
their SimpleExoPlayerView no longer works properly. There
are also valid use cases for having multiple metadata/text
outputs.

Issue: #2933
Issue: #2800
Issue: #2286
Issue: #2240

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=164839882
This commit is contained in:
olly 2017-08-10 04:42:27 -07:00 committed by Oliver Woodman
parent 5ab0c620bf
commit 88bae5d975
3 changed files with 113 additions and 40 deletions

View File

@ -294,9 +294,9 @@ public class PlayerActivity extends Activity implements OnClickListener, EventLi
player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector); player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector);
player.addListener(this); player.addListener(this);
player.addListener(eventLogger); player.addListener(eventLogger);
player.addMetadataOutput(eventLogger);
player.setAudioDebugListener(eventLogger); player.setAudioDebugListener(eventLogger);
player.setVideoDebugListener(eventLogger); player.setVideoDebugListener(eventLogger);
player.setMetadataOutput(eventLogger);
simpleExoPlayerView.setPlayer(player); simpleExoPlayerView.setPlayer(player);
player.setPlayWhenReady(shouldAutoPlay); player.setPlayWhenReady(shouldAutoPlay);

View File

@ -41,6 +41,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoRendererEventListener; import com.google.android.exoplayer2.video.VideoRendererEventListener;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
/** /**
* An {@link ExoPlayer} implementation that uses default {@link Renderer} components. Instances can * An {@link ExoPlayer} implementation that uses default {@link Renderer} components. Instances can
@ -87,6 +88,9 @@ public class SimpleExoPlayer implements ExoPlayer {
private final ExoPlayer player; private final ExoPlayer player;
private final ComponentListener componentListener; private final ComponentListener componentListener;
private final CopyOnWriteArraySet<VideoListener> videoListeners;
private final CopyOnWriteArraySet<TextRenderer.Output> textOutputs;
private final CopyOnWriteArraySet<MetadataRenderer.Output> metadataOutputs;
private final int videoRendererCount; private final int videoRendererCount;
private final int audioRendererCount; private final int audioRendererCount;
@ -99,9 +103,6 @@ public class SimpleExoPlayer implements ExoPlayer {
private int videoScalingMode; private int videoScalingMode;
private SurfaceHolder surfaceHolder; private SurfaceHolder surfaceHolder;
private TextureView textureView; private TextureView textureView;
private TextRenderer.Output textOutput;
private MetadataRenderer.Output metadataOutput;
private VideoListener videoListener;
private AudioRendererEventListener audioDebugListener; private AudioRendererEventListener audioDebugListener;
private VideoRendererEventListener videoDebugListener; private VideoRendererEventListener videoDebugListener;
private DecoderCounters videoDecoderCounters; private DecoderCounters videoDecoderCounters;
@ -113,6 +114,9 @@ public class SimpleExoPlayer implements ExoPlayer {
protected SimpleExoPlayer(RenderersFactory renderersFactory, TrackSelector trackSelector, protected SimpleExoPlayer(RenderersFactory renderersFactory, TrackSelector trackSelector,
LoadControl loadControl) { LoadControl loadControl) {
componentListener = new ComponentListener(); componentListener = new ComponentListener();
videoListeners = new CopyOnWriteArraySet<>();
textOutputs = new CopyOnWriteArraySet<>();
metadataOutputs = new CopyOnWriteArraySet<>();
Looper eventLooper = Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper(); Looper eventLooper = Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper();
Handler eventHandler = new Handler(eventLooper); Handler eventHandler = new Handler(eventLooper);
renderers = renderersFactory.createRenderers(eventHandler, componentListener, componentListener, renderers = renderersFactory.createRenderers(eventHandler, componentListener, componentListener,
@ -440,63 +444,132 @@ public class SimpleExoPlayer implements ExoPlayer {
} }
/** /**
* Sets a listener to receive video events. * Adds a listener to receive video events.
*
* @param listener The listener to register.
*/
public void addVideoListener(VideoListener listener) {
videoListeners.add(listener);
}
/**
* Removes a listener of video events.
*
* @param listener The listener to unregister.
*/
public void removeVideoListener(VideoListener listener) {
videoListeners.remove(listener);
}
/**
* Sets a listener to receive video events, removing all existing listeners.
* *
* @param listener The listener. * @param listener The listener.
* @deprecated Use {@link #addVideoListener(VideoListener)}.
*/ */
@Deprecated
public void setVideoListener(VideoListener listener) { public void setVideoListener(VideoListener listener) {
videoListener = listener; videoListeners.clear();
if (listener != null) {
addVideoListener(listener);
}
} }
/** /**
* Clears the listener receiving video events if it matches the one passed. Else does nothing. * Equivalent to {@link #removeVideoListener(VideoListener)}.
* *
* @param listener The listener to clear. * @param listener The listener to clear.
* @deprecated Use {@link #removeVideoListener(VideoListener)}.
*/ */
@Deprecated
public void clearVideoListener(VideoListener listener) { public void clearVideoListener(VideoListener listener) {
if (videoListener == listener) { removeVideoListener(listener);
videoListener = null;
}
} }
/** /**
* Sets an output to receive text events. * Registers an output to receive text events.
*
* @param listener The output to register.
*/
public void addTextOutput(TextRenderer.Output listener) {
textOutputs.add(listener);
}
/**
* Removes a text output.
*
* @param listener The output to remove.
*/
public void removeTextOutput(TextRenderer.Output listener) {
textOutputs.remove(listener);
}
/**
* Sets an output to receive text events, removing all existing outputs.
* *
* @param output The output. * @param output The output.
* @deprecated Use {@link #addTextOutput(TextRenderer.Output)}.
*/ */
@Deprecated
public void setTextOutput(TextRenderer.Output output) { public void setTextOutput(TextRenderer.Output output) {
textOutput = output; textOutputs.clear();
if (output != null) {
addTextOutput(output);
}
} }
/** /**
* Clears the output receiving text events if it matches the one passed. Else does nothing. * Equivalent to {@link #removeTextOutput(TextRenderer.Output)}.
* *
* @param output The output to clear. * @param output The output to clear.
* @deprecated Use {@link #removeTextOutput(TextRenderer.Output)}.
*/ */
@Deprecated
public void clearTextOutput(TextRenderer.Output output) { public void clearTextOutput(TextRenderer.Output output) {
if (textOutput == output) { removeTextOutput(output);
textOutput = null;
}
} }
/** /**
* Sets a listener to receive metadata events. * Registers an output to receive metadata events.
*
* @param listener The output to register.
*/
public void addMetadataOutput(MetadataRenderer.Output listener) {
metadataOutputs.add(listener);
}
/**
* Removes a metadata output.
*
* @param listener The output to remove.
*/
public void removeMetadataOutput(MetadataRenderer.Output listener) {
metadataOutputs.remove(listener);
}
/**
* Sets an output to receive metadata events, removing all existing outputs.
* *
* @param output The output. * @param output The output.
* @deprecated Use {@link #addMetadataOutput(MetadataRenderer.Output)}.
*/ */
@Deprecated
public void setMetadataOutput(MetadataRenderer.Output output) { public void setMetadataOutput(MetadataRenderer.Output output) {
metadataOutput = output; metadataOutputs.clear();
if (output != null) {
addMetadataOutput(output);
}
} }
/** /**
* Clears the output receiving metadata events if it matches the one passed. Else does nothing. * Equivalent to {@link #removeMetadataOutput(MetadataRenderer.Output)}.
* *
* @param output The output to clear. * @param output The output to clear.
* @deprecated Use {@link #removeMetadataOutput(MetadataRenderer.Output)}.
*/ */
@Deprecated
public void clearMetadataOutput(MetadataRenderer.Output output) { public void clearMetadataOutput(MetadataRenderer.Output output) {
if (metadataOutput == output) { removeMetadataOutput(output);
metadataOutput = null;
}
} }
/** /**
@ -816,7 +889,7 @@ public class SimpleExoPlayer implements ExoPlayer {
@Override @Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthHeightRatio) { float pixelWidthHeightRatio) {
if (videoListener != null) { for (VideoListener videoListener : videoListeners) {
videoListener.onVideoSizeChanged(width, height, unappliedRotationDegrees, videoListener.onVideoSizeChanged(width, height, unappliedRotationDegrees,
pixelWidthHeightRatio); pixelWidthHeightRatio);
} }
@ -828,9 +901,11 @@ public class SimpleExoPlayer implements ExoPlayer {
@Override @Override
public void onRenderedFirstFrame(Surface surface) { public void onRenderedFirstFrame(Surface surface) {
if (videoListener != null && SimpleExoPlayer.this.surface == surface) { if (SimpleExoPlayer.this.surface == surface) {
for (VideoListener videoListener : videoListeners) {
videoListener.onRenderedFirstFrame(); videoListener.onRenderedFirstFrame();
} }
}
if (videoDebugListener != null) { if (videoDebugListener != null) {
videoDebugListener.onRenderedFirstFrame(surface); videoDebugListener.onRenderedFirstFrame(surface);
} }
@ -902,7 +977,7 @@ public class SimpleExoPlayer implements ExoPlayer {
@Override @Override
public void onCues(List<Cue> cues) { public void onCues(List<Cue> cues) {
if (textOutput != null) { for (TextRenderer.Output textOutput : textOutputs) {
textOutput.onCues(cues); textOutput.onCues(cues);
} }
} }
@ -911,7 +986,7 @@ public class SimpleExoPlayer implements ExoPlayer {
@Override @Override
public void onMetadata(Metadata metadata) { public void onMetadata(Metadata metadata) {
if (metadataOutput != null) { for (MetadataRenderer.Output metadataOutput : metadataOutputs) {
metadataOutput.onMetadata(metadata); metadataOutput.onMetadata(metadata);
} }
} }

View File

@ -379,9 +379,7 @@ public final class SimpleExoPlayerView extends FrameLayout {
} }
/** /**
* Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#setTextOutput} and * Set the {@link SimpleExoPlayer} to use.
* {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous
* assignments are overridden.
* <p> * <p>
* To transition a {@link SimpleExoPlayer} from targeting one view to another, it's recommended to * To transition a {@link SimpleExoPlayer} from targeting one view to another, it's recommended to
* use {@link #switchTargetView(SimpleExoPlayer, SimpleExoPlayerView, SimpleExoPlayerView)} rather * use {@link #switchTargetView(SimpleExoPlayer, SimpleExoPlayerView, SimpleExoPlayerView)} rather
@ -397,8 +395,8 @@ public final class SimpleExoPlayerView extends FrameLayout {
} }
if (this.player != null) { if (this.player != null) {
this.player.removeListener(componentListener); this.player.removeListener(componentListener);
this.player.clearTextOutput(componentListener); this.player.removeTextOutput(componentListener);
this.player.clearVideoListener(componentListener); this.player.removeVideoListener(componentListener);
if (surfaceView instanceof TextureView) { if (surfaceView instanceof TextureView) {
this.player.clearVideoTextureView((TextureView) surfaceView); this.player.clearVideoTextureView((TextureView) surfaceView);
} else if (surfaceView instanceof SurfaceView) { } else if (surfaceView instanceof SurfaceView) {
@ -418,8 +416,8 @@ public final class SimpleExoPlayerView extends FrameLayout {
} else if (surfaceView instanceof SurfaceView) { } else if (surfaceView instanceof SurfaceView) {
player.setVideoSurfaceView((SurfaceView) surfaceView); player.setVideoSurfaceView((SurfaceView) surfaceView);
} }
player.setVideoListener(componentListener); player.addVideoListener(componentListener);
player.setTextOutput(componentListener); player.addTextOutput(componentListener);
player.addListener(componentListener); player.addListener(componentListener);
maybeShowController(false); maybeShowController(false);
updateForCurrentTrackSelections(); updateForCurrentTrackSelections();