Qt:ffmpeg视频解码实现

环境配置
1.与ffmpeg有关的dll文件放进项目debug文件下
与ffmpeg有关的lib文件,include文件放入项目当前目录下
2.pro文件配置路径(根据你自己放的路径)
LIBS是导入lib文件,$$PWD是当前目录
INCLUDEPATH是导入include文件中的头文件

LIBS += $$PWD/ffmpeglib/lib/avcodec.lib \
        $$PWD/ffmpeglib/lib/avdevice.lib \
        $$PWD/ffmpeglib/lib/avfilter.lib  \
        $$PWD/ffmpeglib/lib/avformat.lib \
        $$PWD/ffmpeglib/lib/avutil.lib  \
        $$PWD/ffmpeglib/lib/postproc.lib \
        $$PWD/ffmpeglib/lib/swresample.lib \
        $$PWD/ffmpeglib/lib/swscale.lib

INCLUDEPATH +=$$PWD/ffmpeglib/include

3.要使用ffmpeg中函数的地方引入头文件

extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavfilter/avfilter.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libpostproc/postprocess.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>

}
//为什么extern “C”引入头文件呢,因为ffmpeg是由c语言实现的,而该工程是用c++实现的;

解码流程
解码流程图
在这里插入图片描述

1.注册所有组件

函数:

 av_regeister_all();

需要的头文件:

extern "C"
{
    #include <libavdevice/avdevice.h>
}

2.打开视频

AVFormatContext: 叫封装格式上下文结构体,保存了视频流数据的结构体:

{

​ iformat:输入视频的AVInputFormat

​ nb_streams: 输入视频的AVStream流个数,比如视频流,音频流,字幕流

​ streams:输入视频的AVStream[]流数组

}

函数:

int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options);
//参数1:封装格式上下文结构体,保存了视频流数据的结构体
//参数2:视频的路径
//返回值:0打开成功,否则失败

需要的头文件:


extern "C"
{
    #include <libavformat/avformat.h>
}

实现:

 char* video_path="A.avi";
 AVFormatContext* pAVFormatContext=avformat_alloc_context();
    //参数1:AVFormatContext 视频信息
    //参数2:视频路径
 int avformat_open_result
     =avformat_open_input(&pAVFormatContext,video_path,NULL,NULL);
if(avformat_open_result!=0)
{
        //视频异常
        char *error_info=new char[32];
        av_strerror(avformat_open_result,error_info,1024);
        qDebug()<<QString("视频打开失败:%1").arg(error_info);

 }

4.在视频文件里面找视频流

函数:

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
//参数1:
//返回值:return >=0 if OK

需要的头文件:

extern "C"
{
    #include <libavformat/avformat.h>
}

实现:

 int avformat_find_result=avformat_find_stream_info(pAVFormatContext,NULL);
    if(avformat_find_result<0)
    {
        //视频异常
        char *error_info=new char[32];
        av_strerror(avformat_find_result,error_info,1024);
        qDebug()<<QString("获取视频文件信息失败(没有流媒体信息):%1").arg(error_info);
    }

5.找到视频流的解码器

**AVStream:**流数据
{

id:序号
codec:该流对应的AVCodecContext
time_base:该流的时基
r_frame_rate:该流的帧率

}

**AVCodecContext:**编解码器上下文结构体的相关信息:存编解码器信息以及图像宽高以及像素信息

{

codec:编解码器的AVCodec
width, height:图像的宽高(只针对视频)
pix_fmt:像素格式(只针对视频)
sample_rate:采样率(只针对音频)
channels:声道数(只针对音频)
sample_fmt:采样格式(只针对音频)

}

**AVCodec:**编解码器
{

name:编解码器名称
long_name:编解码器长名称
type:编解码器类型
id:编解码器ID
一些编解码的接口函数

}

函数:

AVCodec *avcodec_find_decoder(enum AVCodecID id);

//参数1:解码器的id
//作用:找到流对应的解码器

//返回值:没找到解码器返回NULL

使用:

    //第一点:找到视频中的视频流
	//一个视频中有许多码流,存在AVFOrmatContext中的Streams中,得找到视频码流
    int av_index=-1;
    for(int i=0;i<pAVFormatContext->nb_streams;i++)
    {
        if(pAVFormatContext->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
        {
            av_index = i;
            break;
        }
    }
    if (av_index == -1)//没有出现的情况
    {
        qDebug()<<QString("没有找到视频流");
        return false;
    }
	//第二点:根据视频流->查找到视频解码器上下文->视频压缩数据
    AVCodecContext* pAVCodecContext=pAVFormatContext->streams[av_index]->codec;
    //第三点:根据解码器上下文->获取解码器ID
    AVCodec* pAvcodec = avcodec_find_decoder(pAVCodecContext->codec_id);
 	if (pAvcodec == NULL)
    {
        qDebug()<<QString("没有找到视频解码器");
        return false;
    }

6.打开解码器

函数:

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
//参数1:编解码器上下文结构体
//参数2:解码器
//返回值:0打开成功,否则没打开成功

使用:

int avcodec_open2_result=avcodec_open2(pAVCodecContext,pAvcodec,NULL);
    if(avcodec_open2_result!=0)
    {
        
        char *error_info=new char[32];
        av_strerror(avcodec_open2_result,error_info,1024);
        qDebug()<<QString("打开解码器失败:%1").arg(error_info);
    }

7.读取视频流(一帧一帧读取)

AVPacket:包结构体
{

pts:显示时间戳
dts :解码时间戳
data :压缩编码数据
size :压缩编码数据大小
stream_index :所属的AVStream

}

函数:av_read_frame

int av_read_frame(AVFormatContext *s, AVPacket *pkt);
//参数1:视频上下文结构体信息
//参数2:
//返回值:0读到了数据,否则 < 0 

使用:

//打开文件
    FILE* out_file = fopen("a.h264","wb+");
    if (out_file == NULL){
        qDebug()<<QString("文件不存在 ");
        return false;
    }
    //读取帧数据换成到哪里->缓存到packet里面 
    //包里面不仅在有码流数据,码流数据在buf里面
    AVPacket* av_packet = nullptr;//用于保存一帧码流数据
    av_packet=(AVPacket*)malloc(sizeof(AVPacket));
    //一帧码流数据需要的空间,根据编码器里面的信息计算
    int size=pAVCodecContext->width*pAVCodecContext->height;
    //因为里面有存码流数据,所以的开相应大小的数据
    av_new_packet(av_packet,size);
    //==0读到了数据
    while (av_read_frame(pAVFormatContext,av_packet) == 0)
    {  
        if (av_packet->stream_index == av_index)//判断是不是视频流
        {
            fwrite(av_packet->data,av_packet->size,1,out_file);
        }
        //对av_packet清空
        av_packet_unref(av_packet);
    }
    //关闭存h264的文件
    fclose(out_file);

8.解码

AVFrame:保存解码后的数据
{

data:解码后的图像像素数据(音频采样数据)
linesize:对视频来说是图像中一行像素的大小;对音频帧的大小。
width, height:图像的宽高(只针对视频)
key_frame:是否为关键帧(只针对视频) 。
pict_type:帧类型(只针对视频) 。例如I,P,B

}

函数:

int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
                         int *got_picture_ptr,
                         const AVPacket *avpkt);
//参数1:编解码器上下文结构体
//参数2:保存解码后的数据
//参数3:看是否解码成功,如果没有帧可以解码,返回0的数;解码成功,返回非0
//参数4:压缩后的那帧码流
//参数4用参数1解码,解码后的数据放到参数3中

需要的头文件:

9.转换

函数:

//设置转换规则
struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
                                  int dstW, int dstH, enum AVPixelFormat dstFormat,
                                  int flags, SwsFilter *srcFilter,
                                  SwsFilter *dstFilter, const double *param);
//参数1~3:源图像的宽高,解码器的格式
//参数4~6:目标图像的宽高,目
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值