在之前写过在Android平台上通过MediaCodec的能力,硬解码到纹理上,然后在Unity上使用纹理进行渲染。
在Unity使用Android的MediaCodec硬解码能力进行纹理加载_XR风云的博客-CSDN博客
在做AR云渲染项目中,需要在Unity中利用Android的MediaCodec能力进行对视频进行硬解码,我们知道MediaCodec可以把视频流渲染到一个surface上,怎样把视频流中的纹理在Unity中使用呢?一个办法就是MediaCodec解码完成之后,可以把图像的缓存读取出来,在Unity中利用Texture2D 的LoadRawTextureData能力加载到一个纹理上去, 然后在经过YUV格式转换成RGB格式。这个方法非常耗性能,需要从GPU->CPU->GPU的链条,是非常耗
https://blog.csdn.net/grace_yi/article/details/116497338?spm=1001.2014.3001.5501
之前用的都是JAVA的接口,最近在写native SDK,需要直接使用C++的接口来进行解码,以及纹理处理,那最重要的就是怎样解码出来的画面直接写到纹理上去呢?
我们知道Android NDK提供了MediaCodec C++的解码接口的。跟java的接口非常类似。可以参考java的接口来进行编写。
https://developer.android.com/ndk/reference/group/media#amediacodec_configure
https://developer.android.com/ndk/reference/group/media#amediacodec_configure
当前最主要的还是AMediaCodec_configure这个配置接口,配置渲染到的surface上。这个surface怎样创建出来呢?
surface还是很容易创建出来的,但是怎样可以传入我们自己的textureId呢?这一块找了很多资料,没有直接找到C++的接口。那我就取了个巧,在C++中调用java的接口来进行操作。
1、首先我们利用JNI_OnLoad加载的时候,保存我们的global jvm。然后利用jvm来获取JNIEnv,代码如下。
JavaVM *global_jvm;
jobject g_surfaceTex_obj;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
signal(SIGSEGV, SIG_DFL);
global_jvm = vm;
return JNI_VERSION_1_6;
}
JNIEnv *get_env() {
if (global_jvm == NULL) return NULL;
JNIEnv *jni_env = NULL;
int status = global_jvm->GetEnv((void **)&jni_env, JNI_VERSION_1_6);
return jni_env;
}
2、有了JNIEnv之后,我们就可以调用java的接口来创建SurfaceTexture,Surface,调用UpdateTexImage接口等。
int getTextureID() {
GLuint texture;
GLint currentTexture = 0;
GL(glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, ¤tTexture))
GL(glGenTextures(1, &texture));
GL(glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture));
GL(glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
GL(glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
GL(glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
GL(glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
GL(glBindTexture(GL_TEXTURE_EXTERNAL_OES, currentTexture))
return texture;
}
void createSureface(){
JNIEnv *env = get_env();
jclass surfacetexture_class = env->FindClass("android/graphics/SurfaceTexture");
jmethodID surfacetexture_init = env->GetMethodID(surfacetexture_class, "<init>", "(I)V");
jmethodID surfacetexture_setwh = env->GetMethodID(surfacetexture_class, "setDefaultBufferSize", "(II)V");
jobject surfacetexture_obj = env->NewObject(surfacetexture_class, surfacetexture_init, (jint)getTextureID());
env->CallVoidMethod(surfacetexture_obj, surfacetexture_setwh, width, height);
jclass surface_class = env->FindClass("android/view/Surface");
jmethodID surface_init = env->GetMethodID(surface_class, "<init>", "(Landroid/graphics/SurfaceTexture;)V");
jobject surface_obj = env->NewObject(surface_class, surface_init, surfacetexture_obj);
g_surfaceTex_obj = env->NewGlobalRef(surfacetexture_obj);
env->DeleteLocalRef(surface_obj);
env->DeleteLocalRef(surfacetexture_obj);
}
3、创建好Surface之后,在调用解码的接口AMediaCodec_configure的时候,知道nativeWindow
ANativeWindow* nativeWindow = ANativeWindow_fromSurface(env, surface);
AMediaCodec_configure(videoCodec, videoFormat, nativeWindow, NULL, 0);
4、调用UpdateTexImage更新每一帧数据
JNIEnv *envT = get_env();
jclass surfacetexture_class = envT->FindClass("android/graphics/SurfaceTexture");
jmethodID surfacetexture_update = envT->GetMethodID(surfacetexture_class, "updateTexImage", "()V");
envT->CallVoidMethod(g_surfaceTex_obj, surfacetexture_update);
这样就可以了。这样我们解码之后的数据直接在GPU中存到Texture上了,然后我们拿着TextureId就可以进行渲染了。这个是我们提供的C++版本的使用方法。JAVA版本的使用方法可以点击开头的链接。
参考文献: