diff --git a/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java index cb917b9b79..d3a5726585 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java @@ -136,6 +136,11 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { onPositionReset(positionUs, false); } + @Override + public final void setOperatingRate(float operatingRate) { + onOperatingRateChanged(operatingRate); + } + @Override public final void stop() throws ExoPlaybackException { Assertions.checkState(state == STATE_STARTED); @@ -216,6 +221,17 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { // Do nothing. } + /** + * Called when the operating rate is changed. + *
+ * 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. *
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 0915dc9721..61e9524fd0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -277,6 +277,7 @@ import java.util.Collections; public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { eventHandler.obtainMessage(MSG_PLAYBACK_PARAMETERS_CHANGED, playbackParameters).sendToTarget(); updateTrackSelectionPlaybackSpeed(playbackParameters.speed); + updateRendererOperatingRate(playbackParameters.speed); } // Handler.Callback implementation. @@ -1099,6 +1100,14 @@ import java.util.Collections; } } + private void updateRendererOperatingRate(float operatingRate) { + for (Renderer renderer : renderers) { + if (renderer != null) { + renderer.setOperatingRate(operatingRate); + } + } + } + private boolean shouldTransitionToReadyState(boolean renderersReadyOrEnded) { if (enabledRenderers.length == 0) { // If there are no enabled renderers, determine whether we're ready based on the timeline. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Renderer.java b/library/core/src/main/java/com/google/android/exoplayer2/Renderer.java index e53db4568d..f873eb5c7c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Renderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Renderer.java @@ -192,6 +192,13 @@ public interface Renderer extends PlayerMessage.Target { */ void resetPosition(long positionUs) throws ExoPlaybackException; + /** + * Sets the operating rate of this renderer. + * + * @param operatingRate The renderer operating rate. + */ + void setOperatingRate(float operatingRate); + /** * Incrementally renders the {@link SampleStream}. *
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
index ba5cbe70b3..497048114d 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
@@ -317,12 +317,12 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Override
protected void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format,
- MediaCrypto crypto) {
+ MediaCrypto crypto, float codecOperatingRate) {
codecMaxInputSize = getCodecMaxInputSize(codecInfo, format, getStreamFormats());
codecNeedsDiscardChannelsWorkaround = codecNeedsDiscardChannelsWorkaround(codecInfo.name);
passthroughEnabled = codecInfo.passthrough;
String codecMimeType = codecInfo.mimeType == null ? MimeTypes.AUDIO_RAW : codecInfo.mimeType;
- MediaFormat mediaFormat = getMediaFormat(format, codecMimeType, codecMaxInputSize);
+ MediaFormat mediaFormat = getMediaFormat(format, codecMimeType, codecMaxInputSize, codecOperatingRate);
codec.configure(mediaFormat, /* surface= */ null, crypto, /* flags= */ 0);
if (passthroughEnabled) {
// Store the input MIME type if we're using the passthrough codec.
@@ -633,10 +633,12 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
* @param format The format of the media.
* @param codecMimeType The MIME type handled by the codec.
* @param codecMaxInputSize The maximum input size supported by the codec.
+ * @param codecOperatingRate
* @return The framework media format.
*/
@SuppressLint("InlinedApi")
- protected MediaFormat getMediaFormat(Format format, String codecMimeType, int codecMaxInputSize) {
+ protected MediaFormat getMediaFormat(Format format, String codecMimeType, int codecMaxInputSize,
+ float codecOperatingRate) {
MediaFormat mediaFormat = new MediaFormat();
// Set format parameters that should always be set.
mediaFormat.setString(MediaFormat.KEY_MIME, codecMimeType);
@@ -648,6 +650,10 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
// Set codec configuration values.
if (Util.SDK_INT >= 23) {
mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 0 /* realtime priority */);
+ if (format.sampleRate != Format.NO_VALUE) {
+ mediaFormat.setFloat(
+ MediaFormat.KEY_OPERATING_RATE, codecOperatingRate * format.sampleRate);
+ }
}
return mediaFormat;
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
index 36e9a67153..c4c29a451c 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
@@ -274,6 +274,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private DrmSession
+ * 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.
*
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
index e1b4d8203d..f9a10fa275 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
@@ -23,6 +23,7 @@ import android.media.MediaCodec;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCrypto;
import android.media.MediaFormat;
+import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.support.annotation.CallSuper;
@@ -445,11 +446,14 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
@Override
- protected void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format,
- MediaCrypto crypto) throws DecoderQueryException {
+ protected void configureCodec(MediaCodecInfo codecInfo,
+ MediaCodec codec,
+ Format format,
+ MediaCrypto crypto,
+ float codecOperatingRate) throws DecoderQueryException {
codecMaxValues = getCodecMaxValues(codecInfo, format, getStreamFormats());
MediaFormat mediaFormat = getMediaFormat(format, codecMaxValues, deviceNeedsAutoFrcWorkaround,
- tunnelingAudioSessionId);
+ tunnelingAudioSessionId, codecOperatingRate);
if (surface == null) {
Assertions.checkState(shouldUseDummySurface(codecInfo));
if (dummySurface == null) {
@@ -501,6 +505,17 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
buffersInCodecCount = 0;
}
+ @TargetApi(23)
+ @Override
+ protected void updateCodecOperatingRate(MediaCodec codec, Format format, float codecOperatingRate) {
+ if (format.frameRate == Format.NO_VALUE) {
+ return;
+ }
+ Bundle codecParameters = new Bundle();
+ codecParameters.putFloat(MediaFormat.KEY_OPERATING_RATE, format.frameRate * codecOperatingRate);
+ codec.setParameters(codecParameters);
+ }
+
@Override
protected void onCodecInitialized(String name, long initializedTimestampMs,
long initializationDurationMs) {
@@ -940,6 +955,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
* logic that negatively impacts ExoPlayer.
* @param tunnelingAudioSessionId The audio session id to use for tunneling, or {@link
* 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.
*/
@SuppressLint("InlinedApi")
@@ -947,7 +963,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
Format format,
CodecMaxValues codecMaxValues,
boolean deviceNeedsAutoFrcWorkaround,
- int tunnelingAudioSessionId) {
+ int tunnelingAudioSessionId,
+ float codecOperatingRate) {
MediaFormat mediaFormat = new MediaFormat();
// Set format parameters that should always be set.
mediaFormat.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
@@ -966,6 +983,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
// Set codec configuration values.
if (Util.SDK_INT >= 23) {
mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 0 /* realtime priority */);
+ if (format.frameRate != Format.NO_VALUE) {
+ mediaFormat.setFloat(MediaFormat.KEY_OPERATING_RATE, codecOperatingRate * format.frameRate);
+ }
}
if (deviceNeedsAutoFrcWorkaround) {
mediaFormat.setInteger("auto-frc", 0);
diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/DebugRenderersFactory.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/DebugRenderersFactory.java
index 4bbfef6bb8..fa3b42fec7 100644
--- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/DebugRenderersFactory.java
+++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/DebugRenderersFactory.java
@@ -82,14 +82,14 @@ public class DebugRenderersFactory extends DefaultRenderersFactory {
@Override
protected void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format,
- MediaCrypto crypto) throws DecoderQueryException {
+ MediaCrypto crypto, float codecOperatingRate) throws DecoderQueryException {
// 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
// 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)
// frames up to the current playback position [Internal: b/66494991].
skipToPositionBeforeRenderingFirstFrame = getState() == Renderer.STATE_STARTED;
- super.configureCodec(codecInfo, codec, format, crypto);
+ super.configureCodec(codecInfo, codec, format, crypto, codecOperatingRate);
}
@Override