Firefly开源社区

标题: MEDIACODEC在JNI层下的H264,H265视频硬解码实现(ACODEC ) [打印本页]

作者: jingjin221    时间: 2015-12-22 11:07
标题: MEDIACODEC在JNI层下的H264,H265视频硬解码实现(ACODEC )
本帖最后由 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的基本操作我就不讲了!自己学习吧
    解码的数据结构!
  1. enum Types
  2. {
  3.     METHOD, STATIC_METHOD, FIELD
  4. };

  5. struct audio_track_sys_t {
  6.         jclass audio_track_class;//
  7.         jmethodID audio_track_init, audio_set_volume, audio_get_min_buffer_size, audio_write, audio_play, audio_stop, audio_release;//
  8.         jobject audio_track;//
  9. };

  10. struct decoder_sys_t
  11. {
  12.     jclass media_codec_list_class, media_codec_class, media_format_class;
  13.     jclass buffer_info_class, byte_buffer_class;
  14.         /*
  15.         jclass audio_track_class;//
  16.         jmethodID audio_track_init, audio_set_volume, audio_get_min_buffer_size, audio_write, audio_play, audio_stop, audio_release;//
  17.         */
  18.     jmethodID tostring;
  19.     jmethodID get_codec_count, get_codec_info_at, is_encoder, get_capabilities_for_type;
  20.     jfieldID profile_levels_field, profile_field, level_field;
  21.     jmethodID get_supported_types, get_name;
  22.     jmethodID create_by_codec_name, configure, start, stop, flush, release;
  23.     jmethodID get_output_format, get_input_buffers, get_output_buffers;
  24.     jmethodID dequeue_input_buffer, dequeue_output_buffer, queue_input_buffer;
  25.     jmethodID release_output_buffer;
  26.     jmethodID create_video_format, create_audio_format, set_integer, set_bytebuffer, get_integer;
  27.     jmethodID buffer_info_ctor;
  28.     jmethodID allocate_direct, limit;
  29.     jfieldID size_field, offset_field, pts_field;

  30.     uint32_t nal_size;
  31.         /*
  32.         jobject audio_track;//
  33.         */
  34.     jobject codec;
  35.     jobject buffer_info;
  36.     jobject input_buffers, output_buffers;
  37.     int pixel_format;
  38.     int stride, slice_height;
  39.     int crop_top, crop_left;
  40.     char *name;

  41.     bool allocated;
  42.     bool started;
  43.     bool decoded;
  44.     bool error_state;
  45.     bool error_event_sent;

  46.     /* Direct rendering members. */
  47.     bool direct_rendering;
  48.     int i_output_buffers; /**< number of MediaCodec output buffers */
  49. };


  50. #define OFF(x) offsetof(struct decoder_sys_t, x)
  51. struct classname
  52. {
  53.     const char *name;
  54.     int offset;
  55. };

  56. struct member
  57. {
  58.     const char *name;
  59.     const char *sig;
  60.     const char *class;
  61.     int offset;
  62.     int type;
  63. };
复制代码
   解码器初始化
  1. {
  2.         char *source_path = (char *) (*env)->GetStringUTFChars(env, path, 0);
  3.         const char *mime = "video/avc";
  4.         int width = 1920;
  5.         int height = 1080;
  6.         bool b_direct_renderer = true;
  7.         if ((p_sys = calloc(1, sizeof(*p_sys))) == NULL)
  8.         return -1;

  9. ///////////////////////////////////Find Class and Get Method/Field
  10.                
  11.         for (int i = 0; classes[i].name; i++) {
  12.                 *(jclass*)((uint8_t*)p_sys + classes[i].offset) = (*env)->FindClass(env, classes[i].name);

  13.                 if ((*env)->ExceptionOccurred(env)) {
  14.                     printf("Unable to find class %s", classes[i].name);
  15.                     (*env)->ExceptionClear(env);
  16.                     goto error;
  17.                 }
  18.         }
  19.         
  20.     jclass last_class;
  21.     for (int i = 0; members[i].name; i++) {
  22.         if (i == 0 || strcmp(members[i].class, members[i - 1].class))
  23.             last_class = (*env)->FindClass(env, members[i].class);

  24.         if ((*env)->ExceptionOccurred(env)) {
  25.             printf("Unable to find class %s", members[i].class);
  26.             (*env)->ExceptionClear(env);
  27.             goto error;
  28.         }

  29.         switch (members[i].type) {
  30.         case METHOD:
  31.             *(jmethodID*)((uint8_t*)p_sys + members[i].offset) =
  32.                 (*env)->GetMethodID(env, last_class, members[i].name, members[i].sig);
  33.             break;
  34.         case STATIC_METHOD:
  35.             *(jmethodID*)((uint8_t*)p_sys + members[i].offset) =
  36.                 (*env)->GetStaticMethodID(env, last_class, members[i].name, members[i].sig);
  37.             break;
  38.         case FIELD:
  39.             *(jfieldID*)((uint8_t*)p_sys + members[i].offset) =
  40.                 (*env)->GetFieldID(env, last_class, members[i].name, members[i].sig);
  41.             break;
  42.         }
  43.         if ((*env)->ExceptionOccurred(env)) {
  44.             printf("Unable to find the member %s in %s",
  45.                      members[i].name, members[i].class);
  46.             (*env)->ExceptionClear(env);
  47.             goto error;
  48.         }
  49.     }
  50.         
  51.         printf("////////////// MEDIACODEC LIST\n");
  52.         int num_codecs = (*env)->CallStaticIntMethod(env, p_sys->media_codec_list_class,
  53.                                                  p_sys->get_codec_count);
  54.     jobject codec_name = NULL;
  55.     for (int i = 0; i < num_codecs; i++) {
  56.                 jobject info = NULL;
  57.         jobject name = NULL;
  58.                 jobject types = NULL;
  59.                 jsize name_len = 0;
  60.         const char *name_ptr = NULL;
  61.                 int profile_levels_len = 0, num_types = 0;
  62.         bool found = false;
  63.         
  64.         info = (*env)->CallStaticObjectMethod(env, p_sys->media_codec_list_class,
  65.                                               p_sys->get_codec_info_at, i);
  66.         if ((*env)->CallBooleanMethod(env, info, p_sys->is_encoder))
  67.             goto loopclean;
  68.                
  69.         types = (*env)->CallObjectMethod(env, info, p_sys->get_supported_types);
  70.         num_types = (*env)->GetArrayLength(env, types);
  71.                 name = (*env)->CallObjectMethod(env, info, p_sys->get_name);
  72.                 name_len = (*env)->GetStringUTFLength(env, name);
  73.         name_ptr = (*env)->GetStringUTFChars(env, name, NULL);
  74.         found = false;
  75.                
  76.                 printf("MEDIACODEC::LIST#%s\n", name_ptr);
  77.                 if (!strncmp(name_ptr, "OMX.google.", __MIN(11, name_len)))
  78.             goto loopclean;
  79.                
  80.                 for (int j = 0; j < num_types && !found; j++) {
  81.             jobject type = (*env)->GetObjectArrayElement(env, types, j);
  82.             if (!jstrcmp(env, type, mime)) {
  83.                 found = true;
  84.             }
  85.             (*env)->DeleteLocalRef(env, type);
  86.         }
  87.                         
  88.                 if (found) {
  89.             printf("using %.*s", name_len, name_ptr);
  90.             p_sys->name = malloc(name_len + 1);
  91.             memcpy(p_sys->name, name_ptr, name_len);
  92.             p_sys->name[name_len] = '\0';
  93.             codec_name = name;
  94.         }
  95. loopclean:
  96.         if (name)
  97.             (*env)->ReleaseStringUTFChars(env, name, name_ptr);
  98.         if (types)
  99.             (*env)->DeleteLocalRef(env, types);
  100.         if (info)
  101.             (*env)->DeleteLocalRef(env, info);
  102.         if (found)
  103.             break;
  104.         }

  105.         printf("////////////// MEDIACODEC\n");

  106.         if (!codec_name) {
  107.         printf("No suitable codec matching %s was found", mime);
  108.         goto error;
  109.     }

  110.         printf("////////////// MEDIACODEC Create\n");
  111.         p_sys->codec = (*env)->CallStaticObjectMethod(env, p_sys->media_codec_class,
  112.                                                   p_sys->create_by_codec_name, codec_name);
  113.     if ((*env)->ExceptionOccurred(env)) {
  114.         printf("Exception occurred in MediaCodec.createByCodecName.");
  115.         (*env)->ExceptionClear(env);
  116.         goto error;
  117.     }

  118.         p_sys->allocated = true;
  119.     p_sys->codec = (*env)->NewGlobalRef(env, p_sys->codec);
  120.         
  121.     jobject format = (*env)->CallStaticObjectMethod(env, p_sys->media_format_class,
  122.                          p_sys->create_video_format, (*env)->NewStringUTF(env, mime),
  123.                          width, height);

  124.         printf("////////////// MEDIACODEC Configure\n");
  125.         p_sys->direct_rendering = b_direct_renderer;
  126.     if (p_sys->direct_rendering) {
  127.         jobject surf = surface;
  128.         if (surf) {
  129.                         (*env)->CallVoidMethod(env, p_sys->codec, p_sys->configure, format, surf, NULL, 0);
  130.                         if ((*env)->ExceptionOccurred(env)) {
  131.                     printf("Exception occurred in MediaCodec.configure");
  132.                     (*env)->ExceptionClear(env);
  133.                     goto error;
  134.                 }
  135.         } else {
  136.                     printf("Failed to get the Android Surface, disabling direct rendering.");
  137.             p_sys->direct_rendering = false;
  138.         }
  139.         }

  140.         if (!p_sys->direct_rendering) {
  141.         (*env)->CallVoidMethod(env, p_sys->codec, p_sys->configure, format, NULL, NULL, 0);
  142.         if ((*env)->ExceptionOccurred(env)) {
  143.             printf("Exception occurred in MediaCodec.configure");
  144.             (*env)->ExceptionClear(env);
  145.             goto error;
  146.         }
  147.     }
  148.         
  149.         printf("////////////// MEDIACODEC Start\n");
  150.         (*env)->CallVoidMethod(env, p_sys->codec, p_sys->start);
  151.     if ((*env)->ExceptionOccurred(env)) {
  152.         printf("Exception occurred in MediaCodec.start");
  153.         (*env)->ExceptionClear(env);
  154.         goto error;
  155.     }
  156.     p_sys->started = true;

  157.     p_sys->input_buffers = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_input_buffers);
  158.     p_sys->output_buffers = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_output_buffers);
  159.     p_sys->buffer_info = (*env)->NewObject(env, p_sys->buffer_info_class, p_sys->buffer_info_ctor);
  160.     p_sys->input_buffers = (*env)->NewGlobalRef(env, p_sys->input_buffers);
  161.     p_sys->output_buffers = (*env)->NewGlobalRef(env, p_sys->output_buffers);
  162.     p_sys->buffer_info = (*env)->NewGlobalRef(env, p_sys->buffer_info);
  163.     p_sys->i_output_buffers = (*env)->GetArrayLength(env, p_sys->output_buffers);

  164.         VideoDecoderStart(p_sys);
  165.         //receiver_start(source_path, 2);
  166.         return 0;
  167. error:

  168.         if(p_sys != NULL)
  169.         {
  170.                 free(p_sys);
  171.                 p_sys = NULL;
  172.         }
  173.         return -1;
  174. }
复制代码
解码和渲染
  1. {
  2.         JNIEnv *env;
  3.         struct decoder_sys_t *p_sys = (struct decoder_sys_t *)arg;
  4.         struct frame_queue *p_frame_queue = &programe_media.video.frame_queue;
  5.         int sampleSize;
  6.         int64_t pts;
  7.         int fifo_cnts = 0;
  8.         buffer_t *buffer = NULL;
  9.         
  10.           //Attach主线程
  11.         if((*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL) != JNI_OK)
  12.         {
  13.                    printf("%s: AttachCurrentThread() failed", __FUNCTION__);
  14.                 return NULL;
  15.         }
  16.         sem_wait(&video_decode_sem);
  17.         //while(fifo_count(p_frame_queue->fifo) < 240)
  18.                 //usleep(1000);
  19.         while(true)
  20.         {
  21.                 int input_index = (*env)->CallIntMethod(env, p_sys->codec, p_sys->dequeue_input_buffer, (jlong) 0);
  22.         if ((*env)->ExceptionOccurred(env)) {
  23.             printf("Exception occurred in MediaCodec.dequeueInputBuffer");
  24.             (*env)->ExceptionClear(env);
  25.             p_sys->error_state = true;
  26.             break;
  27.         }
  28.                 if(input_index >= 0) {
  29.                         jobject buf = (*env)->GetObjectArrayElement(env, p_sys->input_buffers, input_index);
  30.                 jsize size = (*env)->GetDirectBufferCapacity(env, buf);
  31.                 uint8_t *bufptr = (*env)->GetDirectBufferAddress(env, buf);

  32.                         //printf("size is %d\n", size);
  33. retry:
  34.                         sampleSize = av_read_one_frame((uint8_t *)bufptr, &pts);
  35.                         if(sampleSize <= 0)
  36.                         {
  37.                                 //printf("VIDEO@sampleSize is %d\n", sampleSize);
  38.                                 goto retry;
  39.                         }
  40.                         size = sampleSize;
  41.                         
  42.                         (*env)->CallVoidMethod(env, p_sys->codec, p_sys->queue_input_buffer, input_index, 0, size, pts, 0);
  43.                 (*env)->DeleteLocalRef(env, buf);
  44.                 if ((*env)->ExceptionOccurred(env)) {
  45.                     printf("Exception in MediaCodec.queueInputBuffer");
  46.                     (*env)->ExceptionClear(env);
  47.                     p_sys->error_state = true;
  48.                     break;
  49.                 }
  50.                 }

  51.                 jlong timeout = 0;
  52.                 int output_index = (*env)->CallIntMethod(env, p_sys->codec, p_sys->dequeue_output_buffer,
  53.                                           p_sys->buffer_info, timeout);
  54.         if ((*env)->ExceptionOccurred(env)) {
  55.             printf("Exception in MediaCodec.dequeueOutputBuffer (GetOutput)");
  56.             (*env)->ExceptionClear(env);
  57.             p_sys->error_state = true;
  58.             return NULL;
  59.         }

  60.                 if(output_index >= 0) {
  61.                         //printf("Buffers returned before output format is set, dropping frame");
  62.                         (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, output_index, p_sys->direct_rendering);
  63.                         if ((*env)->ExceptionOccurred(env)) {
  64.                             printf("Exception in MediaCodec.releaseOutputBuffer");
  65.                             (*env)->ExceptionClear(env);
  66.                             p_sys->error_state = true;
  67.                             return NULL;
  68.                         }
  69.                 }

  70.         }

  71. error:   
  72.         //Detach主线程
  73.         if((*g_jvm)->DetachCurrentThread(g_jvm) != JNI_OK)
  74.         {
  75.         printf("%s: DetachCurrentThread() failed", __FUNCTION__);
  76.     }
  77.    
  78.     pthread_exit(0);
  79. }
复制代码
这就是核心代码,基本就是模拟在JAVA下的解码流程!
另外,注明下!音频解码!音频解码如果也用着套机制,可能对大多数机型不是很友好,具体指MP3格式!,有些机型支持,GOOGLE.MP3.DECODER,有些不支持,不一定可用,所以这里奉劝大家还是自己移植MP3解码库吧,我用的LIBMAD,还不错!









作者: jingjin221    时间: 2016-3-10 13:42
没事,自己顶一下
作者: 0000li    时间: 2016-4-22 16:54
lubuntu下的视频硬解码 跟你的一样吗?
作者: 脑残丞相    时间: 2016-4-27 12:57
很是佩服。学习一下
作者: daihuan509    时间: 2016-5-31 17:32
大神,你太生猛了。
作者: jingjin221    时间: 2016-8-23 09:46
很多同学大多是在JAVA层做的,这样会导致很多奇奇怪怪的问题,建议还是在JNI层下做吧,这样可以一步一步分析是哪里出错了!
作者: sankycui    时间: 2016-8-24 09:18
顶一个
作者: yly123ycyn    时间: 2016-8-25 17:40
最好多写些注释,大神
作者: jingjin221    时间: 2016-11-11 09:58
没事,自己顶!!!
作者: jingjin221    时间: 2016-11-11 17:50

作者: jingjin221    时间: 2016-11-21 12:03
ding
作者: huntengan    时间: 2016-11-24 19:59
先顶,再试试。
已经实现了MEDIACODEC,但很多机子跑步了。看这个效果怎样
作者: csworld    时间: 2016-11-30 20:45
学习下实现思路,多谢
作者: jingjin221    时间: 2016-12-15 09:53
huntengan 发表于 2016-11-24 19:59
先顶,再试试。
已经实现了MEDIACODEC,但很多机子跑步了。看这个效果怎样

这个肯定是完美支持的,只要他支持mediacodec库
作者: jingjin221    时间: 2016-12-15 09:59

作者: jingjin221    时间: 2016-12-16 16:25

作者: jingjin221    时间: 2016-12-23 10:57

作者: wuqingwei    时间: 2017-1-19 12:43
学习了
作者: wod    时间: 2017-1-20 16:52
学习一下
作者: GXL    时间: 2017-1-23 16:23
顶一个
作者: GXL    时间: 2017-1-23 16:26
看看 顺便学习一下
作者: PeterPan    时间: 2017-5-15 09:05
学习一些,我想问问如果是mjpeg格式如何使用mediacodec来硬解码为yuv呢?希望您可以帮助我。
作者: 华夏¥熊猫仔    时间: 2017-7-24 09:04
楼主请给个github的源码下载地址呗
作者: zn123    时间: 2020-10-30 16:07
内容不错 发鸡腿




欢迎光临 Firefly开源社区 (https://dev.t-firefly.com/) Powered by Discuz! X3.1