mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Fix bypass mode when the stream is empty
#minor-release Issue: #8374 PiperOrigin-RevId: 348792965
This commit is contained in:
parent
e71ce06722
commit
5f3d1c1bc8
5 changed files with 58 additions and 31 deletions
|
|
@ -26,6 +26,8 @@
|
||||||
([#5887](https://github.com/google/ExoPlayer/issues/5887)).
|
([#5887](https://github.com/google/ExoPlayer/issues/5887)).
|
||||||
* Fix bug where `AnalyticsListener` callbacks can arrive in the wrong
|
* Fix bug where `AnalyticsListener` callbacks can arrive in the wrong
|
||||||
order ([#8048](https://github.com/google/ExoPlayer/issues/8048)).
|
order ([#8048](https://github.com/google/ExoPlayer/issues/8048)).
|
||||||
|
* Fix `MediaCodecRenderer` issue where empty streams would fail to play in
|
||||||
|
bypass mode ([#8374](https://github.com/google/ExoPlayer/issues/8374)).
|
||||||
* Add `onEvents` callback to `Player.EventListener` and
|
* Add `onEvents` callback to `Player.EventListener` and
|
||||||
`AnalyticsListener` to notify when all simultaneous state changes have
|
`AnalyticsListener` to notify when all simultaneous state changes have
|
||||||
been handled and the values reported through callbacks are again
|
been handled and the values reported through callbacks are again
|
||||||
|
|
|
||||||
|
|
@ -153,27 +153,27 @@ import java.nio.ByteBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void putAccessUnit(DecoderInputBuffer accessUnit) {
|
private void putAccessUnit(DecoderInputBuffer accessUnit) {
|
||||||
@Nullable ByteBuffer accessUnitData = accessUnit.data;
|
|
||||||
if (accessUnitData != null) {
|
|
||||||
accessUnit.flip();
|
|
||||||
ensureSpaceForWrite(accessUnitData.remaining());
|
|
||||||
this.data.put(accessUnitData);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accessUnit.isEndOfStream()) {
|
if (accessUnit.isEndOfStream()) {
|
||||||
setFlags(C.BUFFER_FLAG_END_OF_STREAM);
|
setFlags(C.BUFFER_FLAG_END_OF_STREAM);
|
||||||
}
|
} else {
|
||||||
|
timeUs = accessUnit.timeUs;
|
||||||
if (accessUnit.isDecodeOnly()) {
|
if (accessUnit.isDecodeOnly()) {
|
||||||
setFlags(C.BUFFER_FLAG_DECODE_ONLY);
|
setFlags(C.BUFFER_FLAG_DECODE_ONLY);
|
||||||
}
|
}
|
||||||
if (accessUnit.isKeyFrame()) {
|
if (accessUnit.isKeyFrame()) {
|
||||||
setFlags(C.BUFFER_FLAG_KEY_FRAME);
|
setFlags(C.BUFFER_FLAG_KEY_FRAME);
|
||||||
}
|
}
|
||||||
|
@Nullable ByteBuffer accessUnitData = accessUnit.data;
|
||||||
|
if (accessUnitData != null) {
|
||||||
|
accessUnit.flip();
|
||||||
|
ensureSpaceForWrite(accessUnitData.remaining());
|
||||||
|
this.data.put(accessUnitData);
|
||||||
|
}
|
||||||
accessUnitCount++;
|
accessUnitCount++;
|
||||||
timeUs = accessUnit.timeUs;
|
if (accessUnitCount == 1) {
|
||||||
if (accessUnitCount == 1) { // First read of the buffer
|
|
||||||
firstAccessUnitTimeUs = timeUs;
|
firstAccessUnitTimeUs = timeUs;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
accessUnit.clear();
|
accessUnit.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -397,6 +397,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||||
outputStreamStartPositionUs = C.TIME_UNSET;
|
outputStreamStartPositionUs = C.TIME_UNSET;
|
||||||
outputStreamOffsetUs = C.TIME_UNSET;
|
outputStreamOffsetUs = C.TIME_UNSET;
|
||||||
bypassBatchBuffer = new BatchBuffer();
|
bypassBatchBuffer = new BatchBuffer();
|
||||||
|
bypassBatchBuffer.ensureSpaceForWrite(/* length= */ 0);
|
||||||
|
// MediaCodec outputs audio buffers in native endian:
|
||||||
|
// https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers
|
||||||
|
// and code called from MediaCodecAudioRenderer.processOutputBuffer expects this endianness.
|
||||||
|
bypassBatchBuffer.data.order(ByteOrder.nativeOrder());
|
||||||
resetCodecStateForRelease();
|
resetCodecStateForRelease();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2122,7 +2127,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||||
* iteration of the rendering loop.
|
* iteration of the rendering loop.
|
||||||
* @param elapsedRealtimeUs {@link SystemClock#elapsedRealtime()} in microseconds, measured at the
|
* @param elapsedRealtimeUs {@link SystemClock#elapsedRealtime()} in microseconds, measured at the
|
||||||
* start of the current iteration of the rendering loop.
|
* start of the current iteration of the rendering loop.
|
||||||
* @return If more buffers are ready to be rendered.
|
* @return Whether immediately calling this method again will make more progress.
|
||||||
* @throws ExoPlaybackException If an error occurred while processing a buffer or handling a
|
* @throws ExoPlaybackException If an error occurred while processing a buffer or handling a
|
||||||
* format change.
|
* format change.
|
||||||
*/
|
*/
|
||||||
|
|
@ -2130,7 +2135,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||||
throws ExoPlaybackException {
|
throws ExoPlaybackException {
|
||||||
BatchBuffer batchBuffer = bypassBatchBuffer;
|
BatchBuffer batchBuffer = bypassBatchBuffer;
|
||||||
|
|
||||||
// Let's process the pending buffer if any.
|
// Process any data in the batch buffer.
|
||||||
checkState(!outputStreamEnded);
|
checkState(!outputStreamEnded);
|
||||||
if (!batchBuffer.isEmpty()) { // Optimisation: Do not process buffer if empty.
|
if (!batchBuffer.isEmpty()) { // Optimisation: Do not process buffer if empty.
|
||||||
if (processOutputBuffer(
|
if (processOutputBuffer(
|
||||||
|
|
@ -2145,12 +2150,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||||
batchBuffer.isDecodeOnly(),
|
batchBuffer.isDecodeOnly(),
|
||||||
batchBuffer.isEndOfStream(),
|
batchBuffer.isEndOfStream(),
|
||||||
outputFormat)) {
|
outputFormat)) {
|
||||||
// Buffer completely processed
|
|
||||||
onProcessedOutputBuffer(batchBuffer.getLastAccessUnitTimeUs());
|
onProcessedOutputBuffer(batchBuffer.getLastAccessUnitTimeUs());
|
||||||
} else {
|
} else {
|
||||||
return false; // Could not process buffer, let's try later.
|
// Could not process the whole buffer. Try again later.
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Process the end of stream, if it has been reached.
|
||||||
if (batchBuffer.isEndOfStream()) {
|
if (batchBuffer.isEndOfStream()) {
|
||||||
outputStreamEnded = true;
|
outputStreamEnded = true;
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2185,19 +2191,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||||
onInputFormatChanged(formatHolder);
|
onInputFormatChanged(formatHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean haveDataToProcess = false;
|
||||||
if (batchBuffer.isEndOfStream()) {
|
if (batchBuffer.isEndOfStream()) {
|
||||||
inputStreamEnded = true;
|
inputStreamEnded = true;
|
||||||
|
haveDataToProcess = true;
|
||||||
|
}
|
||||||
|
if (!batchBuffer.isEmpty()) {
|
||||||
|
batchBuffer.flip();
|
||||||
|
haveDataToProcess = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batchBuffer.isEmpty()) {
|
return haveDataToProcess;
|
||||||
return false; // The buffer could not be filled, there is nothing more to do.
|
|
||||||
}
|
|
||||||
batchBuffer.flip(); // Buffer at least partially full, it can now be processed.
|
|
||||||
// MediaCodec outputs buffers in native endian:
|
|
||||||
// https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers
|
|
||||||
// and code called from processOutputBuffer expects this endianness.
|
|
||||||
batchBuffer.data.order(ByteOrder.nativeOrder());
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -56,4 +56,25 @@ public final class SilencePlaybackTest {
|
||||||
DumpFileAsserts.assertOutput(
|
DumpFileAsserts.assertOutput(
|
||||||
applicationContext, playbackOutput, "playbackdumps/silence/500ms.dump");
|
applicationContext, playbackOutput, "playbackdumps/silence/500ms.dump");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_0ms() throws Exception {
|
||||||
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
|
CapturingRenderersFactory capturingRenderersFactory =
|
||||||
|
new CapturingRenderersFactory(applicationContext);
|
||||||
|
SimpleExoPlayer player =
|
||||||
|
new SimpleExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||||
|
.setClock(new AutoAdvancingFakeClock())
|
||||||
|
.build();
|
||||||
|
PlaybackOutput playbackOutput = PlaybackOutput.register(player, capturingRenderersFactory);
|
||||||
|
|
||||||
|
player.setMediaSource(new SilenceMediaSource(/* durationUs= */ 0));
|
||||||
|
player.prepare();
|
||||||
|
player.play();
|
||||||
|
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
player.release();
|
||||||
|
|
||||||
|
DumpFileAsserts.assertOutput(
|
||||||
|
applicationContext, playbackOutput, "playbackdumps/silence/0ms.dump");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
0
testdata/src/test/assets/playbackdumps/silence/0ms.dump
vendored
Normal file
0
testdata/src/test/assets/playbackdumps/silence/0ms.dump
vendored
Normal file
Loading…
Reference in a new issue