private void setVideoSurfaceInternal(Surface surface, boolean ownsSurface) { // Note: We don't turn this method into a no-op if the surface is being replaced with itself // so as to ensure onRenderedFirstFrame callbacks are still called in this case. ExoPlayerMessage[] messages = new ExoPlayerMessage[videoRendererCount]; int count = 0; for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) { messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_SURFACE, surface); } } if (this.surface != null && this.surface != surface) { // We're replacing a surface. Block to ensure that it's not accessed after the method returns. player.blockingSendMessages(messages); // If we created the previous surface, we are responsible for releasing it. if (this.ownsSurface) { this.surface.release(); } } else { player.sendMessages(messages); } this.surface = surface; this.ownsSurface = ownsSurface; }
@Override public Renderer[] createRenderers(Handler eventHandler, VideoRendererEventListener videoRendererEventListener, AudioRendererEventListener audioRendererEventListener, TextOutput textRendererOutput, MetadataOutput metadataRendererOutput) { ArrayList<Renderer> renderersList = new ArrayList<>(); buildVideoRenderers(context, drmSessionManager, allowedVideoJoiningTimeMs, eventHandler, videoRendererEventListener, extensionRendererMode, renderersList); buildAudioRenderers(context, drmSessionManager, buildAudioProcessors(), eventHandler, audioRendererEventListener, extensionRendererMode, renderersList); buildTextRenderers(textRendererOutput, eventHandler.getLooper(), renderersList); buildMetadataRenderers(metadataRendererOutput, eventHandler.getLooper(), renderersList); buildMiscellaneousRenderers(); return renderersList.toArray(new Renderer[renderersList.size()]); }
@Override public void run() { Looper.prepare(); LibvpxVideoRenderer videoRenderer = new LibvpxVideoRenderer(true, 0); DefaultTrackSelector trackSelector = new DefaultTrackSelector(); player = ExoPlayerFactory.newInstance(new Renderer[] {videoRenderer}, trackSelector); player.addListener(this); ExtractorMediaSource mediaSource = new ExtractorMediaSource( uri, new DefaultDataSourceFactory(context, "ExoPlayerExtVp9Test"), MatroskaExtractor.FACTORY, null, null); player.sendMessages(new ExoPlayer.ExoPlayerMessage(videoRenderer, LibvpxVideoRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER, new VpxVideoSurfaceView(context))); player.prepare(mediaSource); player.setPlayWhenReady(true); Looper.loop(); }
@Override public void run() { Looper.prepare(); LibopusAudioRenderer audioRenderer = new LibopusAudioRenderer(); DefaultTrackSelector trackSelector = new DefaultTrackSelector(); player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector); player.addListener(this); ExtractorMediaSource mediaSource = new ExtractorMediaSource( uri, new DefaultDataSourceFactory(context, "ExoPlayerExtOpusTest"), MatroskaExtractor.FACTORY, null, null); player.prepare(mediaSource); player.setPlayWhenReady(true); Looper.loop(); }
@Override public void run() { Looper.prepare(); LibflacAudioRenderer audioRenderer = new LibflacAudioRenderer(); DefaultTrackSelector trackSelector = new DefaultTrackSelector(); player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector); player.addListener(this); ExtractorMediaSource mediaSource = new ExtractorMediaSource( uri, new DefaultDataSourceFactory(context, "ExoPlayerExtFlacTest"), MatroskaExtractor.FACTORY, null, null); player.prepare(mediaSource); player.setPlayWhenReady(true); Looper.loop(); }
@NonNull protected List<Renderer> buildAudioRenderers() { List<Renderer> renderers = new ArrayList<>(); renderers.add(new MediaCodecAudioRenderer(MediaCodecSelector.DEFAULT, drmSessionManager, true, handler, audioRendererEventListener, AudioCapabilities.getCapabilities(context))); // Adds any registered classes List<String> classNames = ExoMedia.Data.registeredRendererClasses.get(ExoMedia.RendererType.AUDIO); if (classNames != null) { for (String className: classNames) { try { Class<?> clazz = Class.forName(className); Constructor<?> constructor = clazz.getConstructor(Handler.class, AudioRendererEventListener.class); Renderer renderer = (Renderer) constructor.newInstance(handler, audioRendererEventListener); renderers.add(renderer); } catch (Exception e) { // Purposefully left blank } } } return renderers; }
@NonNull protected List<Renderer> buildVideoRenderers() { List<Renderer> renderers = new ArrayList<>(); renderers.add(new MediaCodecVideoRenderer(context, MediaCodecSelector.DEFAULT, videoJoiningTimeMs, drmSessionManager, false, handler, videoRendererEventListener, droppedFrameNotificationAmount)); // Adds any registered classes List<String> classNames = ExoMedia.Data.registeredRendererClasses.get(ExoMedia.RendererType.VIDEO); if (classNames != null) { for (String className: classNames) { try { Class<?> clazz = Class.forName(className); Constructor<?> constructor = clazz.getConstructor(boolean.class, long.class, Handler.class, VideoRendererEventListener.class, int.class); Renderer renderer = (Renderer) constructor.newInstance(true, videoJoiningTimeMs, handler, videoRendererEventListener, droppedFrameNotificationAmount); renderers.add(renderer); } catch (Exception e) { // Purposefully left blank } } } return renderers; }
public ExoMediaPlayer(@NonNull Context context) { this.context = context; bufferRepeater.setRepeaterDelay(BUFFER_REPEAT_DELAY); bufferRepeater.setRepeatListener(new BufferRepeatListener()); mainHandler = new Handler(); ComponentListener componentListener = new ComponentListener(); RendererProvider rendererProvider = new RendererProvider(context, mainHandler, componentListener, componentListener, componentListener, componentListener); rendererProvider.setDrmSessionManager(generateDrmSessionManager()); renderers = rendererProvider.generate(); adaptiveTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory); LoadControl loadControl = ExoMedia.Data.loadControl != null ? ExoMedia.Data.loadControl : new DefaultLoadControl(); player = ExoPlayerFactory.newInstance(renderers.toArray(new Renderer[renderers.size()]), trackSelector, loadControl); player.addListener(this); }
protected void sendMessage(int renderType, int messageType, Object message, boolean blocking) { if (renderers.isEmpty()) { return; } List<ExoPlayer.ExoPlayerMessage> messages = new ArrayList<>(); for (Renderer renderer : renderers) { if (renderer.getTrackType() == renderType) { messages.add(new ExoPlayer.ExoPlayerMessage(renderer, messageType, message)); } } if (blocking) { player.blockingSendMessages(messages.toArray(new ExoPlayer.ExoPlayerMessage[messages.size()])); } else { player.sendMessages(messages.toArray(new ExoPlayer.ExoPlayerMessage[messages.size()])); } }
@Override public void run() { Looper.prepare(); LibvpxVideoRenderer videoRenderer = new LibvpxVideoRenderer(true, 0); DefaultTrackSelector trackSelector = new DefaultTrackSelector(); player = ExoPlayerFactory.newInstance(new Renderer[] {videoRenderer}, trackSelector); player.addListener(this); MediaSource mediaSource = new ExtractorMediaSource.Factory( new DefaultDataSourceFactory(context, "ExoPlayerExtVp9Test")) .setExtractorsFactory(MatroskaExtractor.FACTORY) .createMediaSource(uri); player.sendMessages(new ExoPlayer.ExoPlayerMessage(videoRenderer, LibvpxVideoRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER, new VpxVideoSurfaceView(context))); player.prepare(mediaSource); player.setPlayWhenReady(true); Looper.loop(); }
@Override public void run() { Looper.prepare(); LibopusAudioRenderer audioRenderer = new LibopusAudioRenderer(); DefaultTrackSelector trackSelector = new DefaultTrackSelector(); player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector); player.addListener(this); MediaSource mediaSource = new ExtractorMediaSource.Factory( new DefaultDataSourceFactory(context, "ExoPlayerExtOpusTest")) .setExtractorsFactory(MatroskaExtractor.FACTORY) .createMediaSource(uri); player.prepare(mediaSource); player.setPlayWhenReady(true); Looper.loop(); }
@Override public void run() { Looper.prepare(); LibflacAudioRenderer audioRenderer = new LibflacAudioRenderer(); DefaultTrackSelector trackSelector = new DefaultTrackSelector(); player = ExoPlayerFactory.newInstance(new Renderer[] {audioRenderer}, trackSelector); player.addListener(this); MediaSource mediaSource = new ExtractorMediaSource.Factory( new DefaultDataSourceFactory(context, "ExoPlayerExtFlacTest")) .setExtractorsFactory(MatroskaExtractor.FACTORY) .createMediaSource(uri); player.prepare(mediaSource); player.setPlayWhenReady(true); Looper.loop(); }
public FakeExoPlayer(Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl) { this.renderers = renderers; this.trackSelector = trackSelector; this.loadControl = loadControl; this.eventListeners = new CopyOnWriteArraySet<>(); Looper eventListenerLooper = Looper.myLooper(); this.eventListenerHandler = new Handler(eventListenerLooper != null ? eventListenerLooper : Looper.getMainLooper()); this.playbackThread = new HandlerThread("FakeExoPlayer Thread"); playbackThread.start(); this.playbackHandler = new Handler(playbackThread.getLooper()); this.isStartingUp = true; this.isLoading = false; this.playbackState = Player.STATE_IDLE; this.durationUs = C.TIME_UNSET; }
@Override public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { targetBufferSize = 0; for (int i = 0; i < renderers.length; i++) { if (trackSelections.get(i) != null) { targetBufferSize += Util.getDefaultBufferSize(renderers[i].getTrackType()); } } allocator.setTargetBufferSize(targetBufferSize); }
@Override protected void buildVideoRenderers(Context context, Handler mainHandler, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, @ExtensionRendererMode int extensionRendererMode, VideoRendererEventListener eventListener, long allowedVideoJoiningTimeMs, ArrayList<Renderer> out) { out.add(new DebugMediaCodecVideoRenderer(context, MediaCodecSelector.DEFAULT, allowedVideoJoiningTimeMs, mainHandler, drmSessionManager, eventListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY)); }
@Override public void onTracksSelected(Renderer[] renderers, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { int targetBufferSize = 0; for (int i = 0; i < renderers.length; i++) { if (trackSelections.get(i) != null) { targetBufferSize += Util.getDefaultBufferSize(renderers[i].getTrackType()); } } allocator.setTargetBufferSize(targetBufferSize); }
public SimpleExoPlayer2(RenderersFactory renderersFactory, TrackSelector trackSelector, LoadControl loadControl) { componentListener = new ComponentListener(); videoListeners = new CopyOnWriteArraySet<>(); textOutputs = new CopyOnWriteArraySet<>(); metadataOutputs = new CopyOnWriteArraySet<>(); Looper eventLooper = Looper.myLooper() != null ? Looper.myLooper() : Looper.getMainLooper(); Handler eventHandler = new Handler(eventLooper); renderers = renderersFactory.createRenderers(eventHandler, componentListener, componentListener, componentListener, componentListener); // Obtain counts of video and audio renderers. int videoRendererCount = 0; int audioRendererCount = 0; for (Renderer renderer : renderers) { switch (renderer.getTrackType()) { case C.TRACK_TYPE_VIDEO: videoRendererCount++; break; case C.TRACK_TYPE_AUDIO: audioRendererCount++; break; } } this.videoRendererCount = videoRendererCount; this.audioRendererCount = audioRendererCount; // Set initial values. audioVolume = 1; audioSessionId = C.AUDIO_SESSION_ID_UNSET; audioAttributes = AudioAttributes.DEFAULT; videoScalingMode = C.VIDEO_SCALING_MODE_DEFAULT; // Build the player and associated objects. player = createExoPlayerImpl(renderers, trackSelector, loadControl); }
public void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode) { this.videoScalingMode = videoScalingMode; ExoPlayerMessage[] messages = new ExoPlayerMessage[videoRendererCount]; int count = 0; for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) { messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_SCALING_MODE, videoScalingMode); } } player.sendMessages(messages); }
public void setAudioAttributes(AudioAttributes audioAttributes) { this.audioAttributes = audioAttributes; ExoPlayerMessage[] messages = new ExoPlayerMessage[audioRendererCount]; int count = 0; for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) { messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_AUDIO_ATTRIBUTES, audioAttributes); } } player.sendMessages(messages); }
/** * Sets the audio volume, with 0 being silence and 1 being unity gain. * * @param audioVolume The audio volume. */ public void setVolume(float audioVolume) { this.audioVolume = audioVolume; ExoPlayerMessage[] messages = new ExoPlayerMessage[audioRendererCount]; int count = 0; for (Renderer renderer : renderers) { if (renderer.getTrackType() == C.TRACK_TYPE_AUDIO) { messages[count++] = new ExoPlayerMessage(renderer, C.MSG_SET_VOLUME, audioVolume); } } player.sendMessages(messages); }
@NonNull public List<Renderer> generate() { List<Renderer> renderers = new ArrayList<>(); renderers.addAll(buildAudioRenderers()); renderers.addAll(buildVideoRenderers()); renderers.addAll(buildCaptionRenderers()); renderers.addAll(buildMetadataRenderers()); return renderers; }
@NonNull protected List<Renderer> buildCaptionRenderers() { List<Renderer> renderers = new ArrayList<>(); renderers.add(new TextRenderer(captionListener, handler.getLooper())); return renderers; }
@NonNull protected List<Renderer> buildMetadataRenderers() { List<Renderer> renderers = new ArrayList<>(); renderers.add(new MetadataRenderer(metadataListener, handler.getLooper(), MetadataDecoderFactory.DEFAULT)); return renderers; }
@Override 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, drmSessionManager, eventHandler, eventListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY)); }
@Override protected void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format, MediaCrypto crypto) throws DecoderQueryException { // If the codec is being initialized whilst the renderer is started, default behavior is to // render the first frame (i.e. the keyframe before the current position), then drop frames up // to the current playback position. For test runs that place a maximum limit on the number of // dropped frames allowed, this is not desired behavior. Hence we skip (rather than drop) // frames up to the current playback position [Internal: b/66494991]. skipToPositionBeforeRenderingFirstFrame = getState() == Renderer.STATE_STARTED; super.configureCodec(codecInfo, codec, format, crypto); }
protected ExoPlayer createExoPlayerImpl(Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl) { return ExoPlayerFactory.newInstance(renderers, trackSelector, loadControl); }
@Override protected ExoPlayer createExoPlayerImpl(Renderer[] renderers, TrackSelector trackSelector, LoadControl loadControl) { this.player = new FakeExoPlayer(renderers, trackSelector, loadControl); return player; }
@Override public void run() { try { maybeContinueLoading(); boolean allRenderersEnded = true; boolean allRenderersReadyOrEnded = true; if (playbackState == Player.STATE_READY) { for (Renderer renderer : renderers) { renderer.render(rendererPositionUs, C.msToUs(clock.elapsedRealtime())); if (!renderer.isEnded()) { allRenderersEnded = false; } if (!(renderer.isReady() || renderer.isEnded())) { allRenderersReadyOrEnded = false; } } } if (rendererPositionUs >= durationUs && allRenderersEnded) { changePlaybackState(Player.STATE_ENDED); return; } long bufferedPositionUs = mediaPeriod.getBufferedPositionUs(); if (playbackState == Player.STATE_BUFFERING && allRenderersReadyOrEnded && haveSufficientBuffer(!isStartingUp, rendererPositionUs, bufferedPositionUs)) { changePlaybackState(Player.STATE_READY); isStartingUp = false; } else if (playbackState == Player.STATE_READY && !allRenderersReadyOrEnded) { changePlaybackState(Player.STATE_BUFFERING); } // Advance simulated time by 10ms. clock.advanceTime(10); if (playbackState == Player.STATE_READY) { rendererPositionUs += 10000; } this.currentPositionMs = C.usToMs(rendererPositionUs); this.bufferedPositionMs = C.usToMs(bufferedPositionUs); playbackHandler.post(this); } catch (ExoPlaybackException e) { handlePlayerError(e); } }
/** * Builds text renderers for use by 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 outRenderers An array to which the built renderers should be appended. */ private void buildTextRenderers(TextOutput output, Looper outputLooper, List<Renderer> outRenderers) { outRenderers.add(new TextRenderer(output, outputLooper)); }
/** * Builds metadata renderers for use by 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 outRenderers An array to which the built renderers should be appended. */ private void buildMetadataRenderers(MetadataOutput output, Looper outputLooper, List<Renderer> outRenderers) { outRenderers.add(new MetadataRenderer(output, outputLooper)); }
/** * Registers additional customized {@link com.google.android.exoplayer2.Renderer}s * that will be used by the {@link com.google.android.exoplayer2.source.MediaSource}s to * correctly play media. * * @param type The type for the renderer * @param clazz The class of the customized Renderer */ public static void registerRenderer(@NonNull RendererType type, @NonNull Class<? extends Renderer> clazz) { Data.registeredRendererClasses.get(type).add(clazz.getName()); }
/** * Sets the {@link Renderer}s to be used by the test runner. The default value is a single * {@link FakeRenderer} supporting the formats set by {@link #setSupportedFormats(Format...)}. * Setting the renderers is not allowed after a call to * {@link #setRenderersFactory(RenderersFactory)}. * * @param renderers A list of {@link Renderer}s to be used by the test runner. * @return This builder. */ public Builder setRenderers(Renderer... renderers) { Assert.assertNull(renderersFactory); this.renderers = renderers; return this; }