diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java b/demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java index 2343d87242..a19e7b9e2e 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java @@ -22,10 +22,8 @@ import com.google.android.exoplayer.TrackGroup; import com.google.android.exoplayer.TrackGroupArray; import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackSelection; -import com.google.android.exoplayer.audio.AudioTrack; import com.google.android.exoplayer.demo.player.DemoPlayer; -import android.media.MediaCodec.CryptoException; import android.os.SystemClock; import android.util.Log; @@ -128,6 +126,28 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener // DemoPlayer.InfoListener + @Override + public void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs, + long initializationDurationMs) { + Log.d(TAG, "audioDecoderInitialized [" + getSessionTimeString() + ", " + decoderName + "]"); + } + + @Override + public void onVideoDecoderInitialized(String decoderName, long elapsedRealtimeMs, + long initializationDurationMs) { + Log.d(TAG, "videoDecoderInitialized [" + getSessionTimeString() + ", " + decoderName + "]"); + } + + @Override + public void onAudioFormatEnabled(Format format, int trigger, long mediaTimeMs) { + Log.d(TAG, "audioFormat [" + getSessionTimeString() + ", " + format.id + ", " + trigger + "]"); + } + + @Override + public void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs) { + Log.d(TAG, "videoFormat [" + getSessionTimeString() + ", " + format.id + ", " + trigger + "]"); + } + @Override public void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate) { Log.d(TAG, "bandwidth [" + getSessionTimeString() + ", " + bytes + ", " @@ -151,18 +171,6 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener // Do nothing. } - @Override - public void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs) { - Log.d(TAG, "videoFormat [" + getSessionTimeString() + ", " + format.id + ", " - + Integer.toString(trigger) + "]"); - } - - @Override - public void onAudioFormatEnabled(Format format, int trigger, long mediaTimeMs) { - Log.d(TAG, "audioFormat [" + getSessionTimeString() + ", " + format.id + ", " - + Integer.toString(trigger) + "]"); - } - // DemoPlayer.InternalErrorListener @Override @@ -170,48 +178,17 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener printInternalError("loadError", e); } - @Override - public void onRendererInitializationError(Exception e) { - printInternalError("rendererInitError", e); - } - @Override public void onDrmSessionManagerError(Exception e) { printInternalError("drmSessionManagerError", e); } - @Override - public void onDecoderInitializationError(Exception e) { - printInternalError("decoderInitializationError", e); - } - - @Override - public void onAudioTrackInitializationError(AudioTrack.InitializationException e) { - printInternalError("audioTrackInitializationError", e); - } - - @Override - public void onAudioTrackWriteError(AudioTrack.WriteException e) { - printInternalError("audioTrackWriteError", e); - } - @Override public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { printInternalError("audioTrackUnderrun [" + bufferSize + ", " + bufferSizeMs + ", " + elapsedSinceLastFeedMs + "]", null); } - @Override - public void onCryptoError(CryptoException e) { - printInternalError("cryptoError", e); - } - - @Override - public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, - long initializationDurationMs) { - Log.d(TAG, "decoderInitialized [" + getSessionTimeString() + ", " + decoderName + "]"); - } - private void printInternalError(String type, Exception e) { Log.e(TAG, "internalError [" + getSessionTimeString() + ", " + type + "]", e); } diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java index bf9c44290a..b1fa7d5763 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer.demo.player; +import com.google.android.exoplayer.AudioTrackRendererEventListener; import com.google.android.exoplayer.C; import com.google.android.exoplayer.CodecCounters; import com.google.android.exoplayer.DefaultTrackSelectionPolicy; @@ -31,7 +32,6 @@ import com.google.android.exoplayer.SingleSampleSource; import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.VideoTrackRendererEventListener; import com.google.android.exoplayer.audio.AudioCapabilities; -import com.google.android.exoplayer.audio.AudioTrack; import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener; import com.google.android.exoplayer.drm.StreamingDrmSessionManager; import com.google.android.exoplayer.extractor.ExtractorSampleSource; @@ -50,7 +50,6 @@ import com.google.android.exoplayer.util.PlayerControl; import android.content.Context; import android.media.AudioManager; import android.media.MediaCodec; -import android.media.MediaCodec.CryptoException; import android.os.Handler; import android.util.Log; import android.view.Surface; @@ -67,7 +66,7 @@ import java.util.concurrent.CopyOnWriteArrayList; public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.EventListener, ChunkTrackStreamEventListener, ExtractorSampleSource.EventListener, SingleSampleSource.EventListener, DefaultBandwidthMeter.EventListener, - MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener, + VideoTrackRendererEventListener, AudioTrackRendererEventListener, StreamingDrmSessionManager.EventListener, TextRenderer, MetadataRenderer>, DebugTextViewHelper.Provider { @@ -91,12 +90,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even * will be invoked. */ public interface InternalErrorListener { - void onRendererInitializationError(Exception e); - void onAudioTrackInitializationError(AudioTrack.InitializationException e); - void onAudioTrackWriteError(AudioTrack.WriteException e); void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs); - void onDecoderInitializationError(Exception e); - void onCryptoError(CryptoException e); void onLoadError(int sourceId, IOException e); void onDrmSessionManagerError(Exception e); } @@ -105,16 +99,18 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even * A listener for debugging information. */ public interface InfoListener { - void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs); + void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs, + long initializationDurationMs); + void onVideoDecoderInitialized(String decoderName, long elapsedRealtimeMs, + long initializationDurationMs); void onAudioFormatEnabled(Format format, int trigger, long mediaTimeMs); + void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs); void onDroppedFrames(int count, long elapsed); void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate); void onLoadStarted(int sourceId, long length, int type, int trigger, Format format, long mediaStartTimeMs, long mediaEndTimeMs); void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format, long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs); - void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, - long initializationDurationMs); } /** @@ -354,27 +350,6 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even } } - @Override - public void onDecoderInitializationError(Exception e) { - if (internalErrorListener != null) { - internalErrorListener.onDecoderInitializationError(e); - } - } - - @Override - public void onAudioTrackInitializationError(AudioTrack.InitializationException e) { - if (internalErrorListener != null) { - internalErrorListener.onAudioTrackInitializationError(e); - } - } - - @Override - public void onAudioTrackWriteError(AudioTrack.WriteException e) { - if (internalErrorListener != null) { - internalErrorListener.onAudioTrackWriteError(e); - } - } - @Override public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) { if (internalErrorListener != null) { @@ -388,17 +363,11 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even } @Override - public void onCryptoError(CryptoException e) { - if (internalErrorListener != null) { - internalErrorListener.onCryptoError(e); - } - } - - @Override - public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, + public void onAudioDecoderInitialized(String decoderName, long initializedTimestampMs, long initializationDurationMs) { if (infoListener != null) { - infoListener.onDecoderInitialized(decoderName, elapsedRealtimeMs, initializationDurationMs); + infoListener.onAudioDecoderInitialized(decoderName, initializedTimestampMs, + initializationDurationMs); } } @@ -438,6 +407,15 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even this.videoCodecCounters = counters; } + @Override + public void onVideoDecoderInitialized(String decoderName, long initializedTimestampMs, + long initializationDurationMs) { + if (infoListener != null) { + infoListener.onVideoDecoderInitialized(decoderName, initializedTimestampMs, + initializationDurationMs); + } + } + @Override public void onLoadStarted(int sourceId, long length, int type, int trigger, Format format, long mediaStartTimeMs, long mediaEndTimeMs) { diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java index 0f62a71cd0..e587a3a38a 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer/ext/vp9/LibvpxVideoTrackRenderer.java @@ -25,6 +25,7 @@ import com.google.android.exoplayer.FormatHolder; import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackStream; import com.google.android.exoplayer.VideoTrackRendererEventListener; +import com.google.android.exoplayer.VideoTrackRendererEventListener.EventDispatcher; import com.google.android.exoplayer.util.MimeTypes; import android.graphics.Bitmap; @@ -56,8 +57,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { public final CodecCounters codecCounters = new CodecCounters(); private final boolean scaleToFit; - private final Handler eventHandler; - private final VideoTrackRendererEventListener eventListener; + private final EventDispatcher eventDispatcher; private final int maxDroppedFrameCountToNotify; private final FormatHolder formatHolder; @@ -103,12 +103,11 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { public LibvpxVideoTrackRenderer(boolean scaleToFit, Handler eventHandler, VideoTrackRendererEventListener eventListener, int maxDroppedFrameCountToNotify) { this.scaleToFit = scaleToFit; - this.eventHandler = eventHandler; - this.eventListener = eventListener; this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify; previousWidth = -1; previousHeight = -1; formatHolder = new FormatHolder(); + eventDispatcher = new EventDispatcher(eventHandler, eventListener); outputMode = VpxDecoder.OUTPUT_MODE_NONE; } @@ -156,7 +155,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE); decoder.setOutputMode(outputMode); long codecInitializedTimestamp = SystemClock.elapsedRealtime(); - notifyDecoderInitialized(decoder.getName(), codecInitializedTimestamp, + eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp, codecInitializedTimestamp - codecInitializingTimestamp); codecCounters.codecInitCount++; } @@ -208,7 +207,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { codecCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(consecutiveDroppedFrameCount, codecCounters.maxConsecutiveDroppedOutputBufferCount); if (droppedFrameCount == maxDroppedFrameCountToNotify) { - notifyAndResetDroppedFrameCount(); + maybeNotifyDroppedFrameCount(); } outputBuffer.release(); outputBuffer = null; @@ -233,12 +232,12 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { private void renderBuffer() { codecCounters.renderedOutputBufferCount++; consecutiveDroppedFrameCount = 0; - notifyIfVideoSizeChanged(outputBuffer.width, outputBuffer.height); + maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height); if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) { renderRgbFrame(outputBuffer, scaleToFit); if (!drawnToSurface) { drawnToSurface = true; - notifyDrawnToSurface(surface); + eventDispatcher.drawnToSurface(surface); } outputBuffer.release(); } else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null) { @@ -334,7 +333,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { @Override protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException { - notifyVideoCodecCounters(); + eventDispatcher.codecCounters(codecCounters); } @Override @@ -345,7 +344,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { @Override protected void onStopped() { - notifyAndResetDroppedFrameCount(); + maybeNotifyDroppedFrameCount(); } @Override @@ -410,70 +409,21 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { } } - private void notifyIfVideoSizeChanged(final int width, final int height) { - if (previousWidth == -1 || previousHeight == -1 || previousWidth != width - || previousHeight != height) { + private void maybeNotifyVideoSizeChanged(final int width, final int height) { + if (previousWidth != width || previousHeight != height) { previousWidth = width; previousHeight = height; - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onVideoSizeChanged(width, height, 0, 1); - } - }); - } + eventDispatcher.videoSizeChanged(width, height, 0, 1); } } - private void notifyAndResetDroppedFrameCount() { - if (eventHandler != null && eventListener != null && droppedFrameCount > 0) { + private void maybeNotifyDroppedFrameCount() { + if (droppedFrameCount > 0) { long now = SystemClock.elapsedRealtime(); - final int countToNotify = droppedFrameCount; - final long elapsedToNotify = now - droppedFrameAccumulationStartTimeMs; + long elapsedMs = now - droppedFrameAccumulationStartTimeMs; + eventDispatcher.droppedFrameCount(droppedFrameCount, elapsedMs); droppedFrameCount = 0; droppedFrameAccumulationStartTimeMs = now; - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onDroppedFrames(countToNotify, elapsedToNotify); - } - }); - } - } - - private void notifyDrawnToSurface(final Surface surface) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onDrawnToSurface(surface); - } - }); - } - } - - private void notifyDecoderInitialized(final String decoderName, - final long initializedTimestamp, final long initializationDuration) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onDecoderInitialized(decoderName, initializedTimestamp, - initializationDuration); - } - }); - } - } - - private void notifyVideoCodecCounters() { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onVideoCodecCounters(codecCounters); - } - }); } } diff --git a/library/src/main/java/com/google/android/exoplayer/AudioTrackRendererEventListener.java b/library/src/main/java/com/google/android/exoplayer/AudioTrackRendererEventListener.java index d48ed40dc7..ec8365fe1e 100644 --- a/library/src/main/java/com/google/android/exoplayer/AudioTrackRendererEventListener.java +++ b/library/src/main/java/com/google/android/exoplayer/AudioTrackRendererEventListener.java @@ -16,25 +16,33 @@ package com.google.android.exoplayer; import com.google.android.exoplayer.audio.AudioTrack; +import com.google.android.exoplayer.util.Assertions; + +import android.os.Handler; +import android.os.SystemClock; /** - * Optional interface definition for a callback to be notified of audio {@link TrackRenderer} - * events. + * Interface definition for a callback to be notified of audio {@link TrackRenderer} events. */ -public interface AudioTrackRendererEventListener extends TrackRendererEventListener { - /** - * Invoked when an {@link AudioTrack} fails to initialize. - * - * @param e The corresponding exception. - */ - void onAudioTrackInitializationError(AudioTrack.InitializationException e); +public interface AudioTrackRendererEventListener { /** - * Invoked when an {@link AudioTrack} write fails. + * Invoked to pass the codec counters when the renderer is enabled. * - * @param e The corresponding exception. + * @param counters CodecCounters object used by the renderer. */ - void onAudioTrackWriteError(AudioTrack.WriteException e); + void onAudioCodecCounters(CodecCounters counters); + + /** + * Invoked when a decoder is created. + * + * @param decoderName The decoder that was created. + * @param initializedTimestampMs {@link SystemClock#elapsedRealtime()} when initialization + * finished. + * @param initializationDurationMs The time taken to initialize the decoder in milliseconds. + */ + void onAudioDecoderInitialized(String decoderName, long initializedTimestampMs, + long initializationDurationMs); /** * Invoked when an {@link AudioTrack} underrun occurs. @@ -48,9 +56,54 @@ public interface AudioTrackRendererEventListener extends TrackRendererEventListe void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs); /** - * Invoked to pass the codec counters when the renderer is enabled. - * - * @param counters CodecCounters object used by the renderer. + * Dispatches events to a {@link AudioTrackRendererEventListener}. */ - void onAudioCodecCounters(CodecCounters counters); + final class EventDispatcher { + + private final Handler handler; + private final AudioTrackRendererEventListener listener; + + public EventDispatcher(Handler handler, AudioTrackRendererEventListener listener) { + this.handler = listener != null ? Assertions.checkNotNull(handler) : null; + this.listener = listener; + } + + public void codecCounters(final CodecCounters codecCounters) { + if (listener != null) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onAudioCodecCounters(codecCounters); + } + }); + } + } + + public void decoderInitialized(final String decoderName, + final long initializedTimestampMs, final long initializationDurationMs) { + if (listener != null) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onAudioDecoderInitialized(decoderName, initializedTimestampMs, + initializationDurationMs); + } + }); + } + } + + public void audioTrackUnderrun(final int bufferSize, final long bufferSizeMs, + final long elapsedSinceLastFeedMs) { + if (listener != null) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); + } + }); + } + } + + } + } diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java index 055daba5e9..eb1fc1899e 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer; +import com.google.android.exoplayer.AudioTrackRendererEventListener.EventDispatcher; import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer.audio.AudioCapabilities; import com.google.android.exoplayer.audio.AudioTrack; @@ -40,15 +41,6 @@ import java.nio.ByteBuffer; @TargetApi(16) public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implements MediaClock { - /** - * Interface definition for a callback to be notified of {@link MediaCodecAudioTrackRenderer} - * events. - */ - public interface EventListener extends MediaCodecTrackRenderer.EventListener, - AudioTrackRendererEventListener { - // No extra methods - } - /** * The type of a message that can be passed to an instance of this class via * {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object @@ -65,7 +57,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem */ public static final int MSG_SET_PLAYBACK_PARAMS = 2; - private final EventListener eventListener; + private final EventDispatcher eventDispatcher; private final AudioTrack audioTrack; private boolean passthroughEnabled; @@ -107,7 +99,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem * @param eventListener A listener of events. May be null if delivery of events is not required. */ public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector, Handler eventHandler, - EventListener eventListener) { + AudioTrackRendererEventListener eventListener) { this(mediaCodecSelector, null, true, eventHandler, eventListener); } @@ -126,7 +118,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem */ public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector, DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys, - Handler eventHandler, EventListener eventListener) { + Handler eventHandler, AudioTrackRendererEventListener eventListener) { this(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener, null, AudioManager.STREAM_MUSIC); } @@ -149,13 +141,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem */ public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector, DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys, - Handler eventHandler, EventListener eventListener, AudioCapabilities audioCapabilities, - int streamType) { - super(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, - eventListener); - this.eventListener = eventListener; - this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET; - this.audioTrack = new AudioTrack(audioCapabilities, streamType); + Handler eventHandler, AudioTrackRendererEventListener eventListener, + AudioCapabilities audioCapabilities, int streamType) { + super(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys); + audioSessionId = AudioTrack.SESSION_ID_NOT_SET; + audioTrack = new AudioTrack(audioCapabilities, streamType); + eventDispatcher = new EventDispatcher(eventHandler, eventListener); } @Override @@ -235,6 +226,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem return this; } + @Override + protected void onCodecInitialized(String name, long initializedTimestampMs, + long initializationDurationMs) { + eventDispatcher.decoderInitialized(name, initializedTimestampMs, initializationDurationMs); + } + @Override protected void onInputFormatChanged(FormatHolder holder) throws ExoPlaybackException { super.onInputFormatChanged(holder); @@ -275,7 +272,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem @Override protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException { super.onEnabled(formats, joining); - notifyAudioCodecCounters(); + eventDispatcher.codecCounters(codecCounters); } @Override @@ -357,7 +354,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem } audioTrackHasData = false; } catch (AudioTrack.InitializationException e) { - notifyAudioTrackInitializationError(e); throw ExoPlaybackException.createForRenderer(e, getIndex()); } if (getState() == TrackRenderer.STATE_STARTED) { @@ -371,7 +367,8 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs; long bufferSizeUs = audioTrack.getBufferSizeUs(); long bufferSizeMs = bufferSizeUs == C.UNSET_TIME_US ? -1 : bufferSizeUs / 1000; - notifyAudioTrackUnderrun(audioTrack.getBufferSize(), bufferSizeMs, elapsedSinceLastFeedMs); + eventDispatcher.audioTrackUnderrun(audioTrack.getBufferSize(), bufferSizeMs, + elapsedSinceLastFeedMs); } } @@ -380,7 +377,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem handleBufferResult = audioTrack.handleBuffer(buffer, bufferPresentationTimeUs); lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime(); } catch (AudioTrack.WriteException e) { - notifyAudioTrackWriteError(e); throw ExoPlaybackException.createForRenderer(e, getIndex()); } @@ -424,49 +420,4 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem } } - private void notifyAudioTrackInitializationError(final AudioTrack.InitializationException e) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onAudioTrackInitializationError(e); - } - }); - } - } - - private void notifyAudioTrackWriteError(final AudioTrack.WriteException e) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onAudioTrackWriteError(e); - } - }); - } - } - - private void notifyAudioTrackUnderrun(final int bufferSize, final long bufferSizeMs, - final long elapsedSinceLastFeedMs) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); - } - }); - } - } - - private void notifyAudioCodecCounters() { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onAudioCodecCounters(codecCounters); - } - }); - } - } - } diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java index cd1aed48b7..833fbd7e52 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java @@ -27,7 +27,6 @@ import android.media.MediaCodec; import android.media.MediaCodec.CodecException; import android.media.MediaCodec.CryptoException; import android.media.MediaCrypto; -import android.os.Handler; import android.os.Looper; import android.os.SystemClock; @@ -41,20 +40,6 @@ import java.util.List; @TargetApi(16) public abstract class MediaCodecTrackRenderer extends TrackRenderer { - /** - * Interface definition for a callback to be notified of {@link MediaCodecTrackRenderer} events. - */ - public interface EventListener extends TrackRendererEventListener { - - /** - * Invoked when a decoder operation raises a {@link CryptoException}. - * - * @param e The corresponding exception. - */ - void onCryptoError(CryptoException e); - - } - /** * Thrown when a failure occurs instantiating a decoder. */ @@ -167,8 +152,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { private final FormatHolder formatHolder; private final List decodeOnlyPresentationTimestamps; private final MediaCodec.BufferInfo outputBufferInfo; - private final EventListener eventListener; - protected final Handler eventHandler; private Format format; private MediaCodec codec; @@ -204,19 +187,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { * begin in parallel with key acquisition. This parameter specifies whether the renderer is * permitted to play clear regions of encrypted media files before {@code drmSessionManager} * has obtained the keys necessary to decrypt encrypted regions of the media. - * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be - * null if delivery of events is not required. - * @param eventListener A listener of events. May be null if delivery of events is not required. */ public MediaCodecTrackRenderer(MediaCodecSelector mediaCodecSelector, - DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys, - Handler eventHandler, EventListener eventListener) { + DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys) { Assertions.checkState(Util.SDK_INT >= 16); this.mediaCodecSelector = Assertions.checkNotNull(mediaCodecSelector); this.drmSessionManager = drmSessionManager; this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; - this.eventHandler = eventHandler; - this.eventListener = eventListener; codecCounters = new CodecCounters(); buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED); formatHolder = new FormatHolder(); @@ -311,13 +288,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { try { decoderInfo = getDecoderInfo(mediaCodecSelector, format, requiresSecureDecoder); } catch (DecoderQueryException e) { - notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e, - requiresSecureDecoder, DecoderInitializationException.DECODER_QUERY_ERROR)); + throwDecoderInitError(new DecoderInitializationException(format, e, requiresSecureDecoder, + DecoderInitializationException.DECODER_QUERY_ERROR)); } if (decoderInfo == null) { - notifyAndThrowDecoderInitError(new DecoderInitializationException(format, null, - requiresSecureDecoder, DecoderInitializationException.NO_SUITABLE_DECODER_ERROR)); + throwDecoderInitError(new DecoderInitializationException(format, null, requiresSecureDecoder, + DecoderInitializationException.NO_SUITABLE_DECODER_ERROR)); } String codecName = decoderInfo.name; @@ -339,13 +316,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { codec.start(); TraceUtil.endSection(); long codecInitializedTimestamp = SystemClock.elapsedRealtime(); - notifyDecoderInitialized(codecName, codecInitializedTimestamp, + onCodecInitialized(codecName, codecInitializedTimestamp, codecInitializedTimestamp - codecInitializingTimestamp); inputBuffers = codec.getInputBuffers(); outputBuffers = codec.getOutputBuffers(); } catch (Exception e) { - notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e, - requiresSecureDecoder, codecName)); + throwDecoderInitError(new DecoderInitializationException(format, e, requiresSecureDecoder, + codecName)); } codecHotswapDeadlineMs = getState() == TrackRenderer.STATE_STARTED ? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS) : -1; @@ -354,9 +331,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { codecCounters.codecInitCount++; } - private void notifyAndThrowDecoderInitError(DecoderInitializationException e) + private void throwDecoderInitError(DecoderInitializationException e) throws ExoPlaybackException { - notifyDecoderInitializationError(e); throw ExoPlaybackException.createForRenderer(e, getIndex()); } @@ -575,7 +551,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { inputIndex = -1; } } catch (CryptoException e) { - notifyCryptoError(e); throw ExoPlaybackException.createForRenderer(e, getIndex()); } return false; @@ -613,7 +588,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecCounters.inputBufferCount++; } catch (CryptoException e) { - notifyCryptoError(e); throw ExoPlaybackException.createForRenderer(e, getIndex()); } return true; @@ -647,6 +621,21 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { && (bufferEncrypted || !playClearSamplesWithoutKeys); } + /** + * Invoked when a {@link MediaCodec} has been created and configured. + *

+ * The default implementation is a no-op. + * + * @param name The name of the codec that was initialized. + * @param initializedTimestampMs {@link SystemClock#elapsedRealtime()} when initialization + * finished. + * @param initializationDurationMs The time taken to initialize the codec in milliseconds. + */ + protected void onCodecInitialized(String name, long initializedTimestampMs, + long initializationDurationMs) { + // Do nothing. + } + /** * Invoked when a new format is read from the upstream {@link SampleSource}. * @@ -878,41 +867,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { } } - private void notifyDecoderInitializationError(final DecoderInitializationException e) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onDecoderInitializationError(e); - } - }); - } - } - - private void notifyCryptoError(final CryptoException e) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onCryptoError(e); - } - }); - } - } - - private void notifyDecoderInitialized(final String decoderName, - final long initializedTimestamp, final long initializationDuration) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onDecoderInitialized(decoderName, initializedTimestamp, - initializationDuration); - } - }); - } - } - private boolean shouldSkipOutputBuffer(long presentationTimeUs) { // We avoid using decodeOnlyPresentationTimestamps.remove(presentationTimeUs) because it would // box presentationTimeUs, creating a Long object that would need to be garbage collected. diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java index b545672cdb..58b492e726 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecVideoTrackRenderer.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer; import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; +import com.google.android.exoplayer.VideoTrackRendererEventListener.EventDispatcher; import com.google.android.exoplayer.drm.DrmSessionManager; import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.TraceUtil; @@ -40,15 +41,6 @@ import java.nio.ByteBuffer; @TargetApi(16) public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { - /** - * Interface definition for a callback to be notified of {@link MediaCodecVideoTrackRenderer} - * events. - */ - public interface EventListener extends MediaCodecTrackRenderer.EventListener, - VideoTrackRendererEventListener { - // No extra methods - } - private static final String TAG = "MediaCodecVideoRenderer"; // TODO: Use MediaFormat constants if these get exposed through the API. See @@ -59,7 +51,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { private static final String KEY_CROP_TOP = "crop-top"; private final VideoFrameReleaseTimeHelper frameReleaseTimeHelper; - private final EventListener eventListener; + private final EventDispatcher eventDispatcher; private final long allowedJoiningTimeMs; private final int videoScalingMode; private final int maxDroppedFrameCountToNotify; @@ -122,11 +114,11 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { * null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. * @param maxDroppedFrameCountToNotify The maximum number of frames that can be dropped between - * invocations of {@link EventListener#onDroppedFrames(int, long)}. + * invocations of {@link VideoTrackRendererEventListener#onDroppedFrames(int, long)}. */ public MediaCodecVideoTrackRenderer(Context context, MediaCodecSelector mediaCodecSelector, int videoScalingMode, long allowedJoiningTimeMs, Handler eventHandler, - EventListener eventListener, int maxDroppedFrameCountToNotify) { + VideoTrackRendererEventListener eventListener, int maxDroppedFrameCountToNotify) { this(context, mediaCodecSelector, videoScalingMode, allowedJoiningTimeMs, null, false, eventHandler, eventListener, maxDroppedFrameCountToNotify); } @@ -142,26 +134,25 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { * content is not required. * @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions. * For example a media file may start with a short clear region so as to allow playback to - * begin in parallel with key acquisision. This parameter specifies whether the renderer is + * begin in parallel with key acquisition. This parameter specifies whether the renderer is * permitted to play clear regions of encrypted media files before {@code drmSessionManager} * has obtained the keys necessary to decrypt encrypted regions of the media. * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be * null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required. * @param maxDroppedFrameCountToNotify The maximum number of frames that can be dropped between - * invocations of {@link EventListener#onDroppedFrames(int, long)}. + * invocations of {@link VideoTrackRendererEventListener#onDroppedFrames(int, long)}. */ public MediaCodecVideoTrackRenderer(Context context, MediaCodecSelector mediaCodecSelector, int videoScalingMode, long allowedJoiningTimeMs, DrmSessionManager drmSessionManager, - boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener, - int maxDroppedFrameCountToNotify) { - super(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, - eventListener); - this.frameReleaseTimeHelper = new VideoFrameReleaseTimeHelper(context); + boolean playClearSamplesWithoutKeys, Handler eventHandler, + VideoTrackRendererEventListener eventListener, int maxDroppedFrameCountToNotify) { + super(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys); this.videoScalingMode = videoScalingMode; this.allowedJoiningTimeMs = allowedJoiningTimeMs; - this.eventListener = eventListener; this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify; + frameReleaseTimeHelper = new VideoFrameReleaseTimeHelper(context); + eventDispatcher = new EventDispatcher(eventHandler, eventListener); deviceNeedsAutoFrcWorkaround = deviceNeedsAutoFrcWorkaround(); joiningDeadlineMs = -1; currentWidth = -1; @@ -227,8 +218,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { adaptiveMaxWidth = Math.max(adaptiveMaxWidth, format.width); adaptiveMaxHeight = Math.max(adaptiveMaxHeight, format.height); } - if (adaptiveMaxWidth == Format.NO_VALUE - || adaptiveMaxHeight == Format.NO_VALUE) { + if (adaptiveMaxWidth == Format.NO_VALUE || adaptiveMaxHeight == Format.NO_VALUE) { Log.w(TAG, "Maximum dimensions unknown. Assuming 1920x1080."); adaptiveMaxWidth = 1920; adaptiveMaxHeight = 1080; @@ -238,7 +228,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { joiningDeadlineMs = SystemClock.elapsedRealtime() + allowedJoiningTimeMs; } frameReleaseTimeHelper.enable(); - notifyVideoCodecCounters(); + eventDispatcher.codecCounters(codecCounters); } @Override @@ -332,6 +322,12 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { codec.configure(getFrameworkMediaFormat(format), surface, crypto, 0); } + @Override + protected void onCodecInitialized(String name, long initializedTimestampMs, + long initializationDurationMs) { + eventDispatcher.decoderInitialized(name, initializedTimestampMs, initializationDurationMs); + } + @Override protected void onInputFormatChanged(FormatHolder holder) throws ExoPlaybackException { super.onInputFormatChanged(holder); @@ -559,65 +555,34 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { return frameworkMediaFormat; } - private void maybeNotifyVideoSizeChanged() { - if (eventHandler == null || eventListener == null - || (lastReportedWidth == currentWidth && lastReportedHeight == currentHeight - && lastReportedUnappliedRotationDegrees == currentUnappliedRotationDegrees - && lastReportedPixelWidthHeightRatio == currentPixelWidthHeightRatio)) { - return; + private void maybeNotifyDrawnToSurface() { + if (!reportedDrawnToSurface) { + eventDispatcher.drawnToSurface(surface); + reportedDrawnToSurface = true; } - // Make final copies to ensure the runnable reports the correct values. - final int currentWidth = this.currentWidth; - final int currentHeight = this.currentHeight; - final int currentUnappliedRotationDegrees = this.currentUnappliedRotationDegrees; - final float currentPixelWidthHeightRatio = this.currentPixelWidthHeightRatio; - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onVideoSizeChanged(currentWidth, currentHeight, - currentUnappliedRotationDegrees, currentPixelWidthHeightRatio); - } - }); - // Update the last reported values. - lastReportedWidth = currentWidth; - lastReportedHeight = currentHeight; - lastReportedUnappliedRotationDegrees = currentUnappliedRotationDegrees; - lastReportedPixelWidthHeightRatio = currentPixelWidthHeightRatio; } - private void maybeNotifyDrawnToSurface() { - if (eventHandler == null || eventListener == null || reportedDrawnToSurface) { - return; + private void maybeNotifyVideoSizeChanged() { + if (lastReportedWidth != currentWidth || lastReportedHeight != currentHeight + || lastReportedUnappliedRotationDegrees != currentUnappliedRotationDegrees + || lastReportedPixelWidthHeightRatio != currentPixelWidthHeightRatio) { + eventDispatcher.videoSizeChanged(currentWidth, currentHeight, currentUnappliedRotationDegrees, + currentPixelWidthHeightRatio); + lastReportedWidth = currentWidth; + lastReportedHeight = currentHeight; + lastReportedUnappliedRotationDegrees = currentUnappliedRotationDegrees; + lastReportedPixelWidthHeightRatio = currentPixelWidthHeightRatio; } - // Make a final copy to ensure the runnable reports the correct surface. - final Surface surface = this.surface; - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onDrawnToSurface(surface); - } - }); - // Record that we have reported that the surface has been drawn to. - reportedDrawnToSurface = true; } private void maybeNotifyDroppedFrameCount() { - if (eventHandler == null || eventListener == null || droppedFrameCount == 0) { - return; + if (droppedFrameCount > 0) { + long now = SystemClock.elapsedRealtime(); + long elapsedMs = now - droppedFrameAccumulationStartTimeMs; + eventDispatcher.droppedFrameCount(droppedFrameCount, elapsedMs); + droppedFrameCount = 0; + droppedFrameAccumulationStartTimeMs = now; } - long now = SystemClock.elapsedRealtime(); - // Make final copies to ensure the runnable reports the correct values. - final int countToNotify = droppedFrameCount; - final long elapsedToNotify = now - droppedFrameAccumulationStartTimeMs; - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onDroppedFrames(countToNotify, elapsedToNotify); - } - }); - // Reset the dropped frame tracking. - droppedFrameCount = 0; - droppedFrameAccumulationStartTimeMs = now; } /** @@ -638,15 +603,4 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer { return Util.SDK_INT <= 22 && "foster".equals(Util.DEVICE) && "NVIDIA".equals(Util.MANUFACTURER); } - private void notifyVideoCodecCounters() { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onVideoCodecCounters(codecCounters); - } - }); - } - } - } diff --git a/library/src/main/java/com/google/android/exoplayer/TrackRendererEventListener.java b/library/src/main/java/com/google/android/exoplayer/TrackRendererEventListener.java deleted file mode 100644 index 18d1ae7238..0000000000 --- a/library/src/main/java/com/google/android/exoplayer/TrackRendererEventListener.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2014 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.exoplayer; - -/** - * Optional interface definition for a callback to be notified of {@link TrackRenderer} events. - */ -public interface TrackRendererEventListener { - - /** - * Invoked when a decoder fails to initialize. - * - * @param e The corresponding exception. - */ - void onDecoderInitializationError(Exception e); - - /** - * Invoked when a decoder is successfully created. - * - * @param decoderName The decoder that was configured and created. - * @param elapsedRealtimeMs {@code elapsedRealtime} timestamp of when the initialization - * finished. - * @param initializationDurationMs Amount of time taken to initialize the decoder. - */ - void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, - long initializationDurationMs); - -} diff --git a/library/src/main/java/com/google/android/exoplayer/VideoTrackRendererEventListener.java b/library/src/main/java/com/google/android/exoplayer/VideoTrackRendererEventListener.java index 6abb54b52c..7a729d8f6b 100644 --- a/library/src/main/java/com/google/android/exoplayer/VideoTrackRendererEventListener.java +++ b/library/src/main/java/com/google/android/exoplayer/VideoTrackRendererEventListener.java @@ -15,14 +15,35 @@ */ package com.google.android.exoplayer; +import com.google.android.exoplayer.util.Assertions; + +import android.os.Handler; +import android.os.SystemClock; import android.view.Surface; import android.view.TextureView; /** - * Optional interface definition for a callback to be notified of video {@link TrackRenderer} - * events. + * Interface definition for a callback to be notified of video {@link TrackRenderer} events. */ -public interface VideoTrackRendererEventListener extends TrackRendererEventListener { +public interface VideoTrackRendererEventListener { + + /** + * Invoked to pass the codec counters when the renderer is enabled. + * + * @param counters CodecCounters object used by the renderer. + */ + void onVideoCodecCounters(CodecCounters counters); + + /** + * Invoked when a decoder is created. + * + * @param decoderName The decoder that was created. + * @param initializedTimestampMs {@link SystemClock#elapsedRealtime()} when initialization + * finished. + * @param initializationDurationMs The time taken to initialize the decoder in milliseconds. + */ + void onVideoDecoderInitialized(String decoderName, long initializedTimestampMs, + long initializationDurationMs); /** * Invoked to report the number of frames dropped by the renderer. Dropped frames are reported @@ -30,12 +51,12 @@ public interface VideoTrackRendererEventListener extends TrackRendererEventListe * reaches a specified threshold whilst the renderer is started. * * @param count The number of dropped frames. - * @param elapsed The duration in milliseconds over which the frames were dropped. This + * @param elapsedMs The duration in milliseconds over which the frames were dropped. This * duration is timed from when the renderer was started or from when dropped frames were * last reported (whichever was more recent), and not from when the first of the reported * drops occurred. */ - void onDroppedFrames(int count, long elapsed); + void onDroppedFrames(int count, long elapsedMs); /** * Invoked each time there's a change in the size of the video being rendered. @@ -65,10 +86,77 @@ public interface VideoTrackRendererEventListener extends TrackRendererEventListe void onDrawnToSurface(Surface surface); /** - * Invoked to pass the codec counters when the renderer is enabled. - * - * @param counters CodecCounters object used by the renderer. + * Dispatches events to a {@link VideoTrackRendererEventListener}. */ - void onVideoCodecCounters(CodecCounters counters); + final class EventDispatcher { + + private final Handler handler; + private final VideoTrackRendererEventListener listener; + + public EventDispatcher(Handler handler, VideoTrackRendererEventListener listener) { + this.handler = listener != null ? Assertions.checkNotNull(handler) : null; + this.listener = listener; + } + + public void codecCounters(final CodecCounters codecCounters) { + if (listener != null) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onVideoCodecCounters(codecCounters); + } + }); + } + } + + public void decoderInitialized(final String decoderName, + final long initializedTimestampMs, final long initializationDurationMs) { + if (listener != null) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onVideoDecoderInitialized(decoderName, initializedTimestampMs, + initializationDurationMs); + } + }); + } + } + + public void droppedFrameCount(final int droppedFrameCount, final long elapsedMs) { + if (listener != null) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onDroppedFrames(droppedFrameCount, elapsedMs); + } + }); + } + } + + public void videoSizeChanged(final int width, final int height, + final int unappliedRotationDegrees, final float pixelWidthHeightRatio) { + if (listener != null) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onVideoSizeChanged(width, height, unappliedRotationDegrees, + pixelWidthHeightRatio); + } + }); + } + } + + public void drawnToSurface(final Surface surface) { + if (listener != null) { + handler.post(new Runnable() { + @Override + public void run() { + listener.onDrawnToSurface(surface); + } + }); + } + } + + } } diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkTrackStreamEventListener.java b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkTrackStreamEventListener.java index db9d51153c..c8b0084110 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/ChunkTrackStreamEventListener.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/ChunkTrackStreamEventListener.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer.chunk; import com.google.android.exoplayer.C; import com.google.android.exoplayer.Format; import com.google.android.exoplayer.TrackStream; +import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Util; import android.os.Handler; @@ -114,7 +115,7 @@ public interface ChunkTrackStreamEventListener { private final int sourceId; public EventDispatcher(Handler handler, ChunkTrackStreamEventListener listener, int sourceId) { - this.handler = listener != null ? handler : null; + this.handler = listener != null ? Assertions.checkNotNull(handler) : null; this.listener = listener; this.sourceId = sourceId; } diff --git a/library/src/main/java/com/google/android/exoplayer/extensions/AudioDecoderTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/extensions/AudioDecoderTrackRenderer.java index b12ee12c9d..edfcd1a4b9 100644 --- a/library/src/main/java/com/google/android/exoplayer/extensions/AudioDecoderTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/extensions/AudioDecoderTrackRenderer.java @@ -16,6 +16,7 @@ package com.google.android.exoplayer.extensions; import com.google.android.exoplayer.AudioTrackRendererEventListener; +import com.google.android.exoplayer.AudioTrackRendererEventListener.EventDispatcher; import com.google.android.exoplayer.C; import com.google.android.exoplayer.CodecCounters; import com.google.android.exoplayer.DecoderInputBuffer; @@ -46,8 +47,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements public final CodecCounters codecCounters = new CodecCounters(); - private final Handler eventHandler; - private final AudioTrackRendererEventListener eventListener; + private final EventDispatcher eventDispatcher; private final FormatHolder formatHolder; private Format inputFormat; @@ -78,10 +78,9 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements */ public AudioDecoderTrackRenderer(Handler eventHandler, AudioTrackRendererEventListener eventListener) { - this.eventHandler = eventHandler; - this.eventListener = eventListener; - this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET; - this.audioTrack = new AudioTrack(); + eventDispatcher = new EventDispatcher(eventHandler, eventListener); + audioSessionId = AudioTrack.SESSION_ID_NOT_SET; + audioTrack = new AudioTrack(); formatHolder = new FormatHolder(); } @@ -108,7 +107,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements long codecInitializingTimestamp = SystemClock.elapsedRealtime(); decoder = createDecoder(inputFormat); long codecInitializedTimestamp = SystemClock.elapsedRealtime(); - notifyDecoderInitialized(decoder.getName(), codecInitializedTimestamp, + eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp, codecInitializedTimestamp - codecInitializingTimestamp); codecCounters.codecInitCount++; } catch (AudioDecoderException e) { @@ -120,13 +119,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements try { while (drainOutputBuffer()) {} while (feedInputBuffer()) {} - } catch (AudioTrack.InitializationException e) { - notifyAudioTrackInitializationError(e); - throw ExoPlaybackException.createForRenderer(e, getIndex()); - } catch (AudioTrack.WriteException e) { - notifyAudioTrackWriteError(e); - throw ExoPlaybackException.createForRenderer(e, getIndex()); - } catch (AudioDecoderException e) { + } catch (AudioTrack.InitializationException | AudioTrack.WriteException + | AudioDecoderException e) { throw ExoPlaybackException.createForRenderer(e, getIndex()); } codecCounters.ensureUpdated(); @@ -197,7 +191,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs; long bufferSizeUs = audioTrack.getBufferSizeUs(); long bufferSizeMs = bufferSizeUs == C.UNSET_TIME_US ? -1 : bufferSizeUs / 1000; - notifyAudioTrackUnderrun(audioTrack.getBufferSize(), bufferSizeMs, elapsedSinceLastFeedMs); + eventDispatcher.audioTrackUnderrun(audioTrack.getBufferSize(), bufferSizeMs, + elapsedSinceLastFeedMs); } } @@ -311,7 +306,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements @Override protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException { - notifyAudioCodecCounters(); + eventDispatcher.codecCounters(codecCounters); } @Override @@ -360,62 +355,4 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements } } - private void notifyDecoderInitialized(final String decoderName, - final long initializedTimestamp, final long initializationDuration) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onDecoderInitialized(decoderName, initializedTimestamp, - initializationDuration); - } - }); - } - } - - private void notifyAudioTrackInitializationError(final AudioTrack.InitializationException e) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onAudioTrackInitializationError(e); - } - }); - } - } - - private void notifyAudioTrackWriteError(final AudioTrack.WriteException e) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onAudioTrackWriteError(e); - } - }); - } - } - - private void notifyAudioTrackUnderrun(final int bufferSize, final long bufferSizeMs, - final long elapsedSinceLastFeedMs) { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); - } - }); - } - } - - private void notifyAudioCodecCounters() { - if (eventHandler != null && eventListener != null) { - eventHandler.post(new Runnable() { - @Override - public void run() { - eventListener.onAudioCodecCounters(codecCounters); - } - }); - } - } - }