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
This commit is contained in:
olly 2016-05-17 06:49:22 -07:00 committed by Oliver Woodman
parent bfee449ed8
commit a16a333df2
11 changed files with 318 additions and 516 deletions

View File

@ -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);
}

View File

@ -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<List<Id3Frame>>,
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) {

View File

@ -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);
}
});
}
}

View File

@ -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);
}
});
}
}
}
}

View File

@ -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);
}
});
}
}
}

View File

@ -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<Long> 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.
* <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}.
*
@ -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.

View File

@ -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);
}
});
}
}
}

View File

@ -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);
}

View File

@ -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);
}
});
}
}
}
}

View File

@ -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;
}

View File

@ -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);
}
});
}
}
}