环境配置
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:目标图像的宽高,目