Commit badd9356 by Oliver Woodman

Refine use of KEY_OPERATING_RATE

parent f4219b55
...@@ -70,6 +70,9 @@ ...@@ -70,6 +70,9 @@
([#3497](https://github.com/google/ExoPlayer/issues/3497)). ([#3497](https://github.com/google/ExoPlayer/issues/3497)).
* Add `PlayerView.isControllerVisible` * Add `PlayerView.isControllerVisible`
([#4385](https://github.com/google/ExoPlayer/issues/4385)). ([#4385](https://github.com/google/ExoPlayer/issues/4385)).
* Improved performance when playing high frame-rate content, and when playing
at greater than 1x speed
([#2777](https://github.com/google/ExoPlayer/issues/2777)).
* Expose all internal ID3 data stored in MP4 udta boxes, and switch from using * Expose all internal ID3 data stored in MP4 udta boxes, and switch from using
CommentFrame to InternalFrame for frames with gapless metadata in MP4. CommentFrame to InternalFrame for frames with gapless metadata in MP4.
* Allow setting the `Looper`, which is used to access the player, in * Allow setting the `Looper`, which is used to access the player, in
......
...@@ -137,11 +137,6 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { ...@@ -137,11 +137,6 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
} }
@Override @Override
public final void setOperatingRate(float operatingRate) {
onOperatingRateChanged(operatingRate);
}
@Override
public final void stop() throws ExoPlaybackException { public final void stop() throws ExoPlaybackException {
Assertions.checkState(state == STATE_STARTED); Assertions.checkState(state == STATE_STARTED);
state = STATE_ENABLED; state = STATE_ENABLED;
...@@ -222,17 +217,6 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { ...@@ -222,17 +217,6 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
} }
/** /**
* Called when the operating rate is changed.
* <p>
* The default implementation is a no-op.
*
* @param operatingRate The new operating rate.
*/
protected void onOperatingRateChanged(float operatingRate) {
// Do nothing.
}
/**
* Called when the renderer is started. * Called when the renderer is started.
* <p> * <p>
* The default implementation is a no-op. * The default implementation is a no-op.
......
...@@ -78,6 +78,7 @@ import java.util.Collections; ...@@ -78,6 +78,7 @@ import java.util.Collections;
private static final int MSG_SET_SHUFFLE_ENABLED = 13; private static final int MSG_SET_SHUFFLE_ENABLED = 13;
private static final int MSG_SEND_MESSAGE = 14; private static final int MSG_SEND_MESSAGE = 14;
private static final int MSG_SEND_MESSAGE_TO_TARGET_THREAD = 15; private static final int MSG_SEND_MESSAGE_TO_TARGET_THREAD = 15;
private static final int MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL = 16;
private static final int PREPARING_SOURCE_INTERVAL_MS = 10; private static final int PREPARING_SOURCE_INTERVAL_MS = 10;
private static final int RENDERING_INTERVAL_MS = 10; private static final int RENDERING_INTERVAL_MS = 10;
...@@ -275,9 +276,9 @@ import java.util.Collections; ...@@ -275,9 +276,9 @@ import java.util.Collections;
@Override @Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
eventHandler.obtainMessage(MSG_PLAYBACK_PARAMETERS_CHANGED, playbackParameters).sendToTarget(); handler
updateTrackSelectionPlaybackSpeed(playbackParameters.speed); .obtainMessage(MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL, playbackParameters)
updateRendererOperatingRate(playbackParameters.speed); .sendToTarget();
} }
// Handler.Callback implementation. // Handler.Callback implementation.
...@@ -329,6 +330,9 @@ import java.util.Collections; ...@@ -329,6 +330,9 @@ import java.util.Collections;
case MSG_TRACK_SELECTION_INVALIDATED: case MSG_TRACK_SELECTION_INVALIDATED:
reselectTracksInternal(); reselectTracksInternal();
break; break;
case MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL:
handlePlaybackParameters((PlaybackParameters) msg.obj);
break;
case MSG_SEND_MESSAGE: case MSG_SEND_MESSAGE:
sendMessageInternal((PlayerMessage) msg.obj); sendMessageInternal((PlayerMessage) msg.obj);
break; break;
...@@ -1100,14 +1104,6 @@ import java.util.Collections; ...@@ -1100,14 +1104,6 @@ import java.util.Collections;
} }
} }
private void updateRendererOperatingRate(float operatingRate) {
for (Renderer renderer : renderers) {
if (renderer != null) {
renderer.setOperatingRate(operatingRate);
}
}
}
private boolean shouldTransitionToReadyState(boolean renderersReadyOrEnded) { private boolean shouldTransitionToReadyState(boolean renderersReadyOrEnded) {
if (enabledRenderers.length == 0) { if (enabledRenderers.length == 0) {
// If there are no enabled renderers, determine whether we're ready based on the timeline. // If there are no enabled renderers, determine whether we're ready based on the timeline.
...@@ -1557,6 +1553,17 @@ import java.util.Collections; ...@@ -1557,6 +1553,17 @@ import java.util.Collections;
maybeContinueLoading(); maybeContinueLoading();
} }
private void handlePlaybackParameters(PlaybackParameters playbackParameters)
throws ExoPlaybackException {
eventHandler.obtainMessage(MSG_PLAYBACK_PARAMETERS_CHANGED, playbackParameters).sendToTarget();
updateTrackSelectionPlaybackSpeed(playbackParameters.speed);
for (Renderer renderer : renderers) {
if (renderer != null) {
renderer.setOperatingRate(playbackParameters.speed);
}
}
}
private void maybeContinueLoading() { private void maybeContinueLoading() {
MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod(); MediaPeriodHolder loadingPeriodHolder = queue.getLoadingPeriod();
long nextLoadPositionUs = loadingPeriodHolder.getNextLoadPositionUs(); long nextLoadPositionUs = loadingPeriodHolder.getNextLoadPositionUs();
......
...@@ -193,11 +193,16 @@ public interface Renderer extends PlayerMessage.Target { ...@@ -193,11 +193,16 @@ public interface Renderer extends PlayerMessage.Target {
void resetPosition(long positionUs) throws ExoPlaybackException; void resetPosition(long positionUs) throws ExoPlaybackException;
/** /**
* Sets the operating rate of this renderer. * Sets the operating rate of this renderer, where 1 is the default rate, 2 is twice the default
* rate, 0.5 is half the default rate and so on. The operating rate is a hint to the renderer of
* the speed at which playback will proceed, and may be used for resource planning.
* *
* @param operatingRate The renderer operating rate. * <p>The default implementation is a no-op.
*
* @param operatingRate The operating rate.
* @throws ExoPlaybackException If an error occurs handling the operating rate.
*/ */
void setOperatingRate(float operatingRate); default void setOperatingRate(float operatingRate) throws ExoPlaybackException {};
/** /**
* Incrementally renders the {@link SampleStream}. * Incrementally renders the {@link SampleStream}.
......
...@@ -231,7 +231,12 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -231,7 +231,12 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Nullable Handler eventHandler, @Nullable Handler eventHandler,
@Nullable AudioRendererEventListener eventListener, @Nullable AudioRendererEventListener eventListener,
AudioSink audioSink) { AudioSink audioSink) {
super(C.TRACK_TYPE_AUDIO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys); super(
C.TRACK_TYPE_AUDIO,
mediaCodecSelector,
drmSessionManager,
playClearSamplesWithoutKeys,
/* assumedMinimumCodecOperatingRate= */ 44100);
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.audioSink = audioSink; this.audioSink = audioSink;
eventDispatcher = new EventDispatcher(eventHandler, eventListener); eventDispatcher = new EventDispatcher(eventHandler, eventListener);
...@@ -316,13 +321,18 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -316,13 +321,18 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} }
@Override @Override
protected void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format, protected void configureCodec(
MediaCrypto crypto, float codecOperatingRate) { MediaCodecInfo codecInfo,
MediaCodec codec,
Format format,
MediaCrypto crypto,
float codecOperatingRate) {
codecMaxInputSize = getCodecMaxInputSize(codecInfo, format, getStreamFormats()); codecMaxInputSize = getCodecMaxInputSize(codecInfo, format, getStreamFormats());
codecNeedsDiscardChannelsWorkaround = codecNeedsDiscardChannelsWorkaround(codecInfo.name); codecNeedsDiscardChannelsWorkaround = codecNeedsDiscardChannelsWorkaround(codecInfo.name);
passthroughEnabled = codecInfo.passthrough; passthroughEnabled = codecInfo.passthrough;
String codecMimeType = codecInfo.mimeType == null ? MimeTypes.AUDIO_RAW : codecInfo.mimeType; String codecMimeType = codecInfo.mimeType == null ? MimeTypes.AUDIO_RAW : codecInfo.mimeType;
MediaFormat mediaFormat = getMediaFormat(format, codecMimeType, codecMaxInputSize, codecOperatingRate); MediaFormat mediaFormat =
getMediaFormat(format, codecMimeType, codecMaxInputSize, codecOperatingRate);
codec.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0); codec.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0);
if (passthroughEnabled) { if (passthroughEnabled) {
// Store the input MIME type if we're using the passthrough codec. // Store the input MIME type if we're using the passthrough codec.
...@@ -351,6 +361,14 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -351,6 +361,14 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} }
@Override @Override
protected float getCodecOperatingRate(
float operatingRate, Format format, Format[] streamFormats) {
return format.sampleRate == Format.NO_VALUE
? CODEC_OPERATING_RATE_UNSET
: (format.sampleRate * operatingRate);
}
@Override
protected void onCodecInitialized(String name, long initializedTimestampMs, protected void onCodecInitialized(String name, long initializedTimestampMs,
long initializationDurationMs) { long initializationDurationMs) {
eventDispatcher.decoderInitialized(name, initializedTimestampMs, initializationDurationMs); eventDispatcher.decoderInitialized(name, initializedTimestampMs, initializationDurationMs);
...@@ -633,12 +651,13 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -633,12 +651,13 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
* @param format The format of the media. * @param format The format of the media.
* @param codecMimeType The MIME type handled by the codec. * @param codecMimeType The MIME type handled by the codec.
* @param codecMaxInputSize The maximum input size supported by the codec. * @param codecMaxInputSize The maximum input size supported by the codec.
* @param codecOperatingRate * @param codecOperatingRate The codec operating rate, or {@link #CODEC_OPERATING_RATE_UNSET} if
* no codec operating rate should be set.
* @return The framework media format. * @return The framework media format.
*/ */
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
protected MediaFormat getMediaFormat(Format format, String codecMimeType, int codecMaxInputSize, protected MediaFormat getMediaFormat(
float codecOperatingRate) { Format format, String codecMimeType, int codecMaxInputSize, float codecOperatingRate) {
MediaFormat mediaFormat = new MediaFormat(); MediaFormat mediaFormat = new MediaFormat();
// Set format parameters that should always be set. // Set format parameters that should always be set.
mediaFormat.setString(MediaFormat.KEY_MIME, codecMimeType); mediaFormat.setString(MediaFormat.KEY_MIME, codecMimeType);
...@@ -650,9 +669,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -650,9 +669,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
// Set codec configuration values. // Set codec configuration values.
if (Util.SDK_INT >= 23) { if (Util.SDK_INT >= 23) {
mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 0 /* realtime priority */); mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 0 /* realtime priority */);
if (format.sampleRate != Format.NO_VALUE) { if (codecOperatingRate != CODEC_OPERATING_RATE_UNSET) {
mediaFormat.setFloat( mediaFormat.setFloat(MediaFormat.KEY_OPERATING_RATE, codecOperatingRate);
MediaFormat.KEY_OPERATING_RATE, codecOperatingRate * format.sampleRate);
} }
} }
return mediaFormat; return mediaFormat;
......
...@@ -21,6 +21,7 @@ import android.media.MediaCodec.CodecException; ...@@ -21,6 +21,7 @@ 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.media.MediaFormat; import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Looper; import android.os.Looper;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.annotation.CheckResult; import android.support.annotation.CheckResult;
...@@ -161,6 +162,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -161,6 +162,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
/** Indicates no codec operating rate should be set. */
protected static final float CODEC_OPERATING_RATE_UNSET = -1;
private static final String TAG = "MediaCodecRenderer"; private static final String TAG = "MediaCodecRenderer";
/** /**
...@@ -264,6 +268,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -264,6 +268,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
@Nullable @Nullable
private final DrmSessionManager<FrameworkMediaCrypto> drmSessionManager; private final DrmSessionManager<FrameworkMediaCrypto> drmSessionManager;
private final boolean playClearSamplesWithoutKeys; private final boolean playClearSamplesWithoutKeys;
private final float assumedMinimumCodecOperatingRate;
private final DecoderInputBuffer buffer; private final DecoderInputBuffer buffer;
private final DecoderInputBuffer flagsOnlyBuffer; private final DecoderInputBuffer flagsOnlyBuffer;
private final FormatHolder formatHolder; private final FormatHolder formatHolder;
...@@ -274,7 +279,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -274,7 +279,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private DrmSession<FrameworkMediaCrypto> drmSession; private DrmSession<FrameworkMediaCrypto> drmSession;
private DrmSession<FrameworkMediaCrypto> pendingDrmSession; private DrmSession<FrameworkMediaCrypto> pendingDrmSession;
private MediaCodec codec; private MediaCodec codec;
private float codecOperatingRate = 1.0f; private float rendererOperatingRate;
private float codecOperatingRate;
private boolean codecConfiguredWithOperatingRate;
private @Nullable ArrayDeque<MediaCodecInfo> availableCodecInfos; private @Nullable ArrayDeque<MediaCodecInfo> availableCodecInfos;
private @Nullable DecoderInitializationException preferredDecoderInitializationException; private @Nullable DecoderInitializationException preferredDecoderInitializationException;
private @Nullable MediaCodecInfo codecInfo; private @Nullable MediaCodecInfo codecInfo;
...@@ -318,15 +325,22 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -318,15 +325,22 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* 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 assumedMinimumCodecOperatingRate A codec operating rate that all codecs instantiated by
* this renderer are assumed to meet implicitly (i.e. without the operating rate being set
* explicitly using {@link MediaFormat#KEY_OPERATING_RATE}).
*/ */
public MediaCodecRenderer(int trackType, MediaCodecSelector mediaCodecSelector, public MediaCodecRenderer(
int trackType,
MediaCodecSelector mediaCodecSelector,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, @Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys) { boolean playClearSamplesWithoutKeys,
float assumedMinimumCodecOperatingRate) {
super(trackType); super(trackType);
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.assumedMinimumCodecOperatingRate = assumedMinimumCodecOperatingRate;
buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED); buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
flagsOnlyBuffer = DecoderInputBuffer.newFlagsOnlyInstance(); flagsOnlyBuffer = DecoderInputBuffer.newFlagsOnlyInstance();
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
...@@ -334,6 +348,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -334,6 +348,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
outputBufferInfo = new MediaCodec.BufferInfo(); outputBufferInfo = new MediaCodec.BufferInfo();
codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReconfigurationState = RECONFIGURATION_STATE_NONE;
codecReinitializationState = REINITIALIZATION_STATE_NONE; codecReinitializationState = REINITIALIZATION_STATE_NONE;
codecOperatingRate = CODEC_OPERATING_RATE_UNSET;
rendererOperatingRate = 1f;
} }
@Override @Override
...@@ -386,13 +402,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -386,13 +402,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* @param codec The {@link MediaCodec} to configure. * @param codec The {@link MediaCodec} to configure.
* @param format The format for which the codec is being configured. * @param format The format for which the codec is being configured.
* @param crypto For drm protected playbacks, a {@link MediaCrypto} to use for decryption. * @param crypto For drm protected playbacks, a {@link MediaCrypto} to use for decryption.
* @param codecOperatingRate The {@link MediaFormat#KEY_OPERATING_RATE} to use for configuration. * @param codecOperatingRate The codec operating rate, or {@link #CODEC_OPERATING_RATE_UNSET} if
* no codec operating rate should be set.
* @throws DecoderQueryException If an error occurs querying {@code codecInfo}. * @throws DecoderQueryException If an error occurs querying {@code codecInfo}.
*/ */
protected abstract void configureCodec(MediaCodecInfo codecInfo, protected abstract void configureCodec(
MediaCodec codec, Format format, MediaCodecInfo codecInfo,
MediaCodec codec,
Format format,
MediaCrypto crypto, MediaCrypto crypto,
float codecOperatingRate) throws DecoderQueryException; float codecOperatingRate)
throws DecoderQueryException;
protected final void maybeInitCodec() throws ExoPlaybackException { protected final void maybeInitCodec() throws ExoPlaybackException {
if (codec != null || format == null) { if (codec != null || format == null) {
...@@ -484,11 +504,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -484,11 +504,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
@Override @Override
protected void onOperatingRateChanged(float operatingRate) { public final void setOperatingRate(float operatingRate) throws ExoPlaybackException {
codecOperatingRate = operatingRate; rendererOperatingRate = operatingRate;
if (format != null) { updateCodecOperatingRate();
updateCodecOperatingRate(codec, format, operatingRate);
}
} }
@Override @Override
...@@ -537,6 +555,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -537,6 +555,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
codecReceivedEos = false; codecReceivedEos = false;
codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReconfigurationState = RECONFIGURATION_STATE_NONE;
codecReinitializationState = REINITIALIZATION_STATE_NONE; codecReinitializationState = REINITIALIZATION_STATE_NONE;
codecConfiguredWithOperatingRate = false;
if (codec != null) { if (codec != null) {
decoderCounters.decoderReleaseCount++; decoderCounters.decoderReleaseCount++;
try { try {
...@@ -729,13 +748,21 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -729,13 +748,21 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
long codecInitializedTimestamp; long codecInitializedTimestamp;
MediaCodec codec = null; MediaCodec codec = null;
String name = codecInfo.name; String name = codecInfo.name;
updateCodecOperatingRate();
boolean configureWithOperatingRate = codecOperatingRate > assumedMinimumCodecOperatingRate;
try { try {
codecInitializingTimestamp = SystemClock.elapsedRealtime(); codecInitializingTimestamp = SystemClock.elapsedRealtime();
TraceUtil.beginSection("createCodec:" + name); TraceUtil.beginSection("createCodec:" + name);
codec = MediaCodec.createByCodecName(name); codec = MediaCodec.createByCodecName(name);
TraceUtil.endSection(); TraceUtil.endSection();
TraceUtil.beginSection("configureCodec"); TraceUtil.beginSection("configureCodec");
configureCodec(codecInfo, codec, format, crypto, codecOperatingRate); configureCodec(
codecInfo,
codec,
format,
crypto,
configureWithOperatingRate ? codecOperatingRate : CODEC_OPERATING_RATE_UNSET);
codecConfiguredWithOperatingRate = configureWithOperatingRate;
TraceUtil.endSection(); TraceUtil.endSection();
TraceUtil.beginSection("startCodec"); TraceUtil.beginSection("startCodec");
codec.start(); codec.start();
...@@ -1028,30 +1055,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1028,30 +1055,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
if (!keepingCodec) { if (!keepingCodec) {
availableCodecInfos = null; reinitializeCodec();
if (codecReceivedBuffers) {
// Signal end of stream and wait for any final output buffers before re-initialization.
codecReinitializationState = REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM;
} else {
// There aren't any final output buffers, so perform re-initialization immediately.
releaseCodec();
maybeInitCodec();
}
} else { } else {
if (Util.SDK_INT >= 23) { updateCodecOperatingRate();
updateCodecOperatingRate(codec, format, codecOperatingRate);
} }
} }
}
/**
* Updates the {@link MediaCodec} operating rate.
* <p>
* The default implementation is a no-op.
*/
protected void updateCodecOperatingRate(MediaCodec codec, Format format, float codecOperatingRate) {
// Do nothing.
}
/** /**
* Called when the output format of the {@link MediaCodec} changes. * Called when the output format of the {@link MediaCodec} changes.
...@@ -1131,6 +1139,77 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1131,6 +1139,77 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
/** /**
* Returns the {@link MediaFormat#KEY_OPERATING_RATE} value for a given renderer operating rate,
* current format and set of possible stream formats.
*
* <p>The default implementation returns {@link #CODEC_OPERATING_RATE_UNSET}.
*
* @param operatingRate The renderer operating rate.
* @param format The format for which the codec is being configured.
* @param streamFormats The possible stream formats.
* @return The codec operating rate, or {@link #CODEC_OPERATING_RATE_UNSET} if no codec operating
* rate should be set.
*/
protected float getCodecOperatingRate(
float operatingRate, Format format, Format[] streamFormats) {
return CODEC_OPERATING_RATE_UNSET;
}
/**
* Updates the codec operating rate, and the codec itself if necessary.
*
* @throws ExoPlaybackException If an error occurs releasing or initializing a codec.
*/
private void updateCodecOperatingRate() throws ExoPlaybackException {
if (format == null || Util.SDK_INT < 23) {
return;
}
float codecOperatingRate =
getCodecOperatingRate(rendererOperatingRate, format, getStreamFormats());
if (this.codecOperatingRate == codecOperatingRate) {
return;
}
this.codecOperatingRate = codecOperatingRate;
if (codec == null || codecReinitializationState != REINITIALIZATION_STATE_NONE) {
// Either no codec, or it's about to be reinitialized anyway.
} else if (codecOperatingRate == CODEC_OPERATING_RATE_UNSET
&& codecConfiguredWithOperatingRate) {
// We need to clear the operating rate. The only way to do so is to instantiate a new codec
// instance. See [Internal ref: b/71987865].
reinitializeCodec();
} else if (codecOperatingRate != CODEC_OPERATING_RATE_UNSET
&& (codecConfiguredWithOperatingRate
|| codecOperatingRate > assumedMinimumCodecOperatingRate)) {
// We need to set the operating rate, either because we've set it previously or because it's
// above the assumed minimum rate.
Bundle codecParameters = new Bundle();
codecParameters.putFloat(MediaFormat.KEY_OPERATING_RATE, codecOperatingRate);
codec.setParameters(codecParameters);
codecConfiguredWithOperatingRate = true;
}
}
/**
* Starts the process of releasing the existing codec and initializing a new one. This may occur
* immediately, or be deferred until any final output buffers have been dequeued.
*
* @throws ExoPlaybackException If an error occurs releasing or initializing a codec.
*/
private void reinitializeCodec() throws ExoPlaybackException {
availableCodecInfos = null;
if (codecReceivedBuffers) {
// Signal end of stream and wait for any final output buffers before re-initialization.
codecReinitializationState = REINITIALIZATION_STATE_SIGNAL_END_OF_STREAM;
} else {
// There aren't any final output buffers, so perform re-initialization immediately.
releaseCodec();
maybeInitCodec();
}
}
/**
* @return Whether it may be possible to drain more output data. * @return Whether it may be possible to drain more output data.
* @throws ExoPlaybackException If an error occurs draining the output buffer. * @throws ExoPlaybackException If an error occurs draining the output buffer.
*/ */
......
...@@ -23,7 +23,6 @@ import android.media.MediaCodec; ...@@ -23,7 +23,6 @@ import android.media.MediaCodec;
import android.media.MediaCodecInfo.CodecCapabilities; import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCrypto; import android.media.MediaCrypto;
import android.media.MediaFormat; import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.annotation.CallSuper; import android.support.annotation.CallSuper;
...@@ -206,7 +205,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -206,7 +205,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, @Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
boolean playClearSamplesWithoutKeys, @Nullable Handler eventHandler, boolean playClearSamplesWithoutKeys, @Nullable Handler eventHandler,
@Nullable VideoRendererEventListener eventListener, int maxDroppedFramesToNotify) { @Nullable VideoRendererEventListener eventListener, int maxDroppedFramesToNotify) {
super(C.TRACK_TYPE_VIDEO, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys); super(
C.TRACK_TYPE_VIDEO,
mediaCodecSelector,
drmSessionManager,
playClearSamplesWithoutKeys,
/* assumedMinimumCodecOperatingRate= */ 30);
this.allowedJoiningTimeMs = allowedJoiningTimeMs; this.allowedJoiningTimeMs = allowedJoiningTimeMs;
this.maxDroppedFramesToNotify = maxDroppedFramesToNotify; this.maxDroppedFramesToNotify = maxDroppedFramesToNotify;
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
...@@ -446,14 +450,21 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -446,14 +450,21 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
} }
@Override @Override
protected void configureCodec(MediaCodecInfo codecInfo, protected void configureCodec(
MediaCodecInfo codecInfo,
MediaCodec codec, MediaCodec codec,
Format format, Format format,
MediaCrypto crypto, MediaCrypto crypto,
float codecOperatingRate) throws DecoderQueryException { float codecOperatingRate)
throws DecoderQueryException {
codecMaxValues = getCodecMaxValues(codecInfo, format, getStreamFormats()); codecMaxValues = getCodecMaxValues(codecInfo, format, getStreamFormats());
MediaFormat mediaFormat = getMediaFormat(format, codecMaxValues, deviceNeedsAutoFrcWorkaround, MediaFormat mediaFormat =
tunnelingAudioSessionId, codecOperatingRate); getMediaFormat(
format,
codecMaxValues,
codecOperatingRate,
deviceNeedsAutoFrcWorkaround,
tunnelingAudioSessionId);
if (surface == null) { if (surface == null) {
Assertions.checkState(shouldUseDummySurface(codecInfo)); Assertions.checkState(shouldUseDummySurface(codecInfo));
if (dummySurface == null) { if (dummySurface == null) {
...@@ -505,15 +516,19 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -505,15 +516,19 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
buffersInCodecCount = 0; buffersInCodecCount = 0;
} }
@TargetApi(23)
@Override @Override
protected void updateCodecOperatingRate(MediaCodec codec, Format format, float codecOperatingRate) { protected float getCodecOperatingRate(
if (format.frameRate == Format.NO_VALUE) { float operatingRate, Format format, Format[] streamFormats) {
return; // Use the highest known stream frame-rate up front, to avoid having to reconfigure the codec
// should an adaptive switch to that stream occur.
float maxFrameRate = -1;
for (Format streamFormat : streamFormats) {
float streamFrameRate = streamFormat.frameRate;
if (streamFrameRate != Format.NO_VALUE) {
maxFrameRate = Math.max(maxFrameRate, streamFrameRate);
}
} }
Bundle codecParameters = new Bundle(); return maxFrameRate == -1 ? CODEC_OPERATING_RATE_UNSET : (maxFrameRate * operatingRate);
codecParameters.putFloat(MediaFormat.KEY_OPERATING_RATE, format.frameRate * codecOperatingRate);
codec.setParameters(codecParameters);
} }
@Override @Override
...@@ -951,20 +966,21 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -951,20 +966,21 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
* *
* @param format The format of media. * @param format The format of media.
* @param codecMaxValues Codec max values that should be used when configuring the decoder. * @param codecMaxValues Codec max values that should be used when configuring the decoder.
* @param codecOperatingRate The codec operating rate, or {@link #CODEC_OPERATING_RATE_UNSET} if
* no codec operating rate should be set.
* @param deviceNeedsAutoFrcWorkaround Whether the device is known to enable frame-rate conversion * @param deviceNeedsAutoFrcWorkaround Whether the device is known to enable frame-rate conversion
* logic that negatively impacts ExoPlayer. * logic that negatively impacts ExoPlayer.
* @param tunnelingAudioSessionId The audio session id to use for tunneling, or {@link * @param tunnelingAudioSessionId The audio session id to use for tunneling, or {@link
* C#AUDIO_SESSION_ID_UNSET} if tunneling should not be enabled. * C#AUDIO_SESSION_ID_UNSET} if tunneling should not be enabled.
* @param codecOperatingRate
* @return The framework {@link MediaFormat} that should be used to configure the decoder. * @return The framework {@link MediaFormat} that should be used to configure the decoder.
*/ */
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
protected MediaFormat getMediaFormat( protected MediaFormat getMediaFormat(
Format format, Format format,
CodecMaxValues codecMaxValues, CodecMaxValues codecMaxValues,
float codecOperatingRate,
boolean deviceNeedsAutoFrcWorkaround, boolean deviceNeedsAutoFrcWorkaround,
int tunnelingAudioSessionId, int tunnelingAudioSessionId) {
float codecOperatingRate) {
MediaFormat mediaFormat = new MediaFormat(); MediaFormat mediaFormat = new MediaFormat();
// Set format parameters that should always be set. // Set format parameters that should always be set.
mediaFormat.setString(MediaFormat.KEY_MIME, format.sampleMimeType); mediaFormat.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
...@@ -983,8 +999,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -983,8 +999,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
// Set codec configuration values. // Set codec configuration values.
if (Util.SDK_INT >= 23) { if (Util.SDK_INT >= 23) {
mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 0 /* realtime priority */); mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 0 /* realtime priority */);
if (format.frameRate != Format.NO_VALUE) { if (codecOperatingRate != CODEC_OPERATING_RATE_UNSET) {
mediaFormat.setFloat(MediaFormat.KEY_OPERATING_RATE, codecOperatingRate * format.frameRate); mediaFormat.setFloat(MediaFormat.KEY_OPERATING_RATE, codecOperatingRate);
} }
} }
if (deviceNeedsAutoFrcWorkaround) { if (deviceNeedsAutoFrcWorkaround) {
......
...@@ -81,15 +81,20 @@ public class DebugRenderersFactory extends DefaultRenderersFactory { ...@@ -81,15 +81,20 @@ public class DebugRenderersFactory extends DefaultRenderersFactory {
} }
@Override @Override
protected void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format, protected void configureCodec(
MediaCrypto crypto, float codecOperatingRate) throws DecoderQueryException { MediaCodecInfo codecInfo,
MediaCodec codec,
Format format,
MediaCrypto crypto,
float operatingRate)
throws DecoderQueryException {
// If the codec is being initialized whilst the renderer is started, default behavior is to // If the codec is being initialized whilst the renderer is started, default behavior is to
// render the first frame (i.e. the keyframe before the current position), then drop frames up // render the first frame (i.e. the keyframe before the current position), then drop frames up
// to the current playback position. For test runs that place a maximum limit on the number of // to the current playback position. For test runs that place a maximum limit on the number of
// dropped frames allowed, this is not desired behavior. Hence we skip (rather than drop) // dropped frames allowed, this is not desired behavior. Hence we skip (rather than drop)
// frames up to the current playback position [Internal: b/66494991]. // frames up to the current playback position [Internal: b/66494991].
skipToPositionBeforeRenderingFirstFrame = getState() == Renderer.STATE_STARTED; skipToPositionBeforeRenderingFirstFrame = getState() == Renderer.STATE_STARTED;
super.configureCodec(codecInfo, codec, format, crypto, codecOperatingRate); super.configureCodec(codecInfo, codec, format, crypto, operatingRate);
} }
@Override @Override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment