浅显易懂 FFmpeg学习(04)— QT+FFmpeg解码本地视频(下篇)


前言

  本篇,作者将分享如何在QT环境下,使用FFmpeg函数库解码本地视频(下篇)—— 解码本地视频并在窗口中播放。


一、FFmpeg解码流程

  • 下图为解码流程图,黄色部分为解码所调用函数,绿色部分为解码所需要的参数(结构体)。
    在这里插入图片描述

二、解码播放实现效果

  • 本地视频解码后,将解码后的视频帧在窗口中进行播放,如下图所示。
    在这里插入图片描述

三、具体步骤及源码

1.FFmpeg头文件引用

//当前C++兼容C语言
extern "C"
{
    
    
//avcodec:编解码(最重要的库)
#include <libavcodec/avcodec.h>
//avformat:封装格式处理
#include <libavformat/avformat.h>
//swscale:视频像素数据格式转换
#include <libswscale/swscale.h>
//avdevice:各种设备的输入输出
#include <libavdevice/avdevice.h>
//avutil:工具库(大部分库都需要这个库的支持)
#include <libavutil/avutil.h>
}

2.环境注册

  • 我们需要对硬件设备注册、网络注册,以及复用器和编码器注册等。
	//环境注册
    av_register_all();        	  //复用器、编码器等注册
    avdevice_register_all();  	  //硬件设备注册
    avformat_network_init();   	 //网络注册

3.创建封装格式上下文

  • 创建并通过函数avformat_alloc_context()初始化封装格式上下文。
	AVFormatContext *formatContext = avformat_alloc_context();

4.打开多媒体文件

  • 打开本地文件到封装格式上下文中。若打开失败,则对错误信息进行打印。
    int avformat_open_ret = avformat_open_input(&formatContext,filename.toLatin1(),NULL,NULL);
    if(avformat_open_ret < 0)
    {
    
    
        char *result = new char[64];
        av_strerror(avformat_open_ret,result,64);
        qDebug() << QString("错误信息:%1").arg(result);
    }
    qDebug() << "打开多媒体文件成功!";

5.获取音视频流信息

  • 在封装格式上下文中获取音视频流信息。若获取失败,则对错误信息进行打印。
    int avformat_find_ret = avformat_find_stream_info(formatContext,NULL);
    if(avformat_find_ret < 0)
    {
    
    
        char *result = new char[64];
        av_strerror(avformat_open_ret,result,64);
        qDebug() << QString("错误信息:%1").arg(result);
    }
    qDebug() << "获取音视频流信息成功!";

6.寻找视频流

  • 通过遍历封装格式上下文中所有的流信息,找到视频流并保存视频流索引。
    int streamIndex = -1;
    for(int i=0;i<formatContext->nb_streams;i++)
    {
    
    
        if(formatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
    
    
            streamIndex = i;
            break;
        }
    }
    if(streamIndex == -1)
    {
    
    
        qDebug() << "找不到相应的流信息!";
    }

7.获取解码器上下文

  • 通过视频流索引,获取解码器上下文。
	AVCodecContext *codecContext = formatContext->streams[streamIndex]->codec;

8.查找合适的解码器

  • 通过解码上下文的属性id寻找合适的解码器。
	AVCodec *codec = avcodec_find_decoder(codecContext->codec_id);
    if(codec == NULL)
    {
    
    
        qDebug() << "找不到合适的解码器!";
    }

9.打开解码器

  • 通过解码器上下文,打开寻找到的解码器。
    int avcodec_open_ret = avcodec_open2(codecContext,codec,NULL);
    if(avcodec_open_ret < 0)
    {
    
    
        char *result = new char[64];
        av_strerror(avformat_open_ret,result,64);
        qDebug() << QString("错误信息:%1").arg(result);
    }
    qDebug() << "打开解码器成功!";

10.数据容器初始化

  • 对接收数据的数据包、数据帧、数据缓冲区容器动态分配内存空间。
	//数据包初始化
	AVPacket *packet = av_packet_alloc();			
	
	//输入视频帧初始化
    AVFrame *frame = av_frame_alloc();				
    
    //输出RGB帧初始化
    AVFrame *frameRGB = av_frame_alloc();			
    
    //给缓冲区动态分配内存
    uint8_t *pOutbuffer = (uint8_t*)av_malloc(avpicture_get_size(AV_PIX_FMT_RGB32,codecContext->width,codecContext->height));

    //初始化缓冲区
    avpicture_fill((AVPicture *)frameRGB, pOutbuffer,AV_PIX_FMT_RGB32, codecContext->width,codecContext->height);

11.获取图像转换上下文

  • 通过解码器上下文获取图像转换上下文。
    SwsContext *sws = sws_getContext(codecContext->width,codecContext->height,codecContext->pix_fmt,
                               codecContext->width,codecContext->height,AV_PIX_FMT_RGB32,
                               SWS_BICUBIC,NULL,NULL,NULL);

12.读取视频帧并显示

  • 从封装格式上下文读取信息到数据包,再从数据包读取到数据帧,最后将数据转为图像并显示。
	//计算解码帧数
    int frameNum = 0;
    //创建标签显示图像
    QLabel *imgLabel = new QLabel(this);
    imgLabel ->setGeometry(0,0,600,330);
    //读取视频帧
    while(av_read_frame(formatContext,packet) >= 0)
    {
    
    
        if(packet->stream_index == streamIndex)
        {
    
    
            avcodec_send_packet(codecContext,packet);
            int decode_video_ret = avcodec_receive_frame(codecContext,frame);
            if(decode_video_ret >= 0)
            {
    
    
            	//计算帧数
                frameNum++;
				qDebug() << QString("正在解码播放第%1帧数据").arg(frameNum);
				//视频流数据转换为RGB图像数据
                sws_scale(sws, (const unsigned char* const*)frame->data,frame->linesize, 0,
                          codecContext->height,frameRGB->data,frameRGB->linesize);
				//数据转换为图像
                QImage  *tmpImg  = new QImage((uchar *)pOutbuffer,codecContext->width,
                                              codecContext->height,QImage::Format_RGB32);
               	//加载到标签中显示
				imgLabel->setPixmap(QPixmap::fromImage(img));
   				imgLabel->setScaledContents(true);
            }
        }
    }

四、FFmpeg参考资料

版权声明:本文部分内容,参考CSDN博主「雷霄骅」原创文章整理而成,供学习参考使用。
博客地址:https://blog.csdn.net/leixiaohua1020?type=blog


总结

   以上就是浅显易懂 FFmpeg学习(04)— QT+FFmpeg解码本地视频(下篇)的所有内容,希望大家阅读后都能有所收获!原创不易,转载请标明出处,若文章出现有误之处,欢迎读者留言指正批评!

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_59134387/article/details/127235290