|
MEDIACODEC在JNI层下的H264,H265视频硬解码实现(ACODEC )
发表于 2015-12-22 11:07:02
浏览:47747
|
回复:23
打印
只看该作者
[复制链接]
楼主
本帖最后由 jingjin221 于 2016-11-21 12:03 编辑
有疑惑请一起商讨QQ(512975979) 之前提供的代码是基于已有的SDK来开发的,只能适用于这一个机型的运行。比如说,我是基于FIREFLY给的RK3288 ANDROID4.4 PAD的SDK开发的,编译出来的库和APK,只能用于这一个机型! 所以说,要开发一个基于MEDIACODEC,而且是在NATIVE层下的通用视频解码播放器,就必须脱离具体的FRAMEWORK,独立出来。
之前参考了FFMPEG FOR ANDROID,XBMC等一些SDK,他们采用的公用的头文件,和一些通用的基本库,基本是在OMXCODEC下做的解码,渲染的话,FFMPEG是用的软件渲染YUV,而XBMC是用的GL来直接渲染的,这两者差距甚大,而且对我们二次开发难度不小,况且一般我们需要的不是整个模型,只是对其中的一块有兴趣,那么就要进行裁剪,这工作量是很大的,况且通过通用的库编译出来的apk,不一定适用于通用ANDROID机型。毕竟安卓是开源的,太多的厂商对他进行二次开发,导致标准不一等等!
综上所述,我又下载了VLC FOR ANDROID 发现,VLC基本可以支持所有ANDROID设备,为什么呢?我看了VLC的源码,发现了猫腻。VLC的数据输入,解复用,都采用的是FFMPEG来做的,在解码和渲染这一块,针对安卓有特殊处理!VLC针对解码和渲染,这里指硬解码和直接渲染,VLC是直接调用JAVA下的MEDIACODEC,来实现的!也就是说在JNI下直接调用JAVA的接口,这本身和在JAVA下调用MEDIACODEC没有任何区别了!至于为什么要这样做,有点疑惑!为什么不直接在JAVA下用MEDIACODEC呢?JAVA下的MEDIACODEC网上例子很多,基本上MEDIACODEC是绝大多数ANDROID版本支持的硬解码库!自认为是VLC想把解码这一套机制全部封装在通用解码框架里,这样就可以无缝为APP提供通用解码接口了吧!
废话不多说,我们就来试一试,如何在NATIVE下调用JAVA的MEDIACODEC吧!经过测试,这套机制基本通吃所有机型吧!而且清晰方便,兼容性强! JNI的基本操作我就不讲了!自己学习吧
解码的数据结构!
- enum Types
- {
- METHOD, STATIC_METHOD, FIELD
- };
- struct audio_track_sys_t {
- jclass audio_track_class;//
- jmethodID audio_track_init, audio_set_volume, audio_get_min_buffer_size, audio_write, audio_play, audio_stop, audio_release;//
- jobject audio_track;//
- };
- struct decoder_sys_t
- {
- jclass media_codec_list_class, media_codec_class, media_format_class;
- jclass buffer_info_class, byte_buffer_class;
- /*
- jclass audio_track_class;//
- jmethodID audio_track_init, audio_set_volume, audio_get_min_buffer_size, audio_write, audio_play, audio_stop, audio_release;//
- */
- jmethodID tostring;
- jmethodID get_codec_count, get_codec_info_at, is_encoder, get_capabilities_for_type;
- jfieldID profile_levels_field, profile_field, level_field;
- jmethodID get_supported_types, get_name;
- jmethodID create_by_codec_name, configure, start, stop, flush, release;
- jmethodID get_output_format, get_input_buffers, get_output_buffers;
- jmethodID dequeue_input_buffer, dequeue_output_buffer, queue_input_buffer;
- jmethodID release_output_buffer;
- jmethodID create_video_format, create_audio_format, set_integer, set_bytebuffer, get_integer;
- jmethodID buffer_info_ctor;
- jmethodID allocate_direct, limit;
- jfieldID size_field, offset_field, pts_field;
- uint32_t nal_size;
- /*
- jobject audio_track;//
- */
- jobject codec;
- jobject buffer_info;
- jobject input_buffers, output_buffers;
- int pixel_format;
- int stride, slice_height;
- int crop_top, crop_left;
- char *name;
- bool allocated;
- bool started;
- bool decoded;
- bool error_state;
- bool error_event_sent;
- /* Direct rendering members. */
- bool direct_rendering;
- int i_output_buffers; /**< number of MediaCodec output buffers */
- };
- #define OFF(x) offsetof(struct decoder_sys_t, x)
- struct classname
- {
- const char *name;
- int offset;
- };
- struct member
- {
- const char *name;
- const char *sig;
- const char *class;
- int offset;
- int type;
- };
复制代码 解码器初始化
- {
- char *source_path = (char *) (*env)->GetStringUTFChars(env, path, 0);
- const char *mime = "video/avc";
- int width = 1920;
- int height = 1080;
- bool b_direct_renderer = true;
- if ((p_sys = calloc(1, sizeof(*p_sys))) == NULL)
- return -1;
- ///////////////////////////////////Find Class and Get Method/Field
-
- for (int i = 0; classes[i].name; i++) {
- *(jclass*)((uint8_t*)p_sys + classes[i].offset) = (*env)->FindClass(env, classes[i].name);
- if ((*env)->ExceptionOccurred(env)) {
- printf("Unable to find class %s", classes[i].name);
- (*env)->ExceptionClear(env);
- goto error;
- }
- }
-
- jclass last_class;
- for (int i = 0; members[i].name; i++) {
- if (i == 0 || strcmp(members[i].class, members[i - 1].class))
- last_class = (*env)->FindClass(env, members[i].class);
- if ((*env)->ExceptionOccurred(env)) {
- printf("Unable to find class %s", members[i].class);
- (*env)->ExceptionClear(env);
- goto error;
- }
- switch (members[i].type) {
- case METHOD:
- *(jmethodID*)((uint8_t*)p_sys + members[i].offset) =
- (*env)->GetMethodID(env, last_class, members[i].name, members[i].sig);
- break;
- case STATIC_METHOD:
- *(jmethodID*)((uint8_t*)p_sys + members[i].offset) =
- (*env)->GetStaticMethodID(env, last_class, members[i].name, members[i].sig);
- break;
- case FIELD:
- *(jfieldID*)((uint8_t*)p_sys + members[i].offset) =
- (*env)->GetFieldID(env, last_class, members[i].name, members[i].sig);
- break;
- }
- if ((*env)->ExceptionOccurred(env)) {
- printf("Unable to find the member %s in %s",
- members[i].name, members[i].class);
- (*env)->ExceptionClear(env);
- goto error;
- }
- }
-
- printf("////////////// MEDIACODEC LIST\n");
- int num_codecs = (*env)->CallStaticIntMethod(env, p_sys->media_codec_list_class,
- p_sys->get_codec_count);
- jobject codec_name = NULL;
- for (int i = 0; i < num_codecs; i++) {
- jobject info = NULL;
- jobject name = NULL;
- jobject types = NULL;
- jsize name_len = 0;
- const char *name_ptr = NULL;
- int profile_levels_len = 0, num_types = 0;
- bool found = false;
-
- info = (*env)->CallStaticObjectMethod(env, p_sys->media_codec_list_class,
- p_sys->get_codec_info_at, i);
- if ((*env)->CallBooleanMethod(env, info, p_sys->is_encoder))
- goto loopclean;
-
- types = (*env)->CallObjectMethod(env, info, p_sys->get_supported_types);
- num_types = (*env)->GetArrayLength(env, types);
- name = (*env)->CallObjectMethod(env, info, p_sys->get_name);
- name_len = (*env)->GetStringUTFLength(env, name);
- name_ptr = (*env)->GetStringUTFChars(env, name, NULL);
- found = false;
-
- printf("MEDIACODEC::LIST#%s\n", name_ptr);
- if (!strncmp(name_ptr, "OMX.google.", __MIN(11, name_len)))
- goto loopclean;
-
- for (int j = 0; j < num_types && !found; j++) {
- jobject type = (*env)->GetObjectArrayElement(env, types, j);
- if (!jstrcmp(env, type, mime)) {
- found = true;
- }
- (*env)->DeleteLocalRef(env, type);
- }
-
- if (found) {
- printf("using %.*s", name_len, name_ptr);
- p_sys->name = malloc(name_len + 1);
- memcpy(p_sys->name, name_ptr, name_len);
- p_sys->name[name_len] = '\0';
- codec_name = name;
- }
- loopclean:
- if (name)
- (*env)->ReleaseStringUTFChars(env, name, name_ptr);
- if (types)
- (*env)->DeleteLocalRef(env, types);
- if (info)
- (*env)->DeleteLocalRef(env, info);
- if (found)
- break;
- }
- printf("////////////// MEDIACODEC\n");
- if (!codec_name) {
- printf("No suitable codec matching %s was found", mime);
- goto error;
- }
- printf("////////////// MEDIACODEC Create\n");
- p_sys->codec = (*env)->CallStaticObjectMethod(env, p_sys->media_codec_class,
- p_sys->create_by_codec_name, codec_name);
- if ((*env)->ExceptionOccurred(env)) {
- printf("Exception occurred in MediaCodec.createByCodecName.");
- (*env)->ExceptionClear(env);
- goto error;
- }
- p_sys->allocated = true;
- p_sys->codec = (*env)->NewGlobalRef(env, p_sys->codec);
-
- jobject format = (*env)->CallStaticObjectMethod(env, p_sys->media_format_class,
- p_sys->create_video_format, (*env)->NewStringUTF(env, mime),
- width, height);
- printf("////////////// MEDIACODEC Configure\n");
- p_sys->direct_rendering = b_direct_renderer;
- if (p_sys->direct_rendering) {
- jobject surf = surface;
- if (surf) {
- (*env)->CallVoidMethod(env, p_sys->codec, p_sys->configure, format, surf, NULL, 0);
- if ((*env)->ExceptionOccurred(env)) {
- printf("Exception occurred in MediaCodec.configure");
- (*env)->ExceptionClear(env);
- goto error;
- }
- } else {
- printf("Failed to get the Android Surface, disabling direct rendering.");
- p_sys->direct_rendering = false;
- }
- }
- if (!p_sys->direct_rendering) {
- (*env)->CallVoidMethod(env, p_sys->codec, p_sys->configure, format, NULL, NULL, 0);
- if ((*env)->ExceptionOccurred(env)) {
- printf("Exception occurred in MediaCodec.configure");
- (*env)->ExceptionClear(env);
- goto error;
- }
- }
-
- printf("////////////// MEDIACODEC Start\n");
- (*env)->CallVoidMethod(env, p_sys->codec, p_sys->start);
- if ((*env)->ExceptionOccurred(env)) {
- printf("Exception occurred in MediaCodec.start");
- (*env)->ExceptionClear(env);
- goto error;
- }
- p_sys->started = true;
- p_sys->input_buffers = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_input_buffers);
- p_sys->output_buffers = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_output_buffers);
- p_sys->buffer_info = (*env)->NewObject(env, p_sys->buffer_info_class, p_sys->buffer_info_ctor);
- p_sys->input_buffers = (*env)->NewGlobalRef(env, p_sys->input_buffers);
- p_sys->output_buffers = (*env)->NewGlobalRef(env, p_sys->output_buffers);
- p_sys->buffer_info = (*env)->NewGlobalRef(env, p_sys->buffer_info);
- p_sys->i_output_buffers = (*env)->GetArrayLength(env, p_sys->output_buffers);
- VideoDecoderStart(p_sys);
- //receiver_start(source_path, 2);
- return 0;
- error:
- if(p_sys != NULL)
- {
- free(p_sys);
- p_sys = NULL;
- }
- return -1;
- }
复制代码 解码和渲染
- {
- JNIEnv *env;
- struct decoder_sys_t *p_sys = (struct decoder_sys_t *)arg;
- struct frame_queue *p_frame_queue = &programe_media.video.frame_queue;
- int sampleSize;
- int64_t pts;
- int fifo_cnts = 0;
- buffer_t *buffer = NULL;
-
- //Attach主线程
- if((*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL) != JNI_OK)
- {
- printf("%s: AttachCurrentThread() failed", __FUNCTION__);
- return NULL;
- }
- sem_wait(&video_decode_sem);
- //while(fifo_count(p_frame_queue->fifo) < 240)
- //usleep(1000);
- while(true)
- {
- int input_index = (*env)->CallIntMethod(env, p_sys->codec, p_sys->dequeue_input_buffer, (jlong) 0);
- if ((*env)->ExceptionOccurred(env)) {
- printf("Exception occurred in MediaCodec.dequeueInputBuffer");
- (*env)->ExceptionClear(env);
- p_sys->error_state = true;
- break;
- }
- if(input_index >= 0) {
- jobject buf = (*env)->GetObjectArrayElement(env, p_sys->input_buffers, input_index);
- jsize size = (*env)->GetDirectBufferCapacity(env, buf);
- uint8_t *bufptr = (*env)->GetDirectBufferAddress(env, buf);
- //printf("size is %d\n", size);
- retry:
- sampleSize = av_read_one_frame((uint8_t *)bufptr, &pts);
- if(sampleSize <= 0)
- {
- //printf("VIDEO@sampleSize is %d\n", sampleSize);
- goto retry;
- }
- size = sampleSize;
-
- (*env)->CallVoidMethod(env, p_sys->codec, p_sys->queue_input_buffer, input_index, 0, size, pts, 0);
- (*env)->DeleteLocalRef(env, buf);
- if ((*env)->ExceptionOccurred(env)) {
- printf("Exception in MediaCodec.queueInputBuffer");
- (*env)->ExceptionClear(env);
- p_sys->error_state = true;
- break;
- }
- }
- jlong timeout = 0;
- int output_index = (*env)->CallIntMethod(env, p_sys->codec, p_sys->dequeue_output_buffer,
- p_sys->buffer_info, timeout);
- if ((*env)->ExceptionOccurred(env)) {
- printf("Exception in MediaCodec.dequeueOutputBuffer (GetOutput)");
- (*env)->ExceptionClear(env);
- p_sys->error_state = true;
- return NULL;
- }
- if(output_index >= 0) {
- //printf("Buffers returned before output format is set, dropping frame");
- (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, output_index, p_sys->direct_rendering);
- if ((*env)->ExceptionOccurred(env)) {
- printf("Exception in MediaCodec.releaseOutputBuffer");
- (*env)->ExceptionClear(env);
- p_sys->error_state = true;
- return NULL;
- }
- }
- }
- error:
- //Detach主线程
- if((*g_jvm)->DetachCurrentThread(g_jvm) != JNI_OK)
- {
- printf("%s: DetachCurrentThread() failed", __FUNCTION__);
- }
-
- pthread_exit(0);
- }
复制代码 这就是核心代码,基本就是模拟在JAVA下的解码流程!
另外,注明下!音频解码!音频解码如果也用着套机制,可能对大多数机型不是很友好,具体指MP3格式!,有些机型支持,GOOGLE.MP3.DECODER,有些不支持,不一定可用,所以这里奉劝大家还是自己移植MP3解码库吧,我用的LIBMAD,还不错!
|
|