Commit 71fd335b by olly Committed by kim-vde

Simplify output format propagation

PiperOrigin-RevId: 324805335
parent 4d03d308
...@@ -2188,11 +2188,9 @@ public class SimpleExoPlayer extends BasePlayer ...@@ -2188,11 +2188,9 @@ public class SimpleExoPlayer extends BasePlayer
} }
@Override @Override
public void onVideoFrameProcessingOffset( public void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) {
long totalProcessingOffsetUs, int frameCount, Format format) {
for (VideoRendererEventListener videoDebugListener : videoDebugListeners) { for (VideoRendererEventListener videoDebugListener : videoDebugListeners) {
videoDebugListener.onVideoFrameProcessingOffset( videoDebugListener.onVideoFrameProcessingOffset(totalProcessingOffsetUs, frameCount);
totalProcessingOffsetUs, frameCount, format);
} }
} }
......
...@@ -319,11 +319,10 @@ public class AnalyticsCollector ...@@ -319,11 +319,10 @@ public class AnalyticsCollector
} }
@Override @Override
public final void onVideoFrameProcessingOffset( public final void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) {
long totalProcessingOffsetUs, int frameCount, Format format) {
EventTime eventTime = generatePlayingMediaPeriodEventTime(); EventTime eventTime = generatePlayingMediaPeriodEventTime();
for (AnalyticsListener listener : listeners) { for (AnalyticsListener listener : listeners) {
listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount, format); listener.onVideoFrameProcessingOffset(eventTime, totalProcessingOffsetUs, frameCount);
} }
} }
......
...@@ -591,10 +591,9 @@ public interface AnalyticsListener { ...@@ -591,10 +591,9 @@ public interface AnalyticsListener {
* @param totalProcessingOffsetUs The sum of the video frame processing offsets for frames * @param totalProcessingOffsetUs The sum of the video frame processing offsets for frames
* rendered since the last call to this method. * rendered since the last call to this method.
* @param frameCount The number to samples included in {@code totalProcessingOffsetUs}. * @param frameCount The number to samples included in {@code totalProcessingOffsetUs}.
* @param format The video {@link Format} being rendered.
*/ */
default void onVideoFrameProcessingOffset( default void onVideoFrameProcessingOffset(
EventTime eventTime, long totalProcessingOffsetUs, int frameCount, Format format) {} EventTime eventTime, long totalProcessingOffsetUs, int frameCount) {}
/** /**
* Called when a frame is rendered for the first time since setting the surface, or since the * Called when a frame is rendered for the first time since setting the surface, or since the
......
...@@ -91,7 +91,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -91,7 +91,6 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
private boolean codecNeedsDiscardChannelsWorkaround; private boolean codecNeedsDiscardChannelsWorkaround;
private boolean codecNeedsEosBufferTimestampWorkaround; private boolean codecNeedsEosBufferTimestampWorkaround;
@Nullable private Format codecPassthroughFormat; @Nullable private Format codecPassthroughFormat;
@Nullable private Format inputFormat;
private long currentPositionUs; private long currentPositionUs;
private boolean allowFirstBufferPositionDiscontinuity; private boolean allowFirstBufferPositionDiscontinuity;
private boolean allowPositionDiscontinuity; private boolean allowPositionDiscontinuity;
...@@ -379,29 +378,23 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -379,29 +378,23 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Override @Override
protected void onInputFormatChanged(FormatHolder formatHolder) throws ExoPlaybackException { protected void onInputFormatChanged(FormatHolder formatHolder) throws ExoPlaybackException {
super.onInputFormatChanged(formatHolder); super.onInputFormatChanged(formatHolder);
inputFormat = formatHolder.format; eventDispatcher.inputFormatChanged(formatHolder.format);
eventDispatcher.inputFormatChanged(inputFormat);
} }
@Override @Override
protected void onOutputFormatChanged(Format outputFormat) throws ExoPlaybackException { protected void onOutputFormatChanged(Format format, @Nullable MediaFormat mediaFormat)
configureOutput(outputFormat); throws ExoPlaybackException {
}
@Override
protected void configureOutput(Format outputFormat) throws ExoPlaybackException {
Format audioSinkInputFormat; Format audioSinkInputFormat;
@Nullable int[] channelMap = null; @Nullable int[] channelMap = null;
if (codecPassthroughFormat != null) { // Raw codec passthrough if (codecPassthroughFormat != null) { // Raw codec passthrough
audioSinkInputFormat = codecPassthroughFormat; audioSinkInputFormat = codecPassthroughFormat;
} else if (getCodec() == null) { // Codec bypass passthrough } else if (getCodec() == null) { // Codec bypass passthrough
audioSinkInputFormat = outputFormat; audioSinkInputFormat = format;
} else { } else {
MediaFormat mediaFormat = getCodec().getOutputFormat();
@C.PcmEncoding int pcmEncoding; @C.PcmEncoding int pcmEncoding;
if (MimeTypes.AUDIO_RAW.equals(outputFormat.sampleMimeType)) { if (MimeTypes.AUDIO_RAW.equals(format.sampleMimeType)) {
// For PCM streams, the encoder passes through int samples despite set to float mode. // For PCM streams, the encoder passes through int samples despite set to float mode.
pcmEncoding = outputFormat.pcmEncoding; pcmEncoding = format.pcmEncoding;
} else if (Util.SDK_INT >= 24 && mediaFormat.containsKey(MediaFormat.KEY_PCM_ENCODING)) { } else if (Util.SDK_INT >= 24 && mediaFormat.containsKey(MediaFormat.KEY_PCM_ENCODING)) {
pcmEncoding = mediaFormat.getInteger(MediaFormat.KEY_PCM_ENCODING); pcmEncoding = mediaFormat.getInteger(MediaFormat.KEY_PCM_ENCODING);
} else if (mediaFormat.containsKey(VIVO_BITS_PER_SAMPLE_KEY)) { } else if (mediaFormat.containsKey(VIVO_BITS_PER_SAMPLE_KEY)) {
...@@ -409,22 +402,25 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -409,22 +402,25 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
} else { } else {
// If the format is anything other than PCM then we assume that the audio decoder will // If the format is anything other than PCM then we assume that the audio decoder will
// output 16-bit PCM. // output 16-bit PCM.
pcmEncoding = C.ENCODING_PCM_16BIT; pcmEncoding =
MimeTypes.AUDIO_RAW.equals(format.sampleMimeType)
? format.pcmEncoding
: C.ENCODING_PCM_16BIT;
} }
audioSinkInputFormat = audioSinkInputFormat =
new Format.Builder() new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_RAW) .setSampleMimeType(MimeTypes.AUDIO_RAW)
.setPcmEncoding(pcmEncoding) .setPcmEncoding(pcmEncoding)
.setEncoderDelay(outputFormat.encoderDelay) .setEncoderDelay(format.encoderDelay)
.setEncoderPadding(outputFormat.encoderPadding) .setEncoderPadding(format.encoderPadding)
.setChannelCount(mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)) .setChannelCount(mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT))
.setSampleRate(mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE)) .setSampleRate(mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE))
.build(); .build();
if (codecNeedsDiscardChannelsWorkaround if (codecNeedsDiscardChannelsWorkaround
&& audioSinkInputFormat.channelCount == 6 && audioSinkInputFormat.channelCount == 6
&& outputFormat.channelCount < 6) { && format.channelCount < 6) {
channelMap = new int[outputFormat.channelCount]; channelMap = new int[format.channelCount];
for (int i = 0; i < outputFormat.channelCount; i++) { for (int i = 0; i < format.channelCount; i++) {
channelMap[i] = i; channelMap[i] = i;
} }
} }
...@@ -432,7 +428,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -432,7 +428,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
try { try {
audioSink.configure(audioSinkInputFormat, /* specifiedBufferSize= */ 0, channelMap); audioSink.configure(audioSinkInputFormat, /* specifiedBufferSize= */ 0, channelMap);
} catch (AudioSink.ConfigurationException e) { } catch (AudioSink.ConfigurationException e) {
throw createRendererException(e, outputFormat); throw createRendererException(e, format);
} }
} }
...@@ -621,8 +617,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -621,8 +617,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
try { try {
audioSink.playToEndOfStream(); audioSink.playToEndOfStream();
} catch (AudioSink.WriteException e) { } catch (AudioSink.WriteException e) {
Format outputFormat = getCurrentOutputFormat(); @Nullable Format outputFormat = getOutputFormat();
throw createRendererException(e, outputFormat != null ? outputFormat : inputFormat); throw createRendererException(e, outputFormat != null ? outputFormat : getInputFormat());
} }
} }
......
...@@ -364,7 +364,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -364,7 +364,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private float operatingRate; private float operatingRate;
@Nullable private MediaCodec codec; @Nullable private MediaCodec codec;
@Nullable private MediaCodecAdapter codecAdapter; @Nullable private MediaCodecAdapter codecAdapter;
@Nullable private Format codecFormat; @Nullable private Format codecInputFormat;
@Nullable private MediaFormat codecOutputMediaFormat;
private boolean codecOutputMediaFormatChanged;
private float codecOperatingRate; private float codecOperatingRate;
@Nullable private ArrayDeque<MediaCodecInfo> availableCodecInfos; @Nullable private ArrayDeque<MediaCodecInfo> availableCodecInfos;
@Nullable private DecoderInitializationException preferredDecoderInitializationException; @Nullable private DecoderInitializationException preferredDecoderInitializationException;
...@@ -409,7 +411,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -409,7 +411,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
protected DecoderCounters decoderCounters; protected DecoderCounters decoderCounters;
private long outputStreamOffsetUs; private long outputStreamOffsetUs;
private int pendingOutputStreamOffsetCount; private int pendingOutputStreamOffsetCount;
private boolean receivedOutputMediaFormatChange;
/** /**
* @param trackType The track type that the renderer handles. One of the {@code C.TRACK_TYPE_*} * @param trackType The track type that the renderer handles. One of the {@code C.TRACK_TYPE_*}
...@@ -613,35 +614,40 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -613,35 +614,40 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* *
* @param exception The exception. * @param exception The exception.
*/ */
protected void setPendingPlaybackException(ExoPlaybackException exception) { protected final void setPendingPlaybackException(ExoPlaybackException exception) {
pendingPlaybackException = exception; pendingPlaybackException = exception;
} }
/** /**
* Polls the pending output format queue for a given buffer timestamp. If a format is present, it * Updates the output formats for the specified output buffer timestamp, calling {@link
* is removed and returned. Otherwise returns {@code null}. Subclasses should only call this * #onOutputFormatChanged} if a change has occurred.
* method if they are taking over responsibility for output format propagation (e.g., when using *
* video tunneling). * <p>Subclasses should only call this method if operating in a mode where buffers are not
* dequeued from the decoder, for example when using video tunneling).
* *
* @throws ExoPlaybackException Thrown if an error occurs as a result of the output format change. * @throws ExoPlaybackException Thrown if an error occurs as a result of the output format change.
*/ */
protected final void updateOutputFormatForTime(long presentationTimeUs) protected final void updateOutputFormatForTime(long presentationTimeUs)
throws ExoPlaybackException { throws ExoPlaybackException {
boolean outputFormatChanged = false;
@Nullable Format format = formatQueue.pollFloor(presentationTimeUs); @Nullable Format format = formatQueue.pollFloor(presentationTimeUs);
if (format != null) { if (format != null) {
outputFormat = format; outputFormat = format;
onOutputFormatChanged(outputFormat); outputFormatChanged = true;
} else if (receivedOutputMediaFormatChange && outputFormat != null) { }
// No Format change with the MediaFormat change, so we need to update based on the existing if (outputFormatChanged || (codecOutputMediaFormatChanged && outputFormat != null)) {
// Format. onOutputFormatChanged(outputFormat, codecOutputMediaFormat);
configureOutput(outputFormat); codecOutputMediaFormatChanged = false;
} }
}
receivedOutputMediaFormatChange = false; @Nullable
protected Format getInputFormat() {
return inputFormat;
} }
@Nullable @Nullable
protected final Format getCurrentOutputFormat() { protected final Format getOutputFormat() {
return outputFormat; return outputFormat;
} }
...@@ -651,6 +657,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -651,6 +657,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
@Nullable @Nullable
protected final MediaFormat getCodecOutputMediaFormat() {
return codecOutputMediaFormat;
}
@Nullable
protected final MediaCodecInfo getCodecInfo() { protected final MediaCodecInfo getCodecInfo() {
return codecInfo; return codecInfo;
} }
...@@ -905,11 +916,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -905,11 +916,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
protected void resetCodecStateForRelease() { protected void resetCodecStateForRelease() {
resetCodecStateForFlush(); resetCodecStateForFlush();
pendingPlaybackException = null;
c2Mp3TimestampTracker = null;
availableCodecInfos = null; availableCodecInfos = null;
codecInfo = null; codecInfo = null;
codecFormat = null; codecInputFormat = null;
codecOutputMediaFormat = null;
codecOutputMediaFormatChanged = false;
codecHasOutputMediaFormat = false; codecHasOutputMediaFormat = false;
pendingPlaybackException = null;
codecOperatingRate = CODEC_OPERATING_RATE_UNSET; codecOperatingRate = CODEC_OPERATING_RATE_UNSET;
codecAdaptationWorkaroundMode = ADAPTATION_WORKAROUND_MODE_NEVER; codecAdaptationWorkaroundMode = ADAPTATION_WORKAROUND_MODE_NEVER;
codecNeedsReconfigureWorkaround = false; codecNeedsReconfigureWorkaround = false;
...@@ -920,7 +934,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -920,7 +934,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
codecNeedsEosOutputExceptionWorkaround = false; codecNeedsEosOutputExceptionWorkaround = false;
codecNeedsMonoChannelCountWorkaround = false; codecNeedsMonoChannelCountWorkaround = false;
codecNeedsEosPropagation = false; codecNeedsEosPropagation = false;
c2Mp3TimestampTracker = null;
codecReconfigured = false; codecReconfigured = false;
codecReconfigurationState = RECONFIGURATION_STATE_NONE; codecReconfigurationState = RECONFIGURATION_STATE_NONE;
resetCodecBuffers(); resetCodecBuffers();
...@@ -1110,16 +1123,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1110,16 +1123,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
this.codecAdapter = codecAdapter; this.codecAdapter = codecAdapter;
this.codecInfo = codecInfo; this.codecInfo = codecInfo;
this.codecOperatingRate = codecOperatingRate; this.codecOperatingRate = codecOperatingRate;
codecFormat = inputFormat; codecInputFormat = inputFormat;
codecAdaptationWorkaroundMode = codecAdaptationWorkaroundMode(codecName); codecAdaptationWorkaroundMode = codecAdaptationWorkaroundMode(codecName);
codecNeedsReconfigureWorkaround = codecNeedsReconfigureWorkaround(codecName); codecNeedsReconfigureWorkaround = codecNeedsReconfigureWorkaround(codecName);
codecNeedsDiscardToSpsWorkaround = codecNeedsDiscardToSpsWorkaround(codecName, codecFormat); codecNeedsDiscardToSpsWorkaround =
codecNeedsDiscardToSpsWorkaround(codecName, codecInputFormat);
codecNeedsFlushWorkaround = codecNeedsFlushWorkaround(codecName); codecNeedsFlushWorkaround = codecNeedsFlushWorkaround(codecName);
codecNeedsSosFlushWorkaround = codecNeedsSosFlushWorkaround(codecName); codecNeedsSosFlushWorkaround = codecNeedsSosFlushWorkaround(codecName);
codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName); codecNeedsEosFlushWorkaround = codecNeedsEosFlushWorkaround(codecName);
codecNeedsEosOutputExceptionWorkaround = codecNeedsEosOutputExceptionWorkaround(codecName); codecNeedsEosOutputExceptionWorkaround = codecNeedsEosOutputExceptionWorkaround(codecName);
codecNeedsMonoChannelCountWorkaround = codecNeedsMonoChannelCountWorkaround =
codecNeedsMonoChannelCountWorkaround(codecName, codecFormat); codecNeedsMonoChannelCountWorkaround(codecName, codecInputFormat);
codecNeedsEosPropagation = codecNeedsEosPropagation =
codecNeedsEosPropagationWorkaround(codecInfo) || getCodecNeedsEosPropagation(); codecNeedsEosPropagationWorkaround(codecInfo) || getCodecNeedsEosPropagation();
if ("c2.android.mp3.decoder".equals(codecInfo.name)) { if ("c2.android.mp3.decoder".equals(codecInfo.name)) {
...@@ -1234,8 +1248,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1234,8 +1248,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
// For adaptive reconfiguration, decoders expect all reconfiguration data to be supplied at // For adaptive reconfiguration, decoders expect all reconfiguration data to be supplied at
// the start of the buffer that also contains the first frame in the new format. // the start of the buffer that also contains the first frame in the new format.
if (codecReconfigurationState == RECONFIGURATION_STATE_WRITE_PENDING) { if (codecReconfigurationState == RECONFIGURATION_STATE_WRITE_PENDING) {
for (int i = 0; i < codecFormat.initializationData.size(); i++) { for (int i = 0; i < codecInputFormat.initializationData.size(); i++) {
byte[] data = codecFormat.initializationData.get(i); byte[] data = codecInputFormat.initializationData.get(i);
buffer.data.put(data); buffer.data.put(data);
} }
codecReconfigurationState = RECONFIGURATION_STATE_QUEUE_PENDING; codecReconfigurationState = RECONFIGURATION_STATE_QUEUE_PENDING;
...@@ -1270,7 +1284,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1270,7 +1284,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
if (codecReconfigurationState == RECONFIGURATION_STATE_QUEUE_PENDING) { if (codecReconfigurationState == RECONFIGURATION_STATE_QUEUE_PENDING) {
// We received a new format immediately before the end of the stream. We need to clear // We received a new format immediately before the end of the stream. We need to clear
// the corresponding reconfiguration data from the current buffer, but re-write it into // the corresponding reconfiguration data from the current buffer, but re-write it into
// a subsequent buffer if there are any (e.g. if the user seeks backwards). // a subsequent buffer if there are any (for example, if the user seeks backwards).
buffer.clear(); buffer.clear();
codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING; codecReconfigurationState = RECONFIGURATION_STATE_WRITE_PENDING;
} }
...@@ -1393,6 +1407,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1393,6 +1407,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* @param formatHolder A {@link FormatHolder} that holds the new {@link Format}. * @param formatHolder A {@link FormatHolder} that holds the new {@link Format}.
* @throws ExoPlaybackException If an error occurs re-initializing the {@link MediaCodec}. * @throws ExoPlaybackException If an error occurs re-initializing the {@link MediaCodec}.
*/ */
@CallSuper
protected void onInputFormatChanged(FormatHolder formatHolder) throws ExoPlaybackException { protected void onInputFormatChanged(FormatHolder formatHolder) throws ExoPlaybackException {
waitingForFirstSampleInFormat = true; waitingForFirstSampleInFormat = true;
Format newFormat = Assertions.checkNotNull(formatHolder.format); Format newFormat = Assertions.checkNotNull(formatHolder.format);
...@@ -1426,12 +1441,12 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1426,12 +1441,12 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
return; return;
} }
switch (canKeepCodec(codec, codecInfo, codecFormat, newFormat)) { switch (canKeepCodec(codec, codecInfo, codecInputFormat, newFormat)) {
case KEEP_CODEC_RESULT_NO: case KEEP_CODEC_RESULT_NO:
drainAndReinitializeCodec(); drainAndReinitializeCodec();
break; break;
case KEEP_CODEC_RESULT_YES_WITH_FLUSH: case KEEP_CODEC_RESULT_YES_WITH_FLUSH:
codecFormat = newFormat; codecInputFormat = newFormat;
updateCodecOperatingRate(); updateCodecOperatingRate();
if (sourceDrmSession != codecDrmSession) { if (sourceDrmSession != codecDrmSession) {
drainAndUpdateCodecDrmSession(); drainAndUpdateCodecDrmSession();
...@@ -1448,9 +1463,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1448,9 +1463,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
codecNeedsAdaptationWorkaroundBuffer = codecNeedsAdaptationWorkaroundBuffer =
codecAdaptationWorkaroundMode == ADAPTATION_WORKAROUND_MODE_ALWAYS codecAdaptationWorkaroundMode == ADAPTATION_WORKAROUND_MODE_ALWAYS
|| (codecAdaptationWorkaroundMode == ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION || (codecAdaptationWorkaroundMode == ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION
&& newFormat.width == codecFormat.width && newFormat.width == codecInputFormat.width
&& newFormat.height == codecFormat.height); && newFormat.height == codecInputFormat.height);
codecFormat = newFormat; codecInputFormat = newFormat;
updateCodecOperatingRate(); updateCodecOperatingRate();
if (sourceDrmSession != codecDrmSession) { if (sourceDrmSession != codecDrmSession) {
drainAndUpdateCodecDrmSession(); drainAndUpdateCodecDrmSession();
...@@ -1458,7 +1473,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1458,7 +1473,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
break; break;
case KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION: case KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION:
codecFormat = newFormat; codecInputFormat = newFormat;
updateCodecOperatingRate(); updateCodecOperatingRate();
if (sourceDrmSession != codecDrmSession) { if (sourceDrmSession != codecDrmSession) {
drainAndUpdateCodecDrmSession(); drainAndUpdateCodecDrmSession();
...@@ -1470,40 +1485,18 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1470,40 +1485,18 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
/** /**
* Called when the output {@link MediaFormat} of the {@link MediaCodec} changes. * Called when one of the output formats changes.
* *
* <p>The default implementation is a no-op. * <p>The default implementation is a no-op.
* *
* @param codec The {@link MediaCodec} instance. * @param format The input {@link Format} to which future output now corresponds. If the renderer
* @param outputMediaFormat The new output {@link MediaFormat}. * is in bypass mode, this is also the output format.
* @throws ExoPlaybackException Thrown if an error occurs handling the new output media format. * @param mediaFormat The codec output {@link MediaFormat}, or {@code null} if the renderer is in
*/ * bypass mode.
protected void onOutputMediaFormatChanged(MediaCodec codec, MediaFormat outputMediaFormat)
throws ExoPlaybackException {
// Do nothing.
}
/**
* Called when the output {@link Format} changes.
*
* <p>The default implementation is a no-op.
*
* @param outputFormat The new output {@link Format}.
* @throws ExoPlaybackException Thrown if an error occurs handling the new output format.
*/
protected void onOutputFormatChanged(Format outputFormat) throws ExoPlaybackException {
// Do nothing.
}
/**
* Configures the renderer output based on a {@link Format}.
*
* <p>The default implementation is a no-op.
*
* @param outputFormat The format to configure the output with.
* @throws ExoPlaybackException Thrown if an error occurs configuring the output. * @throws ExoPlaybackException Thrown if an error occurs configuring the output.
*/ */
protected void configureOutput(Format outputFormat) throws ExoPlaybackException { protected void onOutputFormatChanged(Format format, @Nullable MediaFormat mediaFormat)
throws ExoPlaybackException {
// Do nothing. // Do nothing.
} }
...@@ -1633,7 +1626,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1633,7 +1626,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
float newCodecOperatingRate = float newCodecOperatingRate =
getCodecOperatingRateV23(operatingRate, codecFormat, getStreamFormats()); getCodecOperatingRateV23(operatingRate, codecInputFormat, getStreamFormats());
if (codecOperatingRate == newCodecOperatingRate) { if (codecOperatingRate == newCodecOperatingRate) {
// No change. // No change.
} else if (newCodecOperatingRate == CODEC_OPERATING_RATE_UNSET) { } else if (newCodecOperatingRate == CODEC_OPERATING_RATE_UNSET) {
...@@ -1721,8 +1714,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1721,8 +1714,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
if (outputIndex < 0) { if (outputIndex < 0) {
if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED /* (-2) */) { if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED /* (-2) */) {
processOutputMediaFormat(); processOutputMediaFormatChanged();
receivedOutputMediaFormatChange = true;
return true; return true;
} else if (outputIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED /* (-3) */) { } else if (outputIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED /* (-3) */) {
processOutputBuffersChanged(); processOutputBuffersChanged();
...@@ -1750,6 +1742,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1750,6 +1742,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
this.outputIndex = outputIndex; this.outputIndex = outputIndex;
outputBuffer = getOutputBuffer(outputIndex); outputBuffer = getOutputBuffer(outputIndex);
// The dequeued buffer is a media buffer. Do some initial setup. // The dequeued buffer is a media buffer. Do some initial setup.
// It will be processed by calling processOutputBuffer (possibly multiple times). // It will be processed by calling processOutputBuffer (possibly multiple times).
if (outputBuffer != null) { if (outputBuffer != null) {
...@@ -1815,8 +1808,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1815,8 +1808,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
return false; return false;
} }
/** Processes a new output {@link MediaFormat}. */ /** Processes a change in the decoder output {@link MediaFormat}. */
private void processOutputMediaFormat() throws ExoPlaybackException { private void processOutputMediaFormatChanged() {
codecHasOutputMediaFormat = true; codecHasOutputMediaFormat = true;
MediaFormat mediaFormat = codecAdapter.getOutputFormat(); MediaFormat mediaFormat = codecAdapter.getOutputFormat();
if (codecAdaptationWorkaroundMode != ADAPTATION_WORKAROUND_MODE_NEVER if (codecAdaptationWorkaroundMode != ADAPTATION_WORKAROUND_MODE_NEVER
...@@ -1830,7 +1823,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1830,7 +1823,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
if (codecNeedsMonoChannelCountWorkaround) { if (codecNeedsMonoChannelCountWorkaround) {
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
} }
onOutputMediaFormatChanged(codec, mediaFormat); codecOutputMediaFormat = mediaFormat;
codecOutputMediaFormatChanged = true;
} }
/** /**
...@@ -1874,7 +1868,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1874,7 +1868,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* by the source. * by the source.
* @param isLastBuffer Whether the buffer is the last sample of the current stream. * @param isLastBuffer Whether the buffer is the last sample of the current stream.
* @param format The {@link Format} associated with the buffer. * @param format The {@link Format} associated with the buffer.
* @return Whether the output buffer was fully processed (e.g. rendered or skipped). * @return Whether the output buffer was fully processed (for example, rendered or skipped).
* @throws ExoPlaybackException If an error occurs processing the output buffer. * @throws ExoPlaybackException If an error occurs processing the output buffer.
*/ */
protected abstract boolean processOutputBuffer( protected abstract boolean processOutputBuffer(
...@@ -2121,7 +2115,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -2121,7 +2115,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
if (!batchBuffer.isEmpty() && waitingForFirstSampleInFormat) { if (!batchBuffer.isEmpty() && waitingForFirstSampleInFormat) {
// This is the first buffer in a new format, the output format must be updated. // This is the first buffer in a new format, the output format must be updated.
outputFormat = Assertions.checkNotNull(inputFormat); outputFormat = Assertions.checkNotNull(inputFormat);
onOutputFormatChanged(outputFormat); onOutputFormatChanged(outputFormat, /* mediaFormat= */ null);
waitingForFirstSampleInFormat = false; waitingForFirstSampleInFormat = false;
} }
......
...@@ -153,9 +153,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -153,9 +153,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
private long totalVideoFrameProcessingOffsetUs; private long totalVideoFrameProcessingOffsetUs;
private int videoFrameProcessingOffsetCount; private int videoFrameProcessingOffsetCount;
@Nullable private MediaFormat currentMediaFormat;
private int mediaFormatWidth;
private int mediaFormatHeight;
private int currentWidth; private int currentWidth;
private int currentHeight; private int currentHeight;
private int currentUnappliedRotationDegrees; private int currentUnappliedRotationDegrees;
...@@ -262,8 +259,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -262,8 +259,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
currentHeight = Format.NO_VALUE; currentHeight = Format.NO_VALUE;
currentPixelWidthHeightRatio = Format.NO_VALUE; currentPixelWidthHeightRatio = Format.NO_VALUE;
scalingMode = VIDEO_SCALING_MODE_DEFAULT; scalingMode = VIDEO_SCALING_MODE_DEFAULT;
mediaFormatWidth = Format.NO_VALUE;
mediaFormatHeight = Format.NO_VALUE;
clearReportedVideoSize(); clearReportedVideoSize();
} }
...@@ -449,7 +444,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -449,7 +444,6 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
@Override @Override
protected void onDisabled() { protected void onDisabled() {
currentMediaFormat = null;
clearReportedVideoSize(); clearReportedVideoSize();
clearRenderedFirstFrame(); clearRenderedFirstFrame();
frameReleaseTimeHelper.disable(); frameReleaseTimeHelper.disable();
...@@ -668,51 +662,37 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -668,51 +662,37 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
} }
@Override @Override
protected void onOutputMediaFormatChanged(MediaCodec codec, MediaFormat outputMediaFormat) { protected void onOutputFormatChanged(Format format, @Nullable MediaFormat mediaFormat) {
currentMediaFormat = outputMediaFormat; @Nullable MediaCodec codec = getCodec();
boolean hasCrop = if (codec != null) {
outputMediaFormat.containsKey(KEY_CROP_RIGHT) // Must be applied each time the output format changes.
&& outputMediaFormat.containsKey(KEY_CROP_LEFT) codec.setVideoScalingMode(scalingMode);
&& outputMediaFormat.containsKey(KEY_CROP_BOTTOM) }
&& outputMediaFormat.containsKey(KEY_CROP_TOP);
mediaFormatWidth =
hasCrop
? outputMediaFormat.getInteger(KEY_CROP_RIGHT)
- outputMediaFormat.getInteger(KEY_CROP_LEFT)
+ 1
: outputMediaFormat.getInteger(MediaFormat.KEY_WIDTH);
mediaFormatHeight =
hasCrop
? outputMediaFormat.getInteger(KEY_CROP_BOTTOM)
- outputMediaFormat.getInteger(KEY_CROP_TOP)
+ 1
: outputMediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
// Must be applied each time the output MediaFormat changes.
codec.setVideoScalingMode(scalingMode);
maybeNotifyVideoFrameProcessingOffset();
}
@Override
protected void onOutputFormatChanged(Format outputFormat) {
configureOutput(outputFormat);
}
@Override
protected void configureOutput(Format outputFormat) {
if (tunneling) { if (tunneling) {
currentWidth = outputFormat.width; currentWidth = format.width;
currentHeight = outputFormat.height; currentHeight = format.height;
} else { } else {
currentWidth = mediaFormatWidth; Assertions.checkNotNull(mediaFormat);
currentHeight = mediaFormatHeight; boolean hasCrop =
} mediaFormat.containsKey(KEY_CROP_RIGHT)
currentPixelWidthHeightRatio = outputFormat.pixelWidthHeightRatio; && mediaFormat.containsKey(KEY_CROP_LEFT)
&& mediaFormat.containsKey(KEY_CROP_BOTTOM)
&& mediaFormat.containsKey(KEY_CROP_TOP);
currentWidth =
hasCrop
? mediaFormat.getInteger(KEY_CROP_RIGHT) - mediaFormat.getInteger(KEY_CROP_LEFT) + 1
: mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
currentHeight =
hasCrop
? mediaFormat.getInteger(KEY_CROP_BOTTOM) - mediaFormat.getInteger(KEY_CROP_TOP) + 1
: mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
}
currentPixelWidthHeightRatio = format.pixelWidthHeightRatio;
if (Util.SDK_INT >= 21) { if (Util.SDK_INT >= 21) {
// On API level 21 and above the decoder applies the rotation when rendering to the surface. // On API level 21 and above the decoder applies the rotation when rendering to the surface.
// Hence currentUnappliedRotation should always be 0. For 90 and 270 degree rotations, we need // Hence currentUnappliedRotation should always be 0. For 90 and 270 degree rotations, we need
// to flip the width, height and pixel aspect ratio to reflect the rotation that was applied. // to flip the width, height and pixel aspect ratio to reflect the rotation that was applied.
if (outputFormat.rotationDegrees == 90 || outputFormat.rotationDegrees == 270) { if (format.rotationDegrees == 90 || format.rotationDegrees == 270) {
int rotatedHeight = currentWidth; int rotatedHeight = currentWidth;
currentWidth = currentHeight; currentWidth = currentHeight;
currentHeight = rotatedHeight; currentHeight = rotatedHeight;
...@@ -720,9 +700,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -720,9 +700,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
} }
} else { } else {
// On API level 20 and below the decoder does not apply the rotation. // On API level 20 and below the decoder does not apply the rotation.
currentUnappliedRotationDegrees = outputFormat.rotationDegrees; currentUnappliedRotationDegrees = format.rotationDegrees;
} }
currentFrameRate = outputFormat.frameRate; currentFrameRate = format.frameRate;
updateSurfaceFrameRate(/* isNewSurface= */ false); updateSurfaceFrameRate(/* isNewSurface= */ false);
} }
...@@ -811,7 +791,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -811,7 +791,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|| (isStarted && shouldForceRenderOutputBuffer(earlyUs, elapsedSinceLastRenderUs))); || (isStarted && shouldForceRenderOutputBuffer(earlyUs, elapsedSinceLastRenderUs)));
if (forceRenderOutputBuffer) { if (forceRenderOutputBuffer) {
long releaseTimeNs = System.nanoTime(); long releaseTimeNs = System.nanoTime();
notifyFrameMetadataListener(presentationTimeUs, releaseTimeNs, format, currentMediaFormat); notifyFrameMetadataListener(presentationTimeUs, releaseTimeNs, format);
if (Util.SDK_INT >= 21) { if (Util.SDK_INT >= 21) {
renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, releaseTimeNs); renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, releaseTimeNs);
} else { } else {
...@@ -857,8 +837,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -857,8 +837,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
if (Util.SDK_INT >= 21) { if (Util.SDK_INT >= 21) {
// Let the underlying framework time the release. // Let the underlying framework time the release.
if (earlyUs < 50000) { if (earlyUs < 50000) {
notifyFrameMetadataListener( notifyFrameMetadataListener(presentationTimeUs, adjustedReleaseTimeNs, format);
presentationTimeUs, adjustedReleaseTimeNs, format, currentMediaFormat);
renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, adjustedReleaseTimeNs); renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, adjustedReleaseTimeNs);
updateVideoFrameProcessingOffsetCounters(earlyUs); updateVideoFrameProcessingOffsetCounters(earlyUs);
return true; return true;
...@@ -877,8 +856,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -877,8 +856,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
return false; return false;
} }
} }
notifyFrameMetadataListener( notifyFrameMetadataListener(presentationTimeUs, adjustedReleaseTimeNs, format);
presentationTimeUs, adjustedReleaseTimeNs, format, currentMediaFormat);
renderOutputBuffer(codec, bufferIndex, presentationTimeUs); renderOutputBuffer(codec, bufferIndex, presentationTimeUs);
updateVideoFrameProcessingOffsetCounters(earlyUs); updateVideoFrameProcessingOffsetCounters(earlyUs);
return true; return true;
...@@ -890,10 +868,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -890,10 +868,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
} }
private void notifyFrameMetadataListener( private void notifyFrameMetadataListener(
long presentationTimeUs, long releaseTimeNs, Format format, MediaFormat mediaFormat) { long presentationTimeUs, long releaseTimeNs, Format format) {
if (frameMetadataListener != null) { if (frameMetadataListener != null) {
frameMetadataListener.onVideoFrameAboutToBeRendered( frameMetadataListener.onVideoFrameAboutToBeRendered(
presentationTimeUs, releaseTimeNs, format, mediaFormat); presentationTimeUs, releaseTimeNs, format, getCodecOutputMediaFormat());
} }
} }
...@@ -1230,10 +1208,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { ...@@ -1230,10 +1208,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
} }
private void maybeNotifyVideoFrameProcessingOffset() { private void maybeNotifyVideoFrameProcessingOffset() {
@Nullable Format outputFormat = getCurrentOutputFormat(); if (videoFrameProcessingOffsetCount != 0) {
if (outputFormat != null && videoFrameProcessingOffsetCount != 0) {
eventDispatcher.reportVideoFrameProcessingOffset( eventDispatcher.reportVideoFrameProcessingOffset(
totalVideoFrameProcessingOffsetUs, videoFrameProcessingOffsetCount, outputFormat); totalVideoFrameProcessingOffsetUs, videoFrameProcessingOffsetCount);
totalVideoFrameProcessingOffsetUs = 0; totalVideoFrameProcessingOffsetUs = 0;
videoFrameProcessingOffsetCount = 0; videoFrameProcessingOffsetCount = 0;
} }
......
...@@ -88,10 +88,8 @@ public interface VideoRendererEventListener { ...@@ -88,10 +88,8 @@ public interface VideoRendererEventListener {
* @param totalProcessingOffsetUs The sum of all video frame processing offset samples for the * @param totalProcessingOffsetUs The sum of all video frame processing offset samples for the
* video frames processed by the renderer in microseconds. * video frames processed by the renderer in microseconds.
* @param frameCount The number of samples included in the {@code totalProcessingOffsetUs}. * @param frameCount The number of samples included in the {@code totalProcessingOffsetUs}.
* @param format The {@link Format} that is currently output.
*/ */
default void onVideoFrameProcessingOffset( default void onVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) {}
long totalProcessingOffsetUs, int frameCount, Format format) {}
/** /**
* Called before a frame is rendered for the first time since setting the surface, and each time * Called before a frame is rendered for the first time since setting the surface, and each time
...@@ -182,13 +180,12 @@ public interface VideoRendererEventListener { ...@@ -182,13 +180,12 @@ public interface VideoRendererEventListener {
} }
/** Invokes {@link VideoRendererEventListener#onVideoFrameProcessingOffset}. */ /** Invokes {@link VideoRendererEventListener#onVideoFrameProcessingOffset}. */
public void reportVideoFrameProcessingOffset( public void reportVideoFrameProcessingOffset(long totalProcessingOffsetUs, int frameCount) {
long totalProcessingOffsetUs, int frameCount, Format format) {
if (handler != null) { if (handler != null) {
handler.post( handler.post(
() -> () ->
castNonNull(listener) castNonNull(listener)
.onVideoFrameProcessingOffset(totalProcessingOffsetUs, frameCount, format)); .onVideoFrameProcessingOffset(totalProcessingOffsetUs, frameCount));
} }
} }
......
...@@ -1955,7 +1955,7 @@ public final class AnalyticsCollectorTest { ...@@ -1955,7 +1955,7 @@ public final class AnalyticsCollectorTest {
@Override @Override
public void onVideoFrameProcessingOffset( public void onVideoFrameProcessingOffset(
EventTime eventTime, long totalProcessingOffsetUs, int frameCount, Format format) { EventTime eventTime, long totalProcessingOffsetUs, int frameCount) {
reportedEvents.add(new ReportedEvent(EVENT_VIDEO_FRAME_PROCESSING_OFFSET, eventTime)); reportedEvents.add(new ReportedEvent(EVENT_VIDEO_FRAME_PROCESSING_OFFSET, eventTime));
} }
......
...@@ -25,7 +25,9 @@ import static org.mockito.ArgumentMatchers.anyLong; ...@@ -25,7 +25,9 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.media.MediaFormat;
import android.os.SystemClock; import android.os.SystemClock;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
...@@ -216,15 +218,16 @@ public class MediaCodecAudioRendererTest { ...@@ -216,15 +218,16 @@ public class MediaCodecAudioRendererTest {
/* eventHandler= */ null, /* eventHandler= */ null,
/* eventListener= */ null) { /* eventListener= */ null) {
@Override @Override
protected void onOutputFormatChanged(Format outputFormat) throws ExoPlaybackException { protected void onOutputFormatChanged(Format format, @Nullable MediaFormat mediaFormat)
super.onOutputFormatChanged(outputFormat); throws ExoPlaybackException {
if (!outputFormat.equals(AUDIO_AAC)) { super.onOutputFormatChanged(format, mediaFormat);
if (!format.equals(AUDIO_AAC)) {
setPendingPlaybackException( setPendingPlaybackException(
ExoPlaybackException.createForRenderer( ExoPlaybackException.createForRenderer(
new AudioSink.ConfigurationException("Test"), new AudioSink.ConfigurationException("Test"),
"rendererName", "rendererName",
/* rendererIndex= */ 0, /* rendererIndex= */ 0,
outputFormat, format,
FORMAT_HANDLED)); FORMAT_HANDLED));
} }
} }
...@@ -254,8 +257,11 @@ public class MediaCodecAudioRendererTest { ...@@ -254,8 +257,11 @@ public class MediaCodecAudioRendererTest {
exceptionThrowingRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000); exceptionThrowingRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
exceptionThrowingRenderer.render(/* positionUs= */ 250, SystemClock.elapsedRealtime() * 1000); exceptionThrowingRenderer.render(/* positionUs= */ 250, SystemClock.elapsedRealtime() * 1000);
MediaFormat mediaFormat = new MediaFormat();
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 32_000);
// Simulating the exception being thrown when not traceable back to render. // Simulating the exception being thrown when not traceable back to render.
exceptionThrowingRenderer.onOutputFormatChanged(changedFormat); exceptionThrowingRenderer.onOutputFormatChanged(changedFormat, mediaFormat);
assertThrows( assertThrows(
ExoPlaybackException.class, ExoPlaybackException.class,
......
...@@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify; ...@@ -29,6 +29,7 @@ import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf; import static org.robolectric.Shadows.shadowOf;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.media.MediaFormat;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.SystemClock; import android.os.SystemClock;
...@@ -37,7 +38,6 @@ import androidx.annotation.Nullable; ...@@ -37,7 +38,6 @@ import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
...@@ -113,9 +113,9 @@ public class MediaCodecVideoRendererTest { ...@@ -113,9 +113,9 @@ public class MediaCodecVideoRendererTest {
} }
@Override @Override
protected void onOutputFormatChanged(Format outputFormat) { protected void onOutputFormatChanged(Format format, @Nullable MediaFormat mediaFormat) {
super.onOutputFormatChanged(outputFormat); super.onOutputFormatChanged(format, mediaFormat);
currentOutputFormat = outputFormat; currentOutputFormat = format;
} }
}; };
...@@ -458,59 +458,4 @@ public class MediaCodecVideoRendererTest { ...@@ -458,59 +458,4 @@ public class MediaCodecVideoRendererTest {
shadowLooper.idle(); shadowLooper.idle();
verify(eventListener, times(2)).onRenderedFirstFrame(any()); verify(eventListener, times(2)).onRenderedFirstFrame(any());
} }
@Test
public void onVideoFrameProcessingOffset_isCalledAfterOutputFormatChanges()
throws ExoPlaybackException {
Format mp4Uhd = VIDEO_H264.buildUpon().setWidth(3840).setHeight(2160).build();
FakeSampleStream fakeSampleStream =
new FakeSampleStream(
/* mediaSourceEventDispatcher= */ null,
DrmSessionManager.DUMMY,
new DrmSessionEventListener.EventDispatcher(),
/* initialFormat= */ mp4Uhd,
ImmutableList.of(
oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME),
format(VIDEO_H264),
oneByteSample(/* timeUs= */ 50, C.BUFFER_FLAG_KEY_FRAME),
oneByteSample(/* timeUs= */ 100),
format(mp4Uhd),
oneByteSample(/* timeUs= */ 150, C.BUFFER_FLAG_KEY_FRAME),
oneByteSample(/* timeUs= */ 200),
oneByteSample(/* timeUs= */ 250),
format(VIDEO_H264),
oneByteSample(/* timeUs= */ 300, C.BUFFER_FLAG_KEY_FRAME),
FakeSampleStreamItem.END_OF_STREAM_ITEM));
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {mp4Uhd},
fakeSampleStream,
/* positionUs= */ 0,
/* joining= */ false,
/* mayRenderStartOfStream= */ true,
/* offsetUs */ 0);
mediaCodecVideoRenderer.setCurrentStreamFinal();
mediaCodecVideoRenderer.start();
int positionUs = 10;
do {
mediaCodecVideoRenderer.render(positionUs, SystemClock.elapsedRealtime() * 1000);
positionUs += 10;
} while (!mediaCodecVideoRenderer.isEnded());
mediaCodecVideoRenderer.stop();
shadowOf(testMainLooper).idle();
InOrder orderVerifier = inOrder(eventListener);
orderVerifier.verify(eventListener).onVideoFrameProcessingOffset(anyLong(), eq(1), eq(mp4Uhd));
orderVerifier
.verify(eventListener)
.onVideoFrameProcessingOffset(anyLong(), eq(2), eq(VIDEO_H264));
orderVerifier.verify(eventListener).onVideoFrameProcessingOffset(anyLong(), eq(3), eq(mp4Uhd));
orderVerifier
.verify(eventListener)
.onVideoFrameProcessingOffset(anyLong(), eq(1), eq(VIDEO_H264));
orderVerifier.verifyNoMoreInteractions();
}
} }
...@@ -67,9 +67,7 @@ public class FakeVideoRenderer extends FakeRenderer { ...@@ -67,9 +67,7 @@ public class FakeVideoRenderer extends FakeRenderer {
super.onStopped(); super.onStopped();
eventDispatcher.droppedFrames(/* droppedFrameCount= */ 0, /* elapsedMs= */ 0); eventDispatcher.droppedFrames(/* droppedFrameCount= */ 0, /* elapsedMs= */ 0);
eventDispatcher.reportVideoFrameProcessingOffset( eventDispatcher.reportVideoFrameProcessingOffset(
/* totalProcessingOffsetUs= */ 400000, /* totalProcessingOffsetUs= */ 400000, /* frameCount= */ 10);
/* frameCount= */ 10,
Assertions.checkNotNull(format));
} }
@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