mirror of
https://github.com/samsonjs/media.git
synced 2026-03-31 10:25:48 +00:00
Keep pending output format after multiple flushes
The AsynchronousMediaCodecCallback has logic to retain a pending output format in case flush() is called. This commit fixes a case where calling flush() again while an output format is pending would nullify the pending output format. A unit test is added in AsynchronousMediaCodecCallback but not the AsynchronousMediaCodecAdapter. That is because the adapter operates directly on top of MediaCodec, but Robolectric's ShadowMediaCodec produces an output format on every MediaCodec.start(). This is unrealistic when operating MediaCodec in asynchronous mode where we need to call MediaCodec.start() after every MediaCodec.flush(). PiperOrigin-RevId: 350176659
This commit is contained in:
parent
a4fbc2c98d
commit
51d90a40ba
2 changed files with 71 additions and 1 deletions
|
|
@ -274,7 +274,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
/** Flushes all available input and output buffers and any error that was previously set. */
|
||||
@GuardedBy("lock")
|
||||
private void flushInternal() {
|
||||
pendingOutputFormat = formats.isEmpty() ? null : formats.getLast();
|
||||
if (!formats.isEmpty()) {
|
||||
pendingOutputFormat = formats.getLast();
|
||||
} else {
|
||||
// pendingOutputFormat may already be non-null following a previous flush, and remains set in
|
||||
// this case.
|
||||
}
|
||||
availableInputBuffers.clear();
|
||||
availableOutputBuffers.clear();
|
||||
bufferInfos.clear();
|
||||
|
|
|
|||
|
|
@ -376,6 +376,65 @@ public class AsynchronousMediaCodecCallbackTest {
|
|||
assertThat(asynchronousMediaCodecCallback.getOutputFormat()).isEqualTo(format);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getOutputFormat_afterFlushWithPendingFormat_returnsPendingFormat() {
|
||||
MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
|
||||
AtomicBoolean flushCompleted = new AtomicBoolean();
|
||||
ShadowLooper shadowCallbackLooper = shadowOf(callbackThread.getLooper());
|
||||
shadowCallbackLooper.pause();
|
||||
|
||||
asynchronousMediaCodecCallback.onOutputFormatChanged(codec, createMediaFormat("format0"));
|
||||
asynchronousMediaCodecCallback.onOutputBufferAvailable(
|
||||
codec, /* index= */ 0, new MediaCodec.BufferInfo());
|
||||
asynchronousMediaCodecCallback.onOutputFormatChanged(codec, createMediaFormat("format1"));
|
||||
asynchronousMediaCodecCallback.onOutputBufferAvailable(
|
||||
codec, /* index= */ 1, new MediaCodec.BufferInfo());
|
||||
asynchronousMediaCodecCallback.flushAsync(
|
||||
/* onFlushCompleted= */ () -> flushCompleted.set(true));
|
||||
// Progress the looper so that flush is completed
|
||||
shadowCallbackLooper.idle();
|
||||
// Enqueue an output buffer to make the pending format available.
|
||||
asynchronousMediaCodecCallback.onOutputBufferAvailable(
|
||||
codec, /* index= */ 2, new MediaCodec.BufferInfo());
|
||||
|
||||
assertThat(flushCompleted.get()).isTrue();
|
||||
assertThat(asynchronousMediaCodecCallback.dequeueOutputBufferIndex(outInfo))
|
||||
.isEqualTo(MediaCodec.INFO_OUTPUT_FORMAT_CHANGED);
|
||||
assertThat(asynchronousMediaCodecCallback.getOutputFormat().getString("name"))
|
||||
.isEqualTo("format1");
|
||||
assertThat(asynchronousMediaCodecCallback.dequeueOutputBufferIndex(outInfo)).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
getOutputFormat_withConsecutiveFlushAndPendingFormatFromFirstFlush_returnsPendingFormat() {
|
||||
MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
|
||||
AtomicInteger flushesCompleted = new AtomicInteger();
|
||||
ShadowLooper shadowCallbackLooper = shadowOf(callbackThread.getLooper());
|
||||
shadowCallbackLooper.pause();
|
||||
|
||||
asynchronousMediaCodecCallback.onOutputFormatChanged(codec, createMediaFormat("format0"));
|
||||
asynchronousMediaCodecCallback.onOutputBufferAvailable(
|
||||
codec, /* index= */ 0, new MediaCodec.BufferInfo());
|
||||
// Flush and progress the looper so that flush is completed.
|
||||
asynchronousMediaCodecCallback.flushAsync(
|
||||
/* onFlushCompleted= */ flushesCompleted::incrementAndGet);
|
||||
shadowCallbackLooper.idle();
|
||||
// Flush again, the pending format from the first flush should remain as pending.
|
||||
asynchronousMediaCodecCallback.flushAsync(
|
||||
/* onFlushCompleted= */ flushesCompleted::incrementAndGet);
|
||||
shadowCallbackLooper.idle();
|
||||
asynchronousMediaCodecCallback.onOutputBufferAvailable(
|
||||
codec, /* index= */ 1, new MediaCodec.BufferInfo());
|
||||
|
||||
assertThat(flushesCompleted.get()).isEqualTo(2);
|
||||
assertThat(asynchronousMediaCodecCallback.dequeueOutputBufferIndex(outInfo))
|
||||
.isEqualTo(MediaCodec.INFO_OUTPUT_FORMAT_CHANGED);
|
||||
assertThat(asynchronousMediaCodecCallback.getOutputFormat().getString("name"))
|
||||
.isEqualTo("format0");
|
||||
assertThat(asynchronousMediaCodecCallback.dequeueOutputBufferIndex(outInfo)).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flush_withPendingFlush_onlyLastFlushCompletes() {
|
||||
ShadowLooper callbackLooperShadow = shadowOf(callbackThread.getLooper());
|
||||
|
|
@ -398,6 +457,12 @@ public class AsynchronousMediaCodecCallbackTest {
|
|||
/* errorCode= */ 0, /* actionCode= */ 0, /* detailMessage= */ "error from codec");
|
||||
}
|
||||
|
||||
private static MediaFormat createMediaFormat(String name) {
|
||||
MediaFormat format = new MediaFormat();
|
||||
format.setString("name", name);
|
||||
return format;
|
||||
}
|
||||
|
||||
private static class TestHandlerThread extends HandlerThread {
|
||||
private boolean quit;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue