在 FFmpeg 学习(六):FFmpeg 核心模块 libavformat 与 libavcodec 分析 中,我们分析了FFmpeg中最重要的两个模块以及重要的结构体之间的关系。

后面的文章,我们先不去继续了解其他模块,先针对在之前的学习中接触到的结构体进行分析,然后在根据功能源码,继续了解FFmpeg。

AVFormatContext是包含码流参数较多的结构体。本文将会详细分析一下该结构体里每个变量的含义和作用。

一、源码整理

首先我们先看一下结构体AVFormatContext的定义的结构体源码(位于libavformat/avformat.h,本人已经将相关注释翻译成中文,方便大家理解):

 /**
* I/O格式上下文
*
* sizeof(AVFormatContext)方法不能在libav*外部调用,使用avformat_alloc_context()来创建一个AVFormatContext.
*/
typedef struct AVFormatContext {
/**
* 一个用来记录和指向avoptions的类。由avformat_all_context()设置。
* 如果(de)muxer存在私有option也会输出。
*/
const AVClass *av_class; /**
* 输入容器的格式结构体
*
* 只在解码中生成,由avformat_open_input()生成
*/
struct AVInputFormat *iformat; /**
* 输出容器的格式的结构体
*
* 只在编码中生成后,必须在调用avformat_write_header()方法之前被生成好。
*/
struct AVOutputFormat *oformat; /**
* 私有数据的格式。这是一个AVOptions-enabled的结构体。
* 当且仅当iformat/oformat.priv_class不为空的时候才会用到。
*
* - 编码时: 由avformat_write_header()设置
* - 解码时: 由avformat_open_input()设置
*/
void *priv_data; /**
* 输入/输出上下文.
*
* - 解码时: 可以由用户自己设置(在avformat_open_intput()之前,而且必须手动关闭),也可以由avformat_open_input()设置.
* - 编码时: 由用户设置(在avformat_write_header之前).调用者必须注意关闭和释放的问题。
*
* 如果在iformat/oformat.flags里面设置了AVFMT_NOFILE的标志,就不要设置设个字段。 因为在这个情况下,编解码器将以其他的方式进行I/O操作,这个字段将为NULL.
*/
AVIOContext *pb; /***************************** 流信息相关字段 ***********************************/
/**
* 流属性标志.是AVFMTCTX_*的集合
* 由libavformat设置.
*/
int ctx_flags; /**
* AVFormatContext.streams -- 流的数量
*
* 由avformat_new_stream()设置,而且不能被其他代码更改.
*/
unsigned int nb_streams;
/**
* 文件中所有流的列表.新的流主要由avformat_new_stream()创建.
*
* - 解码时: 流是在avformat_open_input()方法里,由libavformat创建的。如果在ctx_flags里面设置了AVFMTCTX_NOHEADER,那么新的流也可能由av_read_frame()创建.
* - 编码时: 流是由用户创建的(在调用avformat_write_header()之前).
*
* 在avformat_free_context()释放.
*/
AVStream **streams; #if FF_API_FORMAT_FILENAME
/**
* 输入或输出的文件名
*
* - 解码时: 由avformat_open_input()设置
* - 编码时: 应该在调用avformat_write_header之前由调用者设置
*
* @deprecated 本字段目前已经启用,更改为使用url地址
*/
attribute_deprecated
char filename[];
#endif /**
* 输入或输出的URL. 和旧文件名字段不同的是,这个字段没有长度限制.
*
* - 解码时: 有avformat_open_input()设置, 如果在avformat_open_input()设置的参数为NULL,则初始化为空字符串
* - 编码时: 应该在调用avformat_writer_header()之前由调用者设置(或者调用avformat_init_output_()进行设置),如果在avformat_open_output()设置的参数为NULL,则初始化为空字符串。
*
* 调用avformat_free_context()后由libavformat释放.
*/
char *url; /**
* 第一帧的时间(AV_TIME_BASE:单位为微秒),不要直接设置这个值,这个值是由AVStream推算出来的。
*
* 仅用于解码,由libavformat设置.
*/
int64_t start_time; /**
* 流的时长(单位AV_TIME_BASE:微秒)
*
* 仅用于解码时,由libavformat设置.
*/
int64_t duration; /**
* 所有流的比特率,如果不可用的时候为0。不要设置这个字段,这个字段的值是由FFmpeg自动计算出来的。
*/
int64_t bit_rate; unsigned int packet_size;
int max_delay; /**
* 用于修改编(解)码器行为的标志,由AVFMT_FLAG_*集合构成,需要用户在调用avformat_open_input()或avformat_write_header()之前进行设置
*/
int flags;
#define AVFMT_FLAG_* 0x**** //***** /**
* 在确定输入格式的之前的最大输入数据量.
* 仅用于解码, 在调用avformat_open_input()之前设置。
*/
int64_t probesize; /**
* 从avformat_find_stream_info()的输入数据里面读取的最大时长(单位AV_TIME_BASE:微秒)
* 仅用于解码, 在avformat_find_stream_info()设置
* 可以设置0让avformat使用启发式机制.
*/
int64_t max_analyze_duration; const uint8_t *key;
int keylen; unsigned int nb_programs;
AVProgram **programs; /**
* 强制使用指定codec_id视频解码器
* 仅用于解码时: 由用户自己设置
*/
enum AVCodecID video_codec_id; /**
* 强制使用指定codec_id音频解码器
* 仅用于解码时: 由用户自己设置.
*/
enum AVCodecID audio_codec_id; /**
* 强制使用指定codec_id字母解码器
* 仅用于解码时: 由用户自己设置.
*/
enum AVCodecID subtitle_codec_id; /**
* 每个流的最大内存索引使用量。
* 如果超过了大小,就会丢弃一些,这可能会使得seek操作更慢且不精准。
* 如果提供了全部内存使用索引,这个字段会被忽略掉.
* - 编码时: 未使用
* - 解码时: 由用户设置
*/
unsigned int max_index_size; /**
* 最大缓冲帧的内存使用量(从实时捕获设备中获得的帧数据)
*/
unsigned int max_picture_buffer; /**
* AVChapter数组的数量
*/
unsigned int nb_chapters;
AVChapter **chapters; /**
* 整个文件的元数据
*
* - 解码时: 在avformat_open_input()方法里由libavformat设置
* - 编码时: 可以由用户设置(在avformat_write_header()之前)
*
* 在avformat_free_context()方法里面由libavformat释放
*/
AVDictionary *metadata; /**
* 流开始的绝对时间(真实世界时间)
*/
int64_t start_time_realtime; /**
* 用于确定帧速率的帧数
* 仅在解码时使用
*/
int fps_probe_size; /**
* 错误识别级别.
*/
int error_recognition; /**
* I/O层的自定义中断回调.
*/
AVIOInterruptCB interrupt_callback; /**
* 启动调试的标志
*/
int debug;
#define FF_FDEBUG_TS 0x0001 /**
* 最大缓冲持续时间
*/
int64_t max_interleave_delta; /**
* 允许非标准扩展和实验
*/
int strict_std_compliance; /**
* 检测文件上发生事件的标志
*/
int event_flags;
#define AVFMT_EVENT_FLAG_METADATA_UPDATED 0x0001 /**
* 等待第一个事件戳要读取的最大包数
* 仅解码
*/
int max_ts_probe; /**
* 在编码期间避免负时间戳.
* 值的大小应该是AVFMT_AVOID_NEG_TS_*其中之一.
* 注意,这个设置只会在av_interleaved_write_frame生效
* - 编码时: 由用户设置
* - 解码时: 未使用
*/
int avoid_negative_ts;
#define AVFMT_AVOID_NEG_TS_* /**
* 传输流id.
* 这个将被转移到解码器的私有属性. 所以没有API/ABI兼容性
*/
int ts_id; /**
* 音频预加载时间(单位:毫秒)
* 注意:并非所有的格式都支持这个功能,如果在不支持的时候使用,可能会发生不可预测的事情.
* - 编码时: 由用户设置
* - 解码时: 未使用
*/
int audio_preload; /**
* 最大块时间(单位:微秒).
* 注意:并非所有格式都支持这个功能,如果在不支持的时候使用,可能会发生不可预测的事情.
* - 编码时: 由用户设置
* - 解码时: 未使用
*/
int max_chunk_duration; /**
* 最大块大小(单位:bytes)
* 注意:并非所有格式都支持这个功能,如果在不支持的时候使用,可能会发生不可预测的事情.
* - 编码时: 由用户设置
* - 解码时: 未使用
*/
int max_chunk_size; /**
* 强制使用wallclock时间戳作为数据包的pts/dts
*/
int use_wallclock_as_timestamps; /**
* avio标志
*/
int avio_flags; /**
* 可以用各种方法估计事件的字段
*/
enum AVDurationEstimationMethod duration_estimation_method; /**
* 打开流时跳过初始字节
*/
int64_t skip_initial_bytes; /**
* 纠正单个时间戳溢出
*/
unsigned int correct_ts_overflow; /**
* 强制寻找任何帧
*/
int seek2any; /**
* 在每个包只会刷新I/O context
*/
int flush_packets; /**
* 格式探索得分
*/
int probe_score; /**
* 最大读取字节数(用于识别格式)
*/
int format_probesize; /**
* 允许的编码器列表(通过','分割)
*/
char *codec_whitelist; /**
* 允许的解码器列表(通过','分割 )
*/
char *format_whitelist; ......./**
* 强制视频解码器
*/
AVCodec *video_codec; /**
* 强制音频解码器
*/
AVCodec *audio_codec; /**
* 强制字母解码器
*/
AVCodec *subtitle_codec; /**
* 强制数据解码器
*/
AVCodec *data_codec; /**
* 在元数据头中写入填充的字节数
*/
int metadata_header_padding; /**
* 用户数据(放置私人数据的地方)
*/
void *opaque; /**
* 用于设备和应用程序之间的回调
*/
av_format_control_message control_message_cb; /**
* 输出时间戳偏移量(单位:微秒)
*/
int64_t output_ts_offset; /**
* 转储格式分隔符
*/
uint8_t *dump_separator; /**
* 强制使用的数据解码器id
*/
enum AVCodecID data_codec_id; #if FF_API_OLD_OPEN_CALLBACKS
/**
* 需要为解码开启更多的IO contexts时调用
* @deprecated 已弃用,建议使用io_open and io_close.
*/
attribute_deprecated
int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);
#endif /**
* ',' separated list of allowed protocols.
* - encoding: unused
* - decoding: set by user
*/
char *protocol_whitelist; /**
* 打开新IO流的回调
*/
int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url,
int flags, AVDictionary **options); /**
* 关闭流的回调(流是由AVFormatContext.io_open()打开的)
*/
void (*io_close)(struct AVFormatContext *s, AVIOContext *pb); /**
* ',' 单独的不允许的协议的列表
* - 编码: 没使用到
* - 解码: 由用户设置
*/
char *protocol_blacklist; /**
* 最大流数
* - 编码: 没使用到
* - 解码: 由用户设置
*/
int max_streams;
} AVFormatContext;

二、AVForamtContext 重点字段

在使用FFMPEG进行开发的时候,AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体。下面看几个主要变量的作用(在这里考虑解码的情况):

struct AVInputFormat *iformat:输入数据的封装格式
AVIOContext *pb:输入数据的缓存
unsigned int nb_streams:视音频流的个数
AVStream **streams:视音频流
char filename[]:文件名
int64_t duration:时长(单位:微秒us,转换为秒需要除以1000000)
int bit_rate:比特率(单位bps,转换为kbps需要除以1000)
AVDictionary *metadata:元数据

视频的时长可以转换成HH:MM:SS的形式,示例代码如下:

AVFormatContext *pFormatCtx;
CString timelong;
...
//duration是以微秒为单位
//转换成hh:mm:ss形式
int tns, thh, tmm, tss;
tns = (pFormatCtx->duration)/;
thh = tns / ;
tmm = (tns % ) / ;
tss = (tns % );
timelong.Format("%02d:%02d:%02d",thh,tmm,tss);

视频的原数据(metadata)信息可以通过AVDictionary获取。元数据存储在AVDictionaryEntry结构体中,如下所示:

typedef struct AVDictionaryEntry {
char *key;
char *value;
} AVDictionaryEntry;

每一条元数据分为key和value两个属性。

在ffmpeg中通过av_dict_get()函数获得视频的原数据。

下列代码显示了获取元数据并存入meta字符串变量的过程,注意每一条key和value之间有一个"\t:",value之后有一个"\r\n"

//MetaData------------------------------------------------------------
//从AVDictionary获得
//需要用到AVDictionaryEntry对象
//CString author,copyright,description;
CString meta=NULL,key,value;
AVDictionaryEntry *m = NULL;
//不用一个一个找出来
/*
m=av_dict_get(pFormatCtx->metadata,"author",m,0);
author.Format("作者:%s",m->value);
m=av_dict_get(pFormatCtx->metadata,"copyright",m,0);
copyright.Format("版权:%s",m->value);
m=av_dict_get(pFormatCtx->metadata,"description",m,0);
description.Format("描述:%s",m->value);
*/
//使用循环读出
//(需要读取的数据,字段名称,前一条字段(循环时使用),参数)
while(m=av_dict_get(pFormatCtx->metadata,"",m,AV_DICT_IGNORE_SUFFIX)){
key.Format(m->key);
value.Format(m->value);
meta+=key+"\t:"+value+"\r\n" ;
}

FFmpeg 结构体学习(一): AVFormatContext 分析的更多相关文章

  1. FFmpeg 结构体学习(二): AVStream 分析

    在上文FFmpeg 结构体学习(一): AVFormatContext 分析我们学习了AVFormatContext结构体的相关内容.本文,我们将讲述一下AVStream. AVStream是存储每一 ...

  2. FFmpeg 结构体学习(三): AVPacket 分析

    在上文FFmpeg 结构体学习(二): AVStream 分析我们学习了AVStream结构体的相关内容.本文,我们将讲述一下AVPacket. AVPacket是存储压缩编码数据相关信息的结构体.下 ...

  3. FFmpeg 结构体学习(四): AVFrame 分析

    在上文FFmpeg 结构体学习(三): AVPacket 分析我们学习了AVPacket结构体的相关内容.本文,我们将讲述一下AVFrame. AVFrame是包含码流参数较多的结构体.下面我们来分析 ...

  4. FFmpeg 结构体学习(五): AVCodec 分析

    在上文FFmpeg 结构体学习(四): AVFrame 分析我们学习了AVFrame结构体的相关内容.本文,我们将讲述一下AVCodec. AVCodec是存储编解码器信息的结构体.下面我们来分析一下 ...

  5. FFmpeg 结构体学习(六): AVCodecContext 分析

    在上文FFmpeg 结构体学习(五): AVCodec 分析我们学习了AVCodec结构体的相关内容.本文,我们将讲述一下AVCodecContext. AVCodecContext是包含变量较多的结 ...

  6. FFmpeg 结构体学习(七): AVIOContext 分析

    在上文FFmpeg 结构体学习(六): AVCodecContext 分析我们学习了AVCodec结构体的相关内容.本文,我们将讲述一下AVIOContext. AVIOContext是FFMPEG管 ...

  7. FFmpeg 结构体学习(八):FFMPEG中重要结构体之间的关系

    FFMPEG中结构体很多.最关键的结构体可以分成以下几类: 解协议(http,rtsp,rtmp,mms) AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议 ...

  8. FFMPEG结构体分析:AVFormatContext

    注:写了一系列的结构体的分析的文章,在这里列一个列表: FFMPEG结构体分析:AVFrameFFMPEG结构体分析:AVFormatContextFFMPEG结构体分析:AVCodecContext ...

  9. FFMPEG结构体分析:AVPacket

    注:写了一系列的结构体的分析的文章,在这里列一个列表: FFMPEG结构体分析:AVFrame FFMPEG结构体分析:AVFormatContext FFMPEG结构体分析:AVCodecConte ...

随机推荐

  1. C# 关于X86/X64/AnyCpu 的关系

    电脑硬件CPU可以分为x86与x64, x86的机器只能安装32位的操作系统,如XP, WIN7_86, x64的机器既可以安装32位的系统,又可以安装64位的系统,只是在x64的机器上安装32位的系 ...

  2. ProgressDialog替代

    在API level 26 中,ProgressDialog被声明不赞成使用,应使用的替代方法是ProgressBar 利用下列代码实现ProgressBar的出现和消失 progressBar.se ...

  3. Scyther-Semantics and verification of Security Protocol

    1 .本书前一节主要是介作者自己的生平经历(读完感觉作者是个神童),目标明确作者13岁代码已经写的很溜了.自己也开了网络公司,但是后面又专注于自己的计算机基础理论,修了哲学的博士学位(不得不说很多专业 ...

  4. sql 随笔更新

    SELECT DISTINCT(p.`id`), p.`id` , v.`full_name` , CONCAT(LEFT(v.mobile, 7), '****') , DATE_FORMAT(DA ...

  5. Vuejs自定义select2指令

    在做select2插件的时候遇到一些坑,最终解决如下: Vue.directive('select2', { inserted: function (el, binding, vnode) { var ...

  6. 学习HTML5, Canvas及简单动画的使用

    通过在CSDN中的一些简单课程学习,跟随老师联系,做了如下的月亮,太阳和地球的运动效果.纪录在文章中,用于下次使用. 准备工作如下: 1. 使用三张背景图片 太阳 月亮 地球 2. 在HTML页面中定 ...

  7. Redis实战 - 3.Hash

    hash Redis的Hash有点像一个对象(object),一个Hash里面可以存多个Key-Value对作为它的field,所以它通常可以用来表示对象. Hash里面能存放的值也能作为String ...

  8. C# Conversion Keywords

    主要是解决类与其他不同数据类型的转换 类于类的显式转换: explicit  public static explicit operator B(A a) { return new B(){a.... ...

  9. C++11 带来的新特性 (4)—— 匿名函数(Lambdas)

    1 语法 Lambdas并不是新概念,在其它语言中已经烂大街了.直接进入主题,先看语法: [ captures ] ( params ) specifiers exception attr -> ...

  10. DW1000 用户手册中文版 附录2 IEEE-802.15.4 MAC层

    由于已经在wode中排版无法直接复制到博客中,故本节博客发布使用了图片. 论坛可下载PDF  http://bphero.com.cn/forum.php?mod=viewthread&tid ...