因为工作需要,要在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 |
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
2. 关于ABuffer和ByteBuffer转换的理解
在Native层,使用MediaCodec的接口getInputBuffers得到一个存有ABuffer指针的Vector,即指针数组。而Java层使用的是ByteBuffer的数组。 在JNI接口里,进行了相应的转换将ABuffer*的Vector转换为ByterBuffer的数组。转换操作有:
a. 获取input/outpu的sp
b. 创建ByteBuffer数组
c. 执行循环,循环体步骤如下
ca. 从sp
cb. 使用这个ABuffer的起始地址和size, 使用NewDirectByteBuffer构建一个ByteBuffer,并按照Native层的字节序,对其进行排序。这里是重点,新创建的ByteBuffer与原ABuffer是同一个起始地址,相同的大小,因此实际上它们是同一个存储区,只是名称和类型不同,Buffer的基本元素都是一个字节。
cd. 使用SetObjectArrayElement将排好序的ByteBuffer放到ByteBuffer数组中。
3. 基于以上对ABuffer的理解,在Navtie使用ABuffer时,就不必做上述转换。直接使用memcpy,向其base()+offset的地址拷贝数据即可。