Fetch encoded QP before releasing output buffer
Before this change we first released output frame buffer in the code path which prepends config buffer to a keyframe and then called getOutputFormat(index). getOutputFormat(index) throws an exception if index points to a released buffer. This change rearranges calls such that getOutputFormat(index) always executed before releaseOutputBuffer(index).
Bug: webrtc:15015
Change-Id: Ia64f5d8e7483aeeb316d1a71c0cb79233f4bbee9
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/301364
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39874}
diff --git a/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java b/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java
index 0a6e344..773f2b9 100644
--- a/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java
+++ b/sdk/android/src/java/org/webrtc/HardwareVideoEncoder.java
@@ -606,6 +606,15 @@
Logging.d(TAG, "Sync frame generated");
}
+ // Extract QP before releasing output buffer.
+ Integer qp = null;
+ if (isEncodingStatisticsEnabled) {
+ MediaFormat format = codec.getOutputFormat(index);
+ if (format != null && format.containsKey(MediaFormat.KEY_VIDEO_QP_AVERAGE)) {
+ qp = format.getInteger(MediaFormat.KEY_VIDEO_QP_AVERAGE);
+ }
+ }
+
final ByteBuffer frameBuffer;
final Runnable releaseCallback;
if (isKeyFrame && configBuffer != null) {
@@ -639,15 +648,9 @@
: EncodedImage.FrameType.VideoFrameDelta;
EncodedImage.Builder builder = outputBuilders.poll();
- builder.setBuffer(frameBuffer, releaseCallback).setFrameType(frameType);
-
- if (isEncodingStatisticsEnabled) {
- MediaFormat format = codec.getOutputFormat(index);
- if (format != null && format.containsKey(MediaFormat.KEY_VIDEO_QP_AVERAGE)) {
- int qp = format.getInteger(MediaFormat.KEY_VIDEO_QP_AVERAGE);
- builder.setQp(qp);
- }
- }
+ builder.setBuffer(frameBuffer, releaseCallback);
+ builder.setFrameType(frameType);
+ builder.setQp(qp);
EncodedImage encodedImage = builder.createEncodedImage();
// TODO(mellem): Set codec-specific info.
diff --git a/sdk/android/tests/src/org/webrtc/HardwareVideoEncoderTest.java b/sdk/android/tests/src/org/webrtc/HardwareVideoEncoderTest.java
index 919975d..6c3af8a 100644
--- a/sdk/android/tests/src/org/webrtc/HardwareVideoEncoderTest.java
+++ b/sdk/android/tests/src/org/webrtc/HardwareVideoEncoderTest.java
@@ -18,8 +18,10 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -43,6 +45,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
@@ -289,6 +292,31 @@
}
@Test
+ public void encodingStatistics_fetchedBeforeFrameBufferIsReleased() throws InterruptedException {
+ TestEncoder encoder =
+ new TestEncoderBuilder().setCodecType(H264).SetIsEncodingStatisticsSupported(true).build();
+ assertThat(encoder.initEncode(TEST_ENCODER_SETTINGS, mockEncoderCallback))
+ .isEqualTo(VideoCodecStatus.OK);
+
+ encoder.encode(createTestVideoFrame(/* timestampNs= */ 42), ENCODE_INFO_KEY_FRAME);
+ fakeMediaCodecWrapper.addOutputData(CodecTestHelper.generateRandomData(/* length= */ 100),
+ /* presentationTimestampUs= */ 0,
+ /* flags= */ MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
+ encoder.waitDeliverEncodedImage();
+
+ int frameBufferIndex =
+ fakeMediaCodecWrapper.addOutputData(CodecTestHelper.generateRandomData(/* length= */ 100),
+ /* presentationTimestampUs= */ 0,
+ /* flags= */ MediaCodec.BUFFER_FLAG_SYNC_FRAME);
+ encoder.waitDeliverEncodedImage();
+
+ InOrder inOrder = inOrder(fakeMediaCodecWrapper);
+ inOrder.verify(fakeMediaCodecWrapper).getOutputFormat(eq(frameBufferIndex));
+ inOrder.verify(fakeMediaCodecWrapper)
+ .releaseOutputBuffer(eq(frameBufferIndex), /* render= */ eq(false));
+ }
+
+ @Test
public void testEncodeByteBuffer() {
// Set-up.
HardwareVideoEncoder encoder = new TestEncoderBuilder().build();