From b3b878daa8df9d50db5342d4d138a2d234dcbdb7 Mon Sep 17 00:00:00 2001 From: Jinming he Date: Sat, 7 Apr 2018 11:07:26 +0800 Subject: [PATCH 1/2] Adjust MediaCodec operating rate according to playback rate. --- .../android/exoplayer2/BaseRenderer.java | 16 +++++++++++++ .../exoplayer2/ExoPlayerImplInternal.java | 9 +++++++ .../google/android/exoplayer2/Renderer.java | 7 ++++++ .../audio/MediaCodecAudioRenderer.java | 19 +++++++++++++++ .../mediacodec/MediaCodecRenderer.java | 24 +++++++++++++++++++ .../video/MediaCodecVideoRenderer.java | 18 ++++++++++++++ 6 files changed, 93 insertions(+) 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..f6e557e652 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 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 a937aa1185..cb51baa338 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 @@ -273,6 +273,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. @@ -1075,6 +1076,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 c73081e2ab..092fc92a5e 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 @@ -21,6 +21,7 @@ import android.media.MediaCodec; import android.media.MediaCrypto; import android.media.MediaFormat; import android.media.audiofx.Virtualizer; +import android.os.Bundle; import android.os.Handler; import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; @@ -286,6 +287,19 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media return this; } + @TargetApi(23) + @Override + protected void updateCodecOperatingRate(Format format) { + if (format.sampleRate == Format.NO_VALUE) { + return; + } + MediaCodec codec = getCodec(); + float codecOperatingRate = getCodecOperatingRate(); + Bundle codecParameters = new Bundle(); + codecParameters.putFloat(MediaFormat.KEY_OPERATING_RATE, format.sampleRate * codecOperatingRate); + codec.setParameters(codecParameters); + } + @Override protected void onCodecInitialized(String name, long initializedTimestampMs, long initializationDurationMs) { @@ -550,6 +564,11 @@ 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) { + float codecOperatingRate = getCodecOperatingRate(); + 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 dce3f72302..8398c99fbd 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 @@ -228,6 +228,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private DrmSession pendingDrmSession; private MediaCodec codec; private MediaCodecInfo codecInfo; + private float codecOperatingRate = 1; private @AdaptationWorkaroundMode int codecAdaptationWorkaroundMode; private boolean codecNeedsDiscardToSpsWorkaround; private boolean codecNeedsFlushWorkaround; @@ -450,6 +451,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { return codecInfo; } + protected final float getCodecOperatingRate() { return codecOperatingRate; } + @Override protected void onEnabled(boolean joining) throws ExoPlaybackException { decoderCounters = new DecoderCounters(); @@ -464,6 +467,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } } + @Override + protected void onOperatingRateChanged(float operatingRate) { + codecOperatingRate = operatingRate; + if (codec != null && format != null) { + updateCodecOperatingRate(format); + } + } + @Override protected void onDisabled() { format = null; @@ -916,9 +927,22 @@ public abstract class MediaCodecRenderer extends BaseRenderer { releaseCodec(); maybeInitCodec(); } + } else { + if (Util.SDK_INT >= 23) { + updateCodecOperatingRate(format); + } } } + /** + * Updates the {@link MediaCodec} operating rate. + *

+ * The default implementation is a no-op. + */ + protected void updateCodecOperatingRate(Format format) { + // 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 979088f421..58d44dbd34 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; @@ -471,6 +472,19 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { buffersInCodecCount = 0; } + @TargetApi(23) + @Override + protected void updateCodecOperatingRate(Format format) { + if (format.frameRate == Format.NO_VALUE) { + return; + } + MediaCodec codec = getCodec(); + float codecOperatingRate = getCodecOperatingRate(); + 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) { @@ -969,6 +983,10 @@ 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) { + float codecOperatingRate = getCodecOperatingRate(); + mediaFormat.setFloat(MediaFormat.KEY_OPERATING_RATE, codecOperatingRate * format.frameRate); + } } if (deviceNeedsAutoFrcWorkaround) { mediaFormat.setInteger("auto-frc", 0); From 5cdb886db160e902f9bec6e2398281211302b562 Mon Sep 17 00:00:00 2001 From: Jinming he Date: Mon, 16 Jul 2018 21:36:00 +0800 Subject: [PATCH 2/2] Address comments. --- .../android/exoplayer2/BaseRenderer.java | 2 +- .../audio/MediaCodecAudioRenderer.java | 23 ++++--------------- .../mediacodec/MediaCodecRenderer.java | 21 +++++++++-------- .../video/MediaCodecVideoRenderer.java | 18 ++++++++------- .../testutil/DebugRenderersFactory.java | 4 ++-- 5 files changed, 29 insertions(+), 39 deletions(-) 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 f6e557e652..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 @@ -137,7 +137,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { } @Override - public void setOperatingRate(float operatingRate) { + public final void setOperatingRate(float operatingRate) { onOperatingRateChanged(operatingRate); } 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 a1b79e0895..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 @@ -23,7 +23,6 @@ import android.media.MediaCodec; import android.media.MediaCrypto; import android.media.MediaFormat; import android.media.audiofx.Virtualizer; -import android.os.Bundle; import android.os.Handler; import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; @@ -318,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. @@ -351,19 +350,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media return this; } - @TargetApi(23) - @Override - protected void updateCodecOperatingRate(Format format) { - if (format.sampleRate == Format.NO_VALUE) { - return; - } - MediaCodec codec = getCodec(); - float codecOperatingRate = getCodecOperatingRate(); - Bundle codecParameters = new Bundle(); - codecParameters.putFloat(MediaFormat.KEY_OPERATING_RATE, format.sampleRate * codecOperatingRate); - codec.setParameters(codecParameters); - } - @Override protected void onCodecInitialized(String name, long initializedTimestampMs, long initializationDurationMs) { @@ -647,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); @@ -663,7 +651,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media if (Util.SDK_INT >= 23) { mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 0 /* realtime priority */); if (format.sampleRate != Format.NO_VALUE) { - float codecOperatingRate = getCodecOperatingRate(); mediaFormat.setFloat( MediaFormat.KEY_OPERATING_RATE, codecOperatingRate * format.sampleRate); } 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 665b8b6bc4..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,7 +274,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private DrmSession drmSession; private DrmSession pendingDrmSession; private MediaCodec codec; - private float codecOperatingRate = 1; + private float codecOperatingRate = 1.0f; private @Nullable ArrayDeque availableCodecInfos; private @Nullable DecoderInitializationException preferredDecoderInitializationException; private @Nullable MediaCodecInfo codecInfo; @@ -386,10 +386,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer { * @param codec The {@link MediaCodec} to configure. * @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 codecOperatingRate The {@link MediaFormat#KEY_OPERATING_RATE} to use for configuration. * @throws DecoderQueryException If an error occurs querying {@code codecInfo}. */ - protected abstract void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format, - MediaCrypto crypto) throws DecoderQueryException; + protected abstract void configureCodec(MediaCodecInfo codecInfo, + MediaCodec codec, Format format, + MediaCrypto crypto, + float codecOperatingRate) throws DecoderQueryException; protected final void maybeInitCodec() throws ExoPlaybackException { if (codec != null || format == null) { @@ -466,8 +469,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { return codecInfo; } - protected final float getCodecOperatingRate() { return codecOperatingRate; } - @Override protected void onEnabled(boolean joining) throws ExoPlaybackException { decoderCounters = new DecoderCounters(); @@ -485,8 +486,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Override protected void onOperatingRateChanged(float operatingRate) { codecOperatingRate = operatingRate; - if (codec != null && format != null) { - updateCodecOperatingRate(format); + if (format != null) { + updateCodecOperatingRate(codec, format, operatingRate); } } @@ -734,7 +735,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { codec = MediaCodec.createByCodecName(name); TraceUtil.endSection(); TraceUtil.beginSection("configureCodec"); - configureCodec(codecInfo, codec, format, crypto); + configureCodec(codecInfo, codec, format, crypto, codecOperatingRate); TraceUtil.endSection(); TraceUtil.beginSection("startCodec"); codec.start(); @@ -1038,7 +1039,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { } } else { if (Util.SDK_INT >= 23) { - updateCodecOperatingRate(format); + updateCodecOperatingRate(codec, format, codecOperatingRate); } } } @@ -1048,7 +1049,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { *

* The default implementation is a no-op. */ - protected void updateCodecOperatingRate(Format format) { + protected void updateCodecOperatingRate(MediaCodec codec, Format format, float codecOperatingRate) { // Do nothing. } 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 28f2810e70..80c2a0aaf4 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 @@ -443,11 +443,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,12 +504,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { @TargetApi(23) @Override - protected void updateCodecOperatingRate(Format format) { + protected void updateCodecOperatingRate(MediaCodec codec, Format format, float codecOperatingRate) { if (format.frameRate == Format.NO_VALUE) { return; } - MediaCodec codec = getCodec(); - float codecOperatingRate = getCodecOperatingRate(); Bundle codecParameters = new Bundle(); codecParameters.putFloat(MediaFormat.KEY_OPERATING_RATE, format.frameRate * codecOperatingRate); codec.setParameters(codecParameters); @@ -951,6 +952,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") @@ -958,7 +960,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); @@ -978,7 +981,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { if (Util.SDK_INT >= 23) { mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 0 /* realtime priority */); if (format.frameRate != Format.NO_VALUE) { - float codecOperatingRate = getCodecOperatingRate(); mediaFormat.setFloat(MediaFormat.KEY_OPERATING_RATE, codecOperatingRate * format.frameRate); } } 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