mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Refine use of KEY_OPERATING_RATE
This commit is contained in:
parent
f4219b55e6
commit
badd9356f8
8 changed files with 216 additions and 99 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -136,11 +136,6 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
|
||||||
onPositionReset(positionUs, false);
|
onPositionReset(positionUs, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void setOperatingRate(float operatingRate) {
|
|
||||||
onOperatingRateChanged(operatingRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void stop() throws ExoPlaybackException {
|
public final void stop() throws ExoPlaybackException {
|
||||||
Assertions.checkState(state == STATE_STARTED);
|
Assertions.checkState(state == STATE_STARTED);
|
||||||
|
|
@ -221,17 +216,6 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
@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;
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
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 {
|
||||||
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
|
||||||
@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
|
||||||
}
|
}
|
||||||
|
|
||||||
@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.
|
||||||
|
|
@ -350,6 +360,14 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected float getCodecOperatingRate(
|
||||||
|
float operatingRate, Format format, Format[] streamFormats) {
|
||||||
|
return format.sampleRate == Format.NO_VALUE
|
||||||
|
? CODEC_OPERATING_RATE_UNSET
|
||||||
|
: (format.sampleRate * operatingRate);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCodecInitialized(String name, long initializedTimestampMs,
|
protected void onCodecInitialized(String name, long initializedTimestampMs,
|
||||||
long initializationDurationMs) {
|
long initializationDurationMs) {
|
||||||
|
|
@ -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
|
||||||
// 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;
|
||||||
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 {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 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 {
|
||||||
@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 {
|
||||||
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 {
|
||||||
* 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 {
|
||||||
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 {
|
||||||
* @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,
|
||||||
MediaCrypto crypto,
|
MediaCodec codec,
|
||||||
float codecOperatingRate) throws DecoderQueryException;
|
Format format,
|
||||||
|
MediaCrypto crypto,
|
||||||
|
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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 {
|
||||||
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 {
|
||||||
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,31 +1055,12 @@ 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.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
@ -1130,6 +1138,77 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
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 {
|
||||||
@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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configureCodec(MediaCodecInfo codecInfo,
|
protected void configureCodec(
|
||||||
MediaCodec codec,
|
MediaCodecInfo codecInfo,
|
||||||
Format format,
|
MediaCodec codec,
|
||||||
MediaCrypto crypto,
|
Format format,
|
||||||
float codecOperatingRate) throws DecoderQueryException {
|
MediaCrypto crypto,
|
||||||
|
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 {
|
||||||
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 {
|
||||||
*
|
*
|
||||||
* @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 {
|
||||||
// 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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue