mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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:
parent
bfee449ed8
commit
a16a333df2
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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,66 +555,35 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
||||
return frameworkMediaFormat;
|
||||
}
|
||||
|
||||
private void maybeNotifyDrawnToSurface() {
|
||||
if (!reportedDrawnToSurface) {
|
||||
eventDispatcher.drawnToSurface(surface);
|
||||
reportedDrawnToSurface = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeNotifyVideoSizeChanged() {
|
||||
if (eventHandler == null || eventListener == null
|
||||
|| (lastReportedWidth == currentWidth && lastReportedHeight == currentHeight
|
||||
&& lastReportedUnappliedRotationDegrees == currentUnappliedRotationDegrees
|
||||
&& lastReportedPixelWidthHeightRatio == currentPixelWidthHeightRatio)) {
|
||||
return;
|
||||
}
|
||||
// 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.
|
||||
if (lastReportedWidth != currentWidth || lastReportedHeight != currentHeight
|
||||
|| lastReportedUnappliedRotationDegrees != currentUnappliedRotationDegrees
|
||||
|| lastReportedPixelWidthHeightRatio != currentPixelWidthHeightRatio) {
|
||||
eventDispatcher.videoSizeChanged(currentWidth, currentHeight, currentUnappliedRotationDegrees,
|
||||
currentPixelWidthHeightRatio);
|
||||
lastReportedWidth = currentWidth;
|
||||
lastReportedHeight = currentHeight;
|
||||
lastReportedUnappliedRotationDegrees = currentUnappliedRotationDegrees;
|
||||
lastReportedPixelWidthHeightRatio = currentPixelWidthHeightRatio;
|
||||
}
|
||||
|
||||
private void maybeNotifyDrawnToSurface() {
|
||||
if (eventHandler == null || eventListener == null || reportedDrawnToSurface) {
|
||||
return;
|
||||
}
|
||||
// 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();
|
||||
// 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.
|
||||
long elapsedMs = now - droppedFrameAccumulationStartTimeMs;
|
||||
eventDispatcher.droppedFrameCount(droppedFrameCount, elapsedMs);
|
||||
droppedFrameCount = 0;
|
||||
droppedFrameAccumulationStartTimeMs = now;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the device is known to enable frame-rate conversion logic that negatively
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user