Commit a16a333d by olly Committed by Oliver Woodman

Clean up renderer event listeners.

- Don't report errors to listeners if playback will immediately
  fail. Doing so is redundant, since such errors are immediately
  reported through ExoPlayer's listener as a result of playback
  failure. We were also reporting these errors inconsistently
  across renderers.
- Reduce code duplication through EventDispatcher classes.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=122519976
parent bfee449e
...@@ -22,10 +22,8 @@ import com.google.android.exoplayer.TrackGroup; ...@@ -22,10 +22,8 @@ import com.google.android.exoplayer.TrackGroup;
import com.google.android.exoplayer.TrackGroupArray; import com.google.android.exoplayer.TrackGroupArray;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.TrackSelection; import com.google.android.exoplayer.TrackSelection;
import com.google.android.exoplayer.audio.AudioTrack;
import com.google.android.exoplayer.demo.player.DemoPlayer; import com.google.android.exoplayer.demo.player.DemoPlayer;
import android.media.MediaCodec.CryptoException;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
...@@ -129,6 +127,28 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener ...@@ -129,6 +127,28 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
// DemoPlayer.InfoListener // DemoPlayer.InfoListener
@Override @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) { public void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate) {
Log.d(TAG, "bandwidth [" + getSessionTimeString() + ", " + bytes + ", " Log.d(TAG, "bandwidth [" + getSessionTimeString() + ", " + bytes + ", "
+ getTimeString(elapsedMs) + ", " + bitrateEstimate + "]"); + getTimeString(elapsedMs) + ", " + bitrateEstimate + "]");
...@@ -151,18 +171,6 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener ...@@ -151,18 +171,6 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
// Do nothing. // 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 // DemoPlayer.InternalErrorListener
@Override @Override
...@@ -171,47 +179,16 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener ...@@ -171,47 +179,16 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
} }
@Override @Override
public void onRendererInitializationError(Exception e) {
printInternalError("rendererInitError", e);
}
@Override
public void onDrmSessionManagerError(Exception e) { public void onDrmSessionManagerError(Exception e) {
printInternalError("drmSessionManagerError", e); printInternalError("drmSessionManagerError", e);
} }
@Override @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) { public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
printInternalError("audioTrackUnderrun [" + bufferSize + ", " + bufferSizeMs + ", " printInternalError("audioTrackUnderrun [" + bufferSize + ", " + bufferSizeMs + ", "
+ elapsedSinceLastFeedMs + "]", null); + 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) { private void printInternalError(String type, Exception e) {
Log.e(TAG, "internalError [" + getSessionTimeString() + ", " + type + "]", e); Log.e(TAG, "internalError [" + getSessionTimeString() + ", " + type + "]", e);
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.demo.player; package com.google.android.exoplayer.demo.player;
import com.google.android.exoplayer.AudioTrackRendererEventListener;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.CodecCounters; import com.google.android.exoplayer.CodecCounters;
import com.google.android.exoplayer.DefaultTrackSelectionPolicy; import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
...@@ -31,7 +32,6 @@ import com.google.android.exoplayer.SingleSampleSource; ...@@ -31,7 +32,6 @@ import com.google.android.exoplayer.SingleSampleSource;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.VideoTrackRendererEventListener; import com.google.android.exoplayer.VideoTrackRendererEventListener;
import com.google.android.exoplayer.audio.AudioCapabilities; 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.chunk.ChunkTrackStreamEventListener;
import com.google.android.exoplayer.drm.StreamingDrmSessionManager; import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
import com.google.android.exoplayer.extractor.ExtractorSampleSource; import com.google.android.exoplayer.extractor.ExtractorSampleSource;
...@@ -50,7 +50,6 @@ import com.google.android.exoplayer.util.PlayerControl; ...@@ -50,7 +50,6 @@ import com.google.android.exoplayer.util.PlayerControl;
import android.content.Context; import android.content.Context;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaCodec; import android.media.MediaCodec;
import android.media.MediaCodec.CryptoException;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.view.Surface; import android.view.Surface;
...@@ -67,7 +66,7 @@ import java.util.concurrent.CopyOnWriteArrayList; ...@@ -67,7 +66,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.EventListener, public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.EventListener,
ChunkTrackStreamEventListener, ExtractorSampleSource.EventListener, ChunkTrackStreamEventListener, ExtractorSampleSource.EventListener,
SingleSampleSource.EventListener, DefaultBandwidthMeter.EventListener, SingleSampleSource.EventListener, DefaultBandwidthMeter.EventListener,
MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener, VideoTrackRendererEventListener, AudioTrackRendererEventListener,
StreamingDrmSessionManager.EventListener, TextRenderer, MetadataRenderer<List<Id3Frame>>, StreamingDrmSessionManager.EventListener, TextRenderer, MetadataRenderer<List<Id3Frame>>,
DebugTextViewHelper.Provider { DebugTextViewHelper.Provider {
...@@ -91,12 +90,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -91,12 +90,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
* will be invoked. * will be invoked.
*/ */
public interface InternalErrorListener { 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 onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
void onDecoderInitializationError(Exception e);
void onCryptoError(CryptoException e);
void onLoadError(int sourceId, IOException e); void onLoadError(int sourceId, IOException e);
void onDrmSessionManagerError(Exception e); void onDrmSessionManagerError(Exception e);
} }
...@@ -105,16 +99,18 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -105,16 +99,18 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
* A listener for debugging information. * A listener for debugging information.
*/ */
public interface InfoListener { 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 onAudioFormatEnabled(Format format, int trigger, long mediaTimeMs);
void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs);
void onDroppedFrames(int count, long elapsed); void onDroppedFrames(int count, long elapsed);
void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate); void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate);
void onLoadStarted(int sourceId, long length, int type, int trigger, Format format, void onLoadStarted(int sourceId, long length, int type, int trigger, Format format,
long mediaStartTimeMs, long mediaEndTimeMs); long mediaStartTimeMs, long mediaEndTimeMs);
void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format, void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format,
long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs); long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs);
void onDecoderInitialized(String decoderName, long elapsedRealtimeMs,
long initializationDurationMs);
} }
/** /**
...@@ -355,27 +351,6 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -355,27 +351,6 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
} }
@Override @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) { public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
if (internalErrorListener != null) { if (internalErrorListener != null) {
internalErrorListener.onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs); internalErrorListener.onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs);
...@@ -388,17 +363,11 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -388,17 +363,11 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
} }
@Override @Override
public void onCryptoError(CryptoException e) { public void onAudioDecoderInitialized(String decoderName, long initializedTimestampMs,
if (internalErrorListener != null) {
internalErrorListener.onCryptoError(e);
}
}
@Override
public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs,
long initializationDurationMs) { long initializationDurationMs) {
if (infoListener != null) { if (infoListener != null) {
infoListener.onDecoderInitialized(decoderName, elapsedRealtimeMs, initializationDurationMs); infoListener.onAudioDecoderInitialized(decoderName, initializedTimestampMs,
initializationDurationMs);
} }
} }
...@@ -439,6 +408,15 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even ...@@ -439,6 +408,15 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
} }
@Override @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, public void onLoadStarted(int sourceId, long length, int type, int trigger, Format format,
long mediaStartTimeMs, long mediaEndTimeMs) { long mediaStartTimeMs, long mediaEndTimeMs) {
if (infoListener != null) { if (infoListener != null) {
......
...@@ -25,6 +25,7 @@ import com.google.android.exoplayer.FormatHolder; ...@@ -25,6 +25,7 @@ import com.google.android.exoplayer.FormatHolder;
import com.google.android.exoplayer.TrackRenderer; import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.TrackStream; import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.VideoTrackRendererEventListener; import com.google.android.exoplayer.VideoTrackRendererEventListener;
import com.google.android.exoplayer.VideoTrackRendererEventListener.EventDispatcher;
import com.google.android.exoplayer.util.MimeTypes; import com.google.android.exoplayer.util.MimeTypes;
import android.graphics.Bitmap; import android.graphics.Bitmap;
...@@ -56,8 +57,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -56,8 +57,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
public final CodecCounters codecCounters = new CodecCounters(); public final CodecCounters codecCounters = new CodecCounters();
private final boolean scaleToFit; private final boolean scaleToFit;
private final Handler eventHandler; private final EventDispatcher eventDispatcher;
private final VideoTrackRendererEventListener eventListener;
private final int maxDroppedFrameCountToNotify; private final int maxDroppedFrameCountToNotify;
private final FormatHolder formatHolder; private final FormatHolder formatHolder;
...@@ -103,12 +103,11 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -103,12 +103,11 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
public LibvpxVideoTrackRenderer(boolean scaleToFit, Handler eventHandler, public LibvpxVideoTrackRenderer(boolean scaleToFit, Handler eventHandler,
VideoTrackRendererEventListener eventListener, int maxDroppedFrameCountToNotify) { VideoTrackRendererEventListener eventListener, int maxDroppedFrameCountToNotify) {
this.scaleToFit = scaleToFit; this.scaleToFit = scaleToFit;
this.eventHandler = eventHandler;
this.eventListener = eventListener;
this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify; this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify;
previousWidth = -1; previousWidth = -1;
previousHeight = -1; previousHeight = -1;
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
outputMode = VpxDecoder.OUTPUT_MODE_NONE; outputMode = VpxDecoder.OUTPUT_MODE_NONE;
} }
...@@ -156,7 +155,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -156,7 +155,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE); decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE);
decoder.setOutputMode(outputMode); decoder.setOutputMode(outputMode);
long codecInitializedTimestamp = SystemClock.elapsedRealtime(); long codecInitializedTimestamp = SystemClock.elapsedRealtime();
notifyDecoderInitialized(decoder.getName(), codecInitializedTimestamp, eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
codecInitializedTimestamp - codecInitializingTimestamp); codecInitializedTimestamp - codecInitializingTimestamp);
codecCounters.codecInitCount++; codecCounters.codecInitCount++;
} }
...@@ -208,7 +207,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -208,7 +207,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
codecCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(consecutiveDroppedFrameCount, codecCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(consecutiveDroppedFrameCount,
codecCounters.maxConsecutiveDroppedOutputBufferCount); codecCounters.maxConsecutiveDroppedOutputBufferCount);
if (droppedFrameCount == maxDroppedFrameCountToNotify) { if (droppedFrameCount == maxDroppedFrameCountToNotify) {
notifyAndResetDroppedFrameCount(); maybeNotifyDroppedFrameCount();
} }
outputBuffer.release(); outputBuffer.release();
outputBuffer = null; outputBuffer = null;
...@@ -233,12 +232,12 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -233,12 +232,12 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
private void renderBuffer() { private void renderBuffer() {
codecCounters.renderedOutputBufferCount++; codecCounters.renderedOutputBufferCount++;
consecutiveDroppedFrameCount = 0; consecutiveDroppedFrameCount = 0;
notifyIfVideoSizeChanged(outputBuffer.width, outputBuffer.height); maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) { if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) {
renderRgbFrame(outputBuffer, scaleToFit); renderRgbFrame(outputBuffer, scaleToFit);
if (!drawnToSurface) { if (!drawnToSurface) {
drawnToSurface = true; drawnToSurface = true;
notifyDrawnToSurface(surface); eventDispatcher.drawnToSurface(surface);
} }
outputBuffer.release(); outputBuffer.release();
} else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null) { } else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null) {
...@@ -334,7 +333,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -334,7 +333,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
@Override @Override
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException { protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
notifyVideoCodecCounters(); eventDispatcher.codecCounters(codecCounters);
} }
@Override @Override
...@@ -345,7 +344,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -345,7 +344,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
@Override @Override
protected void onStopped() { protected void onStopped() {
notifyAndResetDroppedFrameCount(); maybeNotifyDroppedFrameCount();
} }
@Override @Override
...@@ -410,70 +409,21 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer { ...@@ -410,70 +409,21 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
} }
} }
private void notifyIfVideoSizeChanged(final int width, final int height) { private void maybeNotifyVideoSizeChanged(final int width, final int height) {
if (previousWidth == -1 || previousHeight == -1 || previousWidth != width if (previousWidth != width || previousHeight != height) {
|| previousHeight != height) {
previousWidth = width; previousWidth = width;
previousHeight = height; previousHeight = height;
if (eventHandler != null && eventListener != null) { eventDispatcher.videoSizeChanged(width, height, 0, 1);
eventHandler.post(new Runnable() {
@Override
public void run() {
eventListener.onVideoSizeChanged(width, height, 0, 1);
}
});
}
} }
} }
private void notifyAndResetDroppedFrameCount() { private void maybeNotifyDroppedFrameCount() {
if (eventHandler != null && eventListener != null && droppedFrameCount > 0) { if (droppedFrameCount > 0) {
long now = SystemClock.elapsedRealtime(); long now = SystemClock.elapsedRealtime();
final int countToNotify = droppedFrameCount; long elapsedMs = now - droppedFrameAccumulationStartTimeMs;
final long elapsedToNotify = now - droppedFrameAccumulationStartTimeMs; eventDispatcher.droppedFrameCount(droppedFrameCount, elapsedMs);
droppedFrameCount = 0; droppedFrameCount = 0;
droppedFrameAccumulationStartTimeMs = now; 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);
}
});
} }
} }
......
...@@ -16,25 +16,33 @@ ...@@ -16,25 +16,33 @@
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.audio.AudioTrack; 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} * Interface definition for a callback to be notified of audio {@link TrackRenderer} events.
* events.
*/ */
public interface AudioTrackRendererEventListener extends TrackRendererEventListener { public interface AudioTrackRendererEventListener {
/** /**
* Invoked when an {@link AudioTrack} fails to initialize. * 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 onAudioTrackInitializationError(AudioTrack.InitializationException e); void onAudioCodecCounters(CodecCounters counters);
/** /**
* Invoked when an {@link AudioTrack} write fails. * Invoked when a decoder is created.
* *
* @param e The corresponding exception. * @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 onAudioTrackWriteError(AudioTrack.WriteException e); void onAudioDecoderInitialized(String decoderName, long initializedTimestampMs,
long initializationDurationMs);
/** /**
* Invoked when an {@link AudioTrack} underrun occurs. * Invoked when an {@link AudioTrack} underrun occurs.
...@@ -48,9 +56,54 @@ public interface AudioTrackRendererEventListener extends TrackRendererEventListe ...@@ -48,9 +56,54 @@ public interface AudioTrackRendererEventListener extends TrackRendererEventListe
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs); void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
/** /**
* Invoked to pass the codec counters when the renderer is enabled. * Dispatches events to a {@link AudioTrackRendererEventListener}.
*
* @param counters CodecCounters object used by the renderer.
*/ */
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);
}
});
}
}
}
} }
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.AudioTrackRendererEventListener.EventDispatcher;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.audio.AudioCapabilities; import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.audio.AudioTrack; import com.google.android.exoplayer.audio.AudioTrack;
...@@ -41,15 +42,6 @@ import java.nio.ByteBuffer; ...@@ -41,15 +42,6 @@ import java.nio.ByteBuffer;
public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implements MediaClock { 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 * 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 * {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
* should be a {@link Float} with 0 being silence and 1 being unity gain. * should be a {@link Float} with 0 being silence and 1 being unity gain.
...@@ -65,7 +57,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -65,7 +57,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
*/ */
public static final int MSG_SET_PLAYBACK_PARAMS = 2; public static final int MSG_SET_PLAYBACK_PARAMS = 2;
private final EventListener eventListener; private final EventDispatcher eventDispatcher;
private final AudioTrack audioTrack; private final AudioTrack audioTrack;
private boolean passthroughEnabled; private boolean passthroughEnabled;
...@@ -107,7 +99,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -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. * @param eventListener A listener of events. May be null if delivery of events is not required.
*/ */
public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector, Handler eventHandler, public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector, Handler eventHandler,
EventListener eventListener) { AudioTrackRendererEventListener eventListener) {
this(mediaCodecSelector, null, true, eventHandler, eventListener); this(mediaCodecSelector, null, true, eventHandler, eventListener);
} }
...@@ -126,7 +118,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -126,7 +118,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
*/ */
public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector, public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector,
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys, DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
Handler eventHandler, EventListener eventListener) { Handler eventHandler, AudioTrackRendererEventListener eventListener) {
this(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, this(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler,
eventListener, null, AudioManager.STREAM_MUSIC); eventListener, null, AudioManager.STREAM_MUSIC);
} }
...@@ -149,13 +141,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -149,13 +141,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
*/ */
public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector, public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector,
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys, DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
Handler eventHandler, EventListener eventListener, AudioCapabilities audioCapabilities, Handler eventHandler, AudioTrackRendererEventListener eventListener,
int streamType) { AudioCapabilities audioCapabilities, int streamType) {
super(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, super(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
eventListener); audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
this.eventListener = eventListener; audioTrack = new AudioTrack(audioCapabilities, streamType);
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET; eventDispatcher = new EventDispatcher(eventHandler, eventListener);
this.audioTrack = new AudioTrack(audioCapabilities, streamType);
} }
@Override @Override
...@@ -236,6 +227,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -236,6 +227,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
} }
@Override @Override
protected void onCodecInitialized(String name, long initializedTimestampMs,
long initializationDurationMs) {
eventDispatcher.decoderInitialized(name, initializedTimestampMs, initializationDurationMs);
}
@Override
protected void onInputFormatChanged(FormatHolder holder) throws ExoPlaybackException { protected void onInputFormatChanged(FormatHolder holder) throws ExoPlaybackException {
super.onInputFormatChanged(holder); super.onInputFormatChanged(holder);
// If the input format is anything other than PCM then we assume that the audio decoder will // If the input format is anything other than PCM then we assume that the audio decoder will
...@@ -275,7 +272,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -275,7 +272,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
@Override @Override
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException { protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
super.onEnabled(formats, joining); super.onEnabled(formats, joining);
notifyAudioCodecCounters(); eventDispatcher.codecCounters(codecCounters);
} }
@Override @Override
...@@ -357,7 +354,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -357,7 +354,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
} }
audioTrackHasData = false; audioTrackHasData = false;
} catch (AudioTrack.InitializationException e) { } catch (AudioTrack.InitializationException e) {
notifyAudioTrackInitializationError(e);
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
if (getState() == TrackRenderer.STATE_STARTED) { if (getState() == TrackRenderer.STATE_STARTED) {
...@@ -371,7 +367,8 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -371,7 +367,8 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs; long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs;
long bufferSizeUs = audioTrack.getBufferSizeUs(); long bufferSizeUs = audioTrack.getBufferSizeUs();
long bufferSizeMs = bufferSizeUs == C.UNSET_TIME_US ? -1 : bufferSizeUs / 1000; 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 ...@@ -380,7 +377,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
handleBufferResult = audioTrack.handleBuffer(buffer, bufferPresentationTimeUs); handleBufferResult = audioTrack.handleBuffer(buffer, bufferPresentationTimeUs);
lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime(); lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime();
} catch (AudioTrack.WriteException e) { } catch (AudioTrack.WriteException e) {
notifyAudioTrackWriteError(e);
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
...@@ -424,49 +420,4 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem ...@@ -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);
}
});
}
}
} }
...@@ -27,7 +27,6 @@ import android.media.MediaCodec; ...@@ -27,7 +27,6 @@ import android.media.MediaCodec;
import android.media.MediaCodec.CodecException; import android.media.MediaCodec.CodecException;
import android.media.MediaCodec.CryptoException; import android.media.MediaCodec.CryptoException;
import android.media.MediaCrypto; import android.media.MediaCrypto;
import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.SystemClock; import android.os.SystemClock;
...@@ -42,20 +41,6 @@ import java.util.List; ...@@ -42,20 +41,6 @@ import java.util.List;
public abstract class MediaCodecTrackRenderer extends TrackRenderer { 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. * Thrown when a failure occurs instantiating a decoder.
*/ */
public static class DecoderInitializationException extends Exception { public static class DecoderInitializationException extends Exception {
...@@ -167,8 +152,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -167,8 +152,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
private final FormatHolder formatHolder; private final FormatHolder formatHolder;
private final List<Long> decodeOnlyPresentationTimestamps; private final List<Long> decodeOnlyPresentationTimestamps;
private final MediaCodec.BufferInfo outputBufferInfo; private final MediaCodec.BufferInfo outputBufferInfo;
private final EventListener eventListener;
protected final Handler eventHandler;
private Format format; private Format format;
private MediaCodec codec; private MediaCodec codec;
...@@ -204,19 +187,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -204,19 +187,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
* begin in parallel with key acquisition. 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} * permitted to play clear regions of encrypted media files before {@code drmSessionManager}
* has obtained the keys necessary to decrypt encrypted regions of the media. * 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, public MediaCodecTrackRenderer(MediaCodecSelector mediaCodecSelector,
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys, DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys) {
Handler eventHandler, EventListener eventListener) {
Assertions.checkState(Util.SDK_INT >= 16); Assertions.checkState(Util.SDK_INT >= 16);
this.mediaCodecSelector = Assertions.checkNotNull(mediaCodecSelector); this.mediaCodecSelector = Assertions.checkNotNull(mediaCodecSelector);
this.drmSessionManager = drmSessionManager; this.drmSessionManager = drmSessionManager;
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
this.eventHandler = eventHandler;
this.eventListener = eventListener;
codecCounters = new CodecCounters(); codecCounters = new CodecCounters();
buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED); buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
...@@ -311,13 +288,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -311,13 +288,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
try { try {
decoderInfo = getDecoderInfo(mediaCodecSelector, format, requiresSecureDecoder); decoderInfo = getDecoderInfo(mediaCodecSelector, format, requiresSecureDecoder);
} catch (DecoderQueryException e) { } catch (DecoderQueryException e) {
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e, throwDecoderInitError(new DecoderInitializationException(format, e, requiresSecureDecoder,
requiresSecureDecoder, DecoderInitializationException.DECODER_QUERY_ERROR)); DecoderInitializationException.DECODER_QUERY_ERROR));
} }
if (decoderInfo == null) { if (decoderInfo == null) {
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, null, throwDecoderInitError(new DecoderInitializationException(format, null, requiresSecureDecoder,
requiresSecureDecoder, DecoderInitializationException.NO_SUITABLE_DECODER_ERROR)); DecoderInitializationException.NO_SUITABLE_DECODER_ERROR));
} }
String codecName = decoderInfo.name; String codecName = decoderInfo.name;
...@@ -339,13 +316,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -339,13 +316,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
codec.start(); codec.start();
TraceUtil.endSection(); TraceUtil.endSection();
long codecInitializedTimestamp = SystemClock.elapsedRealtime(); long codecInitializedTimestamp = SystemClock.elapsedRealtime();
notifyDecoderInitialized(codecName, codecInitializedTimestamp, onCodecInitialized(codecName, codecInitializedTimestamp,
codecInitializedTimestamp - codecInitializingTimestamp); codecInitializedTimestamp - codecInitializingTimestamp);
inputBuffers = codec.getInputBuffers(); inputBuffers = codec.getInputBuffers();
outputBuffers = codec.getOutputBuffers(); outputBuffers = codec.getOutputBuffers();
} catch (Exception e) { } catch (Exception e) {
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e, throwDecoderInitError(new DecoderInitializationException(format, e, requiresSecureDecoder,
requiresSecureDecoder, codecName)); codecName));
} }
codecHotswapDeadlineMs = getState() == TrackRenderer.STATE_STARTED codecHotswapDeadlineMs = getState() == TrackRenderer.STATE_STARTED
? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS) : -1; ? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS) : -1;
...@@ -354,9 +331,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -354,9 +331,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
codecCounters.codecInitCount++; codecCounters.codecInitCount++;
} }
private void notifyAndThrowDecoderInitError(DecoderInitializationException e) private void throwDecoderInitError(DecoderInitializationException e)
throws ExoPlaybackException { throws ExoPlaybackException {
notifyDecoderInitializationError(e);
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
...@@ -575,7 +551,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -575,7 +551,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
inputIndex = -1; inputIndex = -1;
} }
} catch (CryptoException e) { } catch (CryptoException e) {
notifyCryptoError(e);
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
return false; return false;
...@@ -613,7 +588,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -613,7 +588,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReconfigurationState = RECONFIGURATION_STATE_NONE;
codecCounters.inputBufferCount++; codecCounters.inputBufferCount++;
} catch (CryptoException e) { } catch (CryptoException e) {
notifyCryptoError(e);
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
return true; return true;
...@@ -648,6 +622,21 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -648,6 +622,21 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
/** /**
* Invoked when a {@link MediaCodec} has been created and configured.
* <p>
* 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}. * Invoked when a new format is read from the upstream {@link SampleSource}.
* *
* @param formatHolder Holds the new format. * @param formatHolder Holds the new format.
...@@ -878,41 +867,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -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) { private boolean shouldSkipOutputBuffer(long presentationTimeUs) {
// We avoid using decodeOnlyPresentationTimestamps.remove(presentationTimeUs) because it would // We avoid using decodeOnlyPresentationTimestamps.remove(presentationTimeUs) because it would
// box presentationTimeUs, creating a Long object that would need to be garbage collected. // box presentationTimeUs, creating a Long object that would need to be garbage collected.
......
/*
* 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);
}
...@@ -15,14 +15,35 @@ ...@@ -15,14 +15,35 @@
*/ */
package com.google.android.exoplayer; 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.Surface;
import android.view.TextureView; import android.view.TextureView;
/** /**
* Optional interface definition for a callback to be notified of video {@link TrackRenderer} * Interface definition for a callback to be notified of video {@link TrackRenderer} events.
* 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 * Invoked to report the number of frames dropped by the renderer. Dropped frames are reported
...@@ -30,12 +51,12 @@ public interface VideoTrackRendererEventListener extends TrackRendererEventListe ...@@ -30,12 +51,12 @@ public interface VideoTrackRendererEventListener extends TrackRendererEventListe
* reaches a specified threshold whilst the renderer is started. * reaches a specified threshold whilst the renderer is started.
* *
* @param count The number of dropped frames. * @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 * 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 * last reported (whichever was more recent), and not from when the first of the reported
* drops occurred. * 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. * Invoked each time there's a change in the size of the video being rendered.
...@@ -65,10 +86,77 @@ public interface VideoTrackRendererEventListener extends TrackRendererEventListe ...@@ -65,10 +86,77 @@ public interface VideoTrackRendererEventListener extends TrackRendererEventListe
void onDrawnToSurface(Surface surface); void onDrawnToSurface(Surface surface);
/** /**
* Invoked to pass the codec counters when the renderer is enabled. * Dispatches events to a {@link VideoTrackRendererEventListener}.
*
* @param counters CodecCounters object used by the renderer.
*/ */
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);
}
});
}
}
}
} }
...@@ -18,6 +18,7 @@ package com.google.android.exoplayer.chunk; ...@@ -18,6 +18,7 @@ package com.google.android.exoplayer.chunk;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.Format; import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.TrackStream; import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import android.os.Handler; import android.os.Handler;
...@@ -114,7 +115,7 @@ public interface ChunkTrackStreamEventListener { ...@@ -114,7 +115,7 @@ public interface ChunkTrackStreamEventListener {
private final int sourceId; private final int sourceId;
public EventDispatcher(Handler handler, ChunkTrackStreamEventListener listener, 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.listener = listener;
this.sourceId = sourceId; this.sourceId = sourceId;
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer.extensions; package com.google.android.exoplayer.extensions;
import com.google.android.exoplayer.AudioTrackRendererEventListener; import com.google.android.exoplayer.AudioTrackRendererEventListener;
import com.google.android.exoplayer.AudioTrackRendererEventListener.EventDispatcher;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.CodecCounters; import com.google.android.exoplayer.CodecCounters;
import com.google.android.exoplayer.DecoderInputBuffer; import com.google.android.exoplayer.DecoderInputBuffer;
...@@ -46,8 +47,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -46,8 +47,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
public final CodecCounters codecCounters = new CodecCounters(); public final CodecCounters codecCounters = new CodecCounters();
private final Handler eventHandler; private final EventDispatcher eventDispatcher;
private final AudioTrackRendererEventListener eventListener;
private final FormatHolder formatHolder; private final FormatHolder formatHolder;
private Format inputFormat; private Format inputFormat;
...@@ -78,10 +78,9 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -78,10 +78,9 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
*/ */
public AudioDecoderTrackRenderer(Handler eventHandler, public AudioDecoderTrackRenderer(Handler eventHandler,
AudioTrackRendererEventListener eventListener) { AudioTrackRendererEventListener eventListener) {
this.eventHandler = eventHandler; eventDispatcher = new EventDispatcher(eventHandler, eventListener);
this.eventListener = eventListener; audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET; audioTrack = new AudioTrack();
this.audioTrack = new AudioTrack();
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
} }
...@@ -108,7 +107,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -108,7 +107,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
long codecInitializingTimestamp = SystemClock.elapsedRealtime(); long codecInitializingTimestamp = SystemClock.elapsedRealtime();
decoder = createDecoder(inputFormat); decoder = createDecoder(inputFormat);
long codecInitializedTimestamp = SystemClock.elapsedRealtime(); long codecInitializedTimestamp = SystemClock.elapsedRealtime();
notifyDecoderInitialized(decoder.getName(), codecInitializedTimestamp, eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
codecInitializedTimestamp - codecInitializingTimestamp); codecInitializedTimestamp - codecInitializingTimestamp);
codecCounters.codecInitCount++; codecCounters.codecInitCount++;
} catch (AudioDecoderException e) { } catch (AudioDecoderException e) {
...@@ -120,13 +119,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -120,13 +119,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
try { try {
while (drainOutputBuffer()) {} while (drainOutputBuffer()) {}
while (feedInputBuffer()) {} while (feedInputBuffer()) {}
} catch (AudioTrack.InitializationException e) { } catch (AudioTrack.InitializationException | AudioTrack.WriteException
notifyAudioTrackInitializationError(e); | AudioDecoderException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex());
} catch (AudioTrack.WriteException e) {
notifyAudioTrackWriteError(e);
throw ExoPlaybackException.createForRenderer(e, getIndex());
} catch (AudioDecoderException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
codecCounters.ensureUpdated(); codecCounters.ensureUpdated();
...@@ -197,7 +191,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -197,7 +191,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs; long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs;
long bufferSizeUs = audioTrack.getBufferSizeUs(); long bufferSizeUs = audioTrack.getBufferSizeUs();
long bufferSizeMs = bufferSizeUs == C.UNSET_TIME_US ? -1 : bufferSizeUs / 1000; 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 ...@@ -311,7 +306,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
@Override @Override
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException { protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
notifyAudioCodecCounters(); eventDispatcher.codecCounters(codecCounters);
} }
@Override @Override
...@@ -360,62 +355,4 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements ...@@ -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);
}
});
}
}
} }
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