Merge "MediaCodec: address API review #2" into rvc-dev
This commit is contained in:
@@ -16805,6 +16805,7 @@ package android.hardware {
|
||||
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_VIDEO_ENCODE = 65536L; // 0x10000L
|
||||
field public static final int YCBCR_420_888 = 35; // 0x23
|
||||
}
|
||||
|
||||
public final class Sensor {
|
||||
@@ -25205,6 +25206,7 @@ package android.media {
|
||||
method @NonNull public android.media.MediaCodec.OutputFrame getOutputFrame(int);
|
||||
method @Nullable public android.media.Image getOutputImage(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 queueSecureInputBuffer(int, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
|
||||
method public void release();
|
||||
@@ -25305,17 +25307,7 @@ package android.media {
|
||||
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 {
|
||||
ctor public MediaCodec.IncompatibleWithBlockModelException();
|
||||
}
|
||||
|
||||
public static final class MediaCodec.LinearBlock {
|
||||
@@ -25345,12 +25337,12 @@ package android.media {
|
||||
}
|
||||
|
||||
public static final class MediaCodec.OutputFrame {
|
||||
method @NonNull public java.util.Set<java.lang.String> getChangedKeys();
|
||||
method public int getFlags();
|
||||
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 public long getPresentationTimeUs();
|
||||
method public void retrieveChangedKeys(@NonNull java.util.Set<java.lang.String>);
|
||||
}
|
||||
|
||||
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 setFlags(int);
|
||||
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 setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
|
||||
method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long);
|
||||
|
||||
@@ -46,7 +46,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
public final class HardwareBuffer implements Parcelable, AutoCloseable {
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(prefix = { "RGB", "BLOB", "D_", "DS_", "S_" }, value = {
|
||||
@IntDef(prefix = { "RGB", "BLOB", "YCBCR_", "D_", "DS_", "S_" }, value = {
|
||||
RGBA_8888,
|
||||
RGBA_FP16,
|
||||
RGBA_1010102,
|
||||
@@ -54,6 +54,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
|
||||
RGB_888,
|
||||
RGB_565,
|
||||
BLOB,
|
||||
YCBCR_420_888,
|
||||
D_16,
|
||||
D_24,
|
||||
DS_24UI8,
|
||||
@@ -79,6 +80,8 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
|
||||
public static final int RGBA_1010102 = 0x2b;
|
||||
/** Format: opaque format used for raw data transfer; must have a height of 1 */
|
||||
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 */
|
||||
public static final int D_16 = 0x30;
|
||||
/** Format: 24 bits depth */
|
||||
@@ -396,6 +399,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable {
|
||||
case RGB_565:
|
||||
case RGB_888:
|
||||
case BLOB:
|
||||
case YCBCR_420_888:
|
||||
case D_16:
|
||||
case D_24:
|
||||
case DS_24UI8:
|
||||
|
||||
@@ -42,7 +42,9 @@ import java.nio.ByteOrder;
|
||||
import java.nio.ReadOnlyBufferException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
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
|
||||
* GraphicBlock}, pass this flag.
|
||||
* HardwareBuffer}, pass this flag.
|
||||
* <p>
|
||||
* When this flag is set, the following APIs throw {@link IncompatibleWithBlockModelException}.
|
||||
* <ul>
|
||||
@@ -1989,6 +1991,19 @@ final public class MediaCodec {
|
||||
* Thrown when the codec is configured for block model and an incompatible API is called.
|
||||
*/
|
||||
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 {
|
||||
synchronized(mBufferLock) {
|
||||
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);
|
||||
mDequeuedInputBuffers.remove(index);
|
||||
@@ -2784,7 +2801,9 @@ final public class MediaCodec {
|
||||
int flags) throws CryptoException {
|
||||
synchronized(mBufferLock) {
|
||||
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);
|
||||
mDequeuedInputBuffers.remove(index);
|
||||
@@ -2819,7 +2838,9 @@ final public class MediaCodec {
|
||||
public final int dequeueInputBuffer(long timeoutUs) {
|
||||
synchronized (mBufferLock) {
|
||||
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);
|
||||
@@ -2989,179 +3010,22 @@ final public class MediaCodec {
|
||||
}
|
||||
|
||||
/**
|
||||
* Section of memory that represents a graphic block. Applications may
|
||||
* acquire a block via {@link GraphicBlock#obtain} and queue
|
||||
* the block as an input buffer to a codec, or get a block allocated by
|
||||
* codec as an output buffer from {@link OutputFrame}.
|
||||
* Map a {@link HardwareBuffer} object into {@link Image}, so that the content of the buffer is
|
||||
* accessible. Depending on the usage and pixel format of the hardware buffer, it may not be
|
||||
* mappable; this method returns null in that case.
|
||||
*
|
||||
* {@see QueueRequest#setGraphicBlock}
|
||||
* {@see OutputFrame#getGraphicBlock}
|
||||
* @param hardwareBuffer {@link HardwareBuffer} to map.
|
||||
* @return Mapped {@link Image} object, or null if the buffer is not mappable.
|
||||
*/
|
||||
public static final class GraphicBlock {
|
||||
// No public constructors.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the memory and return the mapped region.
|
||||
* <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();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
public static @Nullable Image mapHardwareBuffer(@NonNull HardwareBuffer hardwareBuffer) {
|
||||
return native_mapHardwareBuffer(hardwareBuffer);
|
||||
}
|
||||
|
||||
private static native @Nullable Image native_mapHardwareBuffer(
|
||||
@NonNull HardwareBuffer hardwareBuffer);
|
||||
|
||||
private static native void native_closeMediaImage(long context);
|
||||
|
||||
/**
|
||||
* Builder-like class for queue requests. Use this class to prepare a
|
||||
* queue request and send it.
|
||||
@@ -3185,7 +3049,7 @@ final public class MediaCodec {
|
||||
* @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 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
|
||||
* @throws IllegalStateException if a buffer is already set
|
||||
*/
|
||||
@@ -3197,7 +3061,7 @@ final public class MediaCodec {
|
||||
if (!isAccessible()) {
|
||||
throw new IllegalStateException("The request is stale");
|
||||
}
|
||||
if (mLinearBlock != null || mGraphicBlock != null) {
|
||||
if (mLinearBlock != null || mHardwareBuffer != null) {
|
||||
throw new IllegalStateException("Cannot set block twice");
|
||||
}
|
||||
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}.
|
||||
* <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
|
||||
* @throws IllegalStateException if a buffer is already set
|
||||
*/
|
||||
public @NonNull QueueRequest setGraphicBlock(
|
||||
@NonNull GraphicBlock block) {
|
||||
public @NonNull QueueRequest setHardwareBuffer(
|
||||
@NonNull HardwareBuffer buffer) {
|
||||
if (!isAccessible()) {
|
||||
throw new IllegalStateException("The request is stale");
|
||||
}
|
||||
if (mLinearBlock != null || mGraphicBlock != null) {
|
||||
if (mLinearBlock != null || mHardwareBuffer != null) {
|
||||
throw new IllegalStateException("Cannot set block twice");
|
||||
}
|
||||
mGraphicBlock = block;
|
||||
mHardwareBuffer = buffer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -3375,7 +3244,7 @@ final public class MediaCodec {
|
||||
if (!isAccessible()) {
|
||||
throw new IllegalStateException("The request is stale");
|
||||
}
|
||||
if (mLinearBlock == null && mGraphicBlock == null) {
|
||||
if (mLinearBlock == null && mHardwareBuffer == null) {
|
||||
throw new IllegalStateException("No block is set");
|
||||
}
|
||||
setAccessible(false);
|
||||
@@ -3384,9 +3253,9 @@ final public class MediaCodec {
|
||||
mIndex, mLinearBlock, mOffset, mSize, mCryptoInfo,
|
||||
mPresentationTimeUs, mFlags,
|
||||
mTuningKeys, mTuningValues);
|
||||
} else if (mGraphicBlock != null) {
|
||||
mCodec.native_queueGraphicBlock(
|
||||
mIndex, mGraphicBlock, mPresentationTimeUs, mFlags,
|
||||
} else if (mHardwareBuffer != null) {
|
||||
mCodec.native_queueHardwareBuffer(
|
||||
mIndex, mHardwareBuffer, mPresentationTimeUs, mFlags,
|
||||
mTuningKeys, mTuningValues);
|
||||
}
|
||||
clear();
|
||||
@@ -3397,7 +3266,7 @@ final public class MediaCodec {
|
||||
mOffset = 0;
|
||||
mSize = 0;
|
||||
mCryptoInfo = null;
|
||||
mGraphicBlock = null;
|
||||
mHardwareBuffer = null;
|
||||
mPresentationTimeUs = 0;
|
||||
mFlags = 0;
|
||||
mTuningKeys.clear();
|
||||
@@ -3420,7 +3289,7 @@ final public class MediaCodec {
|
||||
private int mOffset = 0;
|
||||
private int mSize = 0;
|
||||
private MediaCodec.CryptoInfo mCryptoInfo = null;
|
||||
private GraphicBlock mGraphicBlock = null;
|
||||
private HardwareBuffer mHardwareBuffer = null;
|
||||
private long mPresentationTimeUs = 0;
|
||||
private @BufferFlag int mFlags = 0;
|
||||
private final ArrayList<String> mTuningKeys = new ArrayList<>();
|
||||
@@ -3440,9 +3309,9 @@ final public class MediaCodec {
|
||||
@NonNull ArrayList<String> keys,
|
||||
@NonNull ArrayList<Object> values);
|
||||
|
||||
private native void native_queueGraphicBlock(
|
||||
private native void native_queueHardwareBuffer(
|
||||
int index,
|
||||
@NonNull GraphicBlock block,
|
||||
@NonNull HardwareBuffer buffer,
|
||||
long presentationTimeUs,
|
||||
int flags,
|
||||
@NonNull ArrayList<String> keys,
|
||||
@@ -3535,7 +3404,9 @@ final public class MediaCodec {
|
||||
@NonNull BufferInfo info, long timeoutUs) {
|
||||
synchronized (mBufferLock) {
|
||||
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);
|
||||
@@ -3917,7 +3788,10 @@ final public class MediaCodec {
|
||||
public ByteBuffer[] getInputBuffers() {
|
||||
synchronized (mBufferLock) {
|
||||
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) {
|
||||
throw new IllegalStateException();
|
||||
@@ -3953,7 +3827,9 @@ final public class MediaCodec {
|
||||
public ByteBuffer[] getOutputBuffers() {
|
||||
synchronized (mBufferLock) {
|
||||
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) {
|
||||
throw new IllegalStateException();
|
||||
@@ -3985,7 +3861,10 @@ final public class MediaCodec {
|
||||
public ByteBuffer getInputBuffer(int index) {
|
||||
synchronized (mBufferLock) {
|
||||
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);
|
||||
@@ -4019,7 +3898,10 @@ final public class MediaCodec {
|
||||
public Image getInputImage(int index) {
|
||||
synchronized (mBufferLock) {
|
||||
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);
|
||||
@@ -4053,7 +3935,9 @@ final public class MediaCodec {
|
||||
public ByteBuffer getOutputBuffer(int index) {
|
||||
synchronized (mBufferLock) {
|
||||
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);
|
||||
@@ -4086,7 +3970,9 @@ final public class MediaCodec {
|
||||
public Image getOutputImage(int index) {
|
||||
synchronized (mBufferLock) {
|
||||
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);
|
||||
@@ -4112,22 +3998,22 @@ final public class MediaCodec {
|
||||
* @throws IllegalStateException if this output frame is not linear.
|
||||
*/
|
||||
public @Nullable LinearBlock getLinearBlock() {
|
||||
if (mGraphicBlock != null) {
|
||||
if (mHardwareBuffer != null) {
|
||||
throw new IllegalStateException("This output frame is not linear");
|
||||
}
|
||||
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.
|
||||
*/
|
||||
public @Nullable GraphicBlock getGraphicBlock() {
|
||||
public @Nullable HardwareBuffer getHardwareBuffer() {
|
||||
if (mLinearBlock != null) {
|
||||
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.
|
||||
* Client can find out what the change is by querying {@link MediaFormat}
|
||||
* object returned from {@link #getFormat}.
|
||||
*/
|
||||
public void retrieveChangedKeys(@NonNull Set<String> keys) {
|
||||
keys.clear();
|
||||
keys.addAll(mChangedKeys);
|
||||
public @NonNull Set<String> getChangedKeys() {
|
||||
if (mKeySet.isEmpty() && !mChangedKeys.isEmpty()) {
|
||||
mKeySet.addAll(mChangedKeys);
|
||||
}
|
||||
return Collections.unmodifiableSet(mKeySet);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
mLinearBlock = null;
|
||||
mGraphicBlock = null;
|
||||
mHardwareBuffer = null;
|
||||
mFormat = null;
|
||||
mChangedKeys.clear();
|
||||
mKeySet.clear();
|
||||
mLoaded = false;
|
||||
}
|
||||
|
||||
@@ -4194,11 +4083,12 @@ final public class MediaCodec {
|
||||
|
||||
private final int mIndex;
|
||||
private LinearBlock mLinearBlock = null;
|
||||
private GraphicBlock mGraphicBlock = null;
|
||||
private HardwareBuffer mHardwareBuffer = null;
|
||||
private long mPresentationTimeUs = 0;
|
||||
private @BufferFlag int mFlags = 0;
|
||||
private MediaFormat mFormat = null;
|
||||
private final ArrayList<String> mChangedKeys = new ArrayList<>();
|
||||
private final Set<String> mKeySet = new HashSet<>();
|
||||
private boolean mAccessible = false;
|
||||
private boolean mLoaded = false;
|
||||
}
|
||||
@@ -4773,6 +4663,7 @@ final public class MediaCodec {
|
||||
private final ByteBuffer mInfo;
|
||||
private final int mXOffset;
|
||||
private final int mYOffset;
|
||||
private final long mBufferContext;
|
||||
|
||||
private final static int TYPE_YUV = 1;
|
||||
|
||||
@@ -4828,6 +4719,9 @@ final public class MediaCodec {
|
||||
if (mBuffer != null) {
|
||||
java.nio.NioUtils.freeDirectBuffer(mBuffer);
|
||||
}
|
||||
if (mBufferContext != 0) {
|
||||
native_closeMediaImage(mBufferContext);
|
||||
}
|
||||
mIsImageValid = false;
|
||||
}
|
||||
}
|
||||
@@ -4860,6 +4754,8 @@ final public class MediaCodec {
|
||||
mYOffset = yOffset;
|
||||
mInfo = info;
|
||||
|
||||
mBufferContext = 0;
|
||||
|
||||
// read media-info. See MediaImage2
|
||||
if (info.remaining() == 104) {
|
||||
int type = info.getInt();
|
||||
@@ -4921,7 +4817,7 @@ final public class MediaCodec {
|
||||
|
||||
public MediaImage(
|
||||
@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;
|
||||
mHeight = height;
|
||||
mFormat = format;
|
||||
@@ -4941,6 +4837,8 @@ final public class MediaCodec {
|
||||
}
|
||||
cropRect.offset(-xOffset, -yOffset);
|
||||
super.setCropRect(cropRect);
|
||||
|
||||
mBufferContext = context;
|
||||
}
|
||||
|
||||
private class MediaPlane extends Plane {
|
||||
|
||||
@@ -34,10 +34,14 @@
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
#include <nativehelper/ScopedLocalRef.h>
|
||||
|
||||
#include <C2AllocatorGralloc.h>
|
||||
#include <C2BlockInternal.h>
|
||||
#include <C2Buffer.h>
|
||||
|
||||
#include <android/hardware/cas/native/1.0/IDescrambler.h>
|
||||
|
||||
#include <android_runtime/android_hardware_HardwareBuffer.h>
|
||||
|
||||
#include <binder/MemoryHeapBase.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
@@ -58,6 +62,8 @@
|
||||
#include <media/stagefright/PersistentSurface.h>
|
||||
#include <mediadrm/ICrypto.h>
|
||||
|
||||
#include <private/android/AHardwareBufferHelpers.h>
|
||||
|
||||
#include <system/window.h>
|
||||
|
||||
namespace android {
|
||||
@@ -143,15 +149,6 @@ static struct {
|
||||
jfieldID lockId;
|
||||
} gLinearBlockInfo;
|
||||
|
||||
static struct {
|
||||
jclass clazz;
|
||||
jmethodID ctorId;
|
||||
jmethodID setInternalStateId;
|
||||
jfieldID contextId;
|
||||
jfieldID validId;
|
||||
jfieldID lockId;
|
||||
} gGraphicBlockInfo;
|
||||
|
||||
struct fields_t {
|
||||
jmethodID postEventFromNativeID;
|
||||
jmethodID lockAndGetContextID;
|
||||
@@ -167,7 +164,7 @@ struct fields_t {
|
||||
jfieldID patternSkipBlocksID;
|
||||
jfieldID queueRequestIndexID;
|
||||
jfieldID outputFrameLinearBlockID;
|
||||
jfieldID outputFrameGraphicBlockID;
|
||||
jfieldID outputFrameHardwareBufferID;
|
||||
jfieldID outputFrameChangedKeysID;
|
||||
jfieldID outputFrameFormatID;
|
||||
};
|
||||
@@ -176,16 +173,6 @@ static fields_t gFields;
|
||||
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(
|
||||
@@ -706,16 +693,22 @@ status_t JMediaCodec::getOutputFrame(
|
||||
break;
|
||||
}
|
||||
case C2BufferData::GRAPHIC: {
|
||||
std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock};
|
||||
context->mBuffer = c2Buffer;
|
||||
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());
|
||||
const C2Handle *c2Handle = c2Buffer->data().graphicBlocks().front().handle();
|
||||
uint32_t width, height, format, stride, igbp_slot, generation;
|
||||
uint64_t usage, igbp_id;
|
||||
_UnwrapNativeCodec2GrallocMetadata(
|
||||
c2Handle, &width, &height, &format, &usage, &stride, &generation,
|
||||
&igbp_id, &igbp_slot);
|
||||
native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(c2Handle);
|
||||
GraphicBuffer* graphicBuffer = new GraphicBuffer(
|
||||
grallocHandle, GraphicBuffer::CLONE_HANDLE,
|
||||
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;
|
||||
}
|
||||
case C2BufferData::LINEAR_CHUNKS: [[fallthrough]];
|
||||
@@ -737,16 +730,7 @@ status_t JMediaCodec::getOutputFrame(
|
||||
true);
|
||||
env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get());
|
||||
} else {
|
||||
std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock};
|
||||
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());
|
||||
// No-op.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1873,6 +1857,113 @@ static void android_media_MediaCodec_queueSecureInputBuffer(
|
||||
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(
|
||||
JNIEnv *env, jobject keys, jobject values, sp<AMessage> *msg) {
|
||||
static struct Fields {
|
||||
@@ -2031,11 +2122,6 @@ static void android_media_MediaCodec_native_queueLinearBlock(
|
||||
}
|
||||
|
||||
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(
|
||||
index,
|
||||
memory,
|
||||
@@ -2059,10 +2145,10 @@ static void android_media_MediaCodec_native_queueLinearBlock(
|
||||
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,
|
||||
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);
|
||||
|
||||
@@ -2078,29 +2164,21 @@ static void android_media_MediaCodec_native_queueGraphicBlock(
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<C2Buffer> buffer;
|
||||
std::shared_ptr<C2GraphicBlock> block;
|
||||
ScopedLocalRef<jobject> lock{env, env->GetObjectField(bufferObj, gGraphicBlockInfo.lockId)};
|
||||
if (env->MonitorEnter(lock.get()) == JNI_OK) {
|
||||
if (env->GetBooleanField(bufferObj, gGraphicBlockInfo.validId)) {
|
||||
JMediaCodecGraphicBlock *context = (JMediaCodecGraphicBlock *)env->GetLongField(
|
||||
bufferObj, gGraphicBlockInfo.contextId);
|
||||
buffer = context->mBuffer;
|
||||
block = context->mBlock;
|
||||
}
|
||||
env->MonitorExit(lock.get());
|
||||
} else {
|
||||
throwExceptionAsNecessary(env, INVALID_OPERATION);
|
||||
return;
|
||||
}
|
||||
AHardwareBuffer *hardwareBuffer = android_hardware_HardwareBuffer_getNativeHardwareBuffer(
|
||||
env, bufferObj);
|
||||
sp<GraphicBuffer> graphicBuffer{AHardwareBuffer_to_GraphicBuffer(hardwareBuffer)};
|
||||
C2Handle *handle = WrapNativeCodec2GrallocHandle(
|
||||
graphicBuffer->handle, graphicBuffer->format,
|
||||
graphicBuffer->width, graphicBuffer->height,
|
||||
graphicBuffer->usage, graphicBuffer->stride);
|
||||
std::shared_ptr<C2GraphicBlock> block = _C2BlockFactory::CreateGraphicBlock(handle);
|
||||
|
||||
if (!block && !buffer) {
|
||||
if (!block) {
|
||||
throwExceptionAsNecessary(env, BAD_VALUE);
|
||||
return;
|
||||
}
|
||||
if (!buffer) {
|
||||
buffer = C2Buffer::CreateGraphicBuffer(block->share(block->crop(), C2Fence{}));
|
||||
}
|
||||
std::shared_ptr<C2Buffer> buffer = C2Buffer::CreateGraphicBuffer(block->share(
|
||||
block->crop(), C2Fence{}));
|
||||
AString errorDetailMsg;
|
||||
err = codec->queueBuffer(index, buffer, presentationTimeUs, flags, tunings, &errorDetailMsg);
|
||||
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;");
|
||||
CHECK(gFields.outputFrameLinearBlockID != NULL);
|
||||
|
||||
gFields.outputFrameGraphicBlockID =
|
||||
env->GetFieldID(clazz.get(), "mGraphicBlock", "Landroid/media/MediaCodec$GraphicBlock;");
|
||||
CHECK(gFields.outputFrameGraphicBlockID != NULL);
|
||||
gFields.outputFrameHardwareBufferID =
|
||||
env->GetFieldID(clazz.get(), "mHardwareBuffer", "Landroid/hardware/HardwareBuffer;");
|
||||
CHECK(gFields.outputFrameHardwareBufferID != NULL);
|
||||
|
||||
gFields.outputFrameChangedKeysID =
|
||||
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;");
|
||||
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(
|
||||
@@ -2949,196 +3006,6 @@ static jboolean android_media_MediaCodec_LinearBlock_checkCompatible(
|
||||
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[] = {
|
||||
{ "native_release", "()V", (void *)android_media_MediaCodec_release },
|
||||
|
||||
@@ -3184,14 +3051,20 @@ static const JNINativeMethod gMethods[] = {
|
||||
{ "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
|
||||
(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",
|
||||
"(ILandroid/media/MediaCodec$LinearBlock;IILandroid/media/MediaCodec$CryptoInfo;JI"
|
||||
"Ljava/util/ArrayList;Ljava/util/ArrayList;)V",
|
||||
(void *)android_media_MediaCodec_native_queueLinearBlock },
|
||||
|
||||
{ "native_queueGraphicBlock",
|
||||
"(ILandroid/media/MediaCodec$GraphicBlock;JILjava/util/ArrayList;Ljava/util/ArrayList;)V",
|
||||
(void *)android_media_MediaCodec_native_queueGraphicBlock },
|
||||
{ "native_queueHardwareBuffer",
|
||||
"(ILandroid/hardware/HardwareBuffer;JILjava/util/ArrayList;Ljava/util/ArrayList;)V",
|
||||
(void *)android_media_MediaCodec_native_queueHardwareBuffer },
|
||||
|
||||
{ "native_getOutputFrame",
|
||||
"(Landroid/media/MediaCodec$OutputFrame;I)V",
|
||||
@@ -3265,20 +3138,6 @@ static const JNINativeMethod gLinearBlockMethods[] = {
|
||||
(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 result = AndroidRuntime::registerNativeMethods(env,
|
||||
"android/media/MediaCodec", gMethods, NELEM(gMethods));
|
||||
@@ -3289,12 +3148,5 @@ int register_android_media_MediaCodec(JNIEnv *env) {
|
||||
"android/media/MediaCodec$LinearBlock",
|
||||
gLinearBlockMethods,
|
||||
NELEM(gLinearBlockMethods));
|
||||
if (result != JNI_OK) {
|
||||
return result;
|
||||
}
|
||||
result = AndroidRuntime::registerNativeMethods(env,
|
||||
"android/media/MediaCodec$GraphicBlock",
|
||||
gGraphicBlockMethods,
|
||||
NELEM(gGraphicBlockMethods));
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user