Merge "MediaCodec: address API review #2" into rvc-dev am: de9e6f71d0

Change-Id: I0b4d55c804c6c1547daecde18bf3c04fe60be4e9
This commit is contained in:
Wonsik Kim
2020-03-19 21:51:44 +00:00
committed by Automerger Merge Worker
4 changed files with 271 additions and 525 deletions

View File

@@ -16805,6 +16805,7 @@ package android.hardware {
field public static final long USAGE_PROTECTED_CONTENT = 16384L; // 0x4000L field public static final long USAGE_PROTECTED_CONTENT = 16384L; // 0x4000L
field public static final long USAGE_SENSOR_DIRECT_DATA = 8388608L; // 0x800000L field public static final long USAGE_SENSOR_DIRECT_DATA = 8388608L; // 0x800000L
field public static final long USAGE_VIDEO_ENCODE = 65536L; // 0x10000L field public static final long USAGE_VIDEO_ENCODE = 65536L; // 0x10000L
field public static final int YCBCR_420_888 = 35; // 0x23
} }
public final class Sensor { public final class Sensor {
@@ -25205,6 +25206,7 @@ package android.media {
method @NonNull public android.media.MediaCodec.OutputFrame getOutputFrame(int); method @NonNull public android.media.MediaCodec.OutputFrame getOutputFrame(int);
method @Nullable public android.media.Image getOutputImage(int); method @Nullable public android.media.Image getOutputImage(int);
method @NonNull public android.media.MediaCodec.QueueRequest getQueueRequest(int); method @NonNull public android.media.MediaCodec.QueueRequest getQueueRequest(int);
method @Nullable public static android.media.Image mapHardwareBuffer(@NonNull android.hardware.HardwareBuffer);
method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException; method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
method public void queueSecureInputBuffer(int, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException; method public void queueSecureInputBuffer(int, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
method public void release(); method public void release();
@@ -25305,17 +25307,7 @@ package android.media {
method public void set(int, int); method public void set(int, int);
} }
public static final class MediaCodec.GraphicBlock {
method protected void finalize();
method public static boolean isCodecCopyFreeCompatible(@NonNull String[]);
method public boolean isMappable();
method @NonNull public android.media.Image map();
method @NonNull public static android.media.MediaCodec.GraphicBlock obtain(int, int, int, long, @NonNull String[]);
method public void recycle();
}
public class MediaCodec.IncompatibleWithBlockModelException extends java.lang.RuntimeException { public class MediaCodec.IncompatibleWithBlockModelException extends java.lang.RuntimeException {
ctor public MediaCodec.IncompatibleWithBlockModelException();
} }
public static final class MediaCodec.LinearBlock { public static final class MediaCodec.LinearBlock {
@@ -25345,12 +25337,12 @@ package android.media {
} }
public static final class MediaCodec.OutputFrame { public static final class MediaCodec.OutputFrame {
method @NonNull public java.util.Set<java.lang.String> getChangedKeys();
method public int getFlags(); method public int getFlags();
method @NonNull public android.media.MediaFormat getFormat(); method @NonNull public android.media.MediaFormat getFormat();
method @Nullable public android.media.MediaCodec.GraphicBlock getGraphicBlock(); method @Nullable public android.hardware.HardwareBuffer getHardwareBuffer();
method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock(); method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
method public long getPresentationTimeUs(); method public long getPresentationTimeUs();
method public void retrieveChangedKeys(@NonNull java.util.Set<java.lang.String>);
} }
public final class MediaCodec.QueueRequest { public final class MediaCodec.QueueRequest {
@@ -25358,7 +25350,7 @@ package android.media {
method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer); method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer);
method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int); method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int);
method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float); method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float);
method @NonNull public android.media.MediaCodec.QueueRequest setGraphicBlock(@NonNull android.media.MediaCodec.GraphicBlock); method @NonNull public android.media.MediaCodec.QueueRequest setHardwareBuffer(@NonNull android.hardware.HardwareBuffer);
method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int); method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int);
method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @Nullable android.media.MediaCodec.CryptoInfo); method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long); method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long);

View File

@@ -46,7 +46,7 @@ import java.lang.annotation.RetentionPolicy;
public final class HardwareBuffer implements Parcelable, AutoCloseable { public final class HardwareBuffer implements Parcelable, AutoCloseable {
/** @hide */ /** @hide */
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "RGB", "BLOB", "D_", "DS_", "S_" }, value = { @IntDef(prefix = { "RGB", "BLOB", "YCBCR_", "D_", "DS_", "S_" }, value = {
RGBA_8888, RGBA_8888,
RGBA_FP16, RGBA_FP16,
RGBA_1010102, RGBA_1010102,
@@ -54,6 +54,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
RGB_888, RGB_888,
RGB_565, RGB_565,
BLOB, BLOB,
YCBCR_420_888,
D_16, D_16,
D_24, D_24,
DS_24UI8, DS_24UI8,
@@ -79,6 +80,8 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
public static final int RGBA_1010102 = 0x2b; public static final int RGBA_1010102 = 0x2b;
/** Format: opaque format used for raw data transfer; must have a height of 1 */ /** Format: opaque format used for raw data transfer; must have a height of 1 */
public static final int BLOB = 0x21; public static final int BLOB = 0x21;
/** Format: Planar YCbCr 420; must have an even width and height */
public static final int YCBCR_420_888 = 0x23;
/** Format: 16 bits depth */ /** Format: 16 bits depth */
public static final int D_16 = 0x30; public static final int D_16 = 0x30;
/** Format: 24 bits depth */ /** Format: 24 bits depth */
@@ -396,6 +399,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
case RGB_565: case RGB_565:
case RGB_888: case RGB_888:
case BLOB: case BLOB:
case YCBCR_420_888:
case D_16: case D_16:
case D_24: case D_24:
case DS_24UI8: case DS_24UI8:

View File

@@ -42,7 +42,9 @@ import java.nio.ByteOrder;
import java.nio.ReadOnlyBufferException; import java.nio.ReadOnlyBufferException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
@@ -1957,7 +1959,7 @@ final public class MediaCodec {
/** /**
* If this codec is to be used with {@link LinearBlock} and/or {@link * If this codec is to be used with {@link LinearBlock} and/or {@link
* GraphicBlock}, pass this flag. * HardwareBuffer}, pass this flag.
* <p> * <p>
* When this flag is set, the following APIs throw {@link IncompatibleWithBlockModelException}. * When this flag is set, the following APIs throw {@link IncompatibleWithBlockModelException}.
* <ul> * <ul>
@@ -1989,6 +1991,19 @@ final public class MediaCodec {
* Thrown when the codec is configured for block model and an incompatible API is called. * Thrown when the codec is configured for block model and an incompatible API is called.
*/ */
public class IncompatibleWithBlockModelException extends RuntimeException { public class IncompatibleWithBlockModelException extends RuntimeException {
IncompatibleWithBlockModelException() { }
IncompatibleWithBlockModelException(String message) {
super(message);
}
IncompatibleWithBlockModelException(String message, Throwable cause) {
super(message, cause);
}
IncompatibleWithBlockModelException(Throwable cause) {
super(cause);
}
} }
/** /**
@@ -2532,7 +2547,9 @@ final public class MediaCodec {
throws CryptoException { throws CryptoException {
synchronized(mBufferLock) { synchronized(mBufferLock) {
if (mBufferMode == BUFFER_MODE_BLOCK) { if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException(); throw new IncompatibleWithBlockModelException("queueInputBuffer() "
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please use getQueueRequest() to queue buffers");
} }
invalidateByteBuffer(mCachedInputBuffers, index); invalidateByteBuffer(mCachedInputBuffers, index);
mDequeuedInputBuffers.remove(index); mDequeuedInputBuffers.remove(index);
@@ -2784,7 +2801,9 @@ final public class MediaCodec {
int flags) throws CryptoException { int flags) throws CryptoException {
synchronized(mBufferLock) { synchronized(mBufferLock) {
if (mBufferMode == BUFFER_MODE_BLOCK) { if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException(); throw new IncompatibleWithBlockModelException("queueSecureInputBuffer() "
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please use getQueueRequest() to queue buffers");
} }
invalidateByteBuffer(mCachedInputBuffers, index); invalidateByteBuffer(mCachedInputBuffers, index);
mDequeuedInputBuffers.remove(index); mDequeuedInputBuffers.remove(index);
@@ -2819,7 +2838,9 @@ final public class MediaCodec {
public final int dequeueInputBuffer(long timeoutUs) { public final int dequeueInputBuffer(long timeoutUs) {
synchronized (mBufferLock) { synchronized (mBufferLock) {
if (mBufferMode == BUFFER_MODE_BLOCK) { if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException(); throw new IncompatibleWithBlockModelException("dequeueInputBuffer() "
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please use MediaCodec.Callback objectes to get input buffer slots.");
} }
} }
int res = native_dequeueInputBuffer(timeoutUs); int res = native_dequeueInputBuffer(timeoutUs);
@@ -2989,178 +3010,21 @@ final public class MediaCodec {
} }
/** /**
* Section of memory that represents a graphic block. Applications may * Map a {@link HardwareBuffer} object into {@link Image}, so that the content of the buffer is
* acquire a block via {@link GraphicBlock#obtain} and queue * accessible. Depending on the usage and pixel format of the hardware buffer, it may not be
* the block as an input buffer to a codec, or get a block allocated by * mappable; this method returns null in that case.
* codec as an output buffer from {@link OutputFrame}.
* *
* {@see QueueRequest#setGraphicBlock} * @param hardwareBuffer {@link HardwareBuffer} to map.
* {@see OutputFrame#getGraphicBlock} * @return Mapped {@link Image} object, or null if the buffer is not mappable.
*/ */
public static final class GraphicBlock { public static @Nullable Image mapHardwareBuffer(@NonNull HardwareBuffer hardwareBuffer) {
// No public constructors. return native_mapHardwareBuffer(hardwareBuffer);
private GraphicBlock() {}
/**
* Returns true if the buffer is mappable.
* @throws IllegalStateException if invalid
*/
public boolean isMappable() {
synchronized (mLock) {
if (!mValid) {
throw new IllegalStateException("The graphic block is invalid");
}
return mMappable;
}
} }
/** private static native @Nullable Image native_mapHardwareBuffer(
* Map the memory and return the mapped region. @NonNull HardwareBuffer hardwareBuffer);
* <p>
* Calling {@link #recycle} or
* {@link QueueRequest#setGraphicBlock} causes the returned
* {@link Image} object to be closed, if not already.
*
* @return mapped memory region as {@link Image} object
* @throws IllegalStateException if not mappable or invalid
*/
public @NonNull Image map() {
synchronized (mLock) {
if (!mValid) {
throw new IllegalStateException("The graphic block is invalid");
}
if (!mMappable) {
throw new IllegalStateException("The graphic block is not mappable");
}
if (mMapped == null) {
mMapped = native_map();
}
return mMapped;
}
}
private native Image native_map(); private static native void native_closeMediaImage(long context);
/**
* Mark this block as ready to be recycled by the framework once it is
* no longer in use. All operations to this object after
* this call will cause exceptions, as well as attempt to access the
* previously mapped memory region. Caller should clear all references
* to this object after this call.
* <p>
* To avoid excessive memory consumption, it is recommended that callers
* recycle buffers as soon as they no longer need the buffers.
*
* @throws IllegalStateException if invalid
*/
public void recycle() {
synchronized (mLock) {
if (!mValid) {
throw new IllegalStateException("The graphic block is invalid");
}
if (mMapped != null) {
mMapped.close();
mMapped = null;
}
native_recycle();
mValid = false;
mNativeContext = 0;
}
sPool.offer(this);
}
private native void native_recycle();
/**
* Returns true if it is possible to allocate a graphic block that
* can be passed to all listed codecs as an input buffer without
* copying.
* <p>
* Note that even if this function returns true, {@link #obtain}
* may still throw due to invalid arguments or allocation failure.
* In addition, choosing a format that is not natively supported by the
* codec may cause color conversion.
*
* @param codecNames list of codecs that the client wants to use a
* graphic block without copying. Null entries are
* ignored.
*/
public static boolean isCodecCopyFreeCompatible(@NonNull String[] codecNames) {
return native_checkCompatible(codecNames);
}
private static native boolean native_checkCompatible(@NonNull String[] codecNames);
// Called from native
private void setInternalStateLocked(long context, boolean isMappable) {
mNativeContext = context;
mMappable = isMappable;
mValid = (context != 0);
}
private static final BlockingQueue<GraphicBlock> sPool =
new LinkedBlockingQueue<>();
/**
* Obtain a graphic block object of dimension
* {@code width}x{@code height}.
* If {@link #isCodecCopyFreeCompatible} with the same
* {@code codecNames} returned true, the returned
* {@link GraphicBlock} object can be queued to the listed codecs
* without copying. The returned {@link GraphicBlock} object is always
* read/write mappable.
*
* @param width requested width of the graphic block
* @param height requested height of the graphic block
* @param format the format of pixels. One of the {@code COLOR_Format}
* values from {@link MediaCodecInfo.CodecCapabilities}.
* @param usage the usage of the buffer. @HardwareBuffer.Usage
* @param codecNames list of codecs that the client wants to use this
* graphic block without copying. Null entries are
* ignored.
* @return a graphic block object.
* @throws IllegalArgumentException if the parameters are invalid or
* not supported
* @throws IOException if an error occurred while allocating a buffer
*/
public static @NonNull GraphicBlock obtain(
int width,
int height,
int format,
@HardwareBuffer.Usage long usage,
@NonNull String[] codecNames) {
GraphicBlock buffer = sPool.poll();
if (buffer == null) {
buffer = new GraphicBlock();
}
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException(
"non-positive width or height: " + width + "x" + height);
}
synchronized (buffer.mLock) {
buffer.native_obtain(width, height, format, usage, codecNames);
}
return buffer;
}
private native void native_obtain(
int width,
int height,
int format,
@HardwareBuffer.Usage long usage,
@NonNull String[] codecNames);
@Override
protected void finalize() {
native_recycle();
}
private final Object mLock = new Object();
private boolean mValid = false;
private boolean mMappable = false;
private Image mMapped = null;
private long mNativeContext = 0;
}
/** /**
* Builder-like class for queue requests. Use this class to prepare a * Builder-like class for queue requests. Use this class to prepare a
@@ -3185,7 +3049,7 @@ final public class MediaCodec {
* @param offset The byte offset into the input buffer at which the data starts. * @param offset The byte offset into the input buffer at which the data starts.
* @param size The number of bytes of valid input data. * @param size The number of bytes of valid input data.
* @param cryptoInfo Metadata describing the structure of the encrypted input sample. * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
* may be null if clear. * may be null for non-encrypted content.
* @return this object * @return this object
* @throws IllegalStateException if a buffer is already set * @throws IllegalStateException if a buffer is already set
*/ */
@@ -3197,7 +3061,7 @@ final public class MediaCodec {
if (!isAccessible()) { if (!isAccessible()) {
throw new IllegalStateException("The request is stale"); throw new IllegalStateException("The request is stale");
} }
if (mLinearBlock != null || mGraphicBlock != null) { if (mLinearBlock != null || mHardwareBuffer != null) {
throw new IllegalStateException("Cannot set block twice"); throw new IllegalStateException("Cannot set block twice");
} }
mLinearBlock = block; mLinearBlock = block;
@@ -3208,22 +3072,27 @@ final public class MediaCodec {
} }
/** /**
* Set a graphic block to this queue request. Exactly one buffer must * Set a harware graphic buffer to this queue request. Exactly one buffer must
* be set for a queue request before calling {@link #queue}. * be set for a queue request before calling {@link #queue}.
* <p>
* Note: buffers should have format {@link HardwareBuffer#YCBCR_420_888},
* a single layer, and an appropriate usage ({@link HardwareBuffer#USAGE_CPU_READ_OFTEN}
* for software codecs and {@link HardwareBuffer#USAGE_VIDEO_ENCODE} for hardware)
* for codecs to recognize. Codecs may throw exception if the buffer is not recognizable.
* *
* @param block The graphic block object * @param buffer The hardware graphic buffer object
* @return this object * @return this object
* @throws IllegalStateException if a buffer is already set * @throws IllegalStateException if a buffer is already set
*/ */
public @NonNull QueueRequest setGraphicBlock( public @NonNull QueueRequest setHardwareBuffer(
@NonNull GraphicBlock block) { @NonNull HardwareBuffer buffer) {
if (!isAccessible()) { if (!isAccessible()) {
throw new IllegalStateException("The request is stale"); throw new IllegalStateException("The request is stale");
} }
if (mLinearBlock != null || mGraphicBlock != null) { if (mLinearBlock != null || mHardwareBuffer != null) {
throw new IllegalStateException("Cannot set block twice"); throw new IllegalStateException("Cannot set block twice");
} }
mGraphicBlock = block; mHardwareBuffer = buffer;
return this; return this;
} }
@@ -3375,7 +3244,7 @@ final public class MediaCodec {
if (!isAccessible()) { if (!isAccessible()) {
throw new IllegalStateException("The request is stale"); throw new IllegalStateException("The request is stale");
} }
if (mLinearBlock == null && mGraphicBlock == null) { if (mLinearBlock == null && mHardwareBuffer == null) {
throw new IllegalStateException("No block is set"); throw new IllegalStateException("No block is set");
} }
setAccessible(false); setAccessible(false);
@@ -3384,9 +3253,9 @@ final public class MediaCodec {
mIndex, mLinearBlock, mOffset, mSize, mCryptoInfo, mIndex, mLinearBlock, mOffset, mSize, mCryptoInfo,
mPresentationTimeUs, mFlags, mPresentationTimeUs, mFlags,
mTuningKeys, mTuningValues); mTuningKeys, mTuningValues);
} else if (mGraphicBlock != null) { } else if (mHardwareBuffer != null) {
mCodec.native_queueGraphicBlock( mCodec.native_queueHardwareBuffer(
mIndex, mGraphicBlock, mPresentationTimeUs, mFlags, mIndex, mHardwareBuffer, mPresentationTimeUs, mFlags,
mTuningKeys, mTuningValues); mTuningKeys, mTuningValues);
} }
clear(); clear();
@@ -3397,7 +3266,7 @@ final public class MediaCodec {
mOffset = 0; mOffset = 0;
mSize = 0; mSize = 0;
mCryptoInfo = null; mCryptoInfo = null;
mGraphicBlock = null; mHardwareBuffer = null;
mPresentationTimeUs = 0; mPresentationTimeUs = 0;
mFlags = 0; mFlags = 0;
mTuningKeys.clear(); mTuningKeys.clear();
@@ -3420,7 +3289,7 @@ final public class MediaCodec {
private int mOffset = 0; private int mOffset = 0;
private int mSize = 0; private int mSize = 0;
private MediaCodec.CryptoInfo mCryptoInfo = null; private MediaCodec.CryptoInfo mCryptoInfo = null;
private GraphicBlock mGraphicBlock = null; private HardwareBuffer mHardwareBuffer = null;
private long mPresentationTimeUs = 0; private long mPresentationTimeUs = 0;
private @BufferFlag int mFlags = 0; private @BufferFlag int mFlags = 0;
private final ArrayList<String> mTuningKeys = new ArrayList<>(); private final ArrayList<String> mTuningKeys = new ArrayList<>();
@@ -3440,9 +3309,9 @@ final public class MediaCodec {
@NonNull ArrayList<String> keys, @NonNull ArrayList<String> keys,
@NonNull ArrayList<Object> values); @NonNull ArrayList<Object> values);
private native void native_queueGraphicBlock( private native void native_queueHardwareBuffer(
int index, int index,
@NonNull GraphicBlock block, @NonNull HardwareBuffer buffer,
long presentationTimeUs, long presentationTimeUs,
int flags, int flags,
@NonNull ArrayList<String> keys, @NonNull ArrayList<String> keys,
@@ -3535,7 +3404,9 @@ final public class MediaCodec {
@NonNull BufferInfo info, long timeoutUs) { @NonNull BufferInfo info, long timeoutUs) {
synchronized (mBufferLock) { synchronized (mBufferLock) {
if (mBufferMode == BUFFER_MODE_BLOCK) { if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException(); throw new IncompatibleWithBlockModelException("dequeueOutputBuffer() "
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please use MediaCodec.Callback objects to get output buffer slots.");
} }
} }
int res = native_dequeueOutputBuffer(info, timeoutUs); int res = native_dequeueOutputBuffer(info, timeoutUs);
@@ -3917,7 +3788,10 @@ final public class MediaCodec {
public ByteBuffer[] getInputBuffers() { public ByteBuffer[] getInputBuffers() {
synchronized (mBufferLock) { synchronized (mBufferLock) {
if (mBufferMode == BUFFER_MODE_BLOCK) { if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException(); throw new IncompatibleWithBlockModelException("getInputBuffers() "
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please obtain MediaCodec.LinearBlock or HardwareBuffer "
+ "objects and attach to QueueRequest objects.");
} }
if (mCachedInputBuffers == null) { if (mCachedInputBuffers == null) {
throw new IllegalStateException(); throw new IllegalStateException();
@@ -3953,7 +3827,9 @@ final public class MediaCodec {
public ByteBuffer[] getOutputBuffers() { public ByteBuffer[] getOutputBuffers() {
synchronized (mBufferLock) { synchronized (mBufferLock) {
if (mBufferMode == BUFFER_MODE_BLOCK) { if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException(); throw new IncompatibleWithBlockModelException("getOutputBuffers() "
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please use getOutputFrame to get output frames.");
} }
if (mCachedOutputBuffers == null) { if (mCachedOutputBuffers == null) {
throw new IllegalStateException(); throw new IllegalStateException();
@@ -3985,7 +3861,10 @@ final public class MediaCodec {
public ByteBuffer getInputBuffer(int index) { public ByteBuffer getInputBuffer(int index) {
synchronized (mBufferLock) { synchronized (mBufferLock) {
if (mBufferMode == BUFFER_MODE_BLOCK) { if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException(); throw new IncompatibleWithBlockModelException("getInputBuffer() "
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please obtain MediaCodec.LinearBlock or HardwareBuffer "
+ "objects and attach to QueueRequest objects.");
} }
} }
ByteBuffer newBuffer = getBuffer(true /* input */, index); ByteBuffer newBuffer = getBuffer(true /* input */, index);
@@ -4019,7 +3898,10 @@ final public class MediaCodec {
public Image getInputImage(int index) { public Image getInputImage(int index) {
synchronized (mBufferLock) { synchronized (mBufferLock) {
if (mBufferMode == BUFFER_MODE_BLOCK) { if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException(); throw new IncompatibleWithBlockModelException("getInputImage() "
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please obtain MediaCodec.LinearBlock or HardwareBuffer "
+ "objects and attach to QueueRequest objects.");
} }
} }
Image newImage = getImage(true /* input */, index); Image newImage = getImage(true /* input */, index);
@@ -4053,7 +3935,9 @@ final public class MediaCodec {
public ByteBuffer getOutputBuffer(int index) { public ByteBuffer getOutputBuffer(int index) {
synchronized (mBufferLock) { synchronized (mBufferLock) {
if (mBufferMode == BUFFER_MODE_BLOCK) { if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException(); throw new IncompatibleWithBlockModelException("getOutputBuffer() "
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please use getOutputFrame() to get output frames.");
} }
} }
ByteBuffer newBuffer = getBuffer(false /* input */, index); ByteBuffer newBuffer = getBuffer(false /* input */, index);
@@ -4086,7 +3970,9 @@ final public class MediaCodec {
public Image getOutputImage(int index) { public Image getOutputImage(int index) {
synchronized (mBufferLock) { synchronized (mBufferLock) {
if (mBufferMode == BUFFER_MODE_BLOCK) { if (mBufferMode == BUFFER_MODE_BLOCK) {
throw new IncompatibleWithBlockModelException(); throw new IncompatibleWithBlockModelException("getOutputImage() "
+ "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ "Please use getOutputFrame() to get output frames.");
} }
} }
Image newImage = getImage(false /* input */, index); Image newImage = getImage(false /* input */, index);
@@ -4112,22 +3998,22 @@ final public class MediaCodec {
* @throws IllegalStateException if this output frame is not linear. * @throws IllegalStateException if this output frame is not linear.
*/ */
public @Nullable LinearBlock getLinearBlock() { public @Nullable LinearBlock getLinearBlock() {
if (mGraphicBlock != null) { if (mHardwareBuffer != null) {
throw new IllegalStateException("This output frame is not linear"); throw new IllegalStateException("This output frame is not linear");
} }
return mLinearBlock; return mLinearBlock;
} }
/** /**
* Returns the output graphic block, or null if this frame is empty. * Returns the output hardware graphic buffer, or null if this frame is empty.
* *
* @throws IllegalStateException if this output frame is not graphic. * @throws IllegalStateException if this output frame is not graphic.
*/ */
public @Nullable GraphicBlock getGraphicBlock() { public @Nullable HardwareBuffer getHardwareBuffer() {
if (mLinearBlock != null) { if (mLinearBlock != null) {
throw new IllegalStateException("This output frame is not graphic"); throw new IllegalStateException("This output frame is not graphic");
} }
return mGraphicBlock; return mHardwareBuffer;
} }
/** /**
@@ -4153,21 +4039,24 @@ final public class MediaCodec {
} }
/** /**
* Populate {@code keys} with the name of entries that has changed from * Returns an unmodifiable set of the names of entries that has changed from
* the previous frame. The entries may have been removed/changed/added. * the previous frame. The entries may have been removed/changed/added.
* Client can find out what the change is by querying {@link MediaFormat} * Client can find out what the change is by querying {@link MediaFormat}
* object returned from {@link #getFormat}. * object returned from {@link #getFormat}.
*/ */
public void retrieveChangedKeys(@NonNull Set<String> keys) { public @NonNull Set<String> getChangedKeys() {
keys.clear(); if (mKeySet.isEmpty() && !mChangedKeys.isEmpty()) {
keys.addAll(mChangedKeys); mKeySet.addAll(mChangedKeys);
}
return Collections.unmodifiableSet(mKeySet);
} }
void clear() { void clear() {
mLinearBlock = null; mLinearBlock = null;
mGraphicBlock = null; mHardwareBuffer = null;
mFormat = null; mFormat = null;
mChangedKeys.clear(); mChangedKeys.clear();
mKeySet.clear();
mLoaded = false; mLoaded = false;
} }
@@ -4194,11 +4083,12 @@ final public class MediaCodec {
private final int mIndex; private final int mIndex;
private LinearBlock mLinearBlock = null; private LinearBlock mLinearBlock = null;
private GraphicBlock mGraphicBlock = null; private HardwareBuffer mHardwareBuffer = null;
private long mPresentationTimeUs = 0; private long mPresentationTimeUs = 0;
private @BufferFlag int mFlags = 0; private @BufferFlag int mFlags = 0;
private MediaFormat mFormat = null; private MediaFormat mFormat = null;
private final ArrayList<String> mChangedKeys = new ArrayList<>(); private final ArrayList<String> mChangedKeys = new ArrayList<>();
private final Set<String> mKeySet = new HashSet<>();
private boolean mAccessible = false; private boolean mAccessible = false;
private boolean mLoaded = false; private boolean mLoaded = false;
} }
@@ -4773,6 +4663,7 @@ final public class MediaCodec {
private final ByteBuffer mInfo; private final ByteBuffer mInfo;
private final int mXOffset; private final int mXOffset;
private final int mYOffset; private final int mYOffset;
private final long mBufferContext;
private final static int TYPE_YUV = 1; private final static int TYPE_YUV = 1;
@@ -4828,6 +4719,9 @@ final public class MediaCodec {
if (mBuffer != null) { if (mBuffer != null) {
java.nio.NioUtils.freeDirectBuffer(mBuffer); java.nio.NioUtils.freeDirectBuffer(mBuffer);
} }
if (mBufferContext != 0) {
native_closeMediaImage(mBufferContext);
}
mIsImageValid = false; mIsImageValid = false;
} }
} }
@@ -4860,6 +4754,8 @@ final public class MediaCodec {
mYOffset = yOffset; mYOffset = yOffset;
mInfo = info; mInfo = info;
mBufferContext = 0;
// read media-info. See MediaImage2 // read media-info. See MediaImage2
if (info.remaining() == 104) { if (info.remaining() == 104) {
int type = info.getInt(); int type = info.getInt();
@@ -4921,7 +4817,7 @@ final public class MediaCodec {
public MediaImage( public MediaImage(
@NonNull Image.Plane[] planes, int width, int height, int format, boolean readOnly, @NonNull Image.Plane[] planes, int width, int height, int format, boolean readOnly,
long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect) { long timestamp, int xOffset, int yOffset, @Nullable Rect cropRect, long context) {
mWidth = width; mWidth = width;
mHeight = height; mHeight = height;
mFormat = format; mFormat = format;
@@ -4941,6 +4837,8 @@ final public class MediaCodec {
} }
cropRect.offset(-xOffset, -yOffset); cropRect.offset(-xOffset, -yOffset);
super.setCropRect(cropRect); super.setCropRect(cropRect);
mBufferContext = context;
} }
private class MediaPlane extends Plane { private class MediaPlane extends Plane {

View File

@@ -34,10 +34,14 @@
#include <nativehelper/JNIHelp.h> #include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h> #include <nativehelper/ScopedLocalRef.h>
#include <C2AllocatorGralloc.h>
#include <C2BlockInternal.h>
#include <C2Buffer.h> #include <C2Buffer.h>
#include <android/hardware/cas/native/1.0/IDescrambler.h> #include <android/hardware/cas/native/1.0/IDescrambler.h>
#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <binder/MemoryHeapBase.h> #include <binder/MemoryHeapBase.h>
#include <cutils/compiler.h> #include <cutils/compiler.h>
@@ -58,6 +62,8 @@
#include <media/stagefright/PersistentSurface.h> #include <media/stagefright/PersistentSurface.h>
#include <mediadrm/ICrypto.h> #include <mediadrm/ICrypto.h>
#include <private/android/AHardwareBufferHelpers.h>
#include <system/window.h> #include <system/window.h>
namespace android { namespace android {
@@ -143,15 +149,6 @@ static struct {
jfieldID lockId; jfieldID lockId;
} gLinearBlockInfo; } gLinearBlockInfo;
static struct {
jclass clazz;
jmethodID ctorId;
jmethodID setInternalStateId;
jfieldID contextId;
jfieldID validId;
jfieldID lockId;
} gGraphicBlockInfo;
struct fields_t { struct fields_t {
jmethodID postEventFromNativeID; jmethodID postEventFromNativeID;
jmethodID lockAndGetContextID; jmethodID lockAndGetContextID;
@@ -167,7 +164,7 @@ struct fields_t {
jfieldID patternSkipBlocksID; jfieldID patternSkipBlocksID;
jfieldID queueRequestIndexID; jfieldID queueRequestIndexID;
jfieldID outputFrameLinearBlockID; jfieldID outputFrameLinearBlockID;
jfieldID outputFrameGraphicBlockID; jfieldID outputFrameHardwareBufferID;
jfieldID outputFrameChangedKeysID; jfieldID outputFrameChangedKeysID;
jfieldID outputFrameFormatID; jfieldID outputFrameFormatID;
}; };
@@ -176,16 +173,6 @@ static fields_t gFields;
static const void *sRefBaseOwner; static const void *sRefBaseOwner;
struct JMediaCodecGraphicBlock {
std::shared_ptr<C2Buffer> mBuffer;
std::shared_ptr<const C2GraphicView> mReadonlyMapping;
std::shared_ptr<C2GraphicBlock> mBlock;
std::shared_ptr<C2GraphicView> mReadWriteMapping;
sp<MediaCodecBuffer> mLegacyBuffer;
};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
JMediaCodec::JMediaCodec( JMediaCodec::JMediaCodec(
@@ -706,16 +693,22 @@ status_t JMediaCodec::getOutputFrame(
break; break;
} }
case C2BufferData::GRAPHIC: { case C2BufferData::GRAPHIC: {
std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock}; const C2Handle *c2Handle = c2Buffer->data().graphicBlocks().front().handle();
context->mBuffer = c2Buffer; uint32_t width, height, format, stride, igbp_slot, generation;
ScopedLocalRef<jobject> graphicBlock{env, env->NewObject( uint64_t usage, igbp_id;
gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)}; _UnwrapNativeCodec2GrallocMetadata(
env->CallVoidMethod( c2Handle, &width, &height, &format, &usage, &stride, &generation,
graphicBlock.get(), &igbp_id, &igbp_slot);
gGraphicBlockInfo.setInternalStateId, native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(c2Handle);
(jlong)context.release(), GraphicBuffer* graphicBuffer = new GraphicBuffer(
true); grallocHandle, GraphicBuffer::CLONE_HANDLE,
env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get()); width, height, format, 1, usage, stride);
ScopedLocalRef<jobject> hardwareBuffer{
env,
android_hardware_HardwareBuffer_createFromAHardwareBuffer(
env, AHardwareBuffer_from_GraphicBuffer(graphicBuffer))};
env->SetObjectField(
frame, gFields.outputFrameHardwareBufferID, hardwareBuffer.get());
break; break;
} }
case C2BufferData::LINEAR_CHUNKS: [[fallthrough]]; case C2BufferData::LINEAR_CHUNKS: [[fallthrough]];
@@ -737,16 +730,7 @@ status_t JMediaCodec::getOutputFrame(
true); true);
env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get()); env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get());
} else { } else {
std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock}; // No-op.
context->mLegacyBuffer = buffer;
ScopedLocalRef<jobject> graphicBlock{env, env->NewObject(
gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)};
env->CallVoidMethod(
graphicBlock.get(),
gGraphicBlockInfo.setInternalStateId,
(jlong)context.release(),
true);
env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get());
} }
} }
} }
@@ -1873,6 +1857,113 @@ static void android_media_MediaCodec_queueSecureInputBuffer(
env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str()); env, err, ACTION_CODE_FATAL, errorDetailMsg.empty() ? NULL : errorDetailMsg.c_str());
} }
static jobject android_media_MediaCodec_mapHardwareBuffer(JNIEnv *env, jobject bufferObj) {
ALOGV("android_media_MediaCodec_mapHardwareBuffer");
AHardwareBuffer *hardwareBuffer = android_hardware_HardwareBuffer_getNativeHardwareBuffer(
env, bufferObj);
AHardwareBuffer_Desc desc;
AHardwareBuffer_describe(hardwareBuffer, &desc);
if (desc.format != AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420) {
ALOGI("mapHardwareBuffer: unmappable format: %d", desc.format);
return nullptr;
}
if ((desc.usage & AHARDWAREBUFFER_USAGE_CPU_READ_MASK) == 0) {
ALOGI("mapHardwareBuffer: buffer not CPU readable");
return nullptr;
}
bool readOnly = ((desc.usage & AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK) == 0);
uint64_t cpuUsage = desc.usage;
cpuUsage = (cpuUsage & AHARDWAREBUFFER_USAGE_CPU_READ_MASK);
cpuUsage = (cpuUsage & AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK);
AHardwareBuffer_Planes planes;
int err = AHardwareBuffer_lockPlanes(
hardwareBuffer, cpuUsage, -1 /* fence */, nullptr /* rect */, &planes);
if (err != 0) {
ALOGI("mapHardwareBuffer: Failed to lock planes (err=%d)", err);
return nullptr;
}
if (planes.planeCount != 3) {
ALOGI("mapHardwareBuffer: planeCount expected 3, actual %u", planes.planeCount);
return nullptr;
}
ScopedLocalRef<jclass> planeClazz(
env, env->FindClass("android/media/MediaCodec$MediaImage$MediaPlane"));
ScopedLocalRef<jobjectArray> planeArray{
env, env->NewObjectArray(3, planeClazz.get(), NULL)};
CHECK(planeClazz.get() != NULL);
jmethodID planeConstructID = env->GetMethodID(planeClazz.get(), "<init>",
"([Ljava/nio/ByteBuffer;IIIII)V");
// plane indices are Y-U-V.
for (uint32_t i = 0; i < 3; ++i) {
const AHardwareBuffer_Plane &plane = planes.planes[i];
ScopedLocalRef<jobject> byteBuffer{env, CreateByteBuffer(
env,
plane.data,
plane.rowStride * (desc.height - 1) + plane.pixelStride * (desc.width - 1) + 1,
0,
plane.rowStride * (desc.height - 1) + plane.pixelStride * (desc.width - 1) + 1,
readOnly,
true)};
ScopedLocalRef<jobject> planeObj{env, env->NewObject(
planeClazz.get(), planeConstructID,
byteBuffer.get(), plane.rowStride, plane.pixelStride)};
env->SetObjectArrayElement(planeArray.get(), i, planeObj.get());
}
ScopedLocalRef<jclass> imageClazz(
env, env->FindClass("android/media/MediaCodec$MediaImage"));
CHECK(imageClazz.get() != NULL);
jmethodID imageConstructID = env->GetMethodID(imageClazz.get(), "<init>",
"([Landroid/media/Image$Plane;IIIZJIILandroid/graphics/Rect;J)V");
jobject img = env->NewObject(imageClazz.get(), imageConstructID,
planeArray.get(),
desc.width,
desc.height,
desc.format, // ???
(jboolean)readOnly /* readOnly */,
(jlong)0 /* timestamp */,
(jint)0 /* xOffset */, (jint)0 /* yOffset */, nullptr /* cropRect */,
(jlong)hardwareBuffer);
// if MediaImage creation fails, return null
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
return nullptr;
}
AHardwareBuffer_acquire(hardwareBuffer);
return img;
}
static void android_media_MediaCodec_closeMediaImage(JNIEnv *, jlong context) {
if (context == 0) {
return;
}
AHardwareBuffer *hardwareBuffer = (AHardwareBuffer *)context;
int32_t fenceFd = -1;
int err = AHardwareBuffer_unlock(hardwareBuffer, &fenceFd);
if (err == 0) {
sp<Fence> fence{new Fence(fenceFd)};
} else {
ALOGI("closeMediaImage: failed to unlock (err=%d)", err);
// Continue to release the hardwareBuffer
}
AHardwareBuffer_release(hardwareBuffer);
}
static status_t ConvertKeyValueListsToAMessage( static status_t ConvertKeyValueListsToAMessage(
JNIEnv *env, jobject keys, jobject values, sp<AMessage> *msg) { JNIEnv *env, jobject keys, jobject values, sp<AMessage> *msg) {
static struct Fields { static struct Fields {
@@ -2031,11 +2122,6 @@ static void android_media_MediaCodec_native_queueLinearBlock(
} }
NativeCryptoInfo cryptoInfo{env, cryptoInfoObj}; NativeCryptoInfo cryptoInfo{env, cryptoInfoObj};
size_t cryptoSize = 0;
for (int i = 0; i < cryptoInfo.mNumSubSamples; ++i) {
cryptoSize += cryptoInfo.mSubSamples[i].mNumBytesOfClearData;
cryptoSize += cryptoInfo.mSubSamples[i].mNumBytesOfEncryptedData;
}
err = codec->queueEncryptedLinearBlock( err = codec->queueEncryptedLinearBlock(
index, index,
memory, memory,
@@ -2059,10 +2145,10 @@ static void android_media_MediaCodec_native_queueLinearBlock(
throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str()); throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str());
} }
static void android_media_MediaCodec_native_queueGraphicBlock( static void android_media_MediaCodec_native_queueHardwareBuffer(
JNIEnv *env, jobject thiz, jint index, jobject bufferObj, JNIEnv *env, jobject thiz, jint index, jobject bufferObj,
jlong presentationTimeUs, jint flags, jobject keys, jobject values) { jlong presentationTimeUs, jint flags, jobject keys, jobject values) {
ALOGV("android_media_MediaCodec_native_queueGraphicBlock"); ALOGV("android_media_MediaCodec_native_queueHardwareBuffer");
sp<JMediaCodec> codec = getMediaCodec(env, thiz); sp<JMediaCodec> codec = getMediaCodec(env, thiz);
@@ -2078,29 +2164,21 @@ static void android_media_MediaCodec_native_queueGraphicBlock(
return; return;
} }
std::shared_ptr<C2Buffer> buffer; AHardwareBuffer *hardwareBuffer = android_hardware_HardwareBuffer_getNativeHardwareBuffer(
std::shared_ptr<C2GraphicBlock> block; env, bufferObj);
ScopedLocalRef<jobject> lock{env, env->GetObjectField(bufferObj, gGraphicBlockInfo.lockId)}; sp<GraphicBuffer> graphicBuffer{AHardwareBuffer_to_GraphicBuffer(hardwareBuffer)};
if (env->MonitorEnter(lock.get()) == JNI_OK) { C2Handle *handle = WrapNativeCodec2GrallocHandle(
if (env->GetBooleanField(bufferObj, gGraphicBlockInfo.validId)) { graphicBuffer->handle, graphicBuffer->format,
JMediaCodecGraphicBlock *context = (JMediaCodecGraphicBlock *)env->GetLongField( graphicBuffer->width, graphicBuffer->height,
bufferObj, gGraphicBlockInfo.contextId); graphicBuffer->usage, graphicBuffer->stride);
buffer = context->mBuffer; std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(handle);
block = context->mBlock;
}
env->MonitorExit(lock.get());
} else {
throwExceptionAsNecessary(env, INVALID_OPERATION);
return;
}
if (!block && !buffer) { if (!block) {
throwExceptionAsNecessary(env, BAD_VALUE); throwExceptionAsNecessary(env, BAD_VALUE);
return; return;
} }
if (!buffer) { std::shared_ptr<C2Buffer> buffer = C2Buffer::CreateGraphicBuffer(block->share(
buffer = C2Buffer::CreateGraphicBuffer(block->share(block->crop(), C2Fence{})); block->crop(), C2Fence{}));
}
AString errorDetailMsg; AString errorDetailMsg;
err = codec->queueBuffer(index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg); err = codec->queueBuffer(index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg);
throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str()); throwExceptionAsNecessary(env, err, ACTION_CODE_FATAL, errorDetailMsg.c_str());
@@ -2534,9 +2612,9 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) {
env->GetFieldID(clazz.get(), "mLinearBlock", "Landroid/media/MediaCodec$LinearBlock;"); env->GetFieldID(clazz.get(), "mLinearBlock", "Landroid/media/MediaCodec$LinearBlock;");
CHECK(gFields.outputFrameLinearBlockID != NULL); CHECK(gFields.outputFrameLinearBlockID != NULL);
gFields.outputFrameGraphicBlockID = gFields.outputFrameHardwareBufferID =
env->GetFieldID(clazz.get(), "mGraphicBlock", "Landroid/media/MediaCodec$GraphicBlock;"); env->GetFieldID(clazz.get(), "mHardwareBuffer", "Landroid/hardware/HardwareBuffer;");
CHECK(gFields.outputFrameGraphicBlockID != NULL); CHECK(gFields.outputFrameHardwareBufferID != NULL);
gFields.outputFrameChangedKeysID = gFields.outputFrameChangedKeysID =
env->GetFieldID(clazz.get(), "mChangedKeys", "Ljava/util/ArrayList;"); env->GetFieldID(clazz.get(), "mChangedKeys", "Ljava/util/ArrayList;");
@@ -2729,27 +2807,6 @@ static void android_media_MediaCodec_native_init(JNIEnv *env) {
gLinearBlockInfo.lockId = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;"); gLinearBlockInfo.lockId = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
CHECK(gLinearBlockInfo.lockId != NULL); CHECK(gLinearBlockInfo.lockId != NULL);
clazz.reset(env->FindClass("android/media/MediaCodec$GraphicBlock"));
CHECK(clazz.get() != NULL);
gGraphicBlockInfo.clazz = (jclass)env->NewGlobalRef(clazz.get());
gGraphicBlockInfo.ctorId = env->GetMethodID(clazz.get(), "<init>", "()V");
CHECK(gGraphicBlockInfo.ctorId != NULL);
gGraphicBlockInfo.setInternalStateId = env->GetMethodID(
clazz.get(), "setInternalStateLocked", "(JZ)V");
CHECK(gGraphicBlockInfo.setInternalStateId != NULL);
gGraphicBlockInfo.contextId = env->GetFieldID(clazz.get(), "mNativeContext", "J");
CHECK(gGraphicBlockInfo.contextId != NULL);
gGraphicBlockInfo.validId = env->GetFieldID(clazz.get(), "mValid", "Z");
CHECK(gGraphicBlockInfo.validId != NULL);
gGraphicBlockInfo.lockId = env->GetFieldID(clazz.get(), "mLock", "Ljava/lang/Object;");
CHECK(gGraphicBlockInfo.lockId != NULL);
} }
static void android_media_MediaCodec_native_setup( static void android_media_MediaCodec_native_setup(
@@ -2949,196 +3006,6 @@ static jboolean android_media_MediaCodec_LinearBlock_checkCompatible(
return isCompatible; return isCompatible;
} }
// MediaCodec.GraphicBlock
template <class T>
static jobject CreateImage(JNIEnv *env, const std::shared_ptr<T> &view) {
bool readOnly = std::is_const<T>::value;
const C2PlanarLayout layout = view->layout();
jint format = HAL_PIXEL_FORMAT_YCBCR_420_888;
switch (layout.type) {
case C2PlanarLayout::TYPE_YUV: {
if (layout.numPlanes != 3) {
throwExceptionAsNecessary(env, INVALID_OPERATION);
return nullptr;
}
const C2PlaneInfo & yPlane = layout.planes[C2PlanarLayout::PLANE_Y];
const C2PlaneInfo & uPlane = layout.planes[C2PlanarLayout::PLANE_U];
const C2PlaneInfo & vPlane = layout.planes[C2PlanarLayout::PLANE_V];
if (yPlane.rowSampling != 1 || yPlane.colSampling != 1) {
throwExceptionAsNecessary(env, INVALID_OPERATION);
return nullptr;
}
if (uPlane.rowSampling != vPlane.rowSampling
|| uPlane.colSampling != vPlane.colSampling) {
throwExceptionAsNecessary(env, INVALID_OPERATION);
return nullptr;
}
if (uPlane.rowSampling == 2 && uPlane.colSampling == 2) {
format = HAL_PIXEL_FORMAT_YCBCR_420_888;
break;
} else if (uPlane.rowSampling == 1 && uPlane.colSampling == 2) {
format = HAL_PIXEL_FORMAT_YCBCR_422_888;
break;
} else if (uPlane.rowSampling == 1 && uPlane.colSampling == 1) {
format = HAL_PIXEL_FORMAT_YCBCR_444_888;
break;
}
throwExceptionAsNecessary(env, INVALID_OPERATION);
return nullptr;
}
case C2PlanarLayout::TYPE_RGB: {
if (layout.numPlanes != 3) {
throwExceptionAsNecessary(env, INVALID_OPERATION);
return nullptr;
}
format = HAL_PIXEL_FORMAT_FLEX_RGB_888;
break;
}
case C2PlanarLayout::TYPE_RGBA: {
if (layout.numPlanes != 4) {
throwExceptionAsNecessary(env, INVALID_OPERATION);
return nullptr;
}
format = HAL_PIXEL_FORMAT_FLEX_RGBA_8888;
break;
}
case C2PlanarLayout::TYPE_YUVA:
[[fallthrough]];
default:
throwExceptionAsNecessary(env, INVALID_OPERATION);
return nullptr;
}
ScopedLocalRef<jclass> planeClazz(
env, env->FindClass("android/media/MediaCodec$MediaImage$MediaPlane"));
ScopedLocalRef<jobjectArray> planeArray{
env, env->NewObjectArray(layout.numPlanes, planeClazz.get(), NULL)};
CHECK(planeClazz.get() != NULL);
jmethodID planeConstructID = env->GetMethodID(planeClazz.get(), "<init>",
"([Ljava/nio/ByteBuffer;IIIII)V");
// plane indices are happened to be Y-U-V and R-G-B(-A) order.
for (uint32_t i = 0; i < layout.numPlanes; ++i) {
const C2PlaneInfo &plane = layout.planes[i];
if (plane.rowInc < 0 || plane.colInc < 0) {
throwExceptionAsNecessary(env, INVALID_OPERATION);
return nullptr;
}
ssize_t minOffset = plane.minOffset(view->width(), view->height());
ssize_t maxOffset = plane.maxOffset(view->width(), view->height());
ScopedLocalRef<jobject> byteBuffer{env, CreateByteBuffer(
env,
view->data()[plane.rootIx] + plane.offset + minOffset,
maxOffset - minOffset + 1,
0,
maxOffset - minOffset + 1,
readOnly,
true)};
ScopedLocalRef<jobject> jPlane{env, env->NewObject(
planeClazz.get(), planeConstructID,
byteBuffer.get(), plane.rowInc, plane.colInc)};
}
ScopedLocalRef<jclass> imageClazz(
env, env->FindClass("android/media/MediaCodec$MediaImage"));
CHECK(imageClazz.get() != NULL);
jmethodID imageConstructID = env->GetMethodID(imageClazz.get(), "<init>",
"([Landroid/media/Image$Plane;IIIZJIILandroid/graphics/Rect;)V");
jobject img = env->NewObject(imageClazz.get(), imageConstructID,
planeArray.get(),
view->width(),
view->height(),
format,
(jboolean)readOnly /* readOnly */,
(jlong)0 /* timestamp */,
(jint)0 /* xOffset */, (jint)0 /* yOffset */, nullptr /* cropRect */);
// if MediaImage creation fails, return null
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
return nullptr;
}
return img;
}
static jobject android_media_MediaCodec_GraphicBlock_native_map(
JNIEnv *env, jobject thiz) {
JMediaCodecGraphicBlock *context =
(JMediaCodecGraphicBlock *)env->GetLongField(thiz, gGraphicBlockInfo.contextId);
if (context->mBuffer) {
std::shared_ptr<C2Buffer> buffer = context->mBuffer;
if (!context->mReadonlyMapping) {
const C2BufferData data = buffer->data();
if (data.type() != C2BufferData::GRAPHIC) {
throwExceptionAsNecessary(env, INVALID_OPERATION);
return nullptr;
}
if (data.graphicBlocks().size() != 1u) {
throwExceptionAsNecessary(env, INVALID_OPERATION);
return nullptr;
}
C2ConstGraphicBlock block = data.graphicBlocks().front();
context->mReadonlyMapping =
std::make_shared<const C2GraphicView>(block.map().get());
}
return CreateImage(env, context->mReadonlyMapping);
} else if (context->mBlock) {
std::shared_ptr<C2GraphicBlock> block = context->mBlock;
if (!context->mReadWriteMapping) {
context->mReadWriteMapping =
std::make_shared<C2GraphicView>(block->map().get());
}
return CreateImage(env, context->mReadWriteMapping);
} else if (context->mLegacyBuffer) {
}
throwExceptionAsNecessary(env, INVALID_OPERATION);
return nullptr;
}
static void android_media_MediaCodec_GraphicBlock_native_recycle(
JNIEnv *env, jobject thiz) {
JMediaCodecGraphicBlock *context =
(JMediaCodecGraphicBlock *)env->GetLongField(thiz, gGraphicBlockInfo.contextId);
env->CallVoidMethod(thiz, gGraphicBlockInfo.setInternalStateId, 0, false /* isMappable */);
delete context;
}
static void android_media_MediaCodec_GraphicBlock_native_obtain(
JNIEnv *env, jobject thiz,
jint width, jint height, jint format, jlong usage, jobjectArray codecNames) {
std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock};
std::vector<std::string> names;
PopulateNamesVector(env, codecNames, &names);
context->mBlock = MediaCodec::FetchGraphicBlock(width, height, format, usage, names);
if (!context->mBlock) {
jniThrowException(env, "java/io/IOException", nullptr);
return;
}
env->CallVoidMethod(
thiz,
gGraphicBlockInfo.setInternalStateId,
(jlong)context.release(),
true /*isMappable */);
}
static jboolean android_media_MediaCodec_GraphicBlock_checkCompatible(
JNIEnv *env, jobjectArray codecNames) {
std::vector<std::string> names;
PopulateNamesVector(env, codecNames, &names);
bool isCompatible = false;
status_t err = MediaCodec::CanFetchGraphicBlock(names, &isCompatible);
if (err != OK) {
throwExceptionAsNecessary(env, err);
}
return isCompatible;
}
static const JNINativeMethod gMethods[] = { static const JNINativeMethod gMethods[] = {
{ "native_release", "()V", (void *)android_media_MediaCodec_release }, { "native_release", "()V", (void *)android_media_MediaCodec_release },
@@ -3184,14 +3051,20 @@ static const JNINativeMethod gMethods[] = {
{ "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V", { "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
(void *)android_media_MediaCodec_queueSecureInputBuffer }, (void *)android_media_MediaCodec_queueSecureInputBuffer },
{ "native_mapHardwareBuffer",
"(Landroid/hardware/HardwareBuffer;)Landroid/media/Image;",
(void *)android_media_MediaCodec_mapHardwareBuffer },
{ "native_closeMediaImage", "(J)V", (void *)android_media_MediaCodec_closeMediaImage },
{ "native_queueLinearBlock", { "native_queueLinearBlock",
"(ILandroid/media/MediaCodec$LinearBlock;IILandroid/media/MediaCodec$CryptoInfo;JI" "(ILandroid/media/MediaCodec$LinearBlock;IILandroid/media/MediaCodec$CryptoInfo;JI"
"Ljava/util/ArrayList;Ljava/util/ArrayList;)V", "Ljava/util/ArrayList;Ljava/util/ArrayList;)V",
(void *)android_media_MediaCodec_native_queueLinearBlock }, (void *)android_media_MediaCodec_native_queueLinearBlock },
{ "native_queueGraphicBlock", { "native_queueHardwareBuffer",
"(ILandroid/media/MediaCodec$GraphicBlock;JILjava/util/ArrayList;Ljava/util/ArrayList;)V", "(ILandroid/hardware/HardwareBuffer;JILjava/util/ArrayList;Ljava/util/ArrayList;)V",
(void *)android_media_MediaCodec_native_queueGraphicBlock }, (void *)android_media_MediaCodec_native_queueHardwareBuffer },
{ "native_getOutputFrame", { "native_getOutputFrame",
"(Landroid/media/MediaCodec$OutputFrame;I)V", "(Landroid/media/MediaCodec$OutputFrame;I)V",
@@ -3265,20 +3138,6 @@ static const JNINativeMethod gLinearBlockMethods[] = {
(void *)android_media_MediaCodec_LinearBlock_checkCompatible }, (void *)android_media_MediaCodec_LinearBlock_checkCompatible },
}; };
static const JNINativeMethod gGraphicBlockMethods[] = {
{ "native_map", "()Landroid/media/Image;",
(void *)android_media_MediaCodec_GraphicBlock_native_map },
{ "native_recycle", "()V",
(void *)android_media_MediaCodec_GraphicBlock_native_recycle },
{ "native_obtain", "(IIIJ[Ljava/lang/String;)V",
(void *)android_media_MediaCodec_GraphicBlock_native_obtain },
{ "native_checkCompatible", "([Ljava/lang/String;)Z",
(void *)android_media_MediaCodec_GraphicBlock_checkCompatible },
};
int register_android_media_MediaCodec(JNIEnv *env) { int register_android_media_MediaCodec(JNIEnv *env) {
int result = AndroidRuntime::registerNativeMethods(env, int result = AndroidRuntime::registerNativeMethods(env,
"android/media/MediaCodec", gMethods, NELEM(gMethods)); "android/media/MediaCodec", gMethods, NELEM(gMethods));
@@ -3289,12 +3148,5 @@ int register_android_media_MediaCodec(JNIEnv *env) {
"android/media/MediaCodec$LinearBlock", "android/media/MediaCodec$LinearBlock",
gLinearBlockMethods, gLinearBlockMethods,
NELEM(gLinearBlockMethods)); NELEM(gLinearBlockMethods));
if (result != JNI_OK) {
return result;
}
result = AndroidRuntime::registerNativeMethods(env,
"android/media/MediaCodec$GraphicBlock",
gGraphicBlockMethods,
NELEM(gGraphicBlockMethods));
return result; return result;
} }