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.TrackGroupArray;
|
||||||
import com.google.android.exoplayer.TrackRenderer;
|
import com.google.android.exoplayer.TrackRenderer;
|
||||||
import com.google.android.exoplayer.TrackSelection;
|
import com.google.android.exoplayer.TrackSelection;
|
||||||
import com.google.android.exoplayer.audio.AudioTrack;
|
|
||||||
import com.google.android.exoplayer.demo.player.DemoPlayer;
|
import com.google.android.exoplayer.demo.player.DemoPlayer;
|
||||||
|
|
||||||
import android.media.MediaCodec.CryptoException;
|
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@ -128,6 +126,28 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
|
|||||||
|
|
||||||
// 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
|
@Override
|
||||||
public void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate) {
|
public void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate) {
|
||||||
Log.d(TAG, "bandwidth [" + getSessionTimeString() + ", " + bytes + ", "
|
Log.d(TAG, "bandwidth [" + getSessionTimeString() + ", " + bytes + ", "
|
||||||
@ -151,18 +171,6 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
|
|||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs) {
|
|
||||||
Log.d(TAG, "videoFormat [" + getSessionTimeString() + ", " + format.id + ", "
|
|
||||||
+ Integer.toString(trigger) + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAudioFormatEnabled(Format format, int trigger, long mediaTimeMs) {
|
|
||||||
Log.d(TAG, "audioFormat [" + getSessionTimeString() + ", " + format.id + ", "
|
|
||||||
+ Integer.toString(trigger) + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
// DemoPlayer.InternalErrorListener
|
// DemoPlayer.InternalErrorListener
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -170,48 +178,17 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
|
|||||||
printInternalError("loadError", e);
|
printInternalError("loadError", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRendererInitializationError(Exception e) {
|
|
||||||
printInternalError("rendererInitError", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDrmSessionManagerError(Exception e) {
|
public void onDrmSessionManagerError(Exception e) {
|
||||||
printInternalError("drmSessionManagerError", 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
|
@Override
|
||||||
public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
|
public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
|
||||||
printInternalError("audioTrackUnderrun [" + bufferSize + ", " + bufferSizeMs + ", "
|
printInternalError("audioTrackUnderrun [" + bufferSize + ", " + bufferSizeMs + ", "
|
||||||
+ elapsedSinceLastFeedMs + "]", null);
|
+ elapsedSinceLastFeedMs + "]", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCryptoError(CryptoException e) {
|
|
||||||
printInternalError("cryptoError", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
|
||||||
long initializationDurationMs) {
|
|
||||||
Log.d(TAG, "decoderInitialized [" + getSessionTimeString() + ", " + decoderName + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void printInternalError(String type, Exception e) {
|
private void printInternalError(String type, Exception e) {
|
||||||
Log.e(TAG, "internalError [" + getSessionTimeString() + ", " + type + "]", e);
|
Log.e(TAG, "internalError [" + getSessionTimeString() + ", " + type + "]", e);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.demo.player;
|
package com.google.android.exoplayer.demo.player;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.AudioTrackRendererEventListener;
|
||||||
import com.google.android.exoplayer.C;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.CodecCounters;
|
import com.google.android.exoplayer.CodecCounters;
|
||||||
import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
|
import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
|
||||||
@ -31,7 +32,6 @@ import com.google.android.exoplayer.SingleSampleSource;
|
|||||||
import com.google.android.exoplayer.TrackRenderer;
|
import com.google.android.exoplayer.TrackRenderer;
|
||||||
import com.google.android.exoplayer.VideoTrackRendererEventListener;
|
import com.google.android.exoplayer.VideoTrackRendererEventListener;
|
||||||
import com.google.android.exoplayer.audio.AudioCapabilities;
|
import com.google.android.exoplayer.audio.AudioCapabilities;
|
||||||
import com.google.android.exoplayer.audio.AudioTrack;
|
|
||||||
import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener;
|
import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener;
|
||||||
import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
|
import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
|
||||||
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
|
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
|
||||||
@ -50,7 +50,6 @@ import com.google.android.exoplayer.util.PlayerControl;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCodec.CryptoException;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
@ -67,7 +66,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.EventListener,
|
public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.EventListener,
|
||||||
ChunkTrackStreamEventListener, ExtractorSampleSource.EventListener,
|
ChunkTrackStreamEventListener, ExtractorSampleSource.EventListener,
|
||||||
SingleSampleSource.EventListener, DefaultBandwidthMeter.EventListener,
|
SingleSampleSource.EventListener, DefaultBandwidthMeter.EventListener,
|
||||||
MediaCodecVideoTrackRenderer.EventListener, MediaCodecAudioTrackRenderer.EventListener,
|
VideoTrackRendererEventListener, AudioTrackRendererEventListener,
|
||||||
StreamingDrmSessionManager.EventListener, TextRenderer, MetadataRenderer<List<Id3Frame>>,
|
StreamingDrmSessionManager.EventListener, TextRenderer, MetadataRenderer<List<Id3Frame>>,
|
||||||
DebugTextViewHelper.Provider {
|
DebugTextViewHelper.Provider {
|
||||||
|
|
||||||
@ -91,12 +90,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
|||||||
* will be invoked.
|
* will be invoked.
|
||||||
*/
|
*/
|
||||||
public interface InternalErrorListener {
|
public interface InternalErrorListener {
|
||||||
void onRendererInitializationError(Exception e);
|
|
||||||
void onAudioTrackInitializationError(AudioTrack.InitializationException e);
|
|
||||||
void onAudioTrackWriteError(AudioTrack.WriteException e);
|
|
||||||
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
|
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
|
||||||
void onDecoderInitializationError(Exception e);
|
|
||||||
void onCryptoError(CryptoException e);
|
|
||||||
void onLoadError(int sourceId, IOException e);
|
void onLoadError(int sourceId, IOException e);
|
||||||
void onDrmSessionManagerError(Exception e);
|
void onDrmSessionManagerError(Exception e);
|
||||||
}
|
}
|
||||||
@ -105,16 +99,18 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
|||||||
* A listener for debugging information.
|
* A listener for debugging information.
|
||||||
*/
|
*/
|
||||||
public interface InfoListener {
|
public interface InfoListener {
|
||||||
void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs);
|
void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
||||||
|
long initializationDurationMs);
|
||||||
|
void onVideoDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
||||||
|
long initializationDurationMs);
|
||||||
void onAudioFormatEnabled(Format format, int trigger, long mediaTimeMs);
|
void onAudioFormatEnabled(Format format, int trigger, long mediaTimeMs);
|
||||||
|
void onVideoFormatEnabled(Format format, int trigger, long mediaTimeMs);
|
||||||
void onDroppedFrames(int count, long elapsed);
|
void onDroppedFrames(int count, long elapsed);
|
||||||
void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate);
|
void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate);
|
||||||
void onLoadStarted(int sourceId, long length, int type, int trigger, Format format,
|
void onLoadStarted(int sourceId, long length, int type, int trigger, Format format,
|
||||||
long mediaStartTimeMs, long mediaEndTimeMs);
|
long mediaStartTimeMs, long mediaEndTimeMs);
|
||||||
void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format,
|
void onLoadCompleted(int sourceId, long bytesLoaded, int type, int trigger, Format format,
|
||||||
long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs);
|
long mediaStartTimeMs, long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs);
|
||||||
void onDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
|
||||||
long initializationDurationMs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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
|
@Override
|
||||||
public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
|
public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
|
||||||
if (internalErrorListener != null) {
|
if (internalErrorListener != null) {
|
||||||
@ -388,17 +363,11 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCryptoError(CryptoException e) {
|
public void onAudioDecoderInitialized(String decoderName, long initializedTimestampMs,
|
||||||
if (internalErrorListener != null) {
|
|
||||||
internalErrorListener.onCryptoError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
|
||||||
long initializationDurationMs) {
|
long initializationDurationMs) {
|
||||||
if (infoListener != null) {
|
if (infoListener != null) {
|
||||||
infoListener.onDecoderInitialized(decoderName, elapsedRealtimeMs, initializationDurationMs);
|
infoListener.onAudioDecoderInitialized(decoderName, initializedTimestampMs,
|
||||||
|
initializationDurationMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,6 +407,15 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
|
|||||||
this.videoCodecCounters = counters;
|
this.videoCodecCounters = counters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onVideoDecoderInitialized(String decoderName, long initializedTimestampMs,
|
||||||
|
long initializationDurationMs) {
|
||||||
|
if (infoListener != null) {
|
||||||
|
infoListener.onVideoDecoderInitialized(decoderName, initializedTimestampMs,
|
||||||
|
initializationDurationMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoadStarted(int sourceId, long length, int type, int trigger, Format format,
|
public void onLoadStarted(int sourceId, long length, int type, int trigger, Format format,
|
||||||
long mediaStartTimeMs, long mediaEndTimeMs) {
|
long mediaStartTimeMs, long mediaEndTimeMs) {
|
||||||
|
@ -25,6 +25,7 @@ import com.google.android.exoplayer.FormatHolder;
|
|||||||
import com.google.android.exoplayer.TrackRenderer;
|
import com.google.android.exoplayer.TrackRenderer;
|
||||||
import com.google.android.exoplayer.TrackStream;
|
import com.google.android.exoplayer.TrackStream;
|
||||||
import com.google.android.exoplayer.VideoTrackRendererEventListener;
|
import com.google.android.exoplayer.VideoTrackRendererEventListener;
|
||||||
|
import com.google.android.exoplayer.VideoTrackRendererEventListener.EventDispatcher;
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@ -56,8 +57,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
|||||||
public final CodecCounters codecCounters = new CodecCounters();
|
public final CodecCounters codecCounters = new CodecCounters();
|
||||||
|
|
||||||
private final boolean scaleToFit;
|
private final boolean scaleToFit;
|
||||||
private final Handler eventHandler;
|
private final EventDispatcher eventDispatcher;
|
||||||
private final VideoTrackRendererEventListener eventListener;
|
|
||||||
private final int maxDroppedFrameCountToNotify;
|
private final int maxDroppedFrameCountToNotify;
|
||||||
private final FormatHolder formatHolder;
|
private final FormatHolder formatHolder;
|
||||||
|
|
||||||
@ -103,12 +103,11 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
|||||||
public LibvpxVideoTrackRenderer(boolean scaleToFit, Handler eventHandler,
|
public LibvpxVideoTrackRenderer(boolean scaleToFit, Handler eventHandler,
|
||||||
VideoTrackRendererEventListener eventListener, int maxDroppedFrameCountToNotify) {
|
VideoTrackRendererEventListener eventListener, int maxDroppedFrameCountToNotify) {
|
||||||
this.scaleToFit = scaleToFit;
|
this.scaleToFit = scaleToFit;
|
||||||
this.eventHandler = eventHandler;
|
|
||||||
this.eventListener = eventListener;
|
|
||||||
this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify;
|
this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify;
|
||||||
previousWidth = -1;
|
previousWidth = -1;
|
||||||
previousHeight = -1;
|
previousHeight = -1;
|
||||||
formatHolder = new FormatHolder();
|
formatHolder = new FormatHolder();
|
||||||
|
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||||
outputMode = VpxDecoder.OUTPUT_MODE_NONE;
|
outputMode = VpxDecoder.OUTPUT_MODE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +155,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
|||||||
decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE);
|
decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE);
|
||||||
decoder.setOutputMode(outputMode);
|
decoder.setOutputMode(outputMode);
|
||||||
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||||
notifyDecoderInitialized(decoder.getName(), codecInitializedTimestamp,
|
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
|
||||||
codecInitializedTimestamp - codecInitializingTimestamp);
|
codecInitializedTimestamp - codecInitializingTimestamp);
|
||||||
codecCounters.codecInitCount++;
|
codecCounters.codecInitCount++;
|
||||||
}
|
}
|
||||||
@ -208,7 +207,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
|||||||
codecCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(consecutiveDroppedFrameCount,
|
codecCounters.maxConsecutiveDroppedOutputBufferCount = Math.max(consecutiveDroppedFrameCount,
|
||||||
codecCounters.maxConsecutiveDroppedOutputBufferCount);
|
codecCounters.maxConsecutiveDroppedOutputBufferCount);
|
||||||
if (droppedFrameCount == maxDroppedFrameCountToNotify) {
|
if (droppedFrameCount == maxDroppedFrameCountToNotify) {
|
||||||
notifyAndResetDroppedFrameCount();
|
maybeNotifyDroppedFrameCount();
|
||||||
}
|
}
|
||||||
outputBuffer.release();
|
outputBuffer.release();
|
||||||
outputBuffer = null;
|
outputBuffer = null;
|
||||||
@ -233,12 +232,12 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
|||||||
private void renderBuffer() {
|
private void renderBuffer() {
|
||||||
codecCounters.renderedOutputBufferCount++;
|
codecCounters.renderedOutputBufferCount++;
|
||||||
consecutiveDroppedFrameCount = 0;
|
consecutiveDroppedFrameCount = 0;
|
||||||
notifyIfVideoSizeChanged(outputBuffer.width, outputBuffer.height);
|
maybeNotifyVideoSizeChanged(outputBuffer.width, outputBuffer.height);
|
||||||
if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) {
|
if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) {
|
||||||
renderRgbFrame(outputBuffer, scaleToFit);
|
renderRgbFrame(outputBuffer, scaleToFit);
|
||||||
if (!drawnToSurface) {
|
if (!drawnToSurface) {
|
||||||
drawnToSurface = true;
|
drawnToSurface = true;
|
||||||
notifyDrawnToSurface(surface);
|
eventDispatcher.drawnToSurface(surface);
|
||||||
}
|
}
|
||||||
outputBuffer.release();
|
outputBuffer.release();
|
||||||
} else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null) {
|
} else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null) {
|
||||||
@ -334,7 +333,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
|
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
|
||||||
notifyVideoCodecCounters();
|
eventDispatcher.codecCounters(codecCounters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -345,7 +344,7 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStopped() {
|
protected void onStopped() {
|
||||||
notifyAndResetDroppedFrameCount();
|
maybeNotifyDroppedFrameCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -410,70 +409,21 @@ public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyIfVideoSizeChanged(final int width, final int height) {
|
private void maybeNotifyVideoSizeChanged(final int width, final int height) {
|
||||||
if (previousWidth == -1 || previousHeight == -1 || previousWidth != width
|
if (previousWidth != width || previousHeight != height) {
|
||||||
|| previousHeight != height) {
|
|
||||||
previousWidth = width;
|
previousWidth = width;
|
||||||
previousHeight = height;
|
previousHeight = height;
|
||||||
if (eventHandler != null && eventListener != null) {
|
eventDispatcher.videoSizeChanged(width, height, 0, 1);
|
||||||
eventHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
eventListener.onVideoSizeChanged(width, height, 0, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyAndResetDroppedFrameCount() {
|
private void maybeNotifyDroppedFrameCount() {
|
||||||
if (eventHandler != null && eventListener != null && droppedFrameCount > 0) {
|
if (droppedFrameCount > 0) {
|
||||||
long now = SystemClock.elapsedRealtime();
|
long now = SystemClock.elapsedRealtime();
|
||||||
final int countToNotify = droppedFrameCount;
|
long elapsedMs = now - droppedFrameAccumulationStartTimeMs;
|
||||||
final long elapsedToNotify = now - droppedFrameAccumulationStartTimeMs;
|
eventDispatcher.droppedFrameCount(droppedFrameCount, elapsedMs);
|
||||||
droppedFrameCount = 0;
|
droppedFrameCount = 0;
|
||||||
droppedFrameAccumulationStartTimeMs = now;
|
droppedFrameAccumulationStartTimeMs = now;
|
||||||
eventHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
eventListener.onDroppedFrames(countToNotify, elapsedToNotify);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyDrawnToSurface(final Surface surface) {
|
|
||||||
if (eventHandler != null && eventListener != null) {
|
|
||||||
eventHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
eventListener.onDrawnToSurface(surface);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyDecoderInitialized(final String decoderName,
|
|
||||||
final long initializedTimestamp, final long initializationDuration) {
|
|
||||||
if (eventHandler != null && eventListener != null) {
|
|
||||||
eventHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
eventListener.onDecoderInitialized(decoderName, initializedTimestamp,
|
|
||||||
initializationDuration);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyVideoCodecCounters() {
|
|
||||||
if (eventHandler != null && eventListener != null) {
|
|
||||||
eventHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
eventListener.onVideoCodecCounters(codecCounters);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,25 +16,33 @@
|
|||||||
package com.google.android.exoplayer;
|
package com.google.android.exoplayer;
|
||||||
|
|
||||||
import com.google.android.exoplayer.audio.AudioTrack;
|
import com.google.android.exoplayer.audio.AudioTrack;
|
||||||
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional interface definition for a callback to be notified of audio {@link TrackRenderer}
|
* Interface definition for a callback to be notified of audio {@link TrackRenderer} events.
|
||||||
* events.
|
|
||||||
*/
|
*/
|
||||||
public interface AudioTrackRendererEventListener extends TrackRendererEventListener {
|
public interface AudioTrackRendererEventListener {
|
||||||
/**
|
|
||||||
* Invoked when an {@link AudioTrack} fails to initialize.
|
|
||||||
*
|
|
||||||
* @param e The corresponding exception.
|
|
||||||
*/
|
|
||||||
void onAudioTrackInitializationError(AudioTrack.InitializationException e);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
* Invoked when an {@link AudioTrack} underrun occurs.
|
||||||
@ -48,9 +56,54 @@ public interface AudioTrackRendererEventListener extends TrackRendererEventListe
|
|||||||
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
|
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked to pass the codec counters when the renderer is enabled.
|
* Dispatches events to a {@link AudioTrackRendererEventListener}.
|
||||||
*
|
|
||||||
* @param counters CodecCounters object used by the renderer.
|
|
||||||
*/
|
*/
|
||||||
void onAudioCodecCounters(CodecCounters counters);
|
final class EventDispatcher {
|
||||||
|
|
||||||
|
private final Handler handler;
|
||||||
|
private final AudioTrackRendererEventListener listener;
|
||||||
|
|
||||||
|
public EventDispatcher(Handler handler, AudioTrackRendererEventListener listener) {
|
||||||
|
this.handler = listener != null ? Assertions.checkNotNull(handler) : null;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void codecCounters(final CodecCounters codecCounters) {
|
||||||
|
if (listener != null) {
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onAudioCodecCounters(codecCounters);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decoderInitialized(final String decoderName,
|
||||||
|
final long initializedTimestampMs, final long initializationDurationMs) {
|
||||||
|
if (listener != null) {
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onAudioDecoderInitialized(decoderName, initializedTimestampMs,
|
||||||
|
initializationDurationMs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void audioTrackUnderrun(final int bufferSize, final long bufferSizeMs,
|
||||||
|
final long elapsedSinceLastFeedMs) {
|
||||||
|
if (listener != null) {
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onAudioTrackUnderrun(bufferSize, bufferSizeMs, elapsedSinceLastFeedMs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer;
|
package com.google.android.exoplayer;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.AudioTrackRendererEventListener.EventDispatcher;
|
||||||
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
|
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
|
||||||
import com.google.android.exoplayer.audio.AudioCapabilities;
|
import com.google.android.exoplayer.audio.AudioCapabilities;
|
||||||
import com.google.android.exoplayer.audio.AudioTrack;
|
import com.google.android.exoplayer.audio.AudioTrack;
|
||||||
@ -40,15 +41,6 @@ import java.nio.ByteBuffer;
|
|||||||
@TargetApi(16)
|
@TargetApi(16)
|
||||||
public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implements MediaClock {
|
public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implements MediaClock {
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface definition for a callback to be notified of {@link MediaCodecAudioTrackRenderer}
|
|
||||||
* events.
|
|
||||||
*/
|
|
||||||
public interface EventListener extends MediaCodecTrackRenderer.EventListener,
|
|
||||||
AudioTrackRendererEventListener {
|
|
||||||
// No extra methods
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of a message that can be passed to an instance of this class via
|
* The type of a message that can be passed to an instance of this class via
|
||||||
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
|
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
|
||||||
@ -65,7 +57,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
|||||||
*/
|
*/
|
||||||
public static final int MSG_SET_PLAYBACK_PARAMS = 2;
|
public static final int MSG_SET_PLAYBACK_PARAMS = 2;
|
||||||
|
|
||||||
private final EventListener eventListener;
|
private final EventDispatcher eventDispatcher;
|
||||||
private final AudioTrack audioTrack;
|
private final AudioTrack audioTrack;
|
||||||
|
|
||||||
private boolean passthroughEnabled;
|
private boolean passthroughEnabled;
|
||||||
@ -107,7 +99,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
|||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
*/
|
*/
|
||||||
public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector, Handler eventHandler,
|
public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector, Handler eventHandler,
|
||||||
EventListener eventListener) {
|
AudioTrackRendererEventListener eventListener) {
|
||||||
this(mediaCodecSelector, null, true, eventHandler, eventListener);
|
this(mediaCodecSelector, null, true, eventHandler, eventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +118,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
|||||||
*/
|
*/
|
||||||
public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector,
|
public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector,
|
||||||
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
|
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
|
||||||
Handler eventHandler, EventListener eventListener) {
|
Handler eventHandler, AudioTrackRendererEventListener eventListener) {
|
||||||
this(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler,
|
this(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler,
|
||||||
eventListener, null, AudioManager.STREAM_MUSIC);
|
eventListener, null, AudioManager.STREAM_MUSIC);
|
||||||
}
|
}
|
||||||
@ -149,13 +141,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
|||||||
*/
|
*/
|
||||||
public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector,
|
public MediaCodecAudioTrackRenderer(MediaCodecSelector mediaCodecSelector,
|
||||||
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
|
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
|
||||||
Handler eventHandler, EventListener eventListener, AudioCapabilities audioCapabilities,
|
Handler eventHandler, AudioTrackRendererEventListener eventListener,
|
||||||
int streamType) {
|
AudioCapabilities audioCapabilities, int streamType) {
|
||||||
super(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler,
|
super(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
|
||||||
eventListener);
|
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
||||||
this.eventListener = eventListener;
|
audioTrack = new AudioTrack(audioCapabilities, streamType);
|
||||||
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||||
this.audioTrack = new AudioTrack(audioCapabilities, streamType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -235,6 +226,12 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCodecInitialized(String name, long initializedTimestampMs,
|
||||||
|
long initializationDurationMs) {
|
||||||
|
eventDispatcher.decoderInitialized(name, initializedTimestampMs, initializationDurationMs);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onInputFormatChanged(FormatHolder holder) throws ExoPlaybackException {
|
protected void onInputFormatChanged(FormatHolder holder) throws ExoPlaybackException {
|
||||||
super.onInputFormatChanged(holder);
|
super.onInputFormatChanged(holder);
|
||||||
@ -275,7 +272,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
|||||||
@Override
|
@Override
|
||||||
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
|
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
|
||||||
super.onEnabled(formats, joining);
|
super.onEnabled(formats, joining);
|
||||||
notifyAudioCodecCounters();
|
eventDispatcher.codecCounters(codecCounters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -357,7 +354,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
|||||||
}
|
}
|
||||||
audioTrackHasData = false;
|
audioTrackHasData = false;
|
||||||
} catch (AudioTrack.InitializationException e) {
|
} catch (AudioTrack.InitializationException e) {
|
||||||
notifyAudioTrackInitializationError(e);
|
|
||||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
}
|
}
|
||||||
if (getState() == TrackRenderer.STATE_STARTED) {
|
if (getState() == TrackRenderer.STATE_STARTED) {
|
||||||
@ -371,7 +367,8 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
|||||||
long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs;
|
long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs;
|
||||||
long bufferSizeUs = audioTrack.getBufferSizeUs();
|
long bufferSizeUs = audioTrack.getBufferSizeUs();
|
||||||
long bufferSizeMs = bufferSizeUs == C.UNSET_TIME_US ? -1 : bufferSizeUs / 1000;
|
long bufferSizeMs = bufferSizeUs == C.UNSET_TIME_US ? -1 : bufferSizeUs / 1000;
|
||||||
notifyAudioTrackUnderrun(audioTrack.getBufferSize(), bufferSizeMs, elapsedSinceLastFeedMs);
|
eventDispatcher.audioTrackUnderrun(audioTrack.getBufferSize(), bufferSizeMs,
|
||||||
|
elapsedSinceLastFeedMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,7 +377,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
|||||||
handleBufferResult = audioTrack.handleBuffer(buffer, bufferPresentationTimeUs);
|
handleBufferResult = audioTrack.handleBuffer(buffer, bufferPresentationTimeUs);
|
||||||
lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime();
|
lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime();
|
||||||
} catch (AudioTrack.WriteException e) {
|
} catch (AudioTrack.WriteException e) {
|
||||||
notifyAudioTrackWriteError(e);
|
|
||||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,49 +420,4 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.CodecException;
|
||||||
import android.media.MediaCodec.CryptoException;
|
import android.media.MediaCodec.CryptoException;
|
||||||
import android.media.MediaCrypto;
|
import android.media.MediaCrypto;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
|
||||||
@ -41,20 +40,6 @@ import java.util.List;
|
|||||||
@TargetApi(16)
|
@TargetApi(16)
|
||||||
public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface definition for a callback to be notified of {@link MediaCodecTrackRenderer} events.
|
|
||||||
*/
|
|
||||||
public interface EventListener extends TrackRendererEventListener {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoked when a decoder operation raises a {@link CryptoException}.
|
|
||||||
*
|
|
||||||
* @param e The corresponding exception.
|
|
||||||
*/
|
|
||||||
void onCryptoError(CryptoException e);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when a failure occurs instantiating a decoder.
|
* Thrown when a failure occurs instantiating a decoder.
|
||||||
*/
|
*/
|
||||||
@ -167,8 +152,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
private final FormatHolder formatHolder;
|
private final FormatHolder formatHolder;
|
||||||
private final List<Long> decodeOnlyPresentationTimestamps;
|
private final List<Long> decodeOnlyPresentationTimestamps;
|
||||||
private final MediaCodec.BufferInfo outputBufferInfo;
|
private final MediaCodec.BufferInfo outputBufferInfo;
|
||||||
private final EventListener eventListener;
|
|
||||||
protected final Handler eventHandler;
|
|
||||||
|
|
||||||
private Format format;
|
private Format format;
|
||||||
private MediaCodec codec;
|
private MediaCodec codec;
|
||||||
@ -204,19 +187,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
* begin in parallel with key acquisition. This parameter specifies whether the renderer is
|
* begin in parallel with key acquisition. This parameter specifies whether the renderer is
|
||||||
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
|
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
|
||||||
* has obtained the keys necessary to decrypt encrypted regions of the media.
|
* has obtained the keys necessary to decrypt encrypted regions of the media.
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
|
||||||
* null if delivery of events is not required.
|
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
|
||||||
*/
|
*/
|
||||||
public MediaCodecTrackRenderer(MediaCodecSelector mediaCodecSelector,
|
public MediaCodecTrackRenderer(MediaCodecSelector mediaCodecSelector,
|
||||||
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
|
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys) {
|
||||||
Handler eventHandler, EventListener eventListener) {
|
|
||||||
Assertions.checkState(Util.SDK_INT >= 16);
|
Assertions.checkState(Util.SDK_INT >= 16);
|
||||||
this.mediaCodecSelector = Assertions.checkNotNull(mediaCodecSelector);
|
this.mediaCodecSelector = Assertions.checkNotNull(mediaCodecSelector);
|
||||||
this.drmSessionManager = drmSessionManager;
|
this.drmSessionManager = drmSessionManager;
|
||||||
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
|
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
|
||||||
this.eventHandler = eventHandler;
|
|
||||||
this.eventListener = eventListener;
|
|
||||||
codecCounters = new CodecCounters();
|
codecCounters = new CodecCounters();
|
||||||
buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
|
buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
|
||||||
formatHolder = new FormatHolder();
|
formatHolder = new FormatHolder();
|
||||||
@ -311,13 +288,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
try {
|
try {
|
||||||
decoderInfo = getDecoderInfo(mediaCodecSelector, format, requiresSecureDecoder);
|
decoderInfo = getDecoderInfo(mediaCodecSelector, format, requiresSecureDecoder);
|
||||||
} catch (DecoderQueryException e) {
|
} catch (DecoderQueryException e) {
|
||||||
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e,
|
throwDecoderInitError(new DecoderInitializationException(format, e, requiresSecureDecoder,
|
||||||
requiresSecureDecoder, DecoderInitializationException.DECODER_QUERY_ERROR));
|
DecoderInitializationException.DECODER_QUERY_ERROR));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decoderInfo == null) {
|
if (decoderInfo == null) {
|
||||||
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, null,
|
throwDecoderInitError(new DecoderInitializationException(format, null, requiresSecureDecoder,
|
||||||
requiresSecureDecoder, DecoderInitializationException.NO_SUITABLE_DECODER_ERROR));
|
DecoderInitializationException.NO_SUITABLE_DECODER_ERROR));
|
||||||
}
|
}
|
||||||
|
|
||||||
String codecName = decoderInfo.name;
|
String codecName = decoderInfo.name;
|
||||||
@ -339,13 +316,13 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
codec.start();
|
codec.start();
|
||||||
TraceUtil.endSection();
|
TraceUtil.endSection();
|
||||||
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||||
notifyDecoderInitialized(codecName, codecInitializedTimestamp,
|
onCodecInitialized(codecName, codecInitializedTimestamp,
|
||||||
codecInitializedTimestamp - codecInitializingTimestamp);
|
codecInitializedTimestamp - codecInitializingTimestamp);
|
||||||
inputBuffers = codec.getInputBuffers();
|
inputBuffers = codec.getInputBuffers();
|
||||||
outputBuffers = codec.getOutputBuffers();
|
outputBuffers = codec.getOutputBuffers();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e,
|
throwDecoderInitError(new DecoderInitializationException(format, e, requiresSecureDecoder,
|
||||||
requiresSecureDecoder, codecName));
|
codecName));
|
||||||
}
|
}
|
||||||
codecHotswapDeadlineMs = getState() == TrackRenderer.STATE_STARTED
|
codecHotswapDeadlineMs = getState() == TrackRenderer.STATE_STARTED
|
||||||
? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS) : -1;
|
? (SystemClock.elapsedRealtime() + MAX_CODEC_HOTSWAP_TIME_MS) : -1;
|
||||||
@ -354,9 +331,8 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
codecCounters.codecInitCount++;
|
codecCounters.codecInitCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyAndThrowDecoderInitError(DecoderInitializationException e)
|
private void throwDecoderInitError(DecoderInitializationException e)
|
||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
notifyDecoderInitializationError(e);
|
|
||||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,7 +551,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
inputIndex = -1;
|
inputIndex = -1;
|
||||||
}
|
}
|
||||||
} catch (CryptoException e) {
|
} catch (CryptoException e) {
|
||||||
notifyCryptoError(e);
|
|
||||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -613,7 +588,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
codecReconfigurationState = RECONFIGURATION_STATE_NONE;
|
codecReconfigurationState = RECONFIGURATION_STATE_NONE;
|
||||||
codecCounters.inputBufferCount++;
|
codecCounters.inputBufferCount++;
|
||||||
} catch (CryptoException e) {
|
} catch (CryptoException e) {
|
||||||
notifyCryptoError(e);
|
|
||||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -647,6 +621,21 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
|||||||
&& (bufferEncrypted || !playClearSamplesWithoutKeys);
|
&& (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}.
|
* 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) {
|
private boolean shouldSkipOutputBuffer(long presentationTimeUs) {
|
||||||
// We avoid using decodeOnlyPresentationTimestamps.remove(presentationTimeUs) because it would
|
// We avoid using decodeOnlyPresentationTimestamps.remove(presentationTimeUs) because it would
|
||||||
// box presentationTimeUs, creating a Long object that would need to be garbage collected.
|
// box presentationTimeUs, creating a Long object that would need to be garbage collected.
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package com.google.android.exoplayer;
|
package com.google.android.exoplayer;
|
||||||
|
|
||||||
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
|
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.drm.DrmSessionManager;
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
import com.google.android.exoplayer.util.TraceUtil;
|
import com.google.android.exoplayer.util.TraceUtil;
|
||||||
@ -40,15 +41,6 @@ import java.nio.ByteBuffer;
|
|||||||
@TargetApi(16)
|
@TargetApi(16)
|
||||||
public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
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";
|
private static final String TAG = "MediaCodecVideoRenderer";
|
||||||
|
|
||||||
// TODO: Use MediaFormat constants if these get exposed through the API. See
|
// 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 static final String KEY_CROP_TOP = "crop-top";
|
||||||
|
|
||||||
private final VideoFrameReleaseTimeHelper frameReleaseTimeHelper;
|
private final VideoFrameReleaseTimeHelper frameReleaseTimeHelper;
|
||||||
private final EventListener eventListener;
|
private final EventDispatcher eventDispatcher;
|
||||||
private final long allowedJoiningTimeMs;
|
private final long allowedJoiningTimeMs;
|
||||||
private final int videoScalingMode;
|
private final int videoScalingMode;
|
||||||
private final int maxDroppedFrameCountToNotify;
|
private final int maxDroppedFrameCountToNotify;
|
||||||
@ -122,11 +114,11 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
* null if delivery of events is not required.
|
* 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 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
|
* @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,
|
public MediaCodecVideoTrackRenderer(Context context, MediaCodecSelector mediaCodecSelector,
|
||||||
int videoScalingMode, long allowedJoiningTimeMs, Handler eventHandler,
|
int videoScalingMode, long allowedJoiningTimeMs, Handler eventHandler,
|
||||||
EventListener eventListener, int maxDroppedFrameCountToNotify) {
|
VideoTrackRendererEventListener eventListener, int maxDroppedFrameCountToNotify) {
|
||||||
this(context, mediaCodecSelector, videoScalingMode, allowedJoiningTimeMs, null, false,
|
this(context, mediaCodecSelector, videoScalingMode, allowedJoiningTimeMs, null, false,
|
||||||
eventHandler, eventListener, maxDroppedFrameCountToNotify);
|
eventHandler, eventListener, maxDroppedFrameCountToNotify);
|
||||||
}
|
}
|
||||||
@ -142,26 +134,25 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
* content is not required.
|
* content is not required.
|
||||||
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
|
* @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
|
* 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}
|
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
|
||||||
* has obtained the keys necessary to decrypt encrypted regions of the media.
|
* has obtained the keys necessary to decrypt encrypted regions of the media.
|
||||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||||
* null if delivery of events is not required.
|
* 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 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
|
* @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,
|
public MediaCodecVideoTrackRenderer(Context context, MediaCodecSelector mediaCodecSelector,
|
||||||
int videoScalingMode, long allowedJoiningTimeMs, DrmSessionManager drmSessionManager,
|
int videoScalingMode, long allowedJoiningTimeMs, DrmSessionManager drmSessionManager,
|
||||||
boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener,
|
boolean playClearSamplesWithoutKeys, Handler eventHandler,
|
||||||
int maxDroppedFrameCountToNotify) {
|
VideoTrackRendererEventListener eventListener, int maxDroppedFrameCountToNotify) {
|
||||||
super(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler,
|
super(mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys);
|
||||||
eventListener);
|
|
||||||
this.frameReleaseTimeHelper = new VideoFrameReleaseTimeHelper(context);
|
|
||||||
this.videoScalingMode = videoScalingMode;
|
this.videoScalingMode = videoScalingMode;
|
||||||
this.allowedJoiningTimeMs = allowedJoiningTimeMs;
|
this.allowedJoiningTimeMs = allowedJoiningTimeMs;
|
||||||
this.eventListener = eventListener;
|
|
||||||
this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify;
|
this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify;
|
||||||
|
frameReleaseTimeHelper = new VideoFrameReleaseTimeHelper(context);
|
||||||
|
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||||
deviceNeedsAutoFrcWorkaround = deviceNeedsAutoFrcWorkaround();
|
deviceNeedsAutoFrcWorkaround = deviceNeedsAutoFrcWorkaround();
|
||||||
joiningDeadlineMs = -1;
|
joiningDeadlineMs = -1;
|
||||||
currentWidth = -1;
|
currentWidth = -1;
|
||||||
@ -227,8 +218,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
adaptiveMaxWidth = Math.max(adaptiveMaxWidth, format.width);
|
adaptiveMaxWidth = Math.max(adaptiveMaxWidth, format.width);
|
||||||
adaptiveMaxHeight = Math.max(adaptiveMaxHeight, format.height);
|
adaptiveMaxHeight = Math.max(adaptiveMaxHeight, format.height);
|
||||||
}
|
}
|
||||||
if (adaptiveMaxWidth == Format.NO_VALUE
|
if (adaptiveMaxWidth == Format.NO_VALUE || adaptiveMaxHeight == Format.NO_VALUE) {
|
||||||
|| adaptiveMaxHeight == Format.NO_VALUE) {
|
|
||||||
Log.w(TAG, "Maximum dimensions unknown. Assuming 1920x1080.");
|
Log.w(TAG, "Maximum dimensions unknown. Assuming 1920x1080.");
|
||||||
adaptiveMaxWidth = 1920;
|
adaptiveMaxWidth = 1920;
|
||||||
adaptiveMaxHeight = 1080;
|
adaptiveMaxHeight = 1080;
|
||||||
@ -238,7 +228,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
joiningDeadlineMs = SystemClock.elapsedRealtime() + allowedJoiningTimeMs;
|
joiningDeadlineMs = SystemClock.elapsedRealtime() + allowedJoiningTimeMs;
|
||||||
}
|
}
|
||||||
frameReleaseTimeHelper.enable();
|
frameReleaseTimeHelper.enable();
|
||||||
notifyVideoCodecCounters();
|
eventDispatcher.codecCounters(codecCounters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -332,6 +322,12 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
codec.configure(getFrameworkMediaFormat(format), surface, crypto, 0);
|
codec.configure(getFrameworkMediaFormat(format), surface, crypto, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCodecInitialized(String name, long initializedTimestampMs,
|
||||||
|
long initializationDurationMs) {
|
||||||
|
eventDispatcher.decoderInitialized(name, initializedTimestampMs, initializationDurationMs);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onInputFormatChanged(FormatHolder holder) throws ExoPlaybackException {
|
protected void onInputFormatChanged(FormatHolder holder) throws ExoPlaybackException {
|
||||||
super.onInputFormatChanged(holder);
|
super.onInputFormatChanged(holder);
|
||||||
@ -559,66 +555,35 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
|
|||||||
return frameworkMediaFormat;
|
return frameworkMediaFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void maybeNotifyDrawnToSurface() {
|
||||||
|
if (!reportedDrawnToSurface) {
|
||||||
|
eventDispatcher.drawnToSurface(surface);
|
||||||
|
reportedDrawnToSurface = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void maybeNotifyVideoSizeChanged() {
|
private void maybeNotifyVideoSizeChanged() {
|
||||||
if (eventHandler == null || eventListener == null
|
if (lastReportedWidth != currentWidth || lastReportedHeight != currentHeight
|
||||||
|| (lastReportedWidth == currentWidth && lastReportedHeight == currentHeight
|
|| lastReportedUnappliedRotationDegrees != currentUnappliedRotationDegrees
|
||||||
&& lastReportedUnappliedRotationDegrees == currentUnappliedRotationDegrees
|
|| lastReportedPixelWidthHeightRatio != currentPixelWidthHeightRatio) {
|
||||||
&& lastReportedPixelWidthHeightRatio == currentPixelWidthHeightRatio)) {
|
eventDispatcher.videoSizeChanged(currentWidth, currentHeight, currentUnappliedRotationDegrees,
|
||||||
return;
|
currentPixelWidthHeightRatio);
|
||||||
}
|
|
||||||
// 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;
|
lastReportedWidth = currentWidth;
|
||||||
lastReportedHeight = currentHeight;
|
lastReportedHeight = currentHeight;
|
||||||
lastReportedUnappliedRotationDegrees = currentUnappliedRotationDegrees;
|
lastReportedUnappliedRotationDegrees = currentUnappliedRotationDegrees;
|
||||||
lastReportedPixelWidthHeightRatio = currentPixelWidthHeightRatio;
|
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() {
|
private void maybeNotifyDroppedFrameCount() {
|
||||||
if (eventHandler == null || eventListener == null || droppedFrameCount == 0) {
|
if (droppedFrameCount > 0) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
long now = SystemClock.elapsedRealtime();
|
long now = SystemClock.elapsedRealtime();
|
||||||
// Make final copies to ensure the runnable reports the correct values.
|
long elapsedMs = now - droppedFrameAccumulationStartTimeMs;
|
||||||
final int countToNotify = droppedFrameCount;
|
eventDispatcher.droppedFrameCount(droppedFrameCount, elapsedMs);
|
||||||
final long elapsedToNotify = now - droppedFrameAccumulationStartTimeMs;
|
|
||||||
eventHandler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
eventListener.onDroppedFrames(countToNotify, elapsedToNotify);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Reset the dropped frame tracking.
|
|
||||||
droppedFrameCount = 0;
|
droppedFrameCount = 0;
|
||||||
droppedFrameAccumulationStartTimeMs = now;
|
droppedFrameAccumulationStartTimeMs = now;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the device is known to enable frame-rate conversion logic that negatively
|
* 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);
|
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;
|
package com.google.android.exoplayer;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.TextureView;
|
import android.view.TextureView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional interface definition for a callback to be notified of video {@link TrackRenderer}
|
* Interface definition for a callback to be notified of video {@link TrackRenderer} events.
|
||||||
* events.
|
|
||||||
*/
|
*/
|
||||||
public interface VideoTrackRendererEventListener extends TrackRendererEventListener {
|
public interface VideoTrackRendererEventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked to pass the codec counters when the renderer is enabled.
|
||||||
|
*
|
||||||
|
* @param counters CodecCounters object used by the renderer.
|
||||||
|
*/
|
||||||
|
void onVideoCodecCounters(CodecCounters counters);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a decoder is created.
|
||||||
|
*
|
||||||
|
* @param decoderName The decoder that was created.
|
||||||
|
* @param initializedTimestampMs {@link SystemClock#elapsedRealtime()} when initialization
|
||||||
|
* finished.
|
||||||
|
* @param initializationDurationMs The time taken to initialize the decoder in milliseconds.
|
||||||
|
*/
|
||||||
|
void onVideoDecoderInitialized(String decoderName, long initializedTimestampMs,
|
||||||
|
long initializationDurationMs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked to report the number of frames dropped by the renderer. Dropped frames are reported
|
* Invoked to report the number of frames dropped by the renderer. Dropped frames are reported
|
||||||
@ -30,12 +51,12 @@ public interface VideoTrackRendererEventListener extends TrackRendererEventListe
|
|||||||
* reaches a specified threshold whilst the renderer is started.
|
* reaches a specified threshold whilst the renderer is started.
|
||||||
*
|
*
|
||||||
* @param count The number of dropped frames.
|
* @param count The number of dropped frames.
|
||||||
* @param elapsed The duration in milliseconds over which the frames were dropped. This
|
* @param elapsedMs The duration in milliseconds over which the frames were dropped. This
|
||||||
* duration is timed from when the renderer was started or from when dropped frames were
|
* duration is timed from when the renderer was started or from when dropped frames were
|
||||||
* last reported (whichever was more recent), and not from when the first of the reported
|
* last reported (whichever was more recent), and not from when the first of the reported
|
||||||
* drops occurred.
|
* drops occurred.
|
||||||
*/
|
*/
|
||||||
void onDroppedFrames(int count, long elapsed);
|
void onDroppedFrames(int count, long elapsedMs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked each time there's a change in the size of the video being rendered.
|
* Invoked each time there's a change in the size of the video being rendered.
|
||||||
@ -65,10 +86,77 @@ public interface VideoTrackRendererEventListener extends TrackRendererEventListe
|
|||||||
void onDrawnToSurface(Surface surface);
|
void onDrawnToSurface(Surface surface);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked to pass the codec counters when the renderer is enabled.
|
* Dispatches events to a {@link VideoTrackRendererEventListener}.
|
||||||
*
|
|
||||||
* @param counters CodecCounters object used by the renderer.
|
|
||||||
*/
|
*/
|
||||||
void onVideoCodecCounters(CodecCounters counters);
|
final class EventDispatcher {
|
||||||
|
|
||||||
|
private final Handler handler;
|
||||||
|
private final VideoTrackRendererEventListener listener;
|
||||||
|
|
||||||
|
public EventDispatcher(Handler handler, VideoTrackRendererEventListener listener) {
|
||||||
|
this.handler = listener != null ? Assertions.checkNotNull(handler) : null;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void codecCounters(final CodecCounters codecCounters) {
|
||||||
|
if (listener != null) {
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onVideoCodecCounters(codecCounters);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decoderInitialized(final String decoderName,
|
||||||
|
final long initializedTimestampMs, final long initializationDurationMs) {
|
||||||
|
if (listener != null) {
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onVideoDecoderInitialized(decoderName, initializedTimestampMs,
|
||||||
|
initializationDurationMs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void droppedFrameCount(final int droppedFrameCount, final long elapsedMs) {
|
||||||
|
if (listener != null) {
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onDroppedFrames(droppedFrameCount, elapsedMs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void videoSizeChanged(final int width, final int height,
|
||||||
|
final int unappliedRotationDegrees, final float pixelWidthHeightRatio) {
|
||||||
|
if (listener != null) {
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onVideoSizeChanged(width, height, unappliedRotationDegrees,
|
||||||
|
pixelWidthHeightRatio);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawnToSurface(final Surface surface) {
|
||||||
|
if (listener != null) {
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
listener.onDrawnToSurface(surface);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer.chunk;
|
|||||||
import com.google.android.exoplayer.C;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.Format;
|
import com.google.android.exoplayer.Format;
|
||||||
import com.google.android.exoplayer.TrackStream;
|
import com.google.android.exoplayer.TrackStream;
|
||||||
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
import com.google.android.exoplayer.util.Util;
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@ -114,7 +115,7 @@ public interface ChunkTrackStreamEventListener {
|
|||||||
private final int sourceId;
|
private final int sourceId;
|
||||||
|
|
||||||
public EventDispatcher(Handler handler, ChunkTrackStreamEventListener listener, int sourceId) {
|
public EventDispatcher(Handler handler, ChunkTrackStreamEventListener listener, int sourceId) {
|
||||||
this.handler = listener != null ? handler : null;
|
this.handler = listener != null ? Assertions.checkNotNull(handler) : null;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.sourceId = sourceId;
|
this.sourceId = sourceId;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package com.google.android.exoplayer.extensions;
|
package com.google.android.exoplayer.extensions;
|
||||||
|
|
||||||
import com.google.android.exoplayer.AudioTrackRendererEventListener;
|
import com.google.android.exoplayer.AudioTrackRendererEventListener;
|
||||||
|
import com.google.android.exoplayer.AudioTrackRendererEventListener.EventDispatcher;
|
||||||
import com.google.android.exoplayer.C;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.CodecCounters;
|
import com.google.android.exoplayer.CodecCounters;
|
||||||
import com.google.android.exoplayer.DecoderInputBuffer;
|
import com.google.android.exoplayer.DecoderInputBuffer;
|
||||||
@ -46,8 +47,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
|
|||||||
|
|
||||||
public final CodecCounters codecCounters = new CodecCounters();
|
public final CodecCounters codecCounters = new CodecCounters();
|
||||||
|
|
||||||
private final Handler eventHandler;
|
private final EventDispatcher eventDispatcher;
|
||||||
private final AudioTrackRendererEventListener eventListener;
|
|
||||||
private final FormatHolder formatHolder;
|
private final FormatHolder formatHolder;
|
||||||
|
|
||||||
private Format inputFormat;
|
private Format inputFormat;
|
||||||
@ -78,10 +78,9 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
|
|||||||
*/
|
*/
|
||||||
public AudioDecoderTrackRenderer(Handler eventHandler,
|
public AudioDecoderTrackRenderer(Handler eventHandler,
|
||||||
AudioTrackRendererEventListener eventListener) {
|
AudioTrackRendererEventListener eventListener) {
|
||||||
this.eventHandler = eventHandler;
|
eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||||
this.eventListener = eventListener;
|
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
||||||
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
audioTrack = new AudioTrack();
|
||||||
this.audioTrack = new AudioTrack();
|
|
||||||
formatHolder = new FormatHolder();
|
formatHolder = new FormatHolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +107,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
|
|||||||
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
|
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
|
||||||
decoder = createDecoder(inputFormat);
|
decoder = createDecoder(inputFormat);
|
||||||
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||||
notifyDecoderInitialized(decoder.getName(), codecInitializedTimestamp,
|
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
|
||||||
codecInitializedTimestamp - codecInitializingTimestamp);
|
codecInitializedTimestamp - codecInitializingTimestamp);
|
||||||
codecCounters.codecInitCount++;
|
codecCounters.codecInitCount++;
|
||||||
} catch (AudioDecoderException e) {
|
} catch (AudioDecoderException e) {
|
||||||
@ -120,13 +119,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
|
|||||||
try {
|
try {
|
||||||
while (drainOutputBuffer()) {}
|
while (drainOutputBuffer()) {}
|
||||||
while (feedInputBuffer()) {}
|
while (feedInputBuffer()) {}
|
||||||
} catch (AudioTrack.InitializationException e) {
|
} catch (AudioTrack.InitializationException | AudioTrack.WriteException
|
||||||
notifyAudioTrackInitializationError(e);
|
| AudioDecoderException e) {
|
||||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
|
||||||
} catch (AudioTrack.WriteException e) {
|
|
||||||
notifyAudioTrackWriteError(e);
|
|
||||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
|
||||||
} catch (AudioDecoderException e) {
|
|
||||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
}
|
}
|
||||||
codecCounters.ensureUpdated();
|
codecCounters.ensureUpdated();
|
||||||
@ -197,7 +191,8 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
|
|||||||
long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs;
|
long elapsedSinceLastFeedMs = SystemClock.elapsedRealtime() - lastFeedElapsedRealtimeMs;
|
||||||
long bufferSizeUs = audioTrack.getBufferSizeUs();
|
long bufferSizeUs = audioTrack.getBufferSizeUs();
|
||||||
long bufferSizeMs = bufferSizeUs == C.UNSET_TIME_US ? -1 : bufferSizeUs / 1000;
|
long bufferSizeMs = bufferSizeUs == C.UNSET_TIME_US ? -1 : bufferSizeUs / 1000;
|
||||||
notifyAudioTrackUnderrun(audioTrack.getBufferSize(), bufferSizeMs, elapsedSinceLastFeedMs);
|
eventDispatcher.audioTrackUnderrun(audioTrack.getBufferSize(), bufferSizeMs,
|
||||||
|
elapsedSinceLastFeedMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,7 +306,7 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
|
protected void onEnabled(Format[] formats, boolean joining) throws ExoPlaybackException {
|
||||||
notifyAudioCodecCounters();
|
eventDispatcher.codecCounters(codecCounters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -360,62 +355,4 @@ public abstract class AudioDecoderTrackRenderer extends TrackRenderer implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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