From 637afb2db483d1f23c936c501ce056564838f851 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Tue, 25 Feb 2020 14:27:29 -0800 Subject: [PATCH] MediaCodec: address API review #2 - Remove GraphicBlock and use HardwareBuffer instead. - retrieveChangedKeys -> getChangedKeys - More messages for Exceptions Bug: 149487982 Test: atest CtsMediaTestCases -- --module-arg CtsMediaTestCases:size:small Change-Id: I4d19a8dfa6b9f43005c3d3d8ecc840d16aebd356 --- api/current.txt | 18 +- .../java/android/hardware/HardwareBuffer.java | 6 +- media/java/android/media/MediaCodec.java | 312 ++++-------- media/jni/android_media_MediaCodec.cpp | 460 ++++++------------ 4 files changed, 271 insertions(+), 525 deletions(-) diff --git a/api/current.txt b/api/current.txt index c0ab51368458e..e81c7fd0f54bc 100644 --- a/api/current.txt +++ b/api/current.txt @@ -16802,6 +16802,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 { @@ -25210,6 +25211,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(); @@ -25310,17 +25312,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 { @@ -25350,12 +25342,12 @@ package android.media { } public static final class MediaCodec.OutputFrame { + method @NonNull public java.util.Set 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); } public final class MediaCodec.QueueRequest { @@ -25363,7 +25355,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); diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java index e97a5b9e89ea0..dd3493063e287 100644 --- a/core/java/android/hardware/HardwareBuffer.java +++ b/core/java/android/hardware/HardwareBuffer.java @@ -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: diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index f800f9ee91c9a..c0461bc598ed2 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -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. *

* When this flag is set, the following APIs throw {@link IncompatibleWithBlockModelException}. *

    @@ -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. - *

    - * 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. - *

    - * 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. - *

    - * 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 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}. + *

    + * 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 mTuningKeys = new ArrayList<>(); @@ -3440,9 +3309,9 @@ final public class MediaCodec { @NonNull ArrayList keys, @NonNull ArrayList 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 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 keys) { - keys.clear(); - keys.addAll(mChangedKeys); + public @NonNull Set 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 mChangedKeys = new ArrayList<>(); + private final Set 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 { diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 893e51654b682..fc9b91c76a4f7 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -34,10 +34,14 @@ #include #include +#include +#include #include #include +#include + #include #include @@ -58,6 +62,8 @@ #include #include +#include + #include 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 mBuffer; - std::shared_ptr mReadonlyMapping; - - std::shared_ptr mBlock; - std::shared_ptr mReadWriteMapping; - - sp mLegacyBuffer; -}; - //////////////////////////////////////////////////////////////////////////////// JMediaCodec::JMediaCodec( @@ -706,16 +693,22 @@ status_t JMediaCodec::getOutputFrame( break; } case C2BufferData::GRAPHIC: { - std::unique_ptr context{new JMediaCodecGraphicBlock}; - context->mBuffer = c2Buffer; - ScopedLocalRef 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 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 context{new JMediaCodecGraphicBlock}; - context->mLegacyBuffer = buffer; - ScopedLocalRef 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 planeClazz( + env, env->FindClass("android/media/MediaCodec$MediaImage$MediaPlane")); + ScopedLocalRef planeArray{ + env, env->NewObjectArray(3, planeClazz.get(), NULL)}; + CHECK(planeClazz.get() != NULL); + jmethodID planeConstructID = env->GetMethodID(planeClazz.get(), "", + "([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 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 planeObj{env, env->NewObject( + planeClazz.get(), planeConstructID, + byteBuffer.get(), plane.rowStride, plane.pixelStride)}; + + env->SetObjectArrayElement(planeArray.get(), i, planeObj.get()); + } + + ScopedLocalRef imageClazz( + env, env->FindClass("android/media/MediaCodec$MediaImage")); + CHECK(imageClazz.get() != NULL); + + jmethodID imageConstructID = env->GetMethodID(imageClazz.get(), "", + "([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{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 *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 codec = getMediaCodec(env, thiz); @@ -2078,29 +2164,21 @@ static void android_media_MediaCodec_native_queueGraphicBlock( return; } - std::shared_ptr buffer; - std::shared_ptr block; - ScopedLocalRef 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{AHardwareBuffer_to_GraphicBuffer(hardwareBuffer)}; + C2Handle *handle = WrapNativeCodec2GrallocHandle( + graphicBuffer->handle, graphicBuffer->format, + graphicBuffer->width, graphicBuffer->height, + graphicBuffer->usage, graphicBuffer->stride); + std::shared_ptr 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 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(), "", "()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 -static jobject CreateImage(JNIEnv *env, const std::shared_ptr &view) { - bool readOnly = std::is_const::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 planeClazz( - env, env->FindClass("android/media/MediaCodec$MediaImage$MediaPlane")); - ScopedLocalRef planeArray{ - env, env->NewObjectArray(layout.numPlanes, planeClazz.get(), NULL)}; - CHECK(planeClazz.get() != NULL); - jmethodID planeConstructID = env->GetMethodID(planeClazz.get(), "", - "([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 byteBuffer{env, CreateByteBuffer( - env, - view->data()[plane.rootIx] + plane.offset + minOffset, - maxOffset - minOffset + 1, - 0, - maxOffset - minOffset + 1, - readOnly, - true)}; - - ScopedLocalRef jPlane{env, env->NewObject( - planeClazz.get(), planeConstructID, - byteBuffer.get(), plane.rowInc, plane.colInc)}; - } - - ScopedLocalRef imageClazz( - env, env->FindClass("android/media/MediaCodec$MediaImage")); - CHECK(imageClazz.get() != NULL); - - jmethodID imageConstructID = env->GetMethodID(imageClazz.get(), "", - "([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 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(block.map().get()); - } - return CreateImage(env, context->mReadonlyMapping); - } else if (context->mBlock) { - std::shared_ptr block = context->mBlock; - if (!context->mReadWriteMapping) { - context->mReadWriteMapping = - std::make_shared(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 context{new JMediaCodecGraphicBlock}; - std::vector 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 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; }