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:
parent
5ab0c620bf
commit
88bae5d975
@ -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);
|
||||||
|
@ -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.
|
|
||||||
*
|
|
||||||
* @param output The output to clear.
|
|
||||||
*/
|
|
||||||
public void clearTextOutput(TextRenderer.Output output) {
|
|
||||||
if (textOutput == output) {
|
|
||||||
textOutput = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a listener to receive metadata events.
|
* Equivalent to {@link #removeTextOutput(TextRenderer.Output)}.
|
||||||
|
*
|
||||||
|
* @param output The output to clear.
|
||||||
|
* @deprecated Use {@link #removeTextOutput(TextRenderer.Output)}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void clearTextOutput(TextRenderer.Output output) {
|
||||||
|
removeTextOutput(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,8 +901,10 @@ 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) {
|
||||||
videoListener.onRenderedFirstFrame();
|
for (VideoListener videoListener : videoListeners) {
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user