VpxEncoderTest: Record decoded video within crop rectangle if given

The encoder quality test scheme is to record the decoded video with frame size
"width" and "height" obtained from the format of decoder. However, "width" and
"height" is the frame size for decoder's allocation (coded size), which may be
larger than the size from encoding demand. For such case, "crop rectangle"
should be specified in format which occupies a portion of the video frame.

That is, the video content within "crop rectangle" is what we need to record
for calculating PSNR rather than the whole frame.

This CL has changed to use "crop rectangle" to record video if given.

Bug: 119247958
Test: run android.media.cts.VpxEncoderTest#testEncoderQualityVP9
Change-Id: I4ee191b58e3f4154c88cc6fe3e69f3a1b4b7ebd0
diff --git a/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java b/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java
index 20c9d3a..48faf49 100644
--- a/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java
+++ b/tests/tests/media/src/android/media/cts/VpxCodecTestBase.java
@@ -367,24 +367,27 @@
 
     /**
      * Packs YUV420 frame by moving it to a smaller size buffer with stride and slice
-     * height equal to the original frame width and height.
+     * height equal to the crop window.
      */
-    private static byte[] PackYUV420(int width, int height,
+    private static byte[] PackYUV420(int left, int top, int width, int height,
             int stride, int sliceHeight, byte[] src) {
         byte[] dst = new byte[width * height * 3 / 2];
         // Y copy.
         for (int i = 0; i < height; i++) {
-            System.arraycopy(src, i * stride, dst, i * width, width);
+            System.arraycopy(src, (i + top) * stride + left, dst, i * width, width);
         }
         // U and V copy.
         int u_src_offset = stride * sliceHeight;
         int v_src_offset = u_src_offset + u_src_offset / 4;
         int u_dst_offset = width * height;
         int v_dst_offset = u_dst_offset + u_dst_offset / 4;
+        // Downsample and align to floor-2 for crop origin.
+        left /= 2;
+        top /= 2;
         for (int i = 0; i < height / 2; i++) {
-            System.arraycopy(src, u_src_offset + i * (stride / 2),
+            System.arraycopy(src, u_src_offset + (i + top) * (stride / 2) + left,
                     dst, u_dst_offset + i * (width / 2), width / 2);
-            System.arraycopy(src, v_src_offset + i * (stride / 2),
+            System.arraycopy(src, v_src_offset + (i + top) * (stride / 2) + left,
                     dst, v_dst_offset + i * (width / 2), width / 2);
         }
         return dst;
@@ -532,6 +535,10 @@
         int frameCount = ivf.getFrameCount();
         int frameStride = frameWidth;
         int frameSliceHeight = frameHeight;
+        int cropLeft = 0;
+        int cropTop = 0;
+        int cropWidth = frameWidth;
+        int cropHeight = frameHeight;
         assertTrue(frameWidth > 0);
         assertTrue(frameHeight > 0);
         assertTrue(frameCount > 0);
@@ -634,6 +641,28 @@
                             " x " + frameSliceHeight);
                     frameStride = Math.max(frameWidth, frameStride);
                     frameSliceHeight = Math.max(frameHeight, frameSliceHeight);
+
+                    // Parse crop window for the area of recording decoded frame data.
+                    if (format.containsKey("crop-left")) {
+                        cropLeft = format.getInteger("crop-left");
+                    }
+                    if (format.containsKey("crop-top")) {
+                        cropTop = format.getInteger("crop-top");
+                    }
+                    if (format.containsKey("crop-right")) {
+                        cropWidth = format.getInteger("crop-right") - cropLeft + 1;
+                    } else {
+                        cropWidth = frameWidth;
+                    }
+                    if (format.containsKey("crop-bottom")) {
+                        cropHeight = format.getInteger("crop-bottom") - cropTop + 1;
+                    } else {
+                        cropHeight = frameHeight;
+                    }
+                    Log.d(TAG, "Frame crop window origin: " + cropLeft + " x " + cropTop
+                            + ", size: " + cropWidth + " x " + cropHeight);
+                    cropWidth = Math.min(frameWidth - cropLeft, cropWidth);
+                    cropHeight = Math.min(frameHeight - cropTop, cropHeight);
                 }
                 result = decoder.dequeueOutputBuffer(bufferInfo, DEFAULT_DEQUEUE_TIMEOUT_US);
             }
@@ -660,11 +689,11 @@
                             frame = NV12ToYUV420(frameWidth, frameHeight,
                                     frameStride, frameSliceHeight, frame);
                         }
-                        int writeLength = Math.min(frameWidth * frameHeight * 3 / 2, frame.length);
+                        int writeLength = Math.min(cropWidth * cropHeight * 3 / 2, frame.length);
                         // Pack frame if necessary.
                         if (writeLength < frame.length &&
-                                (frameStride > frameWidth || frameSliceHeight > frameHeight)) {
-                            frame = PackYUV420(frameWidth, frameHeight,
+                                (frameStride > cropWidth || frameSliceHeight > cropHeight)) {
+                            frame = PackYUV420(cropLeft, cropTop, cropWidth, cropHeight,
                                     frameStride, frameSliceHeight, frame);
                         }
                         yuv.write(frame, 0, writeLength);