zl程序教程

您现在的位置是:首页 >  工具

当前栏目

【FFMpeg视频开发与应用基础】三、调用FFmpeg SDK对H.264格式的视频压缩码流进行解码

应用基础SDK开发 进行 调用 视频 格式
2023-09-27 14:29:20 时间
《FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK》视频教程已经在“CSDN学院”上线,视频中包含了从0开始逐行代码实现FFMpeg视频开发的过程,欢迎观看!链接地址:FFMpeg视频开发与应用基础——使用FFMpeg工具与SDK

经过了上篇调用FFMpeg SDK对视频进行编码的过程之后,我们可以比较容易地理解本篇的内容,即上一篇的逆过程——将H.264格式的裸码流解码为像素格式的图像信息。


同FFMpeg编码器类似,FFMpeg解码器也需要编码时的各种结构,除此之外,解码器还需要另一个结构——编解码解析器——用于从码流中截取出一帧完整的码流数据单元。因此我们定义一个编解码上下文结构为:

/*************************************************

Struct: CodecCtx

Description: FFMpeg编解码器上下文

*************************************************/

typedef struct

 AVCodec *pCodec; //编解码器实例指针

 AVCodecContext *pCodecContext; //编解码器上下文,指定了编解码的参数

 AVCodecParserContext *pCodecParserCtx; //编解码解析器,从码流中截取完整的一个NAL Unit数据

 AVFrame *frame; //封装图像对象指针

 AVPacket pkt; //封装码流对象实例

} CodecCtx;


同编码器类似,解码器也需要传递参数。不过相比编码器,解码器在运行时所需要的大部分信息都包含在输入码流中,因此输入参数一般只需要指定一个待解码的视频码流文件即可

(2). 按照要求初始化需要的FFMpeg结构

首先,所有涉及到编解码的的功能,都必须要注册音视频编解码器之后才能使用。注册编解码调用下面的函数:

avcodec_register_all();

编解码器注册完成之后,根据指定的CODEC_ID查找指定的codec实例。CODEC_ID通常指定了编解码器的格式,在这里我们使用当前应用最为广泛的H.264格式为例。查找codec调用的函数为avcodec_find_encoder,其声明格式为:

AVCodec *avcodec_find_encoder(enum AVCodecID id);

该函数的输入参数为一个AVCodecID的枚举类型,返回值为一个指向AVCodec结构的指针,用于接收找到的编解码器实例。如果没有找到,那么该函数会返回一个空指针。调用方法如下:

/* find the mpeg1 video encoder */

ctx.codec = avcodec_find_encoder(AV_CODEC_ID_H264); //根据CODEC_ID查找编解码器对象实例的指针

if (!ctx.codec) 

 fprintf(stderr, "Codec not found\n");

 return false;

AVCodec查找成功后,下一步是分配AVCodecContext实例。分配AVCodecContext实例需要我们前面查找到的AVCodec作为参数,调用的是avcodec_alloc_context3函数。其声明方式为:

AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);

其特点同avcodec_find_encoder类似,返回一个指向AVCodecContext实例的指针。如果分配失败,会返回一个空指针。调用方式为:

ctx.c = avcodec_alloc_context3(ctx.codec); //分配AVCodecContext实例

if (!ctx.c)

 fprintf(stderr, "Could not allocate video codec context\n");

 return false;

我们应该记得,在FFMpeg视频编码的实现中,AVCodecContext对象分配完成后,下一步实在该对象中设置编码的参数。而在解码器的实现中,基本不需要额外设置参数信息,因此这个对象更多地作为输出参数接收数据。因此对象分配完成后,不需要进一步的初始化操作。

解码器与编码器实现中不同的一点在于,解码器的实现中需要额外的一个AVCodecParserContext结构,用于从码流中截取一个完整的NAL单元。因此我们需要分配一个AVCodecParserContext类型的对象,使用函数av_parser_init,声明为:

AVCodecParserContext *av_parser_init(int codec_id);

调用方式为:

ctx.pCodecParserCtx = av_parser_init(AV_CODEC_ID_H264);

if (!ctx.pCodecParserCtx)

 printf("Could not allocate video parser context\n");

 return false;

随后,打开AVCodec对象,然后分配AVFrame对象:

//打开AVCodec对象

if (avcodec_open2(ctx.pCodecContext, ctx.pCodec, NULL) 0)

 fprintf(stderr, "Could not open codec\n");

 return false;

//分配AVFrame对象

ctx.frame = av_frame_alloc();

if (!ctx.frame) 

 fprintf(stderr, "Could not allocate video frame\n");

 return false;

(3)、解码循环体

完成必须的codec组件的建立和初始化之后,开始进入正式的解码循环过程。解码循环通常按照以下几个步骤实现:

首先按照某个指定的长度读取一段码流保存到缓存区中。

由于H.264中一个包的长度是不定的,我们读取一段固定长度的码流通常不可能刚好读出一个包的长度。所以我们就需要使用AVCodecParserContext结构对我们读出的码流信息进行解析,直到取出一个完整的H.264包。对码流解析的函数为av_parser_parse2,声明方式如:

int av_parser_parse2(AVCodecParserContext *s,

 AVCodecContext *avctx,

 uint8_t **poutbuf, int *poutbuf_size,

 const uint8_t *buf, int buf_size,

 int64_t pts, int64_t dts,

 int64_t pos);

这个函数的各个参数的意义:


AVCodecParserContext *s:初始化过的AVCodecParserContext对象,决定了码流该以怎样的标准进行解析; AVCodecContext *avctx:预先定义好的AVCodecContext对象; uint8_t **poutbuf:AVPacket::data的地址,保存解析完成的包数据; int *poutbuf_size:AVPacket的实际数据长度;如果没解析出完整的一个包,这个值为0; const uint8_t *buf, int buf_size:输入参数,缓存的地址和长度; int64_t pts, int64_t dts:显示和解码的时间戳; nt64_t pos :码流中的位置; 返回值为解析所使用的比特位的长度;

具体的调用方式为:

len = av_parser_parse2(ctx.pCodecParserCtx, ctx.pCodecContext, 

 (ctx.pkt.data), (ctx.pkt.size), 

 pDataPtr, uDataSize, 

 AV_NOPTS_VALUE, AV_NOPTS_VALUE, AV_NOPTS_VALUE);

如果参数poutbuf_size的值为0,那么应继续解析缓存中剩余的码流;如果缓存中的数据全部解析后依然未能找到一个完整的包,那么继续从输入文件中读取数据到缓存,继续解析操作,直到pkt.size不为0为止。

在最终解析出一个完整的包之后,我们就可以调用解码API进行实际的解码过程了。解码过程调用的函数为avcodec_decode_video2,该函数的声明为:

int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,

 int *got_picture_ptr,

 const AVPacket *avpkt);

这个函数与前篇所遇到的编码函数avcodec_encode_video2有些类似,只是参数的顺序略有不同,解码函数的输入输出参数与编码函数相比交换了位置。该函数各个参数的意义:


AVFrame *picture: 保存解码完成后的像素数据;我们只需要分配对象的空间,像素的空间codec会为我们分配好; int *got_picture_ptr: 标识位,如果为1,那么说明已经有一帧完整的像素帧可以输出了 const AVPacket *avpkt: 前面解析好的码流包;

实际调用的方法为:

int ret = avcodec_decode_video2(ctx.pCodecContext, ctx.frame, got_picture, (ctx.pkt));

if (ret 0) 

 printf("Decode Error.\n");

 return ret;

if (got_picture) 

 //获得一帧完整的图像,写出到输出文件

 write_out_yuv_frame(ctx, inputoutput);

 printf("Succeed to decode 1 frame!\n");

最后,同编码器一样,解码过程的最后一帧可能也存在延迟。处理最后这一帧的方法也跟解码器类似:将AVPacket::data设为NULL,AVPacket::size设为0,然后在调用avcodec_encode_video2完成最后的解码过程:

ctx.pkt.data = NULL;

ctx.pkt.size = 0;

while(1)

 //将编码器中剩余的数据继续输出完

 int ret = avcodec_decode_video2(ctx.pCodecContext, ctx.frame, got_picture, (ctx.pkt));

 if (ret 0) 

 printf("Decode Error.\n");

 return ret;

 if (got_picture) 

 write_out_yuv_frame(ctx, inputoutput);

 printf("Flush Decoder: Succeed to decode 1 frame!\n");

 else

 break;

} //while(1)

(4). 收尾工作

收尾工作主要包括关闭输入输出文件、关闭FFMpeg解码器各个组件。其中关闭解码器组件需要:

avcodec_close(ctx.pCodecContext);

av_free(ctx.pCodecContext);

av_frame_free( (ctx.frame));


1.读取码流数据 - 2.解析数据,是否尚未解析出一个包就已经用完?是返回1,否继续 - 3.解析出一个包?是则继续,否则返回上一步继续解析 - 4.调用avcodec_decode_video2进行解码 - 5.是否解码出一帧完整的图像?是则继续,否则返回上一步继续解码 - 6.写出图像数据 - 返回步骤2继续解析。


物联网平台之python语言的设备端接入、云端sdk集成调用 本文介绍如何调用Python语言的Paho MQTT类库,将设备接入阿里云物联网平台,并进行消息收发。 以及物联网平台提供Python语言的云端SDK供开发人员使用。本文介绍云端Python SDK的安装和配置,及使用Python SDK调用云端API的示例。
阿里云机器翻译产品机器批量翻译服务 Python SDK 调用指南 阿里云机器翻译产品下的机器批量翻译服务,支持同时对多段文本进行翻译。本文介绍如何使用机器批量翻译提供的Python SDK,包括SDK的安装方法及SDK代码示例。
阿里云自然语言处理--文本摘要(电商)Java SDK 调用示例 自然语言处理(Natural Language Processing,简称NLP),是为各类企业及开发者提供的用于文本分析及挖掘的核心工具,旨在帮助用户高效的处理文本,已经广泛应用在电商、文娱、司法、公安、金融、医疗、电力等行业客户的多项业务中,取得了良好的效果。文本摘要服务可以在保证能够反映原文档的重要内容的情况下,尽可能地保持简明扼要,自动抽取输入文本中的关键信息并生成短文本摘要。能让用户在当今世界海量的互联网数据中找到有效的信息,有效缩短检索的时间,提高用户体验。本文将使用Java Common SDK 演示文本摘要(电商)服务的快速调用以供参考。
阿里云自然语言处理--文本相似度(电商)Java SDK 调用示例 自然语言处理(Natural Language Processing,简称NLP),是为各类企业及开发者提供的用于文本分析及挖掘的核心工具,旨在帮助用户高效的处理文本,已经广泛应用在电商、文娱、司法、公安、金融、医疗、电力等行业客户的多项业务中,取得了良好的效果。文本相似度可以提供不同文本之间相似度的计算,并输出一个介于0到1之间的分数,分数越大则文本之间的相似度越高。可广泛应用于信息检索,新闻推荐、智能客服等场景。使用该服务建议分数不要用于直接判断,可以作为特征,并按照范围进行分桶。本文将使用Java Common SDK 演示文本相似度(电商)服务的快速调用以供参考。
阿里云自然语言处理--中心词提取(中文)Java SDK 调用示例 自然语言处理(Natural Language Processing,简称NLP),是为各类企业及开发者提供的用于文本分析及挖掘的核心工具,旨在帮助用户高效的处理文本,已经广泛应用在电商、文娱、司法、公安、金融、医疗、电力等行业客户的多项业务中,取得了良好的效果。NLP基础服务中心词提取是基于海量数据,使用电商标题中心词以及类目进行训练,通过给每个词计算一个相关性分数来衡量每个词与句子的相关性程度,进而识别并提取出句子的中心词。适用于提取电商搜索query、标题及其他类似短文本(一般小于25个词)的中心词。本文将使用Java CommonSDK演示中心词提取(中文)服务的快速调用以供参考。
阿里云自然语言处理--词性标注(电商)Java SDK 调用示例 自然语言处理(Natural Language Processing,简称NLP),是为各类企业及开发者提供的用于文本分析及挖掘的核心工具,旨在帮助用户高效的处理文本,已经广泛应用在电商、文娱、司法、公安、金融、医疗、电力等行业客户的多项业务中,取得了良好的效果。通过词性标注服务,用户可以快速的为每一个词附上对应的词性,结合分词服务,可以快速进行更深层次的文本挖掘处理,无需担心新词发现、歧义消除等问题。本文将使用Java CommonSDK演示词性标注(电商)服务的快速调用以供参考。
Python、Java SDK两种方式调用阿里云人脸检测与五官定位 阿里云视觉智能开放平台基于达摩院自研的人脸人体分析技术,提供人脸检测与五官定位、人脸属性识别、人脸比对、人脸搜索、人体检测、人体属性、行为分析等多种功能,为开发者和企业用户提供高性能高可用的人脸人体识别服务。广泛应用于数字门店、楼宇门禁、身份识别、互动娱乐、IPC摄像头、内容广告等领域。此篇文章简单介绍人脸人体与五官定位python,java两种方式调用。
阿里云机器翻译产品机器批量翻译服务 Java SDK 调用指南 阿里云机器翻译产品下的机器批量翻译服务,支持同时对多段文本进行翻译。本文介绍如何使用机器批量翻译提供的JavaSDK,包括SDK的安装方法及SDK代码示例。
阿里云文字识别(OCR)票据凭证识别Python SDK调用 阿里云文字识别(Optical Character Recognition,OCR)可以将图片中的文字信息转换为可编辑文本,根据客户的业务场景和需求,将产品分为了通用文字识别、个人证照识别、票据凭证识别、教育场景识别、车辆物流识别、企业资质识别、小语种文字识别等,满足各种客户的图片识别需求。此片文章将简单介绍票据凭证识别的python调用
jerry.yin 毕业于上海大学通信与信息工程学院,从事流媒体和视频编解码的研究与开发工作; 研究领域包括视频编解码标准、视频处理和流媒体技术、移动互联网技术等。