Android: Respect input buffer layout of MediaFormat
On Android, MediaCodec can request a specific layout of the input buffer. One can use the stride and slice height to calculate the layout from the Encoder's MediaFormat. The current code assumes a specific layout, which is a problematic in Android 12. Fix this by honoring the stride and slice-height. Bug: webrtc:13427 Change-Id: I2d3e429309e3add3ae668e0390460b51e6a49eb9 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/240680 Reviewed-by: Niels Moller <nisse@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Daniel.L (Byoungchan) Lee <daniel.l@hpcnt.com> Cr-Commit-Position: refs/heads/main@{#36033}
This commit is contained in:
committed by
WebRTC LUCI CQ
parent
39f027e0b0
commit
0b06552ab3
@ -14,55 +14,93 @@ import java.nio.ByteBuffer;
|
||||
|
||||
/** Wraps libyuv methods to Java. All passed byte buffers must be direct byte buffers. */
|
||||
public class YuvHelper {
|
||||
/** Helper method for copying I420 to tightly packed destination buffer. */
|
||||
/**
|
||||
* Copy I420 Buffer to a contiguously allocated buffer.
|
||||
* <p> In Android, MediaCodec can request a buffer of a specific layout with the stride and
|
||||
* slice-height (or plane height), and this function is used in this case.
|
||||
* <p> For more information, see
|
||||
* https://cs.android.com/android/platform/superproject/+/64fea7e5726daebc40f46890100837c01091100d:frameworks/base/media/java/android/media/MediaFormat.java;l=568
|
||||
* @param dstStrideY the stride of output buffers' Y plane.
|
||||
* @param dstSliceHeightY the slice-height of output buffer's Y plane.
|
||||
* @param dstStrideU the stride of output buffers' U (and V) plane.
|
||||
* @param dstSliceHeightU the slice-height of output buffer's U (and V) plane
|
||||
*/
|
||||
public static void I420Copy(ByteBuffer srcY, int srcStrideY, ByteBuffer srcU, int srcStrideU,
|
||||
ByteBuffer srcV, int srcStrideV, ByteBuffer dst, int width, int height) {
|
||||
final int chromaHeight = (height + 1) / 2;
|
||||
final int chromaWidth = (width + 1) / 2;
|
||||
ByteBuffer srcV, int srcStrideV, ByteBuffer dst, int dstWidth, int dstHeight, int dstStrideY,
|
||||
int dstSliceHeightY, int dstStrideU, int dstSliceHeightU) {
|
||||
final int chromaWidth = (dstWidth + 1) / 2;
|
||||
final int chromaHeight = (dstHeight + 1) / 2;
|
||||
|
||||
final int minSize = width * height + chromaWidth * chromaHeight * 2;
|
||||
if (dst.capacity() < minSize) {
|
||||
final int dstStartY = 0;
|
||||
final int dstEndY = dstStartY + dstStrideY * dstHeight;
|
||||
final int dstStartU = dstStartY + dstStrideY * dstSliceHeightY;
|
||||
final int dstEndU = dstStartU + dstStrideU * chromaHeight;
|
||||
final int dstStartV = dstStartU + dstStrideU * dstSliceHeightU;
|
||||
// The last line doesn't need any padding, so use chromaWidth
|
||||
// to calculate the exact end position.
|
||||
final int dstEndV = dstStartV + dstStrideU * (chromaHeight - 1) + chromaWidth;
|
||||
if (dst.capacity() < dstEndV) {
|
||||
throw new IllegalArgumentException("Expected destination buffer capacity to be at least "
|
||||
+ minSize + " was " + dst.capacity());
|
||||
+ dstEndV + " was " + dst.capacity());
|
||||
}
|
||||
|
||||
final int startY = 0;
|
||||
final int startU = height * width;
|
||||
final int startV = startU + chromaHeight * chromaWidth;
|
||||
|
||||
dst.position(startY);
|
||||
dst.limit(dstEndY);
|
||||
dst.position(dstStartY);
|
||||
final ByteBuffer dstY = dst.slice();
|
||||
dst.position(startU);
|
||||
dst.limit(dstEndU);
|
||||
dst.position(dstStartU);
|
||||
final ByteBuffer dstU = dst.slice();
|
||||
dst.position(startV);
|
||||
dst.limit(dstEndV);
|
||||
dst.position(dstStartV);
|
||||
final ByteBuffer dstV = dst.slice();
|
||||
|
||||
nativeI420Copy(srcY, srcStrideY, srcU, srcStrideU, srcV, srcStrideV, dstY, width, dstU,
|
||||
chromaWidth, dstV, chromaWidth, width, height);
|
||||
I420Copy(srcY, srcStrideY, srcU, srcStrideU, srcV, srcStrideV, dstY, dstStrideY, dstU,
|
||||
dstStrideU, dstV, dstStrideU, dstWidth, dstHeight);
|
||||
}
|
||||
|
||||
/** Helper method for copying I420 to tightly packed destination buffer. */
|
||||
public static void I420Copy(ByteBuffer srcY, int srcStrideY, ByteBuffer srcU, int srcStrideU,
|
||||
ByteBuffer srcV, int srcStrideV, ByteBuffer dst, int dstWidth, int dstHeight) {
|
||||
I420Copy(srcY, srcStrideY, srcU, srcStrideU, srcV, srcStrideV, dst, dstWidth, dstHeight,
|
||||
dstWidth, dstHeight, (dstWidth + 1) / 2, (dstHeight + 1) / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy I420 Buffer to a contiguously allocated buffer.
|
||||
* @param dstStrideY the stride of output buffers' Y plane.
|
||||
* @param dstSliceHeightY the slice-height of output buffer's Y plane.
|
||||
*/
|
||||
public static void I420ToNV12(ByteBuffer srcY, int srcStrideY, ByteBuffer srcU, int srcStrideU,
|
||||
ByteBuffer srcV, int srcStrideV, ByteBuffer dst, int dstWidth, int dstHeight, int dstStrideY,
|
||||
int dstSliceHeightY) {
|
||||
final int chromaHeight = (dstHeight + 1) / 2;
|
||||
final int chromaWidth = (dstWidth + 1) / 2;
|
||||
|
||||
final int dstStartY = 0;
|
||||
final int dstEndY = dstStartY + dstStrideY * dstHeight;
|
||||
final int dstStartUV = dstStartY + dstStrideY * dstSliceHeightY;
|
||||
final int dstEndUV = dstStartUV + chromaWidth * chromaHeight * 2;
|
||||
if (dst.capacity() < dstEndUV) {
|
||||
throw new IllegalArgumentException("Expected destination buffer capacity to be at least "
|
||||
+ dstEndUV + " was " + dst.capacity());
|
||||
}
|
||||
|
||||
dst.limit(dstEndY);
|
||||
dst.position(dstStartY);
|
||||
final ByteBuffer dstY = dst.slice();
|
||||
dst.limit(dstEndUV);
|
||||
dst.position(dstStartUV);
|
||||
final ByteBuffer dstUV = dst.slice();
|
||||
|
||||
I420ToNV12(srcY, srcStrideY, srcU, srcStrideU, srcV, srcStrideV, dstY, dstStrideY, dstUV,
|
||||
chromaWidth * 2, dstWidth, dstHeight);
|
||||
}
|
||||
|
||||
/** Helper method for copying I420 to tightly packed NV12 destination buffer. */
|
||||
public static void I420ToNV12(ByteBuffer srcY, int srcStrideY, ByteBuffer srcU, int srcStrideU,
|
||||
ByteBuffer srcV, int srcStrideV, ByteBuffer dst, int width, int height) {
|
||||
final int chromaWidth = (width + 1) / 2;
|
||||
final int chromaHeight = (height + 1) / 2;
|
||||
|
||||
final int minSize = width * height + chromaWidth * chromaHeight * 2;
|
||||
if (dst.capacity() < minSize) {
|
||||
throw new IllegalArgumentException("Expected destination buffer capacity to be at least "
|
||||
+ minSize + " was " + dst.capacity());
|
||||
}
|
||||
|
||||
final int startY = 0;
|
||||
final int startUV = height * width;
|
||||
|
||||
dst.position(startY);
|
||||
final ByteBuffer dstY = dst.slice();
|
||||
dst.position(startUV);
|
||||
final ByteBuffer dstUV = dst.slice();
|
||||
|
||||
nativeI420ToNV12(srcY, srcStrideY, srcU, srcStrideU, srcV, srcStrideV, dstY, width, dstUV,
|
||||
chromaWidth * 2, width, height);
|
||||
ByteBuffer srcV, int srcStrideV, ByteBuffer dst, int dstWidth, int dstHeight) {
|
||||
I420ToNV12(srcY, srcStrideY, srcU, srcStrideU, srcV, srcStrideV, dst, dstWidth, dstHeight,
|
||||
dstWidth, dstHeight);
|
||||
}
|
||||
|
||||
/** Helper method for rotating I420 to tightly packed destination buffer. */
|
||||
@ -109,9 +147,18 @@ public class YuvHelper {
|
||||
src, srcStride, dstY, dstStrideY, dstU, dstStrideU, dstV, dstStrideV, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies I420 to the I420 dst buffer.
|
||||
* <p> Unlike `libyuv::I420Copy`, this function checks if the height <= 0, so flipping is not
|
||||
* supported.
|
||||
*/
|
||||
public static void I420Copy(ByteBuffer srcY, int srcStrideY, ByteBuffer srcU, int srcStrideU,
|
||||
ByteBuffer srcV, int srcStrideV, ByteBuffer dstY, int dstStrideY, ByteBuffer dstU,
|
||||
int dstStrideU, ByteBuffer dstV, int dstStrideV, int width, int height) {
|
||||
if (srcY == null || srcU == null || srcV == null || dstY == null || dstU == null || dstV == null
|
||||
|| width <= 0 || height <= 0) {
|
||||
throw new IllegalArgumentException("Invalid I420Copy input arguments");
|
||||
}
|
||||
nativeI420Copy(srcY, srcStrideY, srcU, srcStrideU, srcV, srcStrideV, dstY, dstStrideY, dstU,
|
||||
dstStrideU, dstV, dstStrideV, width, height);
|
||||
}
|
||||
@ -119,6 +166,10 @@ public class YuvHelper {
|
||||
public static void I420ToNV12(ByteBuffer srcY, int srcStrideY, ByteBuffer srcU, int srcStrideU,
|
||||
ByteBuffer srcV, int srcStrideV, ByteBuffer dstY, int dstStrideY, ByteBuffer dstUV,
|
||||
int dstStrideUV, int width, int height) {
|
||||
if (srcY == null || srcU == null || srcV == null || dstY == null || dstUV == null || width <= 0
|
||||
|| height <= 0) {
|
||||
throw new IllegalArgumentException("Invalid I420ToNV12 input arguments");
|
||||
}
|
||||
nativeI420ToNV12(srcY, srcStrideY, srcU, srcStrideU, srcV, srcStrideV, dstY, dstStrideY, dstUV,
|
||||
dstStrideUV, width, height);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user