diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d558de8583..78a1589842 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -34,6 +34,9 @@ * Parse EXIF rotation data for image inputs. * Track Selection: * Extractors: + * MPEG-TS: Ensure the last frame is rendered by passing the last access + unit of a stream to the sample queue + ([#7909](https://github.com/google/ExoPlayer/issues/7909)). * Audio: * Audio Offload: * Add `AudioSink.getFormatOffloadSupport(Format)` that retrieves level of diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H262Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H262Reader.java index 2c3db44332..1358dbaf42 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H262Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H262Reader.java @@ -218,10 +218,11 @@ public final class H262Reader implements ElementaryStreamReader { @Override public void packetFinished(boolean isEndOfInput) { + checkStateNotNull(output); // Asserts that createTracks has been called. if (isEndOfInput) { @C.BufferFlags int flags = sampleIsKeyframe ? C.BUFFER_FLAG_KEY_FRAME : 0; int size = (int) (totalBytesWritten - samplePosition); - output.sampleMetadata(sampleTimeUs, flags, size, 0, null); + output.sampleMetadata(sampleTimeUs, flags, size, /* offset= */ 0, /* cryptoData= */ null); } } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java index 31321037a4..a623d7dadf 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java @@ -218,8 +218,10 @@ public final class H263Reader implements ElementaryStreamReader { @Override public void packetFinished(boolean isEndOfInput) { + // Assert that createTracks has been called. + checkStateNotNull(sampleReader); if (isEndOfInput) { - sampleReader.onDataEnd(totalBytesWritten, 0, hasOutputFormat); + sampleReader.onDataEnd(totalBytesWritten, /* bytesWrittenPastPosition= */ 0, hasOutputFormat); sampleReader.reset(); } } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H264Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H264Reader.java index 885726fb32..147af676be 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H264Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H264Reader.java @@ -169,6 +169,7 @@ public final class H264Reader implements ElementaryStreamReader { @Override public void packetFinished(boolean isEndOfInput) { + assertTracksCreated(); if (isEndOfInput) { sampleReader.end(totalBytesWritten); } @@ -503,9 +504,9 @@ public final class H264Reader implements ElementaryStreamReader { } public void end(long position) { - // Output a final sample with the nal units currently held + // Output a final sample with the NAL units currently held nalUnitStartPosition = position; - outputSample(0); + outputSample(/* offset= */ 0); readingSample = false; } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H265Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H265Reader.java index 25262f314d..c1e3d4ccb1 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H265Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H265Reader.java @@ -174,6 +174,7 @@ public final class H265Reader implements ElementaryStreamReader { @Override public void packetFinished(boolean isEndOfInput) { + assertTracksCreated(); if (isEndOfInput) { sampleReader.end(totalBytesWritten); } @@ -379,9 +380,9 @@ public final class H265Reader implements ElementaryStreamReader { } public void end(long position) { - // Output a final sample with the nal units currently held + // Output a final sample with the NAL units currently held nalUnitPosition = position; - outputSample(0); + outputSample(/* offset= */ 0); readingSample = false; } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/PesReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/PesReader.java index b328cb32f7..3a64aaf4d4 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/PesReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/PesReader.java @@ -149,7 +149,7 @@ public final class PesReader implements TsPayloadReader { payloadSize -= readLength; if (payloadSize == 0) { // There are bytes left in data, see above, so this is not the end of input - reader.packetFinished(false); + reader.packetFinished(/* isEndOfInput= */ false); setState(STATE_READING_HEADER); } } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/PsExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/PsExtractor.java index 1b6789b2e8..eb3a4418fe 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/PsExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/PsExtractor.java @@ -357,7 +357,7 @@ public final class PsExtractor implements Extractor { pesPayloadReader.packetStarted(timeUs, TsPayloadReader.FLAG_DATA_ALIGNMENT_INDICATOR); pesPayloadReader.consume(data); // We always have complete PES packets with program stream. - pesPayloadReader.packetFinished(false); + pesPayloadReader.packetFinished(/* isEndOfInput= */ false); } private void parseHeader() { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java index 2d35af946e..049e2c003b 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/TsExtractor.java @@ -323,7 +323,7 @@ public final class TsExtractor implements Extractor { } if (!fillBufferWithAtLeastOnePacket(input)) { - // Send a dummy pusi to allow for packetFinished to be triggered on the last unit + // Send a synthesised empty pusi to allow for packetFinished to be triggered on the last unit. for (int i = 0; i < tsPayloadReaders.size(); i++) { TsPayloadReader payloadReader = tsPayloadReaders.valueAt(i); if (payloadReader instanceof PesReader) {