FFmpeg的常见结构体
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FFmpeg的常见结构体相关的知识,希望对你有一定的参考价值。
小程之前介绍过FFmpeg的帧的结构(AVPacket跟AVFrame),帧会在一些流程中使用到。
除了帧结构,FFmpeg还有其它一些结构会在流程中使用到。
FFmpeg还有哪些常见的结构呢?先来看一下这个截图:
这张图中的主角,是AVFormatContext。AVFormatContext,是FFmpeg的基本结构之一,对应于封装格式(或容器格式)。
围绕FFmpeg的“格式场景”,本文介绍FFmpeg常见的数据结构。
按照上图,小程依次介绍图中的几个结构体。
(一)AVCodec
AVCodec是FFmpeg设计上的一个结构体,用来保存编×××的信息,也就是说,AVCodec是编码器或×××。
小程还是以调试的办法,具体看一下AVCodec变量中的内容。
(1)演示代码
演示代码的目录结构是这样的:
其中的FFmpeg静态库是事先编译好的(这里是macos版本,因为小程用了mac电脑),编译的办法可以参考之前的文章,读者可以关注“广州小程”微信公众号,并在相应的菜单项中找到文章。
moments.mp4 是试用的视频文件(mp4封装格式)。对于封装格式,在公众号的“音视频”之“基础概念与流程”中也可以找到,小程有专门介绍过媒体格式。
makefile是编译脚本,用来编译演示代码,当然也可以直接用gcc来编译。
show_avcodec.c就是演示代码了,内容如下:
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
void show_avcodec(const char* filepath) {
av_register_all();
av_log_set_level(AV_LOG_DEBUG);
AVFormatContext* formatContext = avformat_alloc_context();
int status = 0;
int success = 0;
int videostreamidx = -1;
AVCodecContext* codecContext = NULL;
status = avformat_open_input(&formatContext, filepath, NULL, NULL);
if (status == 0) {
status = avformat_find_stream_info(formatContext, NULL);
if (status >= 0) {
for (int i = 0; i < formatContext->nb_streams; i ++) {
if (formatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videostreamidx = i;
break;
}
}
if (videostreamidx > -1) {
codecContext = formatContext->streams[videostreamidx]->codec;
AVCodec* codec = avcodec_find_decoder(codecContext->codec_id);
if (codec) {
status = avcodec_open2(codecContext, codec, NULL);
if (status == 0) {
success = 1;
}
}
}
}
else {
av_log(NULL, AV_LOG_DEBUG, "avformat_find_stream_info error\n");
}
avformat_close_input(&formatContext);
}
avformat_free_context(formatContext);
}
int main(int argc, char *argv[])
{
show_avcodec("moments.mp4");
return 0;
}
(2)编译与调试
makefile的内容:
exe=showavcodec
srcs=show_avcodec.c
$(exe):$(srcs)
gcc -o $(exe) $(srcs) -Iffmpeg/include/ -Lffmpeg -lffmpeg -liconv -lz -g
clean:
rm -f $(exe) *.o
直接执行make来编译,编译后会生成符号表文件即showavcodec.dSYM。
这里只是简单看一下AVCodec的内容,用gdb来调试即可:
gdb showavcodec
b 25
r
在断点的地方,看一下AVCodec变量中的值:
(3)AVCodec结构内容
AVCodec是编×××的结构体,在libavcodec/avcodec.h中定义。
在这个示例中,AVCodec是一个×××。
AVCodec结构中的一些变量,从它的名字或者FFmpeg详细的注释中,可以知道是什么含义。
比如name是编解码的名称,而long_name就是长的名称,等等。
在设计上,AVCodec是编×××的抽象,所以,编×××是有相应的具体实现的。
事实上,每一个编×××都有具体的实现。
比如h264的×××(libavcodec/h264.c):
比如mp3lame的编码器(libavcodec/libmp3lame.c)
FFmpeg会使用上这些具体的编×××的实现,以完成编解码等功能。
(二)AVCodecContext
AVCodecContext可以简单理解为AVCodec的使用场景,而实际上AVCodecContext包括的内容,除了关联AVCodec,还有其它信息。
跟调试AVCodec变量一样,直接使用上面的演示代码就可以调试AVCodecContext,部分代码如下 :
if (videostreamidx > -1) {
codecContext = formatContext->streams[videostreamidx]->codec;
AVCodec* codec = avcodec_find_decoder(codecContext->codec_id);
if (codec) {
status = avcodec_open2(codecContext, codec, NULL);
if (status == 0) {
success = 1;
}
}
}
同样用gdb来调试就可以了,在拿到codecContext后下断点,可以看到AVCodecContext的部分内容如下:
其中有一些变量应该引起注意,比如:
width/height 视频的宽与高
codec_id 编×××的id,根据它可以找到对应的编×××
extradata 对于h264编码的视频,保存了pps与sps的参数信息
profile 视频编码复杂等级
sample_rate 音频的采样率
channels 音频的声道数
sample_fmt 音频的采样格式
跟AVCodec一样,AVCodecContext结构体在libavcodec/avcodec.h中定义。
(三)AVStream
上面介绍了AVCodec、AVCodecContext,现在介绍AVStream。
这三者的大概关系是这样的:
AVStream对应音频流、视频流、字幕等媒体流。
FFmepg以流的概念来封装不同的媒体。
调试AVStream的示例代码与编译,可以查看上面AVCodec调试的介绍。大概如下:
下断点,可以看到AVStream中的内容,比如:
AVStream中的一些变量:
index,流的索引
codec,流对应的avcodeccontext
time_base,时间基准(比例)
duration,流的时长
metadata,流的元信息
nb_frames,流中帧的数量
AVStream结构,在libavformat/avformat.h中定义。
(四)AVFormatContext
AVFormatContext是主角,表示为格式的场景,对应于封装格式(或容器格式)。
同样,使用之前的示例代码,在avformat_open_input函数后下断点:
可以查看avformatcontext结构中的变量值:
AVFormatContext中的metadata记录了多媒体文件的一些信息(比如作者、专辑之类),可以这样取得里面的信息:
if (formatCtx->metadata) {
AVDictionaryEntry *item = NULL;
while((item = av_dict_get(formatCtx->metadata, "", item, AV_DICT_IGNORE_SUFFIX))){
printf("key:%s value:%s \n", item->key, item->value);
}
// 或者这样:
AVDictionaryEntry *tag = NULL;
tag = av_dict_get(formatCtx->metadata, "artist", NULL, 0);
if (tag) {
std::string artist = (char*)tag->value;
}
}
AVFormatContext的一些变量说明:
iformat/oformat,输入/输出格式,在解复用(解封装)或复用(封装)时使用。
pb,输入或输出场景,提供数据操作接口(比如读写、seek等)。
nb_streams,流的个数(以流的方式来复用)。
streams,流的数组。
filename,文件名。
start_time,流的起始时间,以AV_TIME_BASE为单位(除以AV_TIME_BASE转为秒)。
duration,流的时长,以AV_TIME_BASE为单位。
bit_rate,比特率。
probesize,在检测容器格式时,最大的探测大小,在avformat_open_input之前设置(或不设置使用默认值)。
max_analyze_duration,最大的分析数据的时长,在检测编码格式时使用,在avformat_find_stream_info前设置(或不设置),越大越耗时。
metadata,元信息。
AVFormatContext结构,在libavformat/avformat.h中定义。
(五)AVIOContext
AVIOContext是输入输出信息的结构体,它在FFmpeg结构体系中的位置是这样的:
可以看到,AVIOContext是AVFormatContext的一个成员,叫作pb。
pb是提供数据的变量,既用于读(解码)也用于写(编码)。
(1)解码时
在解码时,pb提供解码的原始数据,一般在调用avio_alloc_context创建aviocontext时,指定read与seek函数(自定义的实现,提供读数据、跳转位置的功能),然后把创建的aviocontext(即pb)直接设置给AVFormatContext。比如这样:
pb = avio_alloc_context(readBuf, readBufLen, 0, this, myReadFunc, NULL, mySeekFunc);
mFormatCtx->pb = pb;
或者,在解码时,这样使用aviocontext:
mIOContext.read_packet = myReadFunc;
mIOContext.seek = mySeekFunc;
const int MAX_PRO_SIZE = 321024;
unsigned char probuf = (unsigned char*)av_malloc(MAX_PRO_SIZE);
mIOContext.buffer = probuf;
mIOContext.buf_ptr = probuf;
mIOContext.buffer_size = MAX_PRO_SIZE;
mIOContext.buf_end = probuf + MAX_PRO_SIZE;
mIOContext.max_packet_size = MAX_PRO_SIZE;
formatContext->pb = &mIOContext;
其中,函数myReadFunc与mySeekFunc,按照结构体AVIOContext中的格式说明(参照头文件说明)来定义即可。
解码时,pb的设置,要在avformat_open_input调用前完成。
(2)编码时
在编码写文件时,pb提供写到文件的数据(编码后的数据,对应AVPacket),比如可以直接用avio_open2来打开pb:
if (!(mFormatContext->flags & AVFMT_NOFILE)) {
err = avio_open2(&mFormatContext->pb, url, AVIO_FLAG_WRITE, &mFormatContext->interrupt_callback, NULL);
// ...
}
写文件时,可以用av_write_frame来写入一个packet,也可以用avio_write往pb中写入数据。
编码写文件时,pb的设置,要在avformat_write_header调用前完成。
(3)AVIOContext的变量
这里只列表一部分:
buffer,AVIOContext缓存数据的buffer,起始地址。
buffer_size,buffer的大小。
buf_ptr,操作buffer的当前位置。
buf_end,数据的结束位置,有可能未到buffer的未端。
opaque,指向URLContext,提供读、写、seek等接口,可以让它为空,从而使用自定义的接口。
read_packet/write_packet/seek,读写与seek的接口,可以在avio_alloc_context时指定,从而自定义。
AVIOContext结构体在libavformat/avio.h中定义。
至此,FFmpeg常见的几个结构体就介绍完毕了。
总结一下,本文介绍了FFmpeg的常见结构体,包括AVFormatContext、AVIOContext、AVStream、AVCodecContext、AVCodec等,并且以调试的方式查看了结构体的一些变量值。
以上是关于FFmpeg的常见结构体的主要内容,如果未能解决你的问题,请参考以下文章