FFmpeg编程--解码流程

本文详细介绍了FFmpeg解码的步骤,包括注册、打开文件、探测流信息、查找解码器、解码器初始化、数据格式转换等。此外,还讨论了解码过程中的关键函数和数据结构,如AVFormatContext、AVPacket、AVFrame和SwsContext。最后,文章提供了解码流程的完整代码示例。
摘要由CSDN通过智能技术生成

一、解码流程总览

 

二、解码流程分解

第一步:注册

使用FFmpeg对应的库,都需要进行注册,注册了这个才能正常使用编码器和解码器;

///第一步
av_register_all();

第二步:打开文件

打开文件,根据文件名信息获取对应的FFmpeg全局上下文

///第二步
AVFormatContext *pFormatCtx;	//文件上下文,描述了一个媒体文件或媒体流的构成和基本信息

pFormatCtx = avformat_alloc_context();	//分配指针

if (avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0) {	//打开文件,信息存储到文件上下文中,后续对针对文件上下文即可
	printf("无法打开文件");
    return -1;
}

第三步:探测流信息

一定要探测流信息,拿到流编码的编码格式,不探测流信息则器流编码器拿到的编码类型可能为空,后续进行数据转换的时候就无法知晓原始格式,导致错误;

///第三步
//探寻文件中是否存在信息流
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
	printf("文件中没有发现信息流");
	return -1;
}

//探寻文件中是否存储视频流
int videoStream = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++) {
	if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
		videoStream = i;
	}
}
//如果videoStream为-1 说明没有找到视频流
if (videoStream == -1) {
	printf("文件中未发现视频流");
	return -1;
}

//探寻文件中是否存在音频流
int audioStream = -1
for (i = 0; i < pFormatCtx->nb_streams; i++) {
	if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
		audioStream = i;
	}
}
//如果audioStream 为-1 说明没有找到音频流
if (audioStream == -1) {
	printf("文件中未发现音频流");
	return -1;
}

第四步:查找对应的解码器

依据流的格式查找解码器,软解码还是硬解码是在此处决定的,但是特别注意是否支持硬件,需要自己查找本地的硬件解码器对应的标识,并查询其是否支持。普遍操作是,枚举支持文件后缀解码的所有解码器进行查找,查找到了就是可以硬解了;

注意:解码时查找解码器,编码时查找编码器,两者函数不同,不要弄错了,否则后续能打开但是数据是错的;

///第四步
AVCodecContext *pCodecCtx;      //描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息
AVCodec *pCodec;                //存储编解码器信息的结构体

//查找解码器
pCodecCtx = pFormatCtx->streams[videoStream]->codec;    //获取视频流中编码器上下文
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);     //获取视频流的编码器信息

if (pCodec == NULL) {
    printf("未发现编码器");
    return -1;
}

第五步:打开解码器

打开获取到的解码器

///第五步
//打开解码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
    printf("无法打开编码器");
    return -1;
}

第六步:申请缩放数据格式转换结构体

基本上解码的数据都是yuv系列格式,但是我们显示的数据是rgb等相关颜色空间的数据,所以此处转换结构体就是进行转换前导转换后的描述,给后续转换函数提供转码依据,是很关键并且非常常用的结构体;

///第六步
static struct SwsContext *img_convert_ctx;  //用于视频图像的转换

img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
            pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
            AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);

第七步:计算缩放颜色空间转换后缓存大小

///第七步
int numBytes;	//字节数
numBytes = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width,pCodecCtx->height);

第八步:申请缓存区,将AVFrama的data映射到单独的outBuffer上

申请一个缓存区outBuffer,fill到我们目标帧数据的data上,比如rgb数据,QAVFrame的data上存的是有指定格式的数据且存储有规则,而fill到outBuffer(自己申请的目标格式一帧缓存区),则是我们需要的数据格式存储顺序;

例如:解码转换后的数据为rgb888,实际直接使用data数据是错误的,但是用outBuffer就是对的,所以此处应该是FFmpeg的fill函数做了一些转换;

///第七步
AVFrame *pFrame, *pFrameRGB;    //存储音视频原始数据(即未被编码的数据)的结构体
pFrame = av_frame_alloc();
pFrameRGB = av_frame_alloc();

uint8_t *out_buffer;            //缓存
out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));

avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_BGR24,
            pCodecCtx->width, pCodecCtx->height);

第九步:循环解码

1、获取一帧packet

int y_size = pCodecCtx->width * pCodecCtx->height;
packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
av_new_packet(packet, y_size); //分配packet的数据

int ret, got_picture;
while(1) {
	if (av_read_frame(pFormatCtx, packet) < 0) {	//读取一帧packet数据包
    	break; //这里认为视频读取完了
   	}

	......
}

2、解码获取原始数据

int y_size = pCodecCtx->width * pCodecCtx->height;
packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
av_new_packet(packet, y_size); //分配packet的数据

int ret, got_picture;
while(1) {
	if (av_read_frame(pFormatCt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值