在Android Native层直接调用MediaCodec接口的实现

来源:互联网 发布:域名与服务器 编辑:程序博客网 时间:2024/05/29 03:36

    因为工作需要,要在Native层直接调用MediaCodec接口。不使用Java层的MediaCodec.java,而是直接使用MediaCodec.cpp。

    我们知道,Android上处理视频解码,一般地是使用Java层MediaCodec类开发应用,而MediaCodec.java通过JNI最终调用MediaCodec.cpp的接口。为方便视频流数据的传输和线程控制,提高解码效率,我在工作中,封装了一个基于MediaCodec.cpp的API库,命名为libmediacodec.so,用以被我的Native层应用调用实现视频解码功能。

    实现思路比较简单,就是将原来的android_media_mediacodec.cpp中的接口去除JNI的特征,其对应关系如下表所示:

android_media_mediacodec.cpp接口libmediacodec.so接口static void android_media_MediaCodec_native_setup(
        JNIEnv *env, jobject thiz,
        jstring name, jboolean nameIsType, jboolean encoder)int libmcodec_create(char* name, bool nameIsType, bool encoder)static void android_media_MediaCodec_native_configure(
        JNIEnv *env,
        jobject thiz,
        jobjectArray keys, jobjectArray values,
        jobject jsurface,
        jobject jcrypto,
        jint flags)void libmcodec_configure(void* inSurface, MCFormat format)static jobjectArray android_media_MediaCodec_getBuffers(
        JNIEnv *env, jobject thiz, jboolean input)void mcodec_getBuffers(bool input, Vector<sp<ABuffer>> & buffers)static jint android_media_MediaCodec_dequeueInputBuffer(
        JNIEnv *env, jobject thiz, jlong timeoutUs)int mcodec_dequeueInputBuffer(long timeoutUs)static void android_media_MediaCodec_queueInputBuffer(
        JNIEnv *env,
        jobject thiz,
        jint index,
        jint offset,
        jint size,
        jlong timestampUs,
        jint flags)void mcodec_queueInputBuffer(int index, int offset, int size, long timestampUs, int flags)static jint android_media_MediaCodec_dequeueOutputBuffer(
        JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs)int mcodec_dequeueOutputBuffer(BufferInfo& info, int64_t timeoutUs)static void android_media_MediaCodec_releaseOutputBuffer(
        JNIEnv *env, jobject thiz, jint index, jboolean render)status_t mcodec_releaseOutputBuffer(size_t index, bool render)static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz)void libmcodec_start()static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz)void libmcodec_stop() 

    上面表格libmediacodec.so一列的接口中,前缀带有lib的是对外开放的,其它的只在lib内部被调用。另外,在libmediacodec.so中实现一个libmcodec_decode接口,用于解码。

     几个关键点:

     1. suface是通过从Java层传下来的。

      在JNI接口里获取Surface地址的方法是:

     jint Java_com_xxx_SetSurface(JNIEnv* env, jobject thiz, jobject jsurface)

     {

         jclass clazz=env->FindClass("android/view/Surface");

         jfieldID field_surface = env->GetFieldID(clazz, "mNativeObject","I");

         if(field_surface==NULL)

           return -1;

         void* surface = (void*) env->GetIntField(jsurface,field_surface);

         libmcodec_configure(surface, fomat);

         return 0;

     }

      在libmcodec_configure中,需要将void*类型的surface进行如下转换:

      sp<Surface> sur = reinterpret_castKSurface*>(inSurface);


    2. 关于ABuffer和ByteBuffer转换的理解

       在Native层,使用MediaCodec的接口getInputBuffers得到一个存有ABuffer指针的Vector,即指针数组。而Java层使用的是ByteBuffer的数组。 在JNI接口里,进行了相应的转换将ABuffer*的Vector转换为ByterBuffer的数组。转换操作有:

     a. 获取input/outpu的sp<ABuffer>数组。

     b. 创建ByteBuffer数组

     c.  执行循环,循环体步骤如下

         ca. 从sp<ABuffer>的Vector中获取一个元素(一个ABuffer的指针)。

         cb. 使用这个ABuffer的起始地址和size, 使用NewDirectByteBuffer构建一个ByteBuffer,并按照Native层的字节序,对其进行排序。这里是重点,新创建的ByteBuffer与原ABuffer是同一个起始地址,相同的大小,因此实际上它们是同一个存储区,只是名称和类型不同,Buffer的基本元素都是一个字节。

         cd. 使用SetObjectArrayElement将排好序的ByteBuffer放到ByteBuffer数组中。

    

    3. 基于以上对ABuffer的理解,在Navtie使用ABuffer时,就不必做上述转换。直接使用memcpy,向其base()+offset的地址拷贝数据即可。

    

0 0
原创粉丝点击