diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3a22248fff..584067a1a4 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -19,6 +19,8 @@ * Add `PreloadMediaSource.PreloadControl.onPreloadError` to allow `PreloadMediaSource.PreloadControl` implementations to take actions when error occurs. + * `MediaCodecVideoRenderer` avoids decoding samples that are neither + rendered nor used as reference by other samples. * Transformer: * Add `SurfaceAssetLoader`, which supports queueing video data to Transformer via a `Surface`. diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index cf3eb9abaa..dfe8381711 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -620,7 +620,7 @@ public final class C { *
This can be used to skip decoding of buffers that are not depended on during seeking. See
+ * {@link C#BUFFER_FLAG_NOT_DEPENDED_ON}.
+ *
+ * @param buffer The input buffer.
+ */
+ protected boolean shouldSkipDecoderInputBuffer(DecoderInputBuffer buffer) {
+ return false;
+ }
+
/**
* Returns the presentation time of the last buffer in the stream.
*
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java
index 5f995af7ac..e4a0cd27fd 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java
@@ -1247,7 +1247,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
@Override
protected int getCodecBufferFlags(DecoderInputBuffer buffer) {
- if (Util.SDK_INT >= 34 && tunneling && buffer.timeUs < getLastResetPositionUs()) {
+ if (Util.SDK_INT >= 34 && tunneling && isBufferBeforeStartTime(buffer)) {
// The buffer likely needs to be dropped because its timestamp is less than the start time.
// We can't decide to do this after decoding because we won't get the buffer back from the
// codec in tunneling mode. This may not work perfectly, e.g. when the codec is doing frame
@@ -1257,6 +1257,27 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer
return 0;
}
+ @Override
+ protected boolean shouldSkipDecoderInputBuffer(DecoderInputBuffer buffer) {
+ // TODO: b/351164714 - Do not apply this optimization for buffers with timestamp near
+ // the media duration.
+ if (hasReadStreamToEnd() || buffer.isLastSample()) {
+ // Last buffer is always decoded.
+ return false;
+ }
+ if (buffer.isEncrypted()) {
+ // Commonly used decryption algorithms require updating the initialization vector for each
+ // block processed. Skipping input buffers before the decoder is not allowed.
+ return false;
+ }
+ // Skip buffers without sample dependencies that won't be rendered.
+ return isBufferBeforeStartTime(buffer) && buffer.notDependedOn();
+ }
+
+ private boolean isBufferBeforeStartTime(DecoderInputBuffer buffer) {
+ return buffer.timeUs < getLastResetPositionUs();
+ }
+
@Override
protected void onOutputFormatChanged(Format format, @Nullable MediaFormat mediaFormat) {
@Nullable MediaCodecAdapter codec = getCodec();
diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java
index 02fec74843..12d8fee2ab 100644
--- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java
+++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java
@@ -356,6 +356,73 @@ public class MediaCodecVideoRendererTest {
assertThat(argumentDecoderCounters.getValue().skippedOutputBufferCount).isEqualTo(2);
}
+ @Test
+ public void render_withoutSampleDependencies_rendersLastFrameAfterEndOfStream() throws Exception {
+ ArgumentCaptor This class always marks the samples at the start of each group of picture (GOP) with {@link
* C#BUFFER_FLAG_KEY_FRAME}. Usually, key frames can be decoded independently, without depending
@@ -1661,7 +1661,7 @@ public class FragmentedMp4Extractor implements Extractor {
@C.BufferFlags int sampleFlags = trackBundle.getCurrentSampleFlags();
if ((flags & FLAG_READ_WITHIN_GOP_SAMPLE_DEPENDENCIES) != 0 && !isSampleDependedOn) {
- sampleFlags |= C.BUFFER_FLAG_NO_OTHER_SAMPLE_DEPENDS_ON_THIS;
+ sampleFlags |= C.BUFFER_FLAG_NOT_DEPENDED_ON;
}
// Encryption data.
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java
index 5850bba24e..23e42bafc1 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java
@@ -125,7 +125,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
/**
* Flag to extract additional sample dependency information, and mark output buffers with {@link
- * C#BUFFER_FLAG_NO_OTHER_SAMPLE_DEPENDS_ON_THIS}.
+ * C#BUFFER_FLAG_NOT_DEPENDED_ON}.
*
* This class always marks the samples at the start of each group of picture (GOP) with {@link
* C#BUFFER_FLAG_KEY_FRAME}. Usually, key frames can be decoded independently, without depending
@@ -804,7 +804,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
long timeUs = track.sampleTable.timestampsUs[sampleIndex];
@C.BufferFlags int sampleFlags = track.sampleTable.flags[sampleIndex];
if (!isSampleDependedOn) {
- sampleFlags |= C.BUFFER_FLAG_NO_OTHER_SAMPLE_DEPENDS_ON_THIS;
+ sampleFlags |= C.BUFFER_FLAG_NOT_DEPENDED_ON;
}
if (trueHdSampleRechunker != null) {
trueHdSampleRechunker.sampleMetadata(