美文网首页
ijkplayer学习笔记(五)——视频解码

ijkplayer学习笔记(五)——视频解码

作者: 程序媛的程 | 来源:发表于2021-09-22 16:21 被阅读0次

ijkplayer在视频解码上支持软解和硬解两种方式,可在起播前配置优先使用的解码方式,播放过程中不可切换。iOS平台上硬解使用VideoToolbox,ijkplayer中的音频解码只支持软解,暂不支持硬解。
上一篇中提到了开启解码器stream_component_open方法,本篇将重点学习它的实现:

static int stream_component_open(FFPlayer *ffp, int stream_index)
{
  ......
   // 解码器初始化
   avctx = avcodec_alloc_context3(NULL);
   if (!avctx)
       return AVERROR(ENOMEM);
   //用于将流里面的参数,也就是AVStream里面的参数直接复制到AVCodecContext的上下文当中
   ret = avcodec_parameters_to_context(avctx, ic->streams[stream_index]->codecpar);
   if (ret < 0)
       goto fail;
   //ffmpeg存在多个时间基准(time_base),对应不同的阶段(结构体),每个time_base具体的值不一样,ffmpeg提供函数在各个time_base中进行切换。搞清楚各个time_base的来源,对于阅读ffmpeg的代码很重要
   av_codec_set_pkt_timebase(avctx, ic->streams[stream_index]->time_base);
   // 获取解码器 
   codec = avcodec_find_decoder(avctx->codec_id);

   switch (avctx->codec_type) {
   ......
   case AVMEDIA_TYPE_AUDIO:
         ......
         if ((ret = decoder_start(&is->auddec, audio_thread, ffp, "ff_audio_dec")) < 0)
           goto out;
   case AVMEDIA_TYPE_VIDEO:
       if (ffp->async_init_decoder) {
          ......
       } else {
           decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread);
           //打开ffmpeg的解码器(硬解 or 软解),创建IJKFF_Pipenode。
           ffp->node_vdec = ffpipeline_open_video_decoder(ffp->pipeline, ffp);
           if (!ffp->node_vdec)
               goto fail;
       }
       //开始解码
       if ((ret = decoder_start(&is->viddec, video_thread, ffp, "ff_video_dec")) < 0)
           goto out;
       ......
}

func_open_video_decoder

static IJKFF_Pipenode *func_open_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp)
{
    IJKFF_Pipenode* node = NULL;
    IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;
    //如果配置了videotoolbox,会优先去尝试打开硬件解码器
    if (ffp->videotoolbox) {
        // 硬解
        node = ffpipenode_create_video_decoder_from_ios_videotoolbox(ffp);
        if (!node)
            ALOGE("vtb fail!!! switch to ffmpeg decode!!!! \n");
    }
    if (node == NULL) {
        // 软解
        node = ffpipenode_create_video_decoder_from_ffplay(ffp);
        ffp->stat.vdec_type = FFP_PROPV_DECODER_AVCODEC;
        opaque->is_videotoolbox_open = false;
    } else {
        ffp->stat.vdec_type = FFP_PROPV_DECODER_VIDEOTOOLBOX;
        opaque->is_videotoolbox_open = true;
    }
    ffp_notify_msg2(ffp, FFP_MSG_VIDEO_DECODER_OPEN, opaque->is_videotoolbox_open);
    return node;
}

videotoolbox需要在起播前通过如下方法配置:
ijkmp_set_option_int(_mediaPlayer, IJKMP_OPT_CATEGORY_PLAYER, "videotoolbox", 1);

video的解码线程为video_thread,audio的解码线程为audio_thread

不管视频解码还是音频解码,其基本流程都是从解码前的数据缓冲区中取出一帧数据进行解码,完成后放入相应的解码后的数据缓冲区。

static int video_thread(void *arg)
{
    FFPlayer *ffp = (FFPlayer *)arg;
    int       ret = 0;

    if (ffp->node_vdec) {
        ret = ffpipenode_run_sync(ffp->node_vdec);
    }
    return ret;
}

以视频软解为例,最终,线程会调用到ffplay_video_thread。

ffplay_video_thread

static int ffplay_video_thread(void *arg)
{
    FFPlayer *ffp = arg;
 
    ......
 
    for (;;) {
        // 解码前的video queue中取出一帧数据,送入decoder进行解码,得到解码后的数据
        ret = get_video_frame(ffp, frame);
        ......
        // 解码后的数据送入pictq(这里不仅仅是做了入队处理,还把AVFrame转化成了一个自定义类型SDL_VoutOverlay)
        ret = queue_picture(ffp, frame, pts, duration, av_frame_get_pkt_pos(frame), is->viddec.pkt_serial);
    }
    return 0;
}

参考:https://blog.csdn.net/xipiaoyouzi/article/details/74280170

相关文章

网友评论

      本文标题:ijkplayer学习笔记(五)——视频解码

      本文链接:https://www.haomeiwen.com/subject/cynegltx.html