Switch to getInput/OutputBuffer

Use getInput/OutputBuffer(index) instead of getInput/OutputBuffers() in
Android MediaCodec video encoder and decoder wrappers.

getInput/OutputBuffers(index) are available from SDK 21 which is the minimum required version in WebRTC: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/sdk/android/AndroidManifest.xml

Bug: b/234879577
Change-Id: I79fd234b104420ae3544229e8c62d7db2344cd01
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/265804
Reviewed-by: Xavier Lepaul‎ <xalep@webrtc.org>
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37241}
This commit is contained in:
Sergey Silkin 2022-06-14 10:40:19 +00:00 committed by WebRTC LUCI CQ
parent ef3137a928
commit 7517fb639b
7 changed files with 27 additions and 31 deletions

View File

@ -264,9 +264,9 @@ class AndroidVideoDecoder implements VideoDecoder, VideoSink {
ByteBuffer buffer; ByteBuffer buffer;
try { try {
buffer = codec.getInputBuffers()[index]; buffer = codec.getInputBuffer(index);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Logging.e(TAG, "getInputBuffers failed", e); Logging.e(TAG, "getInputBuffer with index=" + index + " failed", e);
return VideoCodecStatus.ERROR; return VideoCodecStatus.ERROR;
} }
@ -377,14 +377,14 @@ class AndroidVideoDecoder implements VideoDecoder, VideoSink {
// exceeded, deliverDecodedFrame() will be called again on the next iteration of the output // exceeded, deliverDecodedFrame() will be called again on the next iteration of the output
// thread's loop. Blocking here prevents the output thread from busy-waiting while the codec // thread's loop. Blocking here prevents the output thread from busy-waiting while the codec
// is idle. // is idle.
int result = codec.dequeueOutputBuffer(info, DEQUEUE_OUTPUT_BUFFER_TIMEOUT_US); int index = codec.dequeueOutputBuffer(info, DEQUEUE_OUTPUT_BUFFER_TIMEOUT_US);
if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
reformat(codec.getOutputFormat()); reformat(codec.getOutputFormat());
return; return;
} }
if (result < 0) { if (index < 0) {
Logging.v(TAG, "dequeueOutputBuffer returned " + result); Logging.v(TAG, "dequeueOutputBuffer returned " + index);
return; return;
} }
@ -399,9 +399,9 @@ class AndroidVideoDecoder implements VideoDecoder, VideoSink {
hasDecodedFirstFrame = true; hasDecodedFirstFrame = true;
if (surfaceTextureHelper != null) { if (surfaceTextureHelper != null) {
deliverTextureFrame(result, info, rotation, decodeTimeMs); deliverTextureFrame(index, info, rotation, decodeTimeMs);
} else { } else {
deliverByteFrame(result, info, rotation, decodeTimeMs); deliverByteFrame(index, info, rotation, decodeTimeMs);
} }
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
@ -452,7 +452,7 @@ class AndroidVideoDecoder implements VideoDecoder, VideoSink {
} }
private void deliverByteFrame( private void deliverByteFrame(
int result, MediaCodec.BufferInfo info, int rotation, Integer decodeTimeMs) { int index, MediaCodec.BufferInfo info, int rotation, Integer decodeTimeMs) {
// Load dimensions from shared memory under the dimension lock. // Load dimensions from shared memory under the dimension lock.
int width; int width;
int height; int height;
@ -479,7 +479,7 @@ class AndroidVideoDecoder implements VideoDecoder, VideoSink {
stride = info.size * 2 / (height * 3); stride = info.size * 2 / (height * 3);
} }
ByteBuffer buffer = codec.getOutputBuffers()[result]; ByteBuffer buffer = codec.getOutputBuffer(index);
buffer.position(info.offset); buffer.position(info.offset);
buffer.limit(info.offset + info.size); buffer.limit(info.offset + info.size);
buffer = buffer.slice(); buffer = buffer.slice();
@ -491,7 +491,7 @@ class AndroidVideoDecoder implements VideoDecoder, VideoSink {
// All other supported color formats are NV12. // All other supported color formats are NV12.
frameBuffer = copyNV12ToI420Buffer(buffer, stride, sliceHeight, width, height); frameBuffer = copyNV12ToI420Buffer(buffer, stride, sliceHeight, width, height);
} }
codec.releaseOutputBuffer(result, /* render= */ false); codec.releaseOutputBuffer(index, /* render= */ false);
long presentationTimeNs = info.presentationTimeUs * 1000; long presentationTimeNs = info.presentationTimeUs * 1000;
VideoFrame frame = new VideoFrame(frameBuffer, rotation, presentationTimeNs); VideoFrame frame = new VideoFrame(frameBuffer, rotation, presentationTimeNs);

View File

@ -56,7 +56,7 @@ class HardwareVideoEncoder implements VideoEncoder {
/** /**
* Keeps track of the number of output buffers that have been passed down the pipeline and not yet * Keeps track of the number of output buffers that have been passed down the pipeline and not yet
* released. We need to wait for this to go down to zero before operations invalidating the output * released. We need to wait for this to go down to zero before operations invalidating the output
* buffers, i.e., stop() and getOutputBuffers(). * buffers, i.e., stop() and getOutputBuffer().
*/ */
private static class BusyCount { private static class BusyCount {
private final Object countLock = new Object(); private final Object countLock = new Object();
@ -132,7 +132,6 @@ class HardwareVideoEncoder implements VideoEncoder {
// --- Valid and immutable while an encoding session is running. // --- Valid and immutable while an encoding session is running.
@Nullable private MediaCodecWrapper codec; @Nullable private MediaCodecWrapper codec;
@Nullable private ByteBuffer[] outputBuffers;
// Thread that delivers encoded frames to the user callback. // Thread that delivers encoded frames to the user callback.
@Nullable private Thread outputThread; @Nullable private Thread outputThread;
@ -285,7 +284,6 @@ class HardwareVideoEncoder implements VideoEncoder {
sliceHeight = getSliceHeight(inputFormat, height); sliceHeight = getSliceHeight(inputFormat, height);
codec.start(); codec.start();
outputBuffers = codec.getOutputBuffers();
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Logging.e(TAG, "initEncodeInternal failed", e); Logging.e(TAG, "initEncodeInternal failed", e);
release(); release();
@ -335,7 +333,6 @@ class HardwareVideoEncoder implements VideoEncoder {
outputBuilders.clear(); outputBuilders.clear();
codec = null; codec = null;
outputBuffers = null;
outputThread = null; outputThread = null;
// Allow changing thread after release. // Allow changing thread after release.
@ -454,9 +451,9 @@ class HardwareVideoEncoder implements VideoEncoder {
ByteBuffer buffer; ByteBuffer buffer;
try { try {
buffer = codec.getInputBuffers()[index]; buffer = codec.getInputBuffer(index);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Logging.e(TAG, "getInputBuffers failed", e); Logging.e(TAG, "getInputBuffer with index=" + index + " failed", e);
return VideoCodecStatus.ERROR; return VideoCodecStatus.ERROR;
} }
fillInputBuffer(buffer, videoFrameBuffer); fillInputBuffer(buffer, videoFrameBuffer);
@ -582,12 +579,11 @@ class HardwareVideoEncoder implements VideoEncoder {
if (index < 0) { if (index < 0) {
if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
outputBuffersBusyCount.waitForZero(); outputBuffersBusyCount.waitForZero();
outputBuffers = codec.getOutputBuffers();
} }
return; return;
} }
ByteBuffer codecOutputBuffer = outputBuffers[index]; ByteBuffer codecOutputBuffer = codec.getOutputBuffer(index);
codecOutputBuffer.position(info.offset); codecOutputBuffer.position(info.offset);
codecOutputBuffer.limit(info.offset + info.size); codecOutputBuffer.limit(info.offset + info.size);

View File

@ -45,9 +45,9 @@ interface MediaCodecWrapper {
MediaFormat getOutputFormat(); MediaFormat getOutputFormat();
ByteBuffer[] getInputBuffers(); ByteBuffer getInputBuffer(int index);
ByteBuffer[] getOutputBuffers(); ByteBuffer getOutputBuffer(int index);
Surface createInputSurface(); Surface createInputSurface();

View File

@ -88,13 +88,13 @@ class MediaCodecWrapperFactoryImpl implements MediaCodecWrapperFactory {
} }
@Override @Override
public ByteBuffer[] getInputBuffers() { public ByteBuffer getInputBuffer(int index) {
return mediaCodec.getInputBuffers(); return mediaCodec.getInputBuffer(index);
} }
@Override @Override
public ByteBuffer[] getOutputBuffers() { public ByteBuffer getOutputBuffer(int index) {
return mediaCodec.getOutputBuffers(); return mediaCodec.getOutputBuffer(index);
} }
@Override @Override

View File

@ -283,7 +283,7 @@ public class AndroidVideoDecoderTest {
/* presentationTimeUs= */ anyLong(), /* presentationTimeUs= */ anyLong(),
/* flags= */ eq(0)); /* flags= */ eq(0));
ByteBuffer inputBuffer = fakeMediaCodecWrapper.getInputBuffers()[indexCaptor.getValue()]; ByteBuffer inputBuffer = fakeMediaCodecWrapper.getInputBuffer(indexCaptor.getValue());
CodecTestHelper.assertEqualContents( CodecTestHelper.assertEqualContents(
ENCODED_TEST_DATA, inputBuffer, offsetCaptor.getValue(), sizeCaptor.getValue()); ENCODED_TEST_DATA, inputBuffer, offsetCaptor.getValue(), sizeCaptor.getValue());
} }

View File

@ -292,13 +292,13 @@ public class FakeMediaCodecWrapper implements MediaCodecWrapper {
} }
@Override @Override
public ByteBuffer[] getInputBuffers() { public ByteBuffer getInputBuffer(int index) {
return inputBuffers; return inputBuffers[index];
} }
@Override @Override
public ByteBuffer[] getOutputBuffers() { public ByteBuffer getOutputBuffer(int index) {
return outputBuffers; return outputBuffers[index];
} }
@Override @Override

View File

@ -216,7 +216,7 @@ public class HardwareVideoEncoderTest {
verify(fakeMediaCodecWrapper) verify(fakeMediaCodecWrapper)
.queueInputBuffer(indexCaptor.capture(), offsetCaptor.capture(), sizeCaptor.capture(), .queueInputBuffer(indexCaptor.capture(), offsetCaptor.capture(), sizeCaptor.capture(),
anyLong(), anyInt()); anyLong(), anyInt());
ByteBuffer buffer = fakeMediaCodecWrapper.getInputBuffers()[indexCaptor.getValue()]; ByteBuffer buffer = fakeMediaCodecWrapper.getInputBuffer(indexCaptor.getValue());
CodecTestHelper.assertEqualContents( CodecTestHelper.assertEqualContents(
i420, buffer, offsetCaptor.getValue(), sizeCaptor.getValue()); i420, buffer, offsetCaptor.getValue(), sizeCaptor.getValue());
} }