Qt5.12 使用FFmpeg实时解码播放H264/H265摄像头记录(直传数据法)

您所在的位置:网站首页 4k摄像头码流 Qt5.12 使用FFmpeg实时解码播放H264/H265摄像头记录(直传数据法)

Qt5.12 使用FFmpeg实时解码播放H264/H265摄像头记录(直传数据法)

2023-08-23 02:10| 来源: 网络整理| 查看: 265

这第二章主要说明一下流程和使用函数、结构体变量的功能参数。 至于源代码看这位W2Y大神的代码,之前我还想好好研究的,奈何看到了这位大神的代码,直接满足了工作需求,但还是将我从中研究到的FFmpeg知识分享出来。

第一节 解码流程图

解码上下文(环境)初始化

AVStream *->codecpar enum AVCodecID AVCodec * AVCodecContext * AVCodecParameters * AVCodec * AVCodecContext * AVStream * AVCodecParameters * avcodec_find_decoder avcodec_alloc_context3 avcodec_parameters_to_context avcodec_open2

帧格式初始化:

需要解码的帧 解码后YUV储存的帧 解码后RGB储存的帧 AVPacket av_init_packet AVFrame * av_frame_alloc AVFrame * av_frame_alloc

解码步骤:

上面初始化好的 解码后的帧 以同样的格式取出来 YUV格式 结构体里面的一些变量作为参数 SwsContext* 目标RGB帧 AVCodecContext * avcodec_send_packet AVPacket avcodec_receive_frame AVFrame * sws_getContext sws_scale AVFrame *

至此解码结束,但是某些细节和释放空间等并没有写在上面的流程图上。 至于将其播放,只要将最终得到的帧通过信号与槽传到新界面上,然后转为QImage格式不停刷上去就行。

第二节 变量及函数简单解析 //本人提醒 //要注意别在解码过程中突然改变解码中需要用到的结构体或者内存,会崩溃。 //先暂停视频流,再等待解码结束就即时释放结构体和内存,然后进行下一个设备 //这是下面的输出内存申请,如果低于frame_header->width*frame_header->height * sizeof(uint8_t)*3将会崩溃 outRGBBuf = (uint8_t *)av_malloc(frame_header->width*frame_header->height * sizeof(uint8_t)*3+1); //编解码器,类似于接口的存在 struct AVCodec *pAVCodec_decoder; //编解码器上下文,主要存储解码器的相关设置内容 struct AVCodecContext *pAVCodecCtx_decoder = NULL; //压缩的帧,适用于编码后解码前的帧 struct AVPacket mAVPacket_decoder; //原始数据,适用于编码前解码后的帧 //pAVFrame_decoder 存储的初始解码后的数据,即YUV格式 struct AVFrame *pAVFrame_decoder = NULL; //pFrameYUV_decoder 存储的转化后的数据,即RGB格式 struct AVFrame *pFrameYUV_decoder = NULL; //转化格式上下文,主要存储根据参数生成的转化需要的相关设置内容 struct SwsContext* pImageConvertCtx_decoder = NULL; 初始化 { //属性弃用,不需要使用,因为在新版本中会自行注册 //avcodec_register_all(); //avcodec_find_decoder,查找解码器,使用相对应的解码器应该在此判断并调用相对应的解码器 //AVCodec代表着一个查找到的解码器,相当于对应接口 AVCodec *pAVCodec_decoder = avcodec_find_decoder(AV_CODEC_ID_H264); //AVCodec *pAVCodec_decoder= avcodec_find_decoder(AV_CODEC_ID_H265); //avcodec_alloc_context3分配一个以pAVCodec对应解码/编码器为主要内容的AVCodecContext空间 //AVCodecContext对应编解码器的内容 AVCodecContext *pAVCodecCtx_decoder= avcodec_alloc_context3(pAVCodec_decoder); //avcodec_parameters_to_context从AVCodecParameters *中获取关于对应编解码器的参数到AVCodecContext *中 avcodec_parameters_to_context(pAVCodecCtx_decoder, codecParameters) //根据AVCodec *正式初始化AVCodecContext *,如为各结构体分配内存等 avcodec_open2(pAVCodecCtx_decoder, pAVCodec_decoder, NULL) //解码帧,解码后帧的初始化 av_init_packet(&mAVPacket_decoder); pAVFrame_decoder = av_frame_alloc(); pFrameYUV_decoder = av_frame_alloc(); } FFmpeg_H264Decode(unsigned char *inbuf, int inbufSize, int *framePara, unsigned char *outRGBBuf, unsigned char **outYUVBuf) { if (!pAVCodecCtx_decoder || !pAVFrame_decoder || !inbuf || inbufSize //以AVCodecContext *内容解码出来的YUV帧导出到pAVFrame_decoder中 ret = avcodec_receive_frame(pAVCodecCtx_decoder, pAVFrame_decoder); if (ret == 0) { framePara[0] = pAVFrame_decoder->width; framePara[1] = pAVFrame_decoder->height; //这是YUV数据帧 if (outYUVBuf) { //导进YUVBuf里 *outYUVBuf = (unsigned char *)pAVFrame_decoder->data; framePara[2] = pAVFrame_decoder->linesize[0]; framePara[3] = pAVFrame_decoder->linesize[1]; framePara[4] = pAVFrame_decoder->linesize[2]; } else if (outRGBBuf) { //将需要导出的内存空间放进从YUV数据帧转化的RGB帧即pFrameYUV_decoder里 pFrameYUV_decoder->data[0] = outRGBBuf; pFrameYUV_decoder->data[1] = NULL; pFrameYUV_decoder->data[2] = NULL; pFrameYUV_decoder->data[3] = NULL; //导出的相关格式设置 int linesize[4] = { pAVCodecCtx_decoder->width * 3, pAVCodecCtx_decoder->height * 3, 0, 0 }; //sws_getContext获取对应的SwsContext *转化格式上下文,类似于解码的解码器格式AVCodecContext pImageConvertCtx_decoder = sws_getContext(pAVCodecCtx_decoder->width, pAVCodecCtx_decoder->height, AV_PIX_FMT_YUV420P, pAVCodecCtx_decoder->width, pAVCodecCtx_decoder->height, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL); //sws_scale作帧转换 sws_scale(pImageConvertCtx_decoder, (const uint8_t* const *) pAVFrame_decoder->data, pAVFrame_decoder->linesize, 0, pAVCodecCtx_decoder->height, pFrameYUV_decoder->data, linesize); sws_freeContext(pImageConvertCtx_decoder); return 1; } } else if (ret == AVERROR(EAGAIN)) { return 0; } else { return -1; } } return 0; }

以上的代码内容绝大部分源自于开头时写的那位大神代码,其他内容是自己研究后得出的一些学习总结。 老实说,“上下文”我觉得应该解释为“环境”或者“内容”更为贴切。

网上关于FFmpeg解码的内容过多,基本都能找到,rtsp的,读取视频文件等的其实也可以使用这套代码,只要将AVCodecParameters从视频流中取出来就行。

avformat_open_input avformat_find_stream_info AVStream * = AVFormatContext->streams[i];

上述只针对单独解码器,主在视频而非音频,音频在原代码上修改下需求即可

注:不能只初始化一个编码格式就同时解码两个格式的,

比如说一开始初始化解码器解码H264,那么这个解码器的数据就是针对H264的了,不能简单的更改一下avcodec_find_decoder的编解码器就实现切换不同的格式解码;

要对每一种streams格式,单独初始化一次并保留在对应的对象里,判断后就调用那一种的编解码器解码即可。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3