您的位置:首页 > 其它

使用FFMPEG3.4.2版本进行视频的解码为YUV格式

2018-04-06 21:55 731 查看
本文章是转载开源团队路过之君的博客,最他移植ffmpeg解码器到我们的视频项目中。全文如下:最近关注的流媒体方向技术,FFMPEG学习是绕不过去的,不过网上关于FFMPEG的应用基本是基于2015年左右的版本,最新的FFMPEG视频解码部分变动还是挺大的,特此记录一下解码过程:首先当然是FFMPEG的解码器等的初始化:// 一定要先调用该注册FFMPEG,否则下面的都无法使用
av_register_all();// 调用该初始化函数才能读取RTSP/RTMP等网络流的协议数据,否则干瞪眼,只解码本地文件的可以无视
avformat_network_init();初始化完后需要申请一个AVFormatContext对象  *pFormatCtx = avformat_alloc_context();用于解码的上下文,有点FFMPEG基础的都明白这是老套路了。然后呢还是老套路,调用以下两个函数:// decodeUrl即为你要进行解码的链接,可以是本地文件路径,也可以是RTSP/RTMP等网络视频流的URL路径
avformat_open_input(&pFormatCtx, decodeUrl, NULL, NULL);
avformat_find_stream_info(pFormatCtx, NULL);然后就在pFormatCtx->nb_streams找对应的解码类型为视频格式的解码器,这里跟老版本是有点不一样的,codecpar是新推荐的成员变量:videoindex = -1;
    for (uint32_t i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoindex = i;
            break;
        }
    }接下来还是有点区别的,不过一般人还是可以靠自己解决的AVCodec  *pCodec = avcodec_find_decoder(pFormatCtx->streams[videoindex]->codecpar->codec_id);
    if (pCodec == NULL)
    {
        return -1;
    }
AVCodecContext  *pCodecCtx = avcodec_alloc_context3(pCodec);获取视频解码器并创建解码器上下文,很好理解,套路一样就是细节改了改;下面就开始打开解码器了,这个没变:
avcodec_open2(pCodecCtx, pCodec, NULL);然后创建视频帧及YUV帧对象跟数据包对象AVFrame *pFrame = av_frame_alloc();
AVFrame *pFrameYUV = av_frame_alloc();
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
接下来就是循环读取数据并解码了[cpp] view plain https://blog.csdn.net/xinxinsky/article/details/79823971#" target=_blank>copywhile (av_read_frame(pFormatCtx, packet) >= 0)  
{  
 if (packet->stream_index == videoindex)  
{  
// 视频帧进行解码,注意这里很不一样了,之前一个函数调用即可,现在需要自己再次写一个解码函数,具体看下面函数实现  
 ret = Decode(pCodecCtx, pFrame, packet);  
            if (ret < 0)  
            {  
                printf("Decode Error.\n");  
                av_packet_unref(packet);  
                continue;  
            }  
  
// 这里即为解码出来的数据进行YUV转换存储  
            y_size = pCodecCtx->width*pCodecCtx->height;  
            fwrite(pFrame->data[0], 1, y_size, fp_yuv);      //Y   
            fwrite(pFrame->data[1], 1, y_size / 4, fp_yuv);  //U  
            fwrite(pFrame->data[2], 1, y_size / 4, fp_yuv);  //V  
            printf("Write one frame...\n");  
        }  
// 用完了别忘了释放packet  
 av_packet_unref(packet);  
}  
上面的没啥好说的,主要是Decode函数的实现不一样了,改动挺大的,下面贴出实现代码[cpp] view plain copyint Decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt)  
{  
    int ret;  
  
        // 先发送包数据到解码上下文中  
    ret = avcodec_send_packet(dec_ctx, pkt);  
    if (ret < 0)  
    {  
        printf("Error sending a packet for decoding\n");  
        return ret;  
    }  
  
        // 然后从解码上下文中读取帧数据到frame对象中  
    return avcodec_receive_frame(dec_ctx, frame);  
}  
不知道官方基于何种意愿做此修改的,个人觉着麻烦多了,因为我要多写不少代码。。。至此解码工作基本完成,当然用完了资源别忘了还给人家:av_frame_free(&pFrameYUV);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);然后测试了下本地视频文件的解码,测试OK,做完后觉着很不错嘛,然而,然而真的那么如你所愿么?显然不是的。。。在我一时兴起测试RTSP流的时候发现FFMPEG会报错,无法获取,提示什么FFMPEG编译没有添加-lpthread之类的选项,这是什么鬼?懵逼了,因为我是从官方下载的动态链接库,这是你会发现还是GG好使的。。。话不多说,贴解决办法:在avformat_open_input函数调用中最后一个options参数进行设置RTSP传输基于TCP即可。。。AVDictionary* options = NULL;
av_dict_set(&options, "rtsp_transport", "tcp", 0);
avformat_open_input(&pFormatCtx, c_TestFile, NULL, &options);是的,这么设置过后就可以解码RTSP流数据了,知道了后是不是很简单,当然用完了资源记得一定要还:av_dict_free(&options);好的,想到的就这么多了。。。https://blog.csdn.net/xinxinsky/article/details/79823971car-eye 开源网站:www.car-eye.cn
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: