mirror of
https://github.com/androidx/media.git
synced 2025-04-29 22:36:54 +08:00
Do not drop decoder input buffers close to a reset position
This is a workaround for a bug where the positionUs seen by MCVR jumps when audio pre-roll samples are discarded. PiperOrigin-RevId: 743538208 (cherry picked from commit 036bed36326130294f50264659913bdcecb4c9bc)
This commit is contained in:
parent
9cfaf78994
commit
82d7c628da
@ -149,6 +149,17 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
*/
|
||||
private static final long OFFSET_FROM_PERIOD_END_TO_TREAT_AS_LAST_US = 100_000L;
|
||||
|
||||
/**
|
||||
* The offset from {@link #getLastResetPositionUs()} in microseconds, before which input buffers
|
||||
* are not allowed to be dropped.
|
||||
*
|
||||
* <p>This value must be greater than the pre-roll distance used by common audio codecs, such as
|
||||
* 80ms used by Opus <a
|
||||
* href="https://opus-codec.org/docs/opus_in_isobmff.html#4.3.6.2">Encapsulation of Opus in ISO
|
||||
* Base Media File Format</a>
|
||||
*/
|
||||
private static final long OFFSET_FROM_RESET_POSITION_TO_ALLOW_INPUT_BUFFER_DROPPING_US = 200_000L;
|
||||
|
||||
/**
|
||||
* The maximum number of consecutive dropped input buffers that allow discarding frame headers.
|
||||
*
|
||||
@ -616,7 +627,16 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
|
||||
boolean treatDroppedBuffersAsSkipped)
|
||||
throws ExoPlaybackException {
|
||||
if (minEarlyUsToDropDecoderInput != C.TIME_UNSET) {
|
||||
shouldDropDecoderInputBuffers = earlyUs < minEarlyUsToDropDecoderInput;
|
||||
// TODO: b/161996553 - Remove the isAwayFromLastResetPosition check when audio pre-rolling
|
||||
// is implemented correctly. Audio codecs such as Opus require pre-roll samples to be decoded
|
||||
// and discarded on a seek. Depending on the audio decoder, the positionUs may jump forward
|
||||
// by the pre-roll duration. Do not drop more frames than necessary when this happens.
|
||||
boolean isAwayFromLastResetPosition =
|
||||
positionUs
|
||||
> getLastResetPositionUs()
|
||||
+ OFFSET_FROM_RESET_POSITION_TO_ALLOW_INPUT_BUFFER_DROPPING_US;
|
||||
shouldDropDecoderInputBuffers =
|
||||
isAwayFromLastResetPosition && earlyUs < minEarlyUsToDropDecoderInput;
|
||||
}
|
||||
return shouldDropBuffersToKeyframe(earlyUs, elapsedRealtimeUs, isLastFrame)
|
||||
&& maybeDropBuffersToKeyframe(positionUs, treatDroppedBuffersAsSkipped);
|
||||
|
@ -29,6 +29,7 @@ import androidx.media3.exoplayer.DecoderCounters;
|
||||
import androidx.media3.exoplayer.DefaultRenderersFactory;
|
||||
import androidx.media3.exoplayer.ExoPlayer;
|
||||
import androidx.media3.exoplayer.Renderer;
|
||||
import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
||||
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
|
||||
import androidx.media3.exoplayer.mediacodec.MediaCodecAdapter;
|
||||
import androidx.media3.exoplayer.mediacodec.MediaCodecSelector;
|
||||
@ -103,6 +104,14 @@ public class ParseAv1SampleDependenciesPlaybackTest {
|
||||
new ExoPlayer.Builder(applicationContext, renderersFactory)
|
||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||
.build();
|
||||
player.addAnalyticsListener(
|
||||
new AnalyticsListener() {
|
||||
@Override
|
||||
public void onDroppedVideoFrames(EventTime eventTime, int droppedFrames, long elapsedMs) {
|
||||
// Input buffers near the reset position should not be dropped.
|
||||
assertThat(eventTime.currentPlaybackPositionMs).isAtLeast(200);
|
||||
}
|
||||
});
|
||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 1));
|
||||
player.setVideoSurface(surface);
|
||||
player.setMediaItem(MediaItem.fromUri(TEST_MP4_URI));
|
||||
@ -121,7 +130,7 @@ public class ParseAv1SampleDependenciesPlaybackTest {
|
||||
// Which input buffer is dropped first depends on the number of MediaCodec buffer slots.
|
||||
// This means the asserts cannot be isEqualTo.
|
||||
assertThat(decoderCounters.maxConsecutiveDroppedBufferCount).isAtMost(2);
|
||||
assertThat(decoderCounters.droppedInputBufferCount).isAtLeast(8);
|
||||
assertThat(decoderCounters.droppedInputBufferCount).isAtLeast(4);
|
||||
}
|
||||
|
||||
private static final class CapturingRenderersFactoryWithLateThresholdToDropDecoderInputUs
|
||||
@ -155,7 +164,6 @@ public class ParseAv1SampleDependenciesPlaybackTest {
|
||||
/* enableDecoderFallback= */ false,
|
||||
eventHandler,
|
||||
videoRendererEventListener,
|
||||
DefaultRenderersFactory.MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY,
|
||||
/* parseAv1SampleDependencies= */ true,
|
||||
/* lateThresholdToDropDecoderInputUs= */ -100_000_000L)
|
||||
};
|
||||
@ -173,7 +181,6 @@ public class ParseAv1SampleDependenciesPlaybackTest {
|
||||
boolean enableDecoderFallback,
|
||||
@Nullable Handler eventHandler,
|
||||
@Nullable VideoRendererEventListener eventListener,
|
||||
int maxDroppedFramesToNotify,
|
||||
boolean parseAv1SampleDependencies,
|
||||
long lateThresholdToDropDecoderInputUs) {
|
||||
super(
|
||||
@ -184,7 +191,7 @@ public class ParseAv1SampleDependenciesPlaybackTest {
|
||||
.setEnableDecoderFallback(enableDecoderFallback)
|
||||
.setEventHandler(eventHandler)
|
||||
.setEventListener(eventListener)
|
||||
.setMaxDroppedFramesToNotify(maxDroppedFramesToNotify)
|
||||
.setMaxDroppedFramesToNotify(1)
|
||||
.experimentalSetParseAv1SampleDependencies(parseAv1SampleDependencies)
|
||||
.experimentalSetLateThresholdToDropDecoderInputUs(
|
||||
lateThresholdToDropDecoderInputUs));
|
||||
|
Loading…
x
Reference in New Issue
Block a user