Commit 1e12c079 by olly Committed by Oliver Woodman

Move renderer building into a factory class.

To inject custom renderers into SimpleExoPlayer, developers
currently need to extend SimpleExoPlayer and override the
renderer building methods. This is in contrast to the rest
of the library, where we use proper injection. This change
restores consistency. I think it's fine to make
SimpleExoPlayer final again, but if we find people extending
it for non-renderer purposes, we can revert that part of the
change.

ExoPlayerFactory now has analogous methods for the simple
and non-simple cases, which is a nice outcome.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=154295726
parent 0f9d9d56
......@@ -31,7 +31,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
......@@ -253,18 +253,21 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
}
}
@SimpleExoPlayer.ExtensionRendererMode int extensionRendererMode =
@DefaultRenderersFactory.ExtensionRendererMode int extensionRendererMode =
((DemoApplication) getApplication()).useExtensionRenderers()
? (preferExtensionDecoders ? SimpleExoPlayer.EXTENSION_RENDERER_MODE_PREFER
: SimpleExoPlayer.EXTENSION_RENDERER_MODE_ON)
: SimpleExoPlayer.EXTENSION_RENDERER_MODE_OFF;
? (preferExtensionDecoders ? DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
: DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON)
: DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF;
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(this,
drmSessionManager, extensionRendererMode);
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
trackSelectionHelper = new TrackSelectionHelper(trackSelector, videoTrackSelectionFactory);
lastSeenTrackGroupArray = null;
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, new DefaultLoadControl(),
drmSessionManager, extensionRendererMode);
player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector);
player.addListener(this);
eventLogger = new EventLogger(trackSelector);
......
/*
* Copyright (C) 2017 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;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.IntDef;
import android.util.Log;
import com.google.android.exoplayer2.audio.AudioCapabilities;
import com.google.android.exoplayer2.audio.AudioProcessor;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.metadata.MetadataRenderer;
import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
/**
* Default {@link RenderersFactory} implementation.
*/
public class DefaultRenderersFactory implements RenderersFactory {
/**
* The default maximum duration for which a video renderer can attempt to seamlessly join an
* ongoing playback.
*/
public static final long DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS = 5000;
/**
* Modes for using extension renderers.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({EXTENSION_RENDERER_MODE_OFF, EXTENSION_RENDERER_MODE_ON,
EXTENSION_RENDERER_MODE_PREFER})
public @interface ExtensionRendererMode {}
/**
* Do not allow use of extension renderers.
*/
public static final int EXTENSION_RENDERER_MODE_OFF = 0;
/**
* Allow use of extension renderers. Extension renderers are indexed after core renderers of the
* same type. A {@link TrackSelector} that prefers the first suitable renderer will therefore
* prefer to use a core renderer to an extension renderer in the case that both are able to play
* a given track.
*/
public static final int EXTENSION_RENDERER_MODE_ON = 1;
/**
* Allow use of extension renderers. Extension renderers are indexed before core renderers of the
* same type. A {@link TrackSelector} that prefers the first suitable renderer will therefore
* prefer to use an extension renderer to a core renderer in the case that both are able to play
* a given track.
*/
public static final int EXTENSION_RENDERER_MODE_PREFER = 2;
private static final String TAG = "DefaultRenderersFactory";
protected static final int MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY = 50;
private final Context context;
private final DrmSessionManager<FrameworkMediaCrypto> drmSessionManager;
private final @ExtensionRendererMode int extensionRendererMode;
private final long allowedVideoJoiningTimeMs;
/**
* @param context A {@link Context}.
*/
public DefaultRenderersFactory(Context context) {
this(context, null);
}
/**
* @param context A {@link Context}.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if DRM protected
* playbacks are not required.
*/
public DefaultRenderersFactory(Context context,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
this(context, drmSessionManager, EXTENSION_RENDERER_MODE_OFF);
}
/**
* @param context A {@link Context}.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if DRM protected
* playbacks are not required..
* @param extensionRendererMode The extension renderer mode, which determines if and how
* available extension renderers are used. Note that extensions must be included in the
* application build for them to be considered available.
*/
public DefaultRenderersFactory(Context context,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
@ExtensionRendererMode int extensionRendererMode) {
this(context, drmSessionManager, extensionRendererMode,
DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS);
}
/**
* @param context A {@link Context}.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if DRM protected
* playbacks are not required..
* @param extensionRendererMode The extension renderer mode, which determines if and how
* available extension renderers are used. Note that extensions must be included in the
* application build for them to be considered available.
* @param allowedVideoJoiningTimeMs The maximum duration for which video renderers can attempt
* to seamlessly join an ongoing playback.
*/
public DefaultRenderersFactory(Context context,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
@ExtensionRendererMode int extensionRendererMode, long allowedVideoJoiningTimeMs) {
this.context = context;
this.drmSessionManager = drmSessionManager;
this.extensionRendererMode = extensionRendererMode;
this.allowedVideoJoiningTimeMs = allowedVideoJoiningTimeMs;
}
@Override
public Renderer[] createRenderers(Handler eventHandler,
VideoRendererEventListener videoRendererEventListener,
AudioRendererEventListener audioRendererEventListener,
TextRenderer.Output textRendererOutput, MetadataRenderer.Output metadataRendererOutput) {
ArrayList<Renderer> renderersList = new ArrayList<>();
buildVideoRenderers(context, drmSessionManager, allowedVideoJoiningTimeMs,
eventHandler, videoRendererEventListener, extensionRendererMode, renderersList);
buildAudioRenderers(context, drmSessionManager, buildAudioProcessors(),
eventHandler, audioRendererEventListener, extensionRendererMode, renderersList);
buildTextRenderers(context, textRendererOutput, eventHandler.getLooper(),
extensionRendererMode, renderersList);
buildMetadataRenderers(context, metadataRendererOutput, eventHandler.getLooper(),
extensionRendererMode, renderersList);
buildMiscellaneousRenderers(context, eventHandler, extensionRendererMode, renderersList);
return renderersList.toArray(new Renderer[renderersList.size()]);
}
/**
* Builds video renderers for use by the player.
*
* @param context The {@link Context} associated with the player.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player
* will not be used for DRM protected playbacks.
* @param allowedVideoJoiningTimeMs The maximum duration in milliseconds for which video
* renderers can attempt to seamlessly join an ongoing playback.
* @param eventHandler A handler associated with the main thread's looper.
* @param eventListener An event listener.
* @param extensionRendererMode The extension renderer mode.
* @param out An array to which the built renderers should be appended.
*/
protected void buildVideoRenderers(Context context,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, long allowedVideoJoiningTimeMs,
Handler eventHandler, VideoRendererEventListener eventListener,
@ExtensionRendererMode int extensionRendererMode, ArrayList<Renderer> out) {
out.add(new MediaCodecVideoRenderer(context, MediaCodecSelector.DEFAULT,
allowedVideoJoiningTimeMs, drmSessionManager, false, eventHandler, eventListener,
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY));
if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) {
return;
}
int extensionRendererIndex = out.size();
if (extensionRendererMode == EXTENSION_RENDERER_MODE_PREFER) {
extensionRendererIndex--;
}
try {
Class<?> clazz =
Class.forName("com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer");
Constructor<?> constructor = clazz.getConstructor(boolean.class, long.class, Handler.class,
VideoRendererEventListener.class, int.class);
Renderer renderer = (Renderer) constructor.newInstance(true, allowedVideoJoiningTimeMs,
eventHandler, eventListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded LibvpxVideoRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Builds audio renderers for use by the player.
*
* @param context The {@link Context} associated with the player.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player
* will not be used for DRM protected playbacks.
* @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio
* buffers before output. May be empty.
* @param eventHandler A handler to use when invoking event listeners and outputs.
* @param eventListener An event listener.
* @param extensionRendererMode The extension renderer mode.
* @param out An array to which the built renderers should be appended.
*/
protected void buildAudioRenderers(Context context,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
AudioProcessor[] audioProcessors, Handler eventHandler,
AudioRendererEventListener eventListener, @ExtensionRendererMode int extensionRendererMode,
ArrayList<Renderer> out) {
out.add(new MediaCodecAudioRenderer(MediaCodecSelector.DEFAULT, drmSessionManager, true,
eventHandler, eventListener, AudioCapabilities.getCapabilities(context), audioProcessors));
if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) {
return;
}
int extensionRendererIndex = out.size();
if (extensionRendererMode == EXTENSION_RENDERER_MODE_PREFER) {
extensionRendererIndex--;
}
try {
Class<?> clazz =
Class.forName("com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer");
Constructor<?> constructor = clazz.getConstructor(Handler.class,
AudioRendererEventListener.class, AudioProcessor[].class);
Renderer renderer = (Renderer) constructor.newInstance(eventHandler, eventListener,
audioProcessors);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded LibopusAudioRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
Class<?> clazz =
Class.forName("com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer");
Constructor<?> constructor = clazz.getConstructor(Handler.class,
AudioRendererEventListener.class, AudioProcessor[].class);
Renderer renderer = (Renderer) constructor.newInstance(eventHandler, eventListener,
audioProcessors);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded LibflacAudioRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
Class<?> clazz =
Class.forName("com.google.android.exoplayer2.ext.ffmpeg.FfmpegAudioRenderer");
Constructor<?> constructor = clazz.getConstructor(Handler.class,
AudioRendererEventListener.class, AudioProcessor[].class);
Renderer renderer = (Renderer) constructor.newInstance(eventHandler, eventListener,
audioProcessors);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded FfmpegAudioRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Builds text renderers for use by the player.
*
* @param context The {@link Context} associated with the player.
* @param output An output for the renderers.
* @param outputLooper The looper associated with the thread on which the output should be
* called.
* @param extensionRendererMode The extension renderer mode.
* @param out An array to which the built renderers should be appended.
*/
protected void buildTextRenderers(Context context, TextRenderer.Output output,
Looper outputLooper, @ExtensionRendererMode int extensionRendererMode,
ArrayList<Renderer> out) {
out.add(new TextRenderer(output, outputLooper));
}
/**
* Builds metadata renderers for use by the player.
*
* @param context The {@link Context} associated with the player.
* @param output An output for the renderers.
* @param outputLooper The looper associated with the thread on which the output should be
* called.
* @param extensionRendererMode The extension renderer mode.
* @param out An array to which the built renderers should be appended.
*/
protected void buildMetadataRenderers(Context context, MetadataRenderer.Output output,
Looper outputLooper, @ExtensionRendererMode int extensionRendererMode,
ArrayList<Renderer> out) {
out.add(new MetadataRenderer(output, outputLooper));
}
/**
* Builds any miscellaneous renderers used by the player.
*
* @param context The {@link Context} associated with the player.
* @param eventHandler A handler to use when invoking event listeners and outputs.
* @param extensionRendererMode The extension renderer mode.
* @param out An array to which the built renderers should be appended.
*/
protected void buildMiscellaneousRenderers(Context context, Handler eventHandler,
@ExtensionRendererMode int extensionRendererMode, ArrayList<Renderer> out) {
// Do nothing.
}
/**
* Builds an array of {@link AudioProcessor}s that will process PCM audio before output.
*/
protected AudioProcessor[] buildAudioProcessors() {
return new AudioProcessor[0];
}
}
......@@ -26,12 +26,6 @@ import com.google.android.exoplayer2.trackselection.TrackSelector;
*/
public final class ExoPlayerFactory {
/**
* The default maximum duration for which a video renderer can attempt to seamlessly join an
* ongoing playback.
*/
public static final long DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS = 5000;
private ExoPlayerFactory() {}
/**
......@@ -41,10 +35,13 @@ public final class ExoPlayerFactory {
* @param context A {@link Context}.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @deprecated Use {@link #newSimpleInstance(RenderersFactory, TrackSelector, LoadControl)}.
*/
@Deprecated
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
LoadControl loadControl) {
return newSimpleInstance(context, trackSelector, loadControl, null);
RenderersFactory renderersFactory = new DefaultRenderersFactory(context);
return newSimpleInstance(renderersFactory, trackSelector, loadControl);
}
/**
......@@ -56,11 +53,13 @@ public final class ExoPlayerFactory {
* @param loadControl The {@link LoadControl} that will be used by the instance.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks.
* @deprecated Use {@link #newSimpleInstance(RenderersFactory, TrackSelector, LoadControl)}.
*/
@Deprecated
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
LoadControl loadControl, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
return newSimpleInstance(context, trackSelector, loadControl,
drmSessionManager, SimpleExoPlayer.EXTENSION_RENDERER_MODE_OFF);
RenderersFactory renderersFactory = new DefaultRenderersFactory(context, drmSessionManager);
return newSimpleInstance(renderersFactory, trackSelector, loadControl);
}
/**
......@@ -75,12 +74,15 @@ public final class ExoPlayerFactory {
* @param extensionRendererMode The extension renderer mode, which determines if and how available
* extension renderers are used. Note that extensions must be included in the application
* build for them to be considered available.
* @deprecated Use {@link #newSimpleInstance(RenderersFactory, TrackSelector, LoadControl)}.
*/
@Deprecated
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
LoadControl loadControl, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
@SimpleExoPlayer.ExtensionRendererMode int extensionRendererMode) {
return newSimpleInstance(context, trackSelector, loadControl, drmSessionManager,
extensionRendererMode, DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS);
@DefaultRenderersFactory.ExtensionRendererMode int extensionRendererMode) {
RenderersFactory renderersFactory = new DefaultRenderersFactory(context, drmSessionManager,
extensionRendererMode);
return newSimpleInstance(renderersFactory, trackSelector, loadControl);
}
/**
......@@ -97,13 +99,52 @@ public final class ExoPlayerFactory {
* build for them to be considered available.
* @param allowedVideoJoiningTimeMs The maximum duration for which a video renderer can attempt to
* seamlessly join an ongoing playback.
* @deprecated Use {@link #newSimpleInstance(RenderersFactory, TrackSelector, LoadControl)}.
*/
@Deprecated
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector,
LoadControl loadControl, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
@SimpleExoPlayer.ExtensionRendererMode int extensionRendererMode,
@DefaultRenderersFactory.ExtensionRendererMode int extensionRendererMode,
long allowedVideoJoiningTimeMs) {
return new SimpleExoPlayer(context, trackSelector, loadControl, drmSessionManager,
RenderersFactory renderersFactory = new DefaultRenderersFactory(context, drmSessionManager,
extensionRendererMode, allowedVideoJoiningTimeMs);
return newSimpleInstance(renderersFactory, trackSelector, loadControl);
}
/**
* Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated
* {@link Looper}.
*
* @param context A {@link Context}.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
*/
public static SimpleExoPlayer newSimpleInstance(Context context, TrackSelector trackSelector) {
return newSimpleInstance(new DefaultRenderersFactory(context), trackSelector);
}
/**
* Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated
* {@link Looper}.
*
* @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
*/
public static SimpleExoPlayer newSimpleInstance(RenderersFactory renderersFactory,
TrackSelector trackSelector) {
return newSimpleInstance(renderersFactory, trackSelector, new DefaultLoadControl());
}
/**
* Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated
* {@link Looper}.
*
* @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
*/
public static SimpleExoPlayer newSimpleInstance(RenderersFactory renderersFactory,
TrackSelector trackSelector, LoadControl loadControl) {
return new SimpleExoPlayer(renderersFactory, trackSelector, loadControl);
}
/**
......
/*
* Copyright (C) 2016 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;
import android.os.Handler;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.metadata.MetadataRenderer;
import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
/**
* Builds {@link Renderer} instances for use by a {@link SimpleExoPlayer}.
*/
public interface RenderersFactory {
/**
* Builds the {@link Renderer} instances for a {@link SimpleExoPlayer}.
*
* @param eventHandler A handler to use when invoking event listeners and outputs.
* @param videoRendererEventListener An event listener for video renderers.
* @param videoRendererEventListener An event listener for audio renderers.
* @param textRendererOutput An output for text renderers.
* @param metadataRendererOutput An output for metadata renderers.
* @return The {@link Renderer instances}.
*/
Renderer[] createRenderers(Handler eventHandler,
VideoRendererEventListener videoRendererEventListener,
AudioRendererEventListener audioRendererEventListener,
TextRenderer.Output textRendererOutput, MetadataRenderer.Output metadataRendererOutput);
}
......@@ -16,26 +16,18 @@
package com.google.android.exoplayer2;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.media.MediaCodec;
import android.media.PlaybackParams;
import android.os.Handler;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import com.google.android.exoplayer2.audio.AudioCapabilities;
import com.google.android.exoplayer2.audio.AudioProcessor;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataRenderer;
import com.google.android.exoplayer2.source.MediaSource;
......@@ -44,12 +36,7 @@ import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.TextRenderer;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
/**
......@@ -91,38 +78,12 @@ public class SimpleExoPlayer implements ExoPlayer {
}
/**
* Modes for using extension renderers.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({EXTENSION_RENDERER_MODE_OFF, EXTENSION_RENDERER_MODE_ON, EXTENSION_RENDERER_MODE_PREFER})
public @interface ExtensionRendererMode {}
/**
* Do not allow use of extension renderers.
*/
public static final int EXTENSION_RENDERER_MODE_OFF = 0;
/**
* Allow use of extension renderers. Extension renderers are indexed after core renderers of the
* same type. A {@link TrackSelector} that prefers the first suitable renderer will therefore
* prefer to use a core renderer to an extension renderer in the case that both are able to play
* a given track.
*/
public static final int EXTENSION_RENDERER_MODE_ON = 1;
/**
* Allow use of extension renderers. Extension renderers are indexed before core renderers of the
* same type. A {@link TrackSelector} that prefers the first suitable renderer will therefore
* prefer to use an extension renderer to a core renderer in the case that both are able to play
* a given track.
*/
public static final int EXTENSION_RENDERER_MODE_PREFER = 2;
private static final String TAG = "SimpleExoPlayer";
protected static final int MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY = 50;
protected final Renderer[] renderers;
private final ExoPlayer player;
private final Renderer[] renderers;
private final ComponentListener componentListener;
private final Handler mainHandler;
private final int videoRendererCount;
private final int audioRendererCount;
......@@ -147,17 +108,11 @@ public class SimpleExoPlayer implements ExoPlayer {
private int audioStreamType;
private float audioVolume;
protected SimpleExoPlayer(Context context, TrackSelector trackSelector, LoadControl loadControl,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
@ExtensionRendererMode int extensionRendererMode, long allowedVideoJoiningTimeMs) {
mainHandler = new Handler();
protected SimpleExoPlayer(RenderersFactory renderersFactory, TrackSelector trackSelector,
LoadControl loadControl) {
componentListener = new ComponentListener();
// Build the renderers.
ArrayList<Renderer> renderersList = new ArrayList<>();
buildRenderers(context, mainHandler, drmSessionManager, extensionRendererMode,
allowedVideoJoiningTimeMs, renderersList);
renderers = renderersList.toArray(new Renderer[renderersList.size()]);
renderers = renderersFactory.createRenderers(new Handler(), componentListener,
componentListener, componentListener, componentListener);
// Obtain counts of video and audio renderers.
int videoRendererCount = 0;
......@@ -688,190 +643,6 @@ public class SimpleExoPlayer implements ExoPlayer {
return player.isCurrentWindowSeekable();
}
// Renderer building.
private void buildRenderers(Context context, Handler mainHandler,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
@ExtensionRendererMode int extensionRendererMode, long allowedVideoJoiningTimeMs,
ArrayList<Renderer> out) {
buildVideoRenderers(context, mainHandler, drmSessionManager, extensionRendererMode,
componentListener, allowedVideoJoiningTimeMs, out);
buildAudioRenderers(context, mainHandler, drmSessionManager, extensionRendererMode,
componentListener, buildAudioProcessors(), out);
buildTextRenderers(context, mainHandler, extensionRendererMode, componentListener, out);
buildMetadataRenderers(context, mainHandler, extensionRendererMode, componentListener, out);
buildMiscellaneousRenderers(context, mainHandler, extensionRendererMode, out);
}
/**
* Builds video renderers for use by the player.
*
* @param context The {@link Context} associated with the player.
* @param mainHandler A handler associated with the main thread's looper.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player will
* not be used for DRM protected playbacks.
* @param extensionRendererMode The extension renderer mode.
* @param eventListener An event listener.
* @param allowedVideoJoiningTimeMs The maximum duration in milliseconds for which video renderers
* can attempt to seamlessly join an ongoing playback.
* @param out An array to which the built renderers should be appended.
*/
protected void buildVideoRenderers(Context context, Handler mainHandler,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
@ExtensionRendererMode int extensionRendererMode, VideoRendererEventListener eventListener,
long allowedVideoJoiningTimeMs, ArrayList<Renderer> out) {
out.add(new MediaCodecVideoRenderer(context, MediaCodecSelector.DEFAULT,
allowedVideoJoiningTimeMs, drmSessionManager, false, mainHandler, eventListener,
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY));
if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) {
return;
}
int extensionRendererIndex = out.size();
if (extensionRendererMode == EXTENSION_RENDERER_MODE_PREFER) {
extensionRendererIndex--;
}
try {
Class<?> clazz =
Class.forName("com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer");
Constructor<?> constructor = clazz.getConstructor(boolean.class, long.class, Handler.class,
VideoRendererEventListener.class, int.class);
Renderer renderer = (Renderer) constructor.newInstance(true, allowedVideoJoiningTimeMs,
mainHandler, eventListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded LibvpxVideoRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Builds audio renderers for use by the player.
*
* @param context The {@link Context} associated with the player.
* @param mainHandler A handler associated with the main thread's looper.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player will
* not be used for DRM protected playbacks.
* @param extensionRendererMode The extension renderer mode.
* @param eventListener An event listener.
* @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio buffers
* before output. May be empty.
* @param out An array to which the built renderers should be appended.
*/
protected void buildAudioRenderers(Context context, Handler mainHandler,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
@ExtensionRendererMode int extensionRendererMode, AudioRendererEventListener eventListener,
AudioProcessor[] audioProcessors, ArrayList<Renderer> out) {
out.add(new MediaCodecAudioRenderer(MediaCodecSelector.DEFAULT, drmSessionManager, true,
mainHandler, eventListener, AudioCapabilities.getCapabilities(context), audioProcessors));
if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) {
return;
}
int extensionRendererIndex = out.size();
if (extensionRendererMode == EXTENSION_RENDERER_MODE_PREFER) {
extensionRendererIndex--;
}
try {
Class<?> clazz =
Class.forName("com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer");
Constructor<?> constructor = clazz.getConstructor(Handler.class,
AudioRendererEventListener.class, AudioProcessor[].class);
Renderer renderer = (Renderer) constructor.newInstance(mainHandler, eventListener,
audioProcessors);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded LibopusAudioRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
Class<?> clazz =
Class.forName("com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer");
Constructor<?> constructor = clazz.getConstructor(Handler.class,
AudioRendererEventListener.class, AudioProcessor[].class);
Renderer renderer = (Renderer) constructor.newInstance(mainHandler, eventListener,
audioProcessors);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded LibflacAudioRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
Class<?> clazz =
Class.forName("com.google.android.exoplayer2.ext.ffmpeg.FfmpegAudioRenderer");
Constructor<?> constructor = clazz.getConstructor(Handler.class,
AudioRendererEventListener.class, AudioProcessor[].class);
Renderer renderer = (Renderer) constructor.newInstance(mainHandler, eventListener,
audioProcessors);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded FfmpegAudioRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Builds text renderers for use by the player.
*
* @param context The {@link Context} associated with the player.
* @param mainHandler A handler associated with the main thread's looper.
* @param extensionRendererMode The extension renderer mode.
* @param output An output for the renderers.
* @param out An array to which the built renderers should be appended.
*/
protected void buildTextRenderers(Context context, Handler mainHandler,
@ExtensionRendererMode int extensionRendererMode, TextRenderer.Output output,
ArrayList<Renderer> out) {
out.add(new TextRenderer(output, mainHandler.getLooper()));
}
/**
* Builds metadata renderers for use by the player.
*
* @param context The {@link Context} associated with the player.
* @param mainHandler A handler associated with the main thread's looper.
* @param extensionRendererMode The extension renderer mode.
* @param output An output for the renderers.
* @param out An array to which the built renderers should be appended.
*/
protected void buildMetadataRenderers(Context context, Handler mainHandler,
@ExtensionRendererMode int extensionRendererMode, MetadataRenderer.Output output,
ArrayList<Renderer> out) {
out.add(new MetadataRenderer(output, mainHandler.getLooper()));
}
/**
* Builds any miscellaneous renderers used by the player.
*
* @param context The {@link Context} associated with the player.
* @param mainHandler A handler associated with the main thread's looper.
* @param extensionRendererMode The extension renderer mode.
* @param out An array to which the built renderers should be appended.
*/
protected void buildMiscellaneousRenderers(Context context, Handler mainHandler,
@ExtensionRendererMode int extensionRendererMode, ArrayList<Renderer> out) {
// Do nothing.
}
/**
* Builds an array of {@link AudioProcessor}s that will process PCM audio before output.
*/
protected AudioProcessor[] buildAudioProcessors() {
return new AudioProcessor[0];
}
// Internal methods.
private void removeSurfaceCallbacks() {
......
......@@ -25,8 +25,8 @@ import android.net.Uri;
import android.util.Log;
import android.view.Surface;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.SimpleExoPlayer;
......@@ -39,7 +39,7 @@ import com.google.android.exoplayer2.drm.MediaDrmCallback;
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
import com.google.android.exoplayer2.playbacktests.util.ActionSchedule;
import com.google.android.exoplayer2.playbacktests.util.DebugSimpleExoPlayer;
import com.google.android.exoplayer2.playbacktests.util.DebugRenderersFactory;
import com.google.android.exoplayer2.playbacktests.util.DecoderCountersUtil;
import com.google.android.exoplayer2.playbacktests.util.ExoHostedTest;
import com.google.android.exoplayer2.playbacktests.util.HostActivity;
......@@ -296,8 +296,8 @@ public final class DashTestRunner {
protected SimpleExoPlayer buildExoPlayer(HostActivity host, Surface surface,
MappingTrackSelector trackSelector,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
SimpleExoPlayer player = new DebugSimpleExoPlayer(host, trackSelector,
new DefaultLoadControl(), drmSessionManager);
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(
new DebugRenderersFactory(host, drmSessionManager), trackSelector);
player.setVideoSurface(surface);
return player;
}
......
......@@ -18,39 +18,37 @@ package com.google.android.exoplayer2.playbacktests.util;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Handler;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
import com.google.android.exoplayer2.video.VideoRendererEventListener;
import java.util.ArrayList;
/**
* A debug extension of {@link SimpleExoPlayer}. Provides video buffer timestamp assertions.
* A debug extension of {@link DefaultRenderersFactory}. Provides a video renderer that performs
* video buffer timestamp assertions.
*/
@TargetApi(16)
public class DebugSimpleExoPlayer extends SimpleExoPlayer {
public class DebugRenderersFactory extends DefaultRenderersFactory {
public DebugSimpleExoPlayer(Context context, TrackSelector trackSelector,
LoadControl loadControl, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
super(context, trackSelector, loadControl, drmSessionManager,
SimpleExoPlayer.EXTENSION_RENDERER_MODE_OFF, 0);
public DebugRenderersFactory(Context context,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
super(context, drmSessionManager, DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF, 0);
}
@Override
protected void buildVideoRenderers(Context context, Handler mainHandler,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
@ExtensionRendererMode int extensionRendererMode, VideoRendererEventListener eventListener,
long allowedVideoJoiningTimeMs, ArrayList<Renderer> out) {
protected void buildVideoRenderers(Context context,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, long allowedVideoJoiningTimeMs,
Handler eventHandler, VideoRendererEventListener eventListener,
@ExtensionRendererMode int extensionRendererMode, ArrayList<Renderer> out) {
out.add(new DebugMediaCodecVideoRenderer(context, MediaCodecSelector.DEFAULT,
allowedVideoJoiningTimeMs, mainHandler, drmSessionManager, eventListener,
allowedVideoJoiningTimeMs, drmSessionManager, eventHandler, eventListener,
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY));
}
......@@ -70,9 +68,9 @@ public class DebugSimpleExoPlayer extends SimpleExoPlayer {
private int minimumInsertIndex;
public DebugMediaCodecVideoRenderer(Context context, MediaCodecSelector mediaCodecSelector,
long allowedJoiningTimeMs, Handler eventHandler,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
VideoRendererEventListener eventListener, int maxDroppedFrameCountToNotify) {
long allowedJoiningTimeMs, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
Handler eventHandler, VideoRendererEventListener eventListener,
int maxDroppedFrameCountToNotify) {
super(context, mediaCodecSelector, allowedJoiningTimeMs, drmSessionManager, false,
eventHandler, eventListener, maxDroppedFrameCountToNotify);
}
......
......@@ -19,12 +19,13 @@ import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.view.Surface;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
......@@ -326,9 +327,9 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
protected SimpleExoPlayer buildExoPlayer(HostActivity host, Surface surface,
MappingTrackSelector trackSelector,
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(host, trackSelector,
new DefaultLoadControl(), drmSessionManager, SimpleExoPlayer.EXTENSION_RENDERER_MODE_OFF,
0);
RenderersFactory renderersFactory = new DefaultRenderersFactory(host, drmSessionManager,
DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF, 0);
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector);
player.setVideoSurface(surface);
return player;
}
......
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