文章目录
-
- 相关指令
- 相关结构体
-
- av_register_all() 已废弃无需添加
- avformat_network_init() 初始化网络封装库
- AVFormatContext结构体
- AVDictionary 结构体
- AVInputFormat 结构体,传入avformat_open_input函数第三个参数
- AVIOContext结构体 [在AVFormatContext中有AVIOContext *pb成员变量;]
- AVOutputFormat结构体
- AVStream结构体
- AVCodecParameters 结构体:封装编解码相关的参数
- AVCodecContext结构体
- AVPacket 结构体,根据AVPacket中AVStream的编号判断此包视音频分类
- AVFrame 结构体
- AVPicture 结构体
- AVClass
- AVOption 结构体 设置变量的值
- 解码
-
- avformat_open_input() 打开输入媒体
- avformat_close_input() 关闭释放AVFormatContext并置为NULL
- avformat_flush() 丢弃所有内部缓冲数据
- avformat_find_stream_info() 填充AVFormatContext的AVStream
- av_dump_format() 输出文件的信息
- av_find_best_stream() 获取音视频及字幕的stream_index
- av_read_frame() 解封装获取AVPacket
- av_seek_frame() 流位置跳转
- avcodec_flush_buffers() 重置解码器buffer
- avcodec_find_decoder() 找到解码器
- avcodec_alloc_context3()生成解码器上下文
- avcodec_parameters_to_context() 编解码器中的值填充编解码器上下文参数
- avcodec_open2() 打开解码器
- avcodec_send_packet() 输入AVPacket到解码器
- avcodec_receive_frame() 返回解码的数据AVFrame
- sws_getCachedContext() 生成像素格式转换上下文
- sws_scale() 视频像素格式和分辨率的转换
- av_image_fill_arrays() 填充给定图像,avpicture_fill()已被废弃实际调用此函数
- swr_alloc_set_opts() 配置音频转换上下文
- swr_init() 初始化SwrContext上下文
- swr_convert() 音频重采样
- 编码
-
- libavdevice 多媒体设备交互的类库
- avdevice_register_all() 初始化libavdevice并注册所有输入输出设备
- avformat_alloc_context() 创建和分配AVFormatContext地址空间
- avformat_alloc_output_context2() 初始化输出格式的AVFormatContext结构体
- av_find_input_format()函数
- avformat_new_stream() 创建输出码流的AVStream
- av_opt_set() 设置AVOption参数
- avcodec_parameters_from_context() 从AVCodecContext填充AVStream的AVCodecParameters
- av_frame_get_buffer() 为音频或视频数据AVFrame.data and AVFrame.buf数组填配新的缓冲区并进行填充
- avio_open2()函数 打开输入输出文件,创建AVFormatContext中的AVIOContext
- avformat_write_header() 写AVStream流的文件头
- avcodec_send_frame()
- avcodec_receive_packet()
- av_compare_ts() 比较时间决定该写入视频还是音频
- av_interleaved_write_frame()函数
- av_write_frame() 将AVPacket写进Mux
- av_write_trailer() 写文件尾,写入时长信息等
- 视音频滤镜
-
- avfilter_register_all() 已废弃
- AVFilterContext 滤镜实例的结构体
- AVFilter 结构体
- AVFilterPad 结构体
- AVFilterInOut 结构体:滤镜链输入/输出的链接列表
- AVFilterGraph 结构体
- AVFilterLink 链接结构体定义
- avfilter_graph_alloc() 分配filter graph空间,返回一个AVFilterGraph
- avfilter_get_by_name() 根据过滤器名字返回匹配的过滤器对象
- avfilter_graph_create_filter() 创建一个滤镜实例AVFilterContext,并添加到AVFilterGraph中
- avfilter_link() 连接两个滤镜节点
- avfilter_graph_config() 检查有效性并配置图中的所有链接和格式
- avfilter_graph_dump() 将filter graph转换成人类可读的字符串表示
- buffersrc
- av_buffersrc_add_frame() 向FilterGraph中加入一个AVFrame
- buffersink
- av_buffersink_get_frame() 从buffersink中取出一个AVFrame
- avfilter_graph_parse2()
- avfilter_graph_parse_ptr() 将一串通过字符串描述的Graph添加到FilterGraph中[多个滤镜字符串描述](https://www.cnblogs.com/TaigaCon/p/10067871.html)
- ERRORCODE
- 知识点
-
- 音视频同步
-
- 时间基准
-
- AV_TIME_BASE:ffmpeg中的内部计时单位(内部时间基)
- AV_TIME_BASE_Q:ffmpeg内部时间基的分数表示,是AV_TIME_BASE的倒数
- double av_q2d(AVRational a): 把AVRatioal结构转换成double
- time_base :[FFmpeg时间戳详解](https://www.cnblogs.com/leisure_chn/p/10584910.html)
- av_rescale_q() 将时间值从一种时间基转换为另一种时间基
- av_rescale_rnd() 计算 "a * b / c" 的值并分五种方式来取整
- av_packet_rescale_ts() 将AVPacket中时间值从一种时间基转换为另一种时间基
- 举例
- 音频通道相关转换
- AVStream->codecpar->codec_tag = 0 的作用
- 创建自定义AVFilter滤镜
- ffmpeg中编码类型为rawvideo
- ffmpeg参数设置
相关指令
- 查看支持的封装格式FFmpeg封装格式处理
ffmpeg -formats
相关结构体
av_register_all() 已废弃无需添加
avformat_network_init() 初始化网络封装库
AVFormatContext结构体
AVFormatContext描述了一个媒体文件或媒体流的构成和基本信息,位于avformat.h文件中。
typedef struct AVFormatContext {
/**
* A class for logging and @ref avoptions. Set by avformat_alloc_context().
* Exports (de)muxer private options if they exist.
*/
const AVClass *av_class;
/**
* 输入容器格式.
* 用于分流,通过avformat_open_input()设置.
*/
struct AVInputFormat *iformat;
/**
* 输出容器格式。
*
* 用于混流,必须在avformat_write_header()调用前设置.
*/
struct AVOutputFormat *oformat;
/**
* 格式化私有数据. This is an AVOptions-enabled struct
* if and only if iformat/oformat.priv_class is not NULL.
*
* - muxing: set by avformat_write_header()
* 如h264的av_opt_set(m_pVCodecCxt->priv_data,"preset","ultrafast",0);
* - demuxing: set by avformat_open_input()
*/
void *priv_data;
/**
* I/O 上下文.
*
* - 分流: 在avformat_open_input() 之前设置(用户必须手动释放)或者通过avformat_open_input()
* 自动设置.
* - 混流: 在avformat_write_header()之前设置.用户必须注意及时关闭/释放IO上下文。
*
* 不要设置AVFMT_NOFILE标志给iformat/oformat.flags。因为这种情况下,该值为NULL,混/分流器会通
* 过其它方式处理I/O。
*/
AVIOContext *pb;
/* 后面都是流信息 */
/**
* 信号流属性标志,AVFMTCTX_*的组合.
* 通过libavformat设置.
*/
int ctx_flags;
/**
* AVFormatContext.streams中的元素数量,其实就是流的总数.
*
* 通过avformat_new_stream()设置, 禁止其它代码修改。
*/
unsigned int nb_streams;
/**
* 媒体中,所有流的列表,新的流由avformat_new_stream()创建。
*
* - 分流: 流在avformat_open_input()函数中由libavformat创建。如果AVFMTCTX_NOHEANDER被设置
* 带ctx_flags中,新的流可能出现在av_read_frame()中。
* - 混流: 流在avformat_write_header()函数之前被用户创建
*
* 在avformat_free_context()函数中,通过libavformat释放。
*/
AVStream **streams;
/**
* 输入输出文件名
*
* - 分流: 通过avformat_open_input()设置。
* - 混流: 在avformat_write_header()调用前,可以被使用者设置。
*/
char filename[1024];
/**
* 组件第一帧的位置,用AV_TIME_BASE分数秒表示。禁止直接设置,由AVStream的值推导而来。
*
* - 分流:通过libavformat设置.
*/
int64_t start_time;
/**
* 留的时长,以AV_TIME_BASE分数秒为单位。如果您不知道任何单个流的持续时间,也不设置其中的任何一
* 个,请仅设置此值。 如果没有设置,该值可以被AVStream推导出来
*
* 只用于分流操作,通过libavformat设置。
*/
int64_t duration;
/**
* 总流的比特率以bit/s为单位,如果流不可用,该值为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_GENPTS 0x0001 ///< Generate missing pts even if it requires parsing future frames.
#define AVFMT_FLAG_IGNIDX 0x0002 ///< Ignore index.
#define AVFMT_FLAG_NONBLOCK 0x0004 ///< Do not block when reading packets from input.
#define AVFMT_FLAG_IGNDTS 0x0008 ///< Ignore DTS on frames that contain both DTS & PTS
#define AVFMT_FLAG_NOFILLIN 0x0010 ///< Do not infer any values from other values, just return what is stored in the container
#define AVFMT_FLAG_NOPARSE 0x0020 ///< Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled
#define AVFMT_FLAG_NOBUFFER 0x0040 ///< Do not buffer frames when possible
#define AVFMT_FLAG_CUSTOM_IO 0x0080 ///< The caller has supplied a custom AVIOContext, don't avio_close() it.
#define AVFMT_FLAG_DISCARD_CORRUPT 0x0100 ///< Discard frames marked corrupted
#define AVFMT_FLAG_FLUSH_PACKETS 0x0200 ///< Flush the AVIOContext every packet.
/**
* 混流时,尽量避免将随机/不可控的数据写入输出中,包括随机IDs,实时时间戳/日期,混流器版本等等。
*
* 该标记主要用于测试
*/
#define AVFMT_FLAG_BITEXACT 0x0400
#define AVFMT_FLAG_MP4A_LATM 0x8000 ///< Enable RTP MP4A-LATM payload
#define AVFMT_FLAG_SORT_DTS 0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down)
#define AVFMT_FLAG_PRIV_OPT 0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted)
#if FF_API_LAVF_KEEPSIDE_FLAG
#define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000 ///< Don't merge side data but keep it separate. Deprecated, will be the default.
#endif
#define AVFMT_FLAG_FAST_SEEK 0x80000 ///< Enable fast, but inaccurate seeks for some formats
#define AVFMT_FLAG_SHORTEST 0x100000 ///< Stop muxing when the shortest stream stops.
#define AVFMT_FLAG_AUTO_BSF 0x200000 ///< Wait for packet data before writing a header, and add bitstream filters as requested by the muxer
/**
* 从指定容器格式的输入中读取最大数据的大小。
* 仅用于分流操作,用户可以在avformat_open_input()函数前设置。
*/
int64_t probesize;
/**
* 从指定容器格式的输入中读取的最大数据时长(以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;
/**
* 每个索引使用的内存最大值(以字节为单位)。
* 如果索引超出内存限制,项目会被丢弃以保持较小的内存占用。这回导致seeking较慢和不准确(取决于分流
* 器)
* 完全内存索引是强制性的分解器将忽略这一点。
* - 混流操作: 不实用
* - 分流操作: 由用户设置
*/
unsigned int max_index_size;
/**
* 从设备获取的实时帧缓冲的最大内存大小(以字节为单位)
*/
unsigned int max_picture_buffer;
/**
* AVChapter数组中的章节数量。
* 混流时,章节信息通畅会写在文件头中,所以nb_chapters应该在写文件头之前被初始化。一些混流器(例如
* mov、mkv)可以将章节写在预告中。为了在预告中撰写章节,在write_header调用时nb_chapters必须为
* 并且在write_trailer被调用时为非0数。
* - 混流操作: 用户设置
* - 分流操作: libavformat设置
*/
unsigned int nb_chapters;
AVChapter **chapters;
/**
* 适用于整个文件的元数据。
*
* - 分流操作: libavformat在avformat_open_input()函数中设置。
* - 混流操作: 调用者可以在avformat_write_header()函数调用前设置。
*
* 通过libavformat在函数avformat_free_context()中释放。
*/
AVDictionary *metadata;
/**
* 从Unix纪元(1970年1月1日00:00)开始,以真实世界时间开始流的开始时间,以微秒为单位。 即,流在现
* 实世界被使用的pts=0时间。
* - 混流操作: 在avformat_write_header()调用前被调用者设置。如果设置为0或AV_NOPTS_VALUE,则
* 将使用当前时间(wall-time)。
* - 分流操作: 由libavformat设置. 如果AV_NOPTS_VALUE未知,注意,一定数量的帧被获取后,该值可能
* 变得已知。
*/
int64_t start_time_realtime;
/**
* 用于确定avformat_find_stream_info()中帧率的帧数。
* 仅用于分流,在avformat_find_stream_info()调用前由调用者设置
*/
int fps_probe_size;
/**
* 错误识别; 较高的值将检测到更多的错误,但可能会错误检测一些或多或少有效的部分作为错误。 在
* avformat_open_input()之前由调用方设置的仅用于解分流。
*/
int error_recognition;
/**
* I/O层自定义中断回调函数。
*
* 分流操作: avformat_open_input()调用前由用户设置.
* 混流操作: avformat_write_header()调用前由用户设置(主要用于AVFMT_NOFILE 格式)。如果用它来
* 打开文件,该回调也会传递给avio_open2().
*/
AVIOInterruptCB interrupt_callback;
/**
* 启用debug标志。
*/
int debug;
#define FF_FDEBUG_TS 0x0001
/**
* Maximum buffering duration for interleaving.
*
* To ensure all the streams are interleaved correctly,
* av_interleaved_write_frame() will wait until it has at least one packet
* for each stream before actually writing any packets to the output file.
* When some streams are "sparse" (i.e. there are large gaps between
* successive packets), this can result in excessive buffering.
*
* This field specifies the maximum difference between the timestamps of the
* first and the last packet in the muxing queue, above which libavformat
* will output a packet regardless of whether it has queued a packet for all
* the streams.
*
* Muxing only, set by the caller before avformat_write_header().
*/
int64_t max_interleave_delta;
/**
* 允许非标准和实验性扩展
* @see AVCodecContext.strict_std_compliance
*/
int strict_std_compliance;
/**
* 供用户检测文件上发生事件的标志。事件处理后,用户必须清除标志。AVFMT_EVENT_FLAG_ *的组合。
*/
int event_flags;
#define AVFMT_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< The call resulted in updated metadata.
/**
* 在等待第一个时间戳时要读取的最大数据包数。仅用于解码。
*/
int max_ts_probe;
/**
* 避免混流过程中的负面时间戳。 AVFMT_AVOID_NEG_TS_ *常量中的任何值。 请注意,这只适用于使用
* av_interleaved_write_frame。 (interleave_packet_per_dts正在使用中)
* - 混流: 用户设置
* - 分流: 不使用
*/
int avoid_negative_ts;
#define AVFMT_AVOID_NEG_TS_AUTO -1 ///< Enabled when required by target format
#define AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE 1 ///< Shift timestamps so they are non negative
#define AVFMT_AVOID_NEG_TS_MAKE_ZERO 2 ///< Shift timestamps so that they start at 0
/**
* 传输流id。
* 这将被移入分流器的私有选项。 因此没有API / ABI兼容性
*/
int ts_id;
/**
* 音频预加载以微秒为单位。 请注意,并非所有格式都支持此功能,如果在不支持的情况下使用它,则可能会发
* 生不可预知的情况。
* - 编码: 用户设置
* - 解码: 不使用
*/
int audio_preload;
/**
* 最大块时间(以微秒为单位)。 请注意,并非所有格式都支持此功能,如果在不支持的情况下使用它,则可能
* 会发生不可预知的情况。
* - 编码: 用户设置
* - 解码: 不使用
*/
int max_chunk_duration;
/**
* 最大块大小(以字节为单位)。注意,并非所有格式都支持此功能,如果在不支持的情况下使用它,可能会发
* 生不可预知的情况。
* - 编码: 用户设置
* - 解码: 不使用
*/
int max_chunk_size;
/**
* 强制使用wallclock时间戳作为pts / dts数据包在B帧存在的情况下存在未定义的结果。
* - 编码: 不使用
* - 解码: 用户设置
*/
int use_wallclock_as_timestamps;
/**
* avio标志,用于强制使用AVIO_FLAG_DIRECT。
* - 编码: 不使用
* - 解码: 用户设置
*/
int avio_flags;
/**
* 时长字段可以通过各种方式进行计算,并且可以使用此字段了解时长是如何计算的。
* - 编码: 不使用
* - 解码: 用户读取
*/
enum AVDurationEstimationMethod duration_estimation_method;
/**
* 打开流时跳过初始字节
* - 编码: 不使用
* - 解码: 用户设置
*/
int64_t skip_initial_bytes;
/**
* 正确的单个时间戳溢出
* - 编码: 不使用
* - 解码: 用户设置
*/
unsigned int correct_ts_overflow;
/**
* 强制seeking到任意帧(即使没有关键帧)
* - 编码: 不使用
* - 解码: 用户设置
*/
int seek2any;
/**
* 在每个数据包之后刷新I / O上下文。
* - 编码: 用户设置
* - 解码: 不使用
*/
int flush_packets;
/**
* 格式探测分数。 最高分是AVPROBE_SCORE_MAX,当分流器探测格式时设置它。
* - 编码: 不使用
* - 解码: avformat设置,用户读取
*/
int probe_score;
AVDictionary 结构体
//key value 键值对
typedef struct AVDictionaryEntry {
char *key;
char *value;
} AVDictionaryEntry;
struct AVDictionary {
int count;
//可以看成一个动态数组
AVDictionaryEntry *elems;
};
av_dict_*中有些接口的最后一个参数为int flags,此参数可接受的有效值为在dict.h文件中定义的宏:
(1). AV_DICT_MATCH_CASE:设置字典中检索的key区分大小写,默认不区分大小写
(2). AV_DICT_IGNORE_SUFFIX:忽略字典中指定条目key的后缀
(3). AV_DICT_DONT_STRDUP_KEY:若key已分配,则不进行复制
(4). AV_DICT_DONT_STRDUP_VAL:若value已分配,则不进行复制
(5). AV_DICT_DONT_OVERWRITE:若指定key在字典中已存在,则不进行覆盖
(6). AV_DICT_APPEND:若指定key在字典中已存在,则value直接拼接到已存在value值的后面
(7). AV_DICT_MULTIKE:允许在字典中存储多个相同的key
//得到字典metadata(key-value)数
int av_dict_count(const AVDictionary *m)
{
return m ? m->count : 0;
}
AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key,
const AVDictionaryEntry *prev, int flags)
{
unsigned int i, j;
if (!m)
return NULL;
//获取prev下一个元素的索引(m->elems相当于header,所以prev - m->elems即为当前索引)
if (prev)
i = prev - m->elems + 1;
else
i = 0;
for (; i < m->count; i++) {
const char *s = m->elems[i].key;
//是否区分大小写
if (flags & AV_DICT_MATCH_CASE)
for (j = 0; s[j] == key[j] && key[j]; j++)
;
else
for (j = 0; av_toupper(s[j]) == av_toupper(key[j]) && key[j]; j++)
;
//前面进行了字符匹配,如果全部匹配,到这一步key[j]为true则说明前面字符未完全匹配,跳过
//反之key[j]为false,则继续下一步的验证
if (key[j])
continue;
//如果s[j]为false,说明名字完全匹配可以直接返回了
//如果s[j]为true,说明key和s前部分完全匹配,这个时候如果flags为AV_DICT_IGNORE_SUFFIX,则也认为匹配直接返回
//结论:AV_DICT_IGNORE_SUFFIX 表示忽略后缀,eg:"abc"可能找到"abcde"
if (s[j] && !(flags & AV_DICT_IGNORE_SUFFIX))
continue;
return &m->elems[i];
}
return NULL;
}
//需要注意的是设置时的flags
//eg.AV_DICT_IGNORE_SUFFIX可能导致"cp"覆盖"cpa"
int av_dict_set(AVDictionary **pm, const char *key, const char *value,
int flags)
{
AVDictionary *m = *pm;
AVDictionaryEntry *tag = NULL;
char *oldval = NULL, *copy_key = NULL, *copy_value = NULL;
//判断是否允许存储相同key
if (!(flags & AV_DICT_MULTIKEY)) {
//不允许则执行
tag = av_dict_get(m, key, NULL, flags);
}
//判断是否复制一份key
if (flags & AV_DICT_DONT_STRDUP_KEY)
copy_key = (void *)key;
else
copy_key = av_strdup(key);
//判断是否复制一份value
if (flags & AV_DICT_DONT_STRDUP_VAL)
copy_value = (void *)value;
else if (copy_key)
copy_value = av_strdup(value);
//如果为null则新建一个AVDictionary
if (!m)
m = *pm = av_mallocz(sizeof(*m));
if (!m || (key && !copy_key) || (value && !copy_value))
goto err_out;
//如果tag==true,则说明不支持相同key
if (tag) {
//这里可以看出AV_DICT_DONT_OVERWRITE表示不能覆盖之前的key
if (flags & AV_DICT_DONT_OVERWRITE) {
av_free(copy_key);
av_free(copy_value);
return 0;
}
//添加值
if (flags & AV_DICT_APPEND)
oldval = tag->value;
//释放旧的key-value
else
av_free(tag->value);
av_free(tag->key);
*tag = m->elems[--m->count];
} else if (copy_value) {
//调整字典链表的大小(新开辟了一个字典的内存)
AVDictionaryEntry *tmp = av_realloc(m->elems,
(m->count + 1) * sizeof(*m->elems));
if (!tmp)
goto err_out;
m->elems = tmp;
}
//设置值
if (copy_value) {
m->elems[m->count].key = copy_key;
m->elems[m->count].value = copy_value;
//合并值
if (oldval && flags & AV_DICT_APPEND) {
size_t len = strlen(oldval) + strlen(copy_value) + 1;
char *newval = av_mallocz(len);
if (!newval)
goto err_out;
av_strlcat(newval, oldval, len);
av_freep(&oldval);
av_strlcat(newval, copy_value, len);
m->elems[m->count].value = newval;
av_freep(©_value);
}
m->count++;
} else {
av_freep(©_key);
}
//没有元素时释放
if (!m->count) {
av_freep(&m->elems);
av_freep(pm);
}
return 0;
err_out:
if (m && !m->count) {
av_freep(&m->elems);
av_freep(pm);
}
av_free(copy_key);
av_free(copy_value);
return AVERROR(ENOMEM);
}
int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value,
int flags)
{
char valuestr[22];
snprintf(valuestr, sizeof(valuestr), "%"PRId64, value);
//设置不复制value
flags &= ~AV_DICT_DONT_STRDUP_VAL;
return av_dict_set(pm, key, valuestr, flags);
}
static int parse_key_value_pair(AVDictionary **pm, const char **buf,
const char *key_val_sep, const char *pairs_sep,
int flags)
{
char *key = av_get_token(buf, key_val_sep);
char *val = NULL;
int ret;
if (key && *key && strspn(*buf, key_val_sep)) {
(*buf)++;
val = av_get_token(buf, pairs_sep);
}
if (key && *key && val && *val)
ret = av_dict_set(pm, key, val, flags);
else
ret = AVERROR(EINVAL);
av_freep(&key);
av_freep(&val);
return ret;
}
//将给定的key-value(pairs)string集解析到字典中
int av_dict_parse_string(AVDictionary **pm, const char *str,
const char *key_val_sep, const char *pairs_sep,
int flags)
{
int ret;
if (!str)
return 0;
/* ignore STRDUP flags */
flags &= ~(AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
while (*str) {
if ((ret = parse_key_value_pair(pm, &str, key_val_sep, pairs_sep, flags)) < 0)
return ret;
if (*str)
str++;
}
return 0;
}
void av_dict_free(AVDictionary **pm)
{
AVDictionary *m = *pm;
if (m) {
while (m->count--) {
av_freep(&m->elems[m->count].key);
av_freep(&m->elems[m->count].value);
}
av_freep(&m->elems);
}
av_freep(pm);
}
//复制一个字典
int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags)
{
AVDictionaryEntry *t = NULL;
while ((t = av_dict_get(src, "", t, AV_DICT_IGNORE_SUFFIX))) {
int ret = av_dict_set(dst, t->key, t->value, flags);
if (ret < 0)
return ret;
}
return 0;
}
//得到字典的string结果集。
//key_val_sep表示key-value的分割符
//pairs_sep表示每个key-value(pairs)之间的分割符
//注意不能使用' ','\'
//一般配合av_dict_parse_string使用
int av_dict_get_string(const AVDictionary *m, char **buffer,
const char key_val_sep, const char pairs_sep)
{
AVDictionaryEntry *t = NULL;
AVBPrint bprint;
int cnt = 0;
char special_chars[] = {pairs_sep, key_val_sep, ' '};
if (!buffer || pairs_sep == ' ' || key_val_sep == ' ' || pairs_sep == key_val_sep ||
pairs_sep == '\' || key_val_sep == '\')
return AVERROR(EINVAL);
if (!av_dict_count(m)) {
*buffer = av_strdup("");
return *buffer ? 0 : AVERROR(ENOMEM);
}
av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);
while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) {
if (cnt++)
av_bprint_append_data(&bprint, &pairs_sep, 1);
av_bprint_escape(&bprint, t->key, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
av_bprint_append_data(&bprint, &key_val_sep, 1);
av_bprint_escape(&bprint, t->value, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
}
return av_bprint_finalize(&bprint, buffer);
}
AVInputFormat 结构体,传入avformat_open_input函数第三个参数
表示输入文件容器格式,一种文件容器格式对应一个AVInputFormat 结构,在程序运行时有多个实例,位于avoformat.h文件中。
typedef struct AVInputFormat {
const char *name; // 输入格式的短名称
const char *long_name; // 格式的长名称(相对于短名称而言,更易于阅读)
/**
* Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS,
* AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH,
* AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS.
*/
int flags;
const char *extensions; // 如果定义了扩展,就不会进行格式探测。但因为该功能目前支持不够,不推荐使用
const struct AVCodecTag * const *codec_tag; // 见名知意
const AVClass *priv_class; ///< AVClass for the private context
const char *mime_type; // mime类型,它用于在探测时检查匹配的mime类型。
/* 此行下方的任何字段都不是公共API的一部分。 它们不能在libavformat之外使用,可以随意更改和删除。
* 应在上方添加新的公共字段。*/
struct AVInputFormat *next; // 用于链接下一个AVInputFormat
int raw_codec_id; // 原始demuxers将它们的解码器id保存在这里。
int priv_data_size; // 私有数据大小,可以用于确定需要分配多大的内存来容纳下这些数据。
/**
* 判断给定文件是否有可能被解析为此格式。 提供的缓冲区保证为AVPROBE_PADDING_SIZE字节大,因此除非您需 * 要更多,否则无需检查。
*/
int (*read_probe)(AVProbeData *);
/**
* 读取格式头,并初始化AVFormatContext结构体
* @return 0 表示操作成功
*/
int (*read_header)(struct AVFormatContext *);
/**
* 读取一个packet并存入pkt指针中。pts和flags会被同时设置。
* @return 0 表示操作成功, < 0 发生异常
* 当返回异常时,pkt可定没有allocated或者在函数返回之前被释放了。
*/
int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);
// 关闭流,AVFormatContext和AVStreams并不会被这个函数释放。
int (*read_close)(struct AVFormatContext *);
/**
* 在stream_index的流中,使用一个给定的timestamp,seek到附近帧。
* @param stream_index 不能为-1
* @param flags 如果没有完全匹配,决定向前还是向后匹配。
* @return >= 0 成功
*/
int (*read_seek)(struct AVFormatContext *,
int stream_index, int64_t timestamp, int flags);
// 获取stream[stream_index]的下一个时间戳,如果发生异常返回AV_NOPTS_VALUE
int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index,
int64_t *pos, int64_t pos_limit);
// 开始或者恢复播放,只有在播放rtsp格式的网络格式才有意义。
int (*read_play)(struct AVFormatContext *);
int (*read_pause)(struct AVFormatContext *);// 暂停播放,只有在播放rtsp格式的网络格式才有意义。
/**
* 快进到指定的时间戳
* @param stream_index 需要快进操作的流
* @param ts 需要快进到的地方
* @param min_ts max_ts seek的区间,ts需要在这个范围中。
*/
int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags);
// 返回设备列表和其属性
int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);
// 初始化设备能力子模块
int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
// 释放设备能力子模块
int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
} AVInputFormat;
AVIOContext结构体 [在AVFormatContext中有AVIOContext *pb成员变量;]
AVIOContext是FFmpeg管理输入输出数据的结构体,位于avio.h文件中。
typedef struct AVIOContext {
/**
* A class for private options.
*
* If this AVIOContext is created by avio_open2(), av_class is set and
* passes the options down to protocols.
*
* If this AVIOContext is manually allocated, then av_class may be set by
* the caller.
*
* warning -- this field can be NULL, be sure to not pass this AVIOContext
* to any av_opt_* functions in that case.
*/
const AVClass *av_class;
/*
* The following shows the relationship between buffer, buf_ptr, buf_end, buf_size,
* and pos, when reading and when writing (since AVIOContext is used for both):
*
**********************************************************************************
* READING
**********************************************************************************
*
* | buffer_size |
* |---------------------------------------|
* | |
*
* buffer buf_ptr buf_end
* +---------------+-----------------------+
* |/ / / / / / / /|/ / / / / / /| |
* read buffer: |/ / consumed / | to be read /| |
* |/ / / / / / / /|/ / / / / / /| |
* +---------------+-----------------------+
*
* pos
* +-------------------------------------------+-----------------+
* input file: | | |
* +-------------------------------------------+-----------------+
*
*
**********************************************************************************
* WRITING
**********************************************************************************
*
* | buffer_size |
* |-------------------------------|
* | |
*
* buffer buf_ptr buf_end
* +-------------------+-----------+
* |/ / / / / / / / / /| |
* write buffer: | / to be flushed / | |
* |/ / / / / / / / / /| |
* +-------------------+-----------+
*
* pos
* +--------------------------+-----------------------------------+
* output file: | | |
* +--------------------------+-----------------------------------+
*
*/
unsigned char *buffer; /**< Start of the buffer. */
int buffer_size; /**< Maximum buffer size */
unsigned char *buf_ptr; /**< Current position in the buffer */
unsigned char *buf_end; /**< End of the data, may be less than
buffer+buffer_size if the read function returned
less data than requested, e.g. for streams where
no more data has been received yet. */
void *opaque; /**< A private pointer, passed to the read/write/seek/...
functions. */
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);
int64_t (*seek)(void *opaque, int64_t offset, int whence);
int64_t pos; /**< position in the file of the current buffer */
int must_flush; /**< true if the next seek should flush */
int eof_reached; /**< true if eof reached */
int write_flag; /**< true if open for writing */
int max_packet_size;
unsigned long checksum;
unsigned char *checksum_ptr;
unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
int error; /**< contains the error code or 0 if no error happened */
/**
* Pause or resume playback for network streaming protocols - e.g. MMS.
*/
int (*read_pause)(void *opaque, int pause);
/**
* Seek to a given timestamp in stream with the specified stream_index.
* Needed for some network streaming protocols which don't support seeking
* to byte position.
*/
int64_t (*read_seek)(void *opaque, int stream_index,
int64_t timestamp, int flags);
/**
* A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.
*/
int seekable;
/**
* max filesize, used to limit allocations
* This field is internal to libavformat and access from outside is not allowed.
*/
int64_t maxsize;
/**
* avio_read and avio_write should if possible be satisfied directly
* instead of going through a buffer, and avio_seek will always
* call the underlying seek function directly.
*/
int direct;
/**
* Bytes read statistic
* This field is internal to libavformat and access from outside is not allowed.
*/
int64_t bytes_read;
/**
* seek statistic
* This field is internal to libavformat and access from outside is not allowed.
*/
int seek_count;
/**
* writeout statistic
* This field is internal to libavformat and access from outside is not allowed.
*/
int writeout_count;
/**
* Original buffer size
* used internally after probing and ensure seekback to reset the buffer size
* This field is internal to libavformat and access from outside is not allowed.
*/
int orig_buffer_size;
/**
* Threshold to favor readahead over seek.
* This is current internal only, do not use from outside.
*/
int short_seek_threshold;
/**
* ',' separated list of allowed protocols.
*/
const char *protocol_whitelist;
/**
* ',' separated list of disallowed protocols.
*/
const char *protocol_blacklist;
/**
* A callback that is used instead of write_packet.
*/
int (*write_data_type)(void *opaque, uint8_t *buf, int buf_size,
enum AVIODataMarkerType type, int64_t time);
/**
* If set, don't call write_data_type separately for AVIO_DATA_MARKER_BOUNDARY_POINT,
* but ignore them and treat them as AVIO_DATA_MARKER_UNKNOWN (to avoid needlessly
* small chunks of data returned from the callback).
*/
int ignore_boundary_point;
/**
* Internal, not meant to be used from outside of AVIOContext.
*/
enum AVIODataMarkerType current_type;
int64_t last_time;
} AVIOContext;
unsigned char *buffer; //缓存开始位置。
int buffer_size; //缓冲区的大小。
unsigned char *buf_ptr;//当前指针在缓冲区中的位置,即当前指针读取到的位置。
unsigned char *buf_end; //缓存结束的位置
void *opaque;//一个私有指针,传递给read / write / seek / ...函数。
opaque指向了URLContext。URLContext结构体中还有一个结构体URLProtocol。
ps:每种协议(rtp,rtmp,file等)对应一个URLProtocol。
int64_t pos; //当前缓冲区在文件中的位置。
int must_flush; //如果下一次seek需要刷新则为真。
int eof_reached; //是否读到eof,文件结尾。
int (*read_pause)(void *opaque, int pause);//暂停或恢复播放网络流媒体协议 - 例如 MMS。
int64_t (*read_seek)(void *opaque, int stream_index, int64_t timestamp, int flags);
//使用指定的stream_index查找流中给定的时间戳(对于不支持寻找字节位置的网络流协议)。
AVOutputFormat结构体
AVOutpufFormat与AVInputFormat类似,是类似COM 接口的数据结构,表示输出文件容器格式,着重于功能函数,位于Avformat.h文件中。
ffmpeg支持各种各样的输出文件格式,MP4,FLV,3GP等等。而 AVOutputFormat 结构体则保存了这些格式的信息和一些常规设置。每一种封装对应一个 AVOutputFormat 结构,ffmpeg将AVOutputFormat 按照链表存储.
const char *name;
const char *long_name;//格式的描述性名称,易于阅读。
enum AVCodecID audio_codec; //默认的音频编解码器
enum AVCodecID video_codec; //默认的视频编解码器
enum AVCodecID subtitle_codec; //默认的字幕编解码器
struct AVOutputFormat *next;
int (*write_header)(struct AVFormatContext *);
int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);//写一个数据包。 如果在标志中设置AVFMT_ALLOW_FLUSH,则pkt可以为NULL。
int (*write_trailer)(struct AVFormatContext *);
int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, AVPacket *in, int flush);
int (*control_message)(struct AVFormatContext *s, int type, void *data, size_t data_size);//允许从应用程序向设备发送消息。
int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index, AVFrame **frame, unsigned flags);//写一个未编码的AVFrame。
int (*init)(struct AVFormatContext *);//初始化格式。 可以在此处分配数据,并设置在发送数据包之前需要设置的任何AVFormatContext或AVStream参数。
void (*deinit)(struct AVFormatContext *);//取消初始化格式。
int (*check_bitstream)(struct AVFormatContext *, const AVPacket *pkt);//设置任何必要的比特流过滤,并提取全局头部所需的任何额外数据。
AVStream结构体
结构体定义
typedef struct AVStream {
int index; /**< stream index in AVFormatContext */
/**
* Format-specific stream ID.
* decoding: set by libavformat
* encoding: set by the user, replaced by libavformat if left unset
*/
int id;
#if FF_API_LAVF_AVCTX
/**
* @deprecated use the codecpar struct instead
*/
attribute_deprecated
AVCodecContext *codec;
#endif
void *priv_data;
#if FF_API_LAVF_FRAC
/**
* @deprecated this field is unused
*/
attribute_deprecated
struct AVFrac pts;
#endif
/**
* This is the fundamental unit of time (in seconds) in terms
* of which frame timestamps are represented.
*
* decoding: set by libavformat
* encoding: May be set by the caller before avformat_write_header() to
* provide a hint to the muxer about the desired timebase. In
* avformat_write_header(), the muxer will overwrite this field
* with the timebase that will actually be used for the timestamps
* written into the file (which may or may not be related to the
* user-provided one, depending on the format).
*/
AVRational time_base;
/**
* Decoding: pts of the first frame of the stream in presentation order, in stream time base.
* Only set this if you are absolutely 100% sure that the value you set
* it to really is the pts of the first frame.
* This may be undefined (AV_NOPTS_VALUE).
* @note The ASF header does NOT contain a correct start_time the ASF
* demuxer must NOT set this.
*/
int64_t start_time;
/**
* Decoding: duration of the stream, in stream time base.
* If a source file does not specify a duration, but does specify
* a bitrate, this value will be estimated from bitrate and file size.
*/
int64_t duration;
int64_t nb_frames; ///< number of frames in this stream if known or 0
int disposition; /**< AV_DISPOSITION_* bit field */
enum AVDiscard discard; ///< Selects which packets can be discarded at will and do not need to be demuxed.
/**
* sample aspect ratio (0 if unknown)
* - encoding: Set by user.
* - decoding: Set by libavformat.
*/
AVRational sample_aspect_ratio;
AVDictionary *metadata;
/**
* Average framerate
*
* - demuxing: May be set by libavformat when creating the stream or in
* avformat_find_stream_info().
* - muxing: May be set by the caller before avformat_write_header().
*/
AVRational avg_frame_rate;
/**
* For streams with AV_DISPOSITION_ATTACHED_PIC disposition, this packet
* will contain the attached picture.
*
* decoding: set by libavformat, must not be modified by the caller.
* encoding: unused
*/
AVPacket attached_pic;
/**
* An array of side data that applies to the whole stream (i.e. the
* container does not allow it to change between packets).
*
* There may be no overlap between the side data in this array and side data
* in the packets. I.e. a given side data is either exported by the muxer
* (demuxing) / set by the caller (muxing) in this array, then it never
* appears in the packets, or the side data is exported / sent through
* the packets (always in the first packet where the value becomes known or
* changes), then it does not appear in this array.
*
* - demuxing: Set by libavformat when the stream is created.
* - muxing: May be set by the caller before avformat_write_header().
*
* Freed by libavformat in avformat_free_context().
*
* @see av_format_inject_global_side_data()
*/
AVPacketSideData *side_data;
/**
* The number of elements in the AVStream.side_data array.
*/
int nb_side_data;
/**
* Flags for the user to detect events happening on the stream. Flags must
* be cleared by the user once the event has been handled.
* A combination of AVSTREAM_EVENT_FLAG_*.
*/
int event_flags;
#define AVSTREAM_EVENT_FLAG_METADATA_UPDATED 0x0001 ///< The call resulted in updated metadata.
/*
* Codec parameters associated with this stream. Allocated and freed by
* libavformat in avformat_new_stream() and avformat_free_context()
* respectively.
*
* - demuxing: filled by libavformat on stream creation or in
* avformat_find_stream_info()
* - muxing: filled by the caller before avformat_write_header()
*/
AVCodecParameters *codecpar;
/*****************************************************************
* All fields below this line are not part of the public API. They
* may not be used outside of libavformat and can be changed and
* removed at will.
* New public fields should be added right above.
*****************************************************************
*/
/**
* Stream information used internally by av_find_stream_info()
*/
#define MAX_STD_TIMEBASES (30*12+30+3+6)
struct {
int64_t last_dts;
int64_t duration_gcd;
int duration_count;
int64_t rfps_duration_sum;
double (*duration_error)[2][MAX_STD_TIMEBASES];
int64_t codec_info_duration;
int64_t codec_info_duration_fields;
/**
* 0 -> decoder has not been searched for yet.
* >0 -> decoder found
* <0 -> decoder with codec_id == -found_decoder has not been found
*/
int found_decoder;
int64_t last_duration;
/**
* Those are used for average framerate estimation.
*/
int64_t fps_first_dts;
int fps_first_dts_idx;
int64_t fps_last_dts;
int fps_last_dts_idx;
} *info;
int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */
// Timestamp generation support:
/**
* Timestamp corresponding to the last dts sync point.
*
* Initialized when AVCodecParserContext.dts_sync_point >= 0 and
* a DTS is received from the underlying container. Otherwise set to
* AV_NOPTS_VALUE by default.
*/
int64_t first_dts;
int64_t cur_dts;
int64_t last_IP_pts;
int last_IP_duration;
/**
* Number of packets to buffer for codec probing
*/
int probe_packets;
/**
* Number of frames that have been demuxed during av_find_stream_info()
*/
int codec_info_nb_frames;
/* av_read_frame() support */
enum AVStreamParseType need_parsing;
struct AVCodecParserContext *parser;
/**
* last packet in packet_buffer for this stream when muxing.
*/
struct AVPacketList *last_in_packet_buffer;
AVProbeData probe_data;
#define MAX_REORDER_DELAY 16
int64_t pts_buffer[MAX_REORDER_DELAY+1];
AVIndexEntry *index_entries; /**< Only used if the format does not
support seeking natively. */
int nb_index_entries;
unsigned int index_entries_allocated_size;
/**
* Real base framerate of the stream.
* This is the lowest framerate with which all timestamps can be
* represented accurately (it is the least common multiple of all
* framerates in the stream). Note, this value is just a guess!
* For example, if the time base is 1/90000 and all frames have either
* approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1.
*
* Code outside avformat should access this field using:
* av_stream_get/set_r_frame_rate(stream)
*/
AVRational r_frame_rate;
/**
* Stream Identifier
* This is the MPEG-TS stream identifier +1
* 0 means unknown
*/
int stream_identifier;
int64_t interleaver_chunk_size;
int64_t interleaver_chunk_duration;
/**
* stream probing state
* -1 -> probing finished
* 0 -> no probing requested
* rest -> perform probing with request_probe being the minimum score to accept.
* NOT PART OF PUBLIC API
*/
int request_probe;
/**
* Indicates that everything up to the next keyframe
* should be discarded.
*/
int skip_to_keyframe;
/**
* Number of samples to skip at the start of the frame decoded from the next packet.
*/
int skip_samples;
/**
* If not 0, the number of samples that should be skipped from the start of
* the stream (the samples are removed from packets with pts==0, which also
* assumes negative timestamps do not happen).
* Intended for use with formats such as mp3 with ad-hoc gapless audio
* support.
*/
int64_t start_skip_samples;
/**
* If not 0, the first audio sample that should be discarded from the stream.
* This is broken by design (needs global sample count), but can't be
* avoided for broken by design formats such as mp3 with ad-hoc gapless
* audio support.
*/
int64_t first_discard_sample;
/**
* The sample after last sample that is intended to be discarded after
* first_discard_sample. Works on frame boundaries only. Used to prevent
* early EOF if the gapless info is broken (considered concatenated mp3s).
*/
int64_t last_discard_sample;
/**
* Number of internally decoded frames, used internally in libavformat, do not access
* its lifetime differs from info which is why it is not in that structure.
*/
int nb_decoded_frames;
/**
* Timestamp offset added to timestamps before muxing
* NOT PART OF PUBLIC API
*/
int64_t mux_ts_offset;
/**
* Internal data to check for wrapping of the time stamp
*/
int64_t pts_wrap_reference;
/**
* Options for behavior, when a wrap is detected.
*
* Defined by AV_PTS_WRAP_ values.
*
* If correction is enabled, there are two possibilities:
* If the first time stamp is near the wrap point, the wrap offset
* will be subtracted, which will create negative time stamps.
* Otherwise the offset will be added.
*/
int pts_wrap_behavior;
/**
* Internal data to prevent doing update_initial_durations() twice
*/
int update_initial_durations_done;
/**
* Internal data to generate dts from pts
*/
int64_t pts_reorder_error[MAX_REORDER_DELAY+1];
uint8_t pts_reorder_error_count[MAX_REORDER_DELAY+1];
/**
* Internal data to analyze DTS and detect faulty mpeg streams
*/
int64_t last_dts_for_order_check;
uint8_t dts_ordered;
uint8_t dts_misordered;
/**
* Internal data to inject global side data
*/
int inject_global_side_data;
/**
* String containing paris of key and values describing recommended encoder configuration.
* Paris are separated by ','.
* Keys are separated from values by '='.
*/
char *recommended_encoder_configuration;
/**
* display aspect ratio (0 if unknown)
* - encoding: unused
* - decoding: Set by libavformat to calculate sample_aspect_ratio internally
*/
AVRational display_aspect_ratio;
struct FFFrac *priv_pts;
/**
* An opaque field for libavformat internal usage.
* Must not be accessed in any way by callers.
*/
AVStreamInternal *internal;
} AVStream;
常见变量及其作用
int index; //在AVFormatContext中的索引,这个数字是自动生成的,可以通过这个数字从AVFormatContext::streams表中索引到该流。
int id;//流的标识,依赖于具体的容器格式。解码:由libavformat设置。编码:由用户设置,如果未设置则由libavformat替换。
AVCodecContext *codec;//指向该流对应的AVCodecContext结构,调用avformat_open_input时生成。
AVRational time_base;//这是表示帧时间戳的基本时间单位(以秒为单位)。该流中媒体数据的pts和dts都将以这个时间基准为粒度。
int64_t start_time;//流的起始时间,以流的时间基准为单位。如需设置,100%确保你设置它的值真的是第一帧的pts。
int64_t duration;//解码:流的持续时间。如果源文件未指定持续时间,但指定了比特率,则将根据比特率和文件大小估计该值。
int64_t nb_frames; //此流中的帧数(如果已知)或0。
enum AVDiscard discard;//选择哪些数据包可以随意丢弃,不需要去demux。
AVRational sample_aspect_ratio;//样本长宽比(如果未知,则为0)。
AVDictionary *metadata;//元数据信息。
AVRational avg_frame_rate;//平均帧速率。解封装:可以在创建流时设置为libavformat,也可以在avformat_find_stream_info()中设置。封装:可以由调用者在avformat_write_header()之前设置。
AVPacket attached_pic;//附带的图片。比如说一些MP3,AAC音频文件附带的专辑封面。
int probe_packets;//编解码器用于probe的包的个数。
int codec_info_nb_frames;//在av_find_stream_info()期间已经解封装的帧数。
int request_probe;//流探测状态,1表示探测完成,0表示没有探测请求,rest 执行探测。
int skip_to_keyframe;//表示应丢弃直到下一个关键帧的所有内容。
int skip_samples;//在从下一个数据包解码的帧开始时要跳过的采样数。
int64_t start_skip_samples;//如果不是0,则应该从流的开始跳过的采样的数目。
int64_t first_discard_sample;//如果不是0,则应该从流中丢弃第一个音频样本。
int64_t pts_reorder_error[MAX_REORDER_DELAY+1];
uint8_t pts_reorder_error_count[MAX_REORDER_DELAY+1];//内部数据,从pts生成dts。
int64_t last_dts_for_order_check;
uint8_t dts_ordered;
uint8_t dts_misordered;//内部数据,用于分析dts和检测故障mpeg流。
AVRational display_aspect_ratio;//显示宽高比。
AVCodecParameters 结构体:封装编解码相关的参数
在解封装时调用AVFormat_find_stream_info函数中对AVStream的填充,AVStream结构体的AVCodecParameters *codecpar
/*
* Codec parameters associated with this stream. Allocated and freed by
* libavformat in avformat_new_stream() and avformat_free_context()
* respectively.
*
* - demuxing: filled by libavformat on stream creation or in
* avformat_find_stream_info()
* - muxing: filled by the caller before avformat_write_header()
*/
AVCodecParameters *codecpar;
相关操作函数:
/**
* Allocate a new AVCodecParameters and set its fields to default values
* (unknown/invalid/0). The returned struct must be freed with
* avcodec_parameters_free().
*/
AVCodecParameters *avcodec_parameters_alloc(void);
/**
* Free an AVCodecParameters instance and everything associated with it and
* write NULL to the supplied pointer.
*/
void avcodec_parameters_free(AVCodecParameters **par);
/**
* Copy the contents of src to dst. Any allocated fields in dst are freed and
* replaced with newly allocated duplicates of the corresponding fields in src.
*
* @return >= 0 on success, a negative AVERROR code on failure.
*/
int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src);
typedef struct AVCodecParameters {
/**
* General type of the encoded data.
*/
enum AVMediaType codec_type;
/**
* Specific type of the encoded data (the codec used).
*/
enum AVCodecID codec_id;
/**
* Additional information about the codec (corresponds to the AVI FOURCC).
*/
uint32_t codec_tag;
/**
* Extra binary data needed for initializing the decoder, codec-dependent.
*
* Must be allocated with av_malloc() and will be freed by
* avcodec_parameters_free(). The allocated size of extradata must be at
* least extradata_size + AV_INPUT_BUFFER_PADDING_SIZE, with the padding
* bytes zeroed.
*/
uint8_t *extradata;
/**
* Size of the extradata content in bytes.
*/
int extradata_size;
/**
* - video: the pixel format, the value corresponds to enum AVPixelFormat.
* - audio: the sample format, the value corresponds to enum AVSampleFormat.
*/
int format;
/**
* The average bitrate of the encoded data (in bits per second).
*/
int64_t bit_rate;
/**
* The number of bits per sample in the codedwords.
*
* This is basically the bitrate per sample. It is mandatory for a bunch of
* formats to actually decode them. It's the number of bits for one sample in
* the actual coded bitstream.
*
* This could be for example 4 for ADPCM
* For PCM formats this matches bits_per_raw_sample
* Can be 0
*/
int bits_per_coded_sample;
/**
* This is the number of valid bits in each output sample. If the
* sample format has more bits, the least significant bits are additional
* padding bits, which are always 0. Use right shifts to reduce the sample
* to its actual size. For example, audio formats with 24 bit samples will
* have bits_per_raw_sample set to 24, and format set to AV_SAMPLE_FMT_S32.
* To get the original sample use "(int32_t)sample >> 8"."
*
* For ADPCM this might be 12 or 16 or similar
* Can be 0
*/
int bits_per_raw_sample;
/**
* Codec-specific bitstream restrictions that the stream conforms to.
*/
int profile;
int level;
/**
* Video only. The dimensions of the video frame in pixels.
*/
int width;
int height;
/**
* Video only. The aspect ratio (width / height) which a single pixel
* should have when displayed.
*
* When the aspect ratio is unknown / undefined, the numerator should be
* set to 0 (the denominator may have any value).
*/
AVRational sample_aspect_ratio;
/**
* Video only. The order of the fields in interlaced video.
*/
enum AVFieldOrder field_order;
/**
* Video only. Additional colorspace characteristics.
*/
enum AVColorRange color_range;
enum AVColorPrimaries color_primaries;
enum AVColorTransferCharacteristic color_trc;
enum AVColorSpace color_space;
enum AVChromaLocation chroma_location;
/**
* Video only. Number of delayed frames.
*/
int video_delay;
/**
* Audio only. The channel layout bitmask. May be 0 if the channel layout is
* unknown or unspecified, otherwise the number of bits set must be equal to
* the channels field.
*/
uint64_t channel_layout;
/**
* Audio only. The number of audio channels.
*/
int channels;
/**
* Audio only. The number of audio samples per second.
*/
int sample_rate;
/**
* Audio only. The number of bytes per coded audio frame, required by some
* formats.
*
* Corresponds to nBlockAlign in WAVEFORMATEX.
*/
int block_align;
/**
* Audio only. Audio frame size, if known. Required by some formats to be static.
*/
int frame_size;
/**
* Audio only. The amount of padding (in samples) inserted by the encoder at
* the beginning of the audio. I.e. this number of leading decoded samples
* must be discarded by the caller to get the original audio without leading
* padding.
*/
int initial_padding;
/**
* Audio only. The amount of padding (in samples) appended by the encoder to
* the end of the audio. I.e. this number of decoded samples must be
* discarded by the caller from the end of the stream to get the original
* audio without any trailing padding.
*/
int trailing_padding;
/**
* Audio only. Number of samples to skip after a discontinuity.
*/
int seek_preroll;
} AVCodecParameters;
AVCodecContext结构体
描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息,位于avcodec.h文件中
enum AVMediaType codec_type; //编解码器的类型(视频,音频...)。
const struct AVCodec *codec; //采用的解码器AVCodec(H.264,MPEG2...)。
int64_t bit_rate;//平均比特率。
uint8_t *extradata;//针对特定编码器包含的附加信息(例如对于H.264解码器来说,存储SPS,PPS等)。
int extradata_size;
AVRational time_base;//时间的基准单位,根据该参数,可以把PTS转化为实际的时间(单位为秒s)。
int delay;//编码:从编码器输入到解码器输出的帧延迟数。解码:除了规范中规定的标准解码器外产生的帧延迟数。
int width, height;//代表宽和高(仅视频)。
int refs;//运动估计参考帧的个数(H.264的话会有多帧,MPEG2这类的一般就没有了)。
int sample_rate; //采样率(仅音频)。
int channels; //声道数(仅音频)。
enum AVSampleFormat sample_fmt; //音频采样格式,编码:由用户设置。解码:由libavcodec设置。
int frame_size;//音频帧中每个声道的采样数。编码:由libavcodec在avcodec_open2()中设置。 解码:可以由一些解码器设置以指示恒定的帧大小.
int frame_number;//帧计数器,由libavcodec设置。解码:从解码器返回的帧的总数。编码:到目前为止传递给编码器的帧的总数。
uint64_t channel_layout;//音频声道布局。编码:由用户设置。解码:由用户设置,可能被libavcodec覆盖。
enum AVAudioServiceType audio_service_type;//音频流传输的服务类型。编码:由用户设置。解码:由libavcodec设置。
AVPacket 结构体,根据AVPacket中AVStream的编号判断此包视音频分类
- AVPacketAVPacket用于存储压缩数据,位于avcodec.h文件中。
通常由demuxer导出,然后作为输入传递给解码器;或者作为编码器的输出,然后传递给muxer。对于视频,它通常应包含一个压缩帧。对于音频,它可能包含几个压缩帧,位于avcodec.h文件中。允许编码器输出空分组,没有压缩数据,仅包含辅助数据 - AVPacket必须由av_packet_alloc分配内存,同时必须由av_packet_free()释放。
- AVPacket分配内存后能够被多次用来存储不同的数据。av_packet_unref释放任何持帧的引用,并结构体还原到未被使用的状态
结构体定义
typedef struct AVPacket {
/**
* A reference to the reference-counted buffer where the packet data is
* stored.
* May be NULL, then the packet data is not reference-counted.
*/
AVBufferRef *buf;
/**
* Presentation timestamp in AVStream->time_base units; the time at which
* the decompressed packet will be presented to the user.
* Can be AV_NOPTS_VALUE if it is not stored in the file.
* pts MUST be larger or equal to dts as presentation cannot happen before
* decompression, unless one wants to view hex dumps. Some formats misuse
* the terms dts and pts/cts to mean something different. Such timestamps
* must be converted to true pts/dts before they are stored in AVPacket.
*/
int64_t pts;
/**
* Decompression timestamp in AVStream->time_base units; the time at which
* the packet is decompressed.
* Can be AV_NOPTS_VALUE if it is not stored in the file.
*/
int64_t dts;
uint8_t *data;
int size;
int stream_index;
/**
* A combination of AV_PKT_FLAG values
*/
int flags;
/**
* Additional packet data that can be provided by the container.
* Packet can contain several types of side information.
*/
AVPacketSideData *side_data;
int side_data_elems;
/**
* Duration of this packet in AVStream->time_base units, 0 if unknown.
* Equals next_pts - this_pts in presentation order.
*/
int64_t duration;
int64_t pos; ///< byte position in stream, -1 if unknown
#if FF_API_CONVERGENCE_DURATION
/**
* @deprecated Same as the duration field, but as int64_t. This was required
* for Matroska subtitles, whose duration values could overflow when the
* duration field was still an int.
*/
attribute_deprecated
int64_t convergence_duration;
#endif
} AVPacket;
AVBufferRef *buf; //缓冲包数据存储位置。
int64_t pts; //显示时间戳,以AVStream->time_base为单位。
int64_t dts; //解码时间戳,以AVStream->time_base为单位。
uint8_t *data; //压缩编码的数据。
int size;//data的大小。
int stream_index;//给出所属媒体流的索引。
int flags;//标识域,其中,最低位置1表示该数据是一个关键帧。
AVPacketSideData *side_data;//容器提供的额外数据包。
int64_t duration;//Packet持续的时间,以AVStream->time_base为单位。
int64_t pos;//表示该数据在媒体流中的字节偏移量。
AVFrame 结构体
- AVFrame用来存储解码后的(或原始)音频或视频数据,位于avcodec.h文件中。
- AVFrame必须由av_frame_alloc()分配内存,同时必须由av_frame_free()释放。
- AVFrame分配内存后能够被多次用来存储不同的数据(例如:decoder解码后的帧)。av_frame_unref释放任何持帧的引用,并结构体还原到未被使用的状态。参考:ffmpeg AVFrame结构体及其相关函数
常见变量及其作用
uint8_t * data [AV_NUM_DATA_POINTERS];//解码后原始数据(对视频来说是YUV,RGB,对音频来说是PCM)。
int linesize[AV_NUM_DATA_POINTERS];//在视频中,表示图片一行数据的大小。
uint8_t **extended_data;//指向数据平面/通道。
int width, height;//一个视频帧的宽度和高度。
int nb_samples;//这个AVFrame中的每个音频通道中包含的音频帧个数,如设置1024
int format;//表示解码后的数据类型或格式,-1表示未被设置或不能识别的类型。
int key_frame;//是否为关键帧。
enum AVPictureType pict_type;//帧的类型。
AVRational sample_aspect_ratio;//视频帧的宽高比,0表示未知。
int64_t pts;//显示时间戳,表示该什么时候被显示。
int64_t pkt_dts;//从AVPacket中拷贝的值。
int coded_picture_number;//编码帧序号。
int display_picture_number;//显示帧需要。
void *opaque;//用户私有信息。
int repeat_pict;//解码时,每帧图片延迟的时间,extra_delay = repeat_pict / (2*fps)。
int interlaced_frame;//是否是隔行扫描
int sample_rate;//音频的采样率。
uint64_t channel_layout;//音频的布局方式。
/**
* MPEG vs JPEG YUV range.
* - encoding: Set by user
* - decoding: Set by libavcodec
*/
enum AVColorRange color_range;
enum AVColorPrimaries color_primaries;
enum AVColorTransferCharacteristic color_trc;
/**
* YUV colorspace type.
* - encoding: Set by user
* - decoding: Set by libavcodec
*/
enum AVColorSpace colorspace;
enum AVChromaLocation chroma_location;
/**
* frame timestamp estimated using various heuristics, in stream time base
* - encoding: unused
* - decoding: set by libavcodec, read by user.
*/
int64_t best_effort_timestamp;
/**
* reordered pos from the last AVPacket that has been input into the decoder
* - encoding: unused
* - decoding: Read by user.
*/
int64_t pkt_pos;
/**
* duration of the corresponding packet, expressed in
* AVStream->time_base units, 0 if unknown.
* - encoding: unused
* - decoding: Read by user.
*/
int64_t pkt_duration;
/**
* metadata.
* - encoding: Set by user.
* - decoding: Set by libavcodec.
*/
AVDictionary *metadata;
/**
* decode error flags of the frame, set to a combination of
* FF_DECODE_ERROR_xxx flags if the decoder produced a frame, but there
* were errors during the decoding.
* - encoding: unused
* - decoding: set by libavcodec, read by user.
*/
int decode_error_flags;
/**
* number of audio channels, only used for audio.
* - encoding: unused
* - decoding: Read by user.
*/
int channels;//音频通道个数
/**
* size of the corresponding packet containing the compressed
* frame.
* It is set to a negative value if unknown.
* - encoding: unused
* - decoding: set by libavcodec, read by user.
*/
int pkt_size;
int8_t *qscale_table;
int qstride;
int qscale_type;
AVBufferRef *qp_table_buf;
/**
* For hwaccel-format frames, this should be a reference to the
* AVHWFramesContext describing the frame.
*/
AVBufferRef *hw_frames_ctx;
/**
* AVBufferRef for free use by the API user. FFmpeg will never check the
* contents of the buffer ref. FFmpeg calls av_buffer_unref() on it when
* the frame is unreferenced. av_frame_copy_props() calls create a new
* reference with av_buffer_ref() for the target frame's opaque_ref field.
*
* This is unrelated to the opaque field, although it serves a similar
* purpose.
*/
AVBufferRef *opaque_ref;
AVPicture 结构体
可见此结构结构体为AVFrame的属性抽取,被废弃建议使用AVFrame or imgutils代替
/**
* Picture data structure.
*
* Up to four components can be stored into it, the last component is
* alpha.
* @deprecated use AVFrame or imgutils functions instead
*/
typedef struct AVPicture {
attribute_deprecated
uint8_t *data[AV_NUM_DATA_POINTERS]; ///< pointers to the image data planes
attribute_deprecated
int linesize[AV_NUM_DATA_POINTERS]; ///< number of bytes per line
} AVPicture;
AVClass
FFmpeg源代码简单分析:结构体成员管理系统-AVClass:AVClass最主要的作用就是给结构体(例如AVFormatContext等)增加AVOption功能的支持。换句话说AVClass就是AVOption和目标结构体之间的“桥梁”。AVClass要求必须声明为目标结构体的第一个变量。
AVOption 结构体 设置变量的值
使用AVOption之后,传递两个字符串(一个是变量的名称,一个是变量的值)就可以改变系统中变量的值
除了可以对FFmpeg常用结构体AVFormatContext,AVCodecContext等进行赋值之外,还可以对它们的私有数据priv_data进行赋值。这个字段里通常存储了各种编码器特有的结构体。而这些结构体的定义在FFmpeg的SDK中是找不到的。例如使用libx264进行编码的时候,通过AVCodecContext的priv_data字段可以对X264Context结构体中的变量进行赋值,设置preset,profile等。使用libx265进行编码的时候,通过AVCodecContext的priv_data字段可以对libx265Context结构体中的变量进行赋值,设置preset,tune等
/**
* AVOption
*/
typedef struct AVOption {
const char *name; //名称
/**
* short English help text
* @todo What about other languages?
*/
const char *help; //简短的帮助
/**
* The offset relative to the context structure where the option
* value is stored. It should be 0 for named constants.
*/
int offset; //选项相对结构体首部地址的偏移量(这个很重要)
enum AVOptionType type; //选项的类型
/**
* the default value for scalar options //选项的默认值
*/
union {
int64_t i64;
double dbl;
const char *str;
/* TODO those are unused now */
AVRational q;
} default_val;
double min; ///< 选项的最小值
double max; ///< 选项的最大值
int flags; //一些标记
#define AV_OPT_FLAG_ENCODING_PARAM 1 ///< a generic parameter which can be set by the user for muxing or encoding
#define AV_OPT_FLAG_DECODING_PARAM 2 ///< a generic parameter which can be set by the user for demuxing or decoding
#if FF_API_OPT_TYPE_METADATA
#define AV_OPT_FLAG_METADATA 4 ///< some data extracted or inserted into the file like title, comment, ...
#endif
#define AV_OPT_FLAG_AUDIO_PARAM 8
#define AV_OPT_FLAG_VIDEO_PARAM 16
#define AV_OPT_FLAG_SUBTITLE_PARAM 32
/**
* The option is inteded for exporting values to the caller.
*/
#define AV_OPT_FLAG_EXPORT 64
/**
* The option may not be set through the AVOptions API, only read.
* This flag only makes sense when AV_OPT_FLAG_EXPORT is also set.
*/
#define AV_OPT_FLAG_READONLY 128
#define AV_OPT_FLAG_FILTERING_PARAM (1<<16) ///< a generic parameter which can be set by the user for filtering
//FIXME think about enc-audio, ... style flags
/**
* The logical unit to which the option belongs. Non-constant
* options and corresponding named constants share the same
* unit. May be NULL.
*/
const char *unit; //该选项所属的逻辑单元,可以为空
} AVOption;
AVOptionType是一个枚举类型
enum AVOptionType{
AV_OPT_TYPE_FLAGS,
AV_OPT_TYPE_INT,
AV_OPT_TYPE_INT64,
AV_OPT_TYPE_DOUBLE,
AV_OPT_TYPE_FLOAT,
AV_OPT_TYPE_STRING,
AV_OPT_TYPE_RATIONAL,
AV_OPT_TYPE_BINARY, ///< offset must point to a pointer immediately followed by an int for the length
AV_OPT_TYPE_DICT,
AV_OPT_TYPE_CONST = 128,
AV_OPT_TYPE_IMAGE_SIZE = MKBETAG('S','I','Z','E'), ///< offset must point to two consecutive integers
AV_OPT_TYPE_PIXEL_FMT = MKBETAG('P','F','M','T'),
AV_OPT_TYPE_SAMPLE_FMT = MKBETAG('S','F','M','T'),
AV_OPT_TYPE_VIDEO_RATE = MKBETAG('V','R','A','T'), ///< offset must point to AVRational
AV_OPT_TYPE_DURATION = MKBETAG('D','U','R',' '),
AV_OPT_TYPE_COLOR = MKBETAG('C','O','L','R'),
AV_OPT_TYPE_CHANNEL_LAYOUT = MKBETAG('C','H','L','A'),
#if FF_API_OLD_AVOPTIONS
FF_OPT_TYPE_FLAGS = 0,
FF_OPT_TYPE_INT,
FF_OPT_TYPE_INT64,
FF_OPT_TYPE_DOUBLE,
FF_OPT_TYPE_FLOAT,
FF_OPT_TYPE_STRING,
FF_OPT_TYPE_RATIONAL,
FF_OPT_TYPE_BINARY, ///< offset must point to a pointer immediately followed by an int for the length
FF_OPT_TYPE_CONST=128,
#endif
};
解码
avformat_open_input() 打开输入媒体
初始化所有与媒体读写有关的结构们,例如AVIOContext,AVInputFormat等等。分析init_input函数,找出AVIOContext的初始化过程
参考:ffmpeg框架阅读笔记二 : 寻找AVIOContext初始化过程,自定义初始化。
/*
* 此函数只是读取文件头,并不会填充流信息,因此我们需要接下来调用avformat_find_stream_info获取文件中的流信息
* avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options)打开视频文件
* url:文件名或者网络地址
* AVInputFormat:文件格式描述
* 通常简单的指明NULL告诉libavformat去自动探测文件格式
* AVDictionary:格式参数
* 返回0 on success, a negative AVERROR on failure
* */
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
//参数ps包含一切媒体相关的上下文结构,有它就有了一切,本函数如果打开媒体成功,
//会返回一个AVFormatContext的实例.
//参数filename是媒体文件名或URL.
//参数fmt是要打开的媒体格式的操作结构,因为是读,所以是inputFormat.此处可以
//传入一个调用者定义的inputFormat,对应命令行中的 -f xxx段,如果指定了它,
//在打开文件中就不会探测文件的实际格式了,以它为准了.
//参数options是对某种格式的一些操作,是为了在命令行中可以对不同的格式传入
//特殊的操作参数而建的, 为了了解流程,完全可以无视它.
int avformat_open_input(AVFormatContext **ps,
const char *filename,
AVInputFormat *fmt,
AVDictionary **options)
{
AVFormatContext *s = *ps;
int ret = 0;
AVFormatParameters ap = { { 0 } };
AVDictionary *tmp = NULL;
//创建上下文结构
if (!s && !(s = avformat_alloc_context())) //可以在函数外创建
return AVERROR(ENOMEM);
//如果用户指定了输入格式,直接使用它
if (fmt)
s->iformat = fmt; //输入格式可通过AVProbeData(包含文件名/buffer)传入av_probe_input_format函数来获得。
//忽略
if (options)
av_dict_copy(&tmp, *options, 0);
if ((ret = av_opt_set_dict(s, &tmp)) < 0)
goto fail;
//打开输入媒体(如果需要的话),初始化所有与媒体读写有关的结构们,比如
//AVIOContext,AVInputFormat等等
if ((ret = init_input(s, filename)) < 0) //后面分析该函数。
goto fail;
//执行完此函数后,s->pb和s->iformat都已经指向了有效实例.pb是用于读写数据的,它
//把媒体数据当做流来读写,不管是什么媒体格式,而iformat把pb读出来的流按某种媒体格
//式进行分析,也就是说pb在底层,iformat在上层.
//很多静态图像文件格式,都被当作一个格式处理,比如要打开.jpeg文件,需要的格式
//名为image2.此处还不是很了解具体细节,作不得准哦.
/* check filename in case an image number is expected */
if (s->iformat->flags & AVFMT_NEEDNUMBER) {
if (!av_filename_number_test(filename)) {
ret = AVERROR(EINVAL);
goto fail;
}
}
s->duration = s->start_time = AV_NOPTS_VALUE;
//上下文中保存下文件名
av_strlcpy(s->filename, filename, sizeof(s->filename));
/* allocate private data */
//为当前格式分配私有数据,主要用于某格式的读写操作时所用的私有结构.
//此结构的大小在定义AVInputFormat时已指定了.
if (s->iformat->priv_data_size > 0) {
if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
ret = AVERROR(ENOMEM);
goto fail;
}
//这个可以先不必管它
if (s->iformat->priv_class) {
*(const AVClass**) s->priv_data = s->iformat->priv_class;
av_opt_set_defaults(s->priv_data);
if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
goto fail;
}
}
/* e.g. AVFMT_NOFILE formats will not have a AVIOContext */
//从mp3文件中读ID3数据并保存之.
if (s->pb)
ff_id3v2_read(s, ID3v2_DEFAULT_MAGIC); //ID3v2是某些MP3文件的标签帧,包含歌曲的某些信息。是ID3v1(尾部128byte)的扩展。
//读一下媒体的头部,在read_header()中主要是做某种格式的初始化工作,比如填充自己的
//私有结构,根据流的数量分配流结构并初始化,把文件指针指向数据区开始处等.
//当mp3文件时,调用ff_mp3_demuxer的read_header() 。在里面检测媒体信息,如果没有则说明没有id3v2头,跳转到尾部,去读id3v1信息。
if (!(s->flags & AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
if ((ret = s->iformat->read_header(s, &ap)) < 0)//内部使用一系列函数,分析mp3头部信息
goto fail;
//保存数据区开始的位置
if (!(s->flags & AVFMT_FLAG_PRIV_OPT) && s->pb && !s->data_offset)
s->data_offset = avio_tell(s->pb);
s->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
if (options) {
av_dict_free(options);
*options = tmp;
}
*ps = s;
//执行成功
return 0;
//执行失败
fail: av_dict_free(&tmp);
if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
avio_close(s->pb);
avformat_free_context(s);
*ps = NULL;
return ret;
}
在init_input()函数中调用avio_open()函数
//打开输入媒体并填充其AVInputFormat结构
static int init_input(AVFormatContext *s, const char *filename)
{
int ret;
AVProbeData pd = { filename, NULL, 0 };
//当调用者已指定了pb(数据取得的方式)--一般不会这样.除非自定义AVIOContext,不使用file或者pipe
if (s->pb) {
s->flags |= AVFMT_FLAG_CUSTOM_IO; //自定义输入源,设置自定义标志
if (!s->iformat)
//如果已指定了pb但没指定iformat,以pb读取媒体数据进行探测,取得.取得iformat.
return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0);
else if (s->iformat->flags & AVFMT_NOFILE)
//如果已指定pb也指定了iformat,但是又指定了不需要文件(也包括URL指定的地址),这就矛盾了,
//此时应是不需要pb的,因为不需操作文件,提示一下吧,也不算错.
av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "
"will be ignored with AVFMT_NOFILE format.
");
return 0;
}
//一般会执行到这里
if ((s->iformat && s->iformat->flags & AVFMT_NOFILE)
|| (!s->iformat && (s->iformat = av_probe_input_format(&pd, 0))))//根据AVProbeData的内容来获取格式。可以根据文件名或者
//buffer数据。内部首先匹配demuxer的AVInputformat全局变量,然后调用其read_probe成员。该函数在对应格式中初始化,读格式信息
//如果已指定了iformat并且不需要文件,也就不需要pb了,可以直接返回
//如果没指定iformat,但是可以从文件名中猜出iformat,也成功.
return 0;
//如果从文件名中也猜不出媒体格式,则只能打开这个文件进行探测了,先打开文件
if ((ret = avio_open(&s->pb, filename, AVIO_FLAG_READ)) < 0)
return ret;
if (s->iformat)
return 0;
//再探测之
return av_probe_input_buffer(s->pb, &s->iformat, filename, s, 0, 0);
}
avformat_close_input() 关闭释放AVFormatContext并置为NULL
/**
* Close an opened input AVFormatContext. Free it and all its contents
* and set *s to NULL.
*/
void avformat_close_input(AVFormatContext **s);
avformat_flush() 丢弃所有内部缓冲数据
/**
* 通常只适用于可以重新同步的格式。这包括无头格式,如MPEG-TS/TS,但也应该与NUT、Ogg和有限的AVI一起使用
*
* 调用此函数时,流集合、检测到的持续时间、流参数和编解码器不会更改。
* 如果想要完全重置,最好打开一个新的AVFormatContext
*
* This does not flush the AVIOContext (s->pb). If necessary, call
* avio_flush(s->pb) before calling this function.
*
* @param s media file handle
* @return >=0 on success, error code otherwise
*/
int avformat_flush(AVFormatContext *s);
avformat_find_stream_info() 填充AVFormatContext的AVStream
该函数主要用于给每个媒体流(音频/视频)的AVStream结构体赋值。浏览一下这个函数的代码,会发现它其实已经实现了解码器的查找,解码器的打开,视音频帧的读取,视音频帧的解码等工作。换句话说,该函数实际上已经“走通”的解码的整个流程。下面看一下除了成员变量赋值之外,该函数的几个关键流程。
1.查找解码器:find_decoder()
2.打开解码器:avcodec_open2()
3.读取完整的一帧压缩编码的数据:read_frame_internal()
注:av_read_frame()内部实际上就是调用的read_frame_internal()。
4.解码一些压缩编码数据:try_decode_frame()
/**
* Read packets of a media file to get stream information. This
* is useful for file formats with no headers such as MPEG. This
* function also computes the real framerate in case of MPEG-2 repeat
* frame mode.
* The logical file position is not changed by this function;
* examined packets may be buffered for later processing.
*
* @param ic media file handle
* @param options If non-NULL, an ic.nb_streams long array of pointers to
* dictionaries, where i-th member contains options for
* codec corresponding to i-th stream.
* On return each dictionary will be filled with options that were not found.
* @return >=0 if OK, AVERROR_xxx on error
*
* @note this function isn't guaranteed to open all the codecs, so
* options being non-empty at return is a perfectly normal behavior.
*
* @todo Let the user decide somehow what information is needed so that
* we do not waste time getting stuff the user does not need.
*/
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
av_dump_format() 输出文件的信息
/*输出文件的信息,也就是我们在使用ffmpeg时能看到的文件详细信息
* av_dump_format()是一个手工调试的函数,能使我们看到m_avFormatContext->streams里面有什么内容。
*打印输入或者输出格式的信息,例如视频总时长,波特率,流,容器,程序,元数据,边数据,编码器,时间戳
*一般接下来我们使用av_find_stream_info()函数,它的作用是为m_avFormatContext->streams填充上正确的信息
* 第二个参数:-1表示自己选择,显示流编号,随便填
* 第三个参数:url
* 最后一个参数用于指定dump的是不是输出文件,我们dump的是输入文件,因此一定要是0
* */
void av_dump_format(AVFormatContext *ic,int index,const char *url,int is_output);
av_find_best_stream() 获取音视频及字幕的stream_index
enum AVMediaType {
AVMEDIA_TYPE_UNKNOWN = -1, ///< Usually treated as AVMEDIA_TYPE_DATA
AVMEDIA_TYPE_VIDEO,
AVMEDIA_TYPE_AUDIO,
AVMEDIA_TYPE_DATA, ///< Opaque data information usually continuous
AVMEDIA_TYPE_SUBTITLE,
AVMEDIA_TYPE_ATTACHMENT, ///< Opaque data information usually sparse
AVMEDIA_TYPE_NB
};
/**
* Find the "best" stream in the file.
* The best stream is determined according to various heuristics as the most
* likely to be what the user expects.
* 如果decoder参数为非空,av_find_best_stream将查找流的编解码器的默认解码器;找不到解码器的流将被忽略
*
* @param ic media file handle
* @param type 流类型:视频、音频、字幕等.
* @param wanted_stream_nb 请求的流编号,或-1表示自动选择
* @param related_stream 尝试查找与流相关的流(例如在同一个程序)到这个,或者-1如果没有
* @param decoder_ret if non-NULL,如果非空,则返回所选流的解码器
* @param flags flags; none are currently defined
* @return 如果成功,则返回非负的流数;如果找不到具有请求类型的流,则返回AVERROR_STREAM_NOT_FOUND;
* 如果找到流但没有解码器,则返回AVERROR_DECODER_NOT_FOUND
* @note 如果av_find_best_stream成功返回,且decoder_ret不为空,则*decoder_ret保证设置为有效的AVCodec
*/
int av_find_best_stream(AVFormatContext *ic,
enum AVMediaType type,
int wanted_stream_nb,
int related_stream,
AVCodec **decoder_ret,
int flags);
另一种方式,遍历
for (i = 0; i < ifmt_ctx->nb_streams; i++)
{
//Create output AVStream according to input AVStream
AVFormatContext *ofmt_ctx;
AVStream *in_stream = ifmt_ctx->streams[i];
AVStream *out_stream = NULL;
if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoindex = i;
out_stream = avformat_new_stream(ofmt_ctx_v, in_stream->codec->codec);
ofmt_ctx = ofmt_ctx_v;
}
else if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
audioindex = i;
out_stream = avformat_new_stream(ofmt_ctx_a, in_stream->codec->codec);
ofmt_ctx = ofmt_ctx_a;
}
else
{
break;
}
}
av_read_frame() 解封装获取AVPacket
av_read_frame()声明位于libavformatavformat.h,其作用是读取码流中的音频若干帧或者视频一帧。例如,解码视频的时候,每解码一个视频帧,需要先调用 av_read_frame()获得一帧视频的压缩数据,然后才能对该数据进行解码。
/**
* Return the next frame of a stream.
* This function returns what is stored in the file, and does not validate
* that what is there are valid frames for the decoder. It will split what is
* stored in the file into frames and return one for each call. It will not
* omit invalid data between valid frames so as to give the decoder the maximum
* information possible for decoding.
*
* If pkt->buf is NULL, then the packet is valid until the next
* av_read_frame() or until avformat_close_input(). Otherwise the packet
* is valid indefinitely. In both cases the packet must be freed with
* av_free_packet when it is no longer needed. For video, the packet contains
* exactly one frame. For audio, it contains an integer number of frames if each
* frame has a known fixed size (e.g. PCM or ADPCM data). If the audio frames
* have a variable size (e.g. MPEG audio), then it contains one frame.
*
* pkt->pts, pkt->dts and pkt->duration are always set to correct
* values in AVStream.time_base units (and guessed if the format cannot
* provide them). pkt->pts can be AV_NOPTS_VALUE if the video format
* has B-frames, so it is better to rely on pkt->dts if you do not
* decompress the payload.
*
* @return 0 if OK, < 0 on error or end of file
*/
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
av_seek_frame() 流位置跳转
流指定时使用AVStream的时间基,未指定使用AV_TIME_BASE时间基
/**
* Seek to the keyframe at timestamp.
* 'timestamp' in 'stream_index'.
*
* @param s media file handle
* @param 如果 stream_index为 (-1), 选择默认的流进行跳转(默认视频),
* timestamp 自动转换,将AV_TIME_BASE 转换成特定流的time_base.
* @param timestamp 为AVStream的时间基 , 流不确定时stream_index为 (-1), 使用 AV_TIME_BASE.
* @param flags flags which select direction and seeking mode
#define AVSEEK_FLAG_BACKWARD 1 ///< seek backward:请求的时间戳之前查找帧
#define AVSEEK_FLAG_BYTE 2 ///< seeking :基于字节位置的查找
#define AVSEEK_FLAG_ANY 4 ///< seek到任意帧,注意不一定是关键帧,因此使用时可能会导致花屏
#define AVSEEK_FLAG_FRAME 8 ///< seeking :基于帧数量快进
* @return >= 0 on success
*/
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,
int flags);
avcodec_flush_buffers() 重置解码器buffer
/**
* Reset the internal decoder state / flush internal buffers. Should be called
* e.g. when seeking or when switching to a different stream.
*
* @note when refcounted frames are not used (i.e. avctx->refcounted_frames is 0),
* this invalidates the frames previously returned from the decoder. When
* refcounted frames are used, the decoder just releases any references it might
* keep internally, but the caller's reference remains valid.
*/
avcodec_flush_buffers(m_pVCodecCxt);
avcodec_find_decoder() 找到解码器
/**
* Find a registered decoder with a matching codec ID.
*
* @param id AVCodecID of the requested decoder
* @return A decoder if one was found, NULL otherwise.
*/
AVCodec *avcodec_find_decoder(enum AVCodecID id);
avcodec_alloc_context3()生成解码器上下文
/** 生成解码器上下文
* Allocate an AVCodecContext and set its fields to default values. The
* resulting struct should be freed with avcodec_free_context().
*
* @param codec if non-NULL, allocate private data and initialize defaults
* for the given codec. It is illegal to then call avcodec_open2()
* with a different codec.
* codec非空则默认初始化并且avcodec_open2()要使用同一个codec对象
* If NULL, then the codec-specific defaults won't be initialized,
* which may result in suboptimal default settings (this is
* important mainly for encoders, e.g. libx264).
* codec为空则不会被默认初始化可能会导致不是最优的设置
*
* @return An AVCodecContext filled with default values or NULL on failure.
*
* AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
*/
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);
avcodec_parameters_to_context() 编解码器中的值填充编解码器上下文参数
/**
* 编解码器中任何在par中具有对应字段的已分配字段都将被释放并替换为par中相应字段的副本。
* 编解码器中的字段在par中没有对应的字段不会被触摸
*
* @return >= 0 on success, a negative AVERROR code on failure.
*/
int avcodec_parameters_to_context(AVCodecContext *codec,
const AVCodecParameters *par);
avcodec_open2() 打开解码器
avcodec_open2()的定义位于libavcodecutils.c
/*
*
* 打开解码器,该函数用于初始化一个视音频编解码器的AVCodecContext
*(1)为各种结构体分配内存(通过各种av_malloc()实现)。
*(2)将输入的AVDictionary形式的选项设置到AVCodecContext。
*(3)其他一些零零碎碎的检查,比如说检查编解码器是否处于“实验”阶段。
*(4)如果是编码器,检查输入参数是否符合编码器的要求
*(5)调用AVCodec的init()初始化具体的解码器。
* * @param codec if non-NULL, allocate private data and initialize defaults
* for the given codec. It is illegal to then call avcodec_open2()
* with a different codec.
* If NULL, then the codec-specific defaults won't be initialized,
* which may result in suboptimal default settings (this is
* important mainly for encoders, e.g. libx264).
* */
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
//检查输入参数是否符合【编码器】要求
if (av_codec_is_encoder(avctx->codec)) {
int i;
//如果包含采样率参数(表明是音频),检查采样率是否符合要求
if (avctx->codec->sample_fmts) {
//遍历编码器支持的所有采样率
for (i = 0; avctx->codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; i++) {
//如果设置的采样率==编码器支持的采样率,跳出循环。
if (avctx->sample_fmt == avctx->codec->sample_fmts[i])
break;
if (avctx->channels == 1 &&
av_get_planar_sample_fmt(avctx->sample_fmt) ==
av_get_planar_sample_fmt(avctx->codec->sample_fmts[i])) {
avctx->sample_fmt = avctx->codec->sample_fmts[i];
break;
}
}
//再检查一下采样率取值是否正确
//注意,此时的i值没有变化
if (avctx->codec->sample_fmts[i] == AV_SAMPLE_FMT_NONE) {
char buf[128];
snprintf(buf, sizeof(buf), "%d", avctx->sample_fmt);
av_log(avctx, AV_LOG_ERROR, "Specified sample format %s is invalid or not supported
",
(char *)av_x_if_null(av_get_sample_fmt_name(avctx->sample_fmt), buf));
ret = AVERROR(EINVAL);
goto free_and_end;
}
}
//检查像素格式
if (avctx->codec->pix_fmts) {
for (i = 0; avctx->codec->pix_fmts[i] != AV_PIX_FMT_NONE; i++)
if (avctx->pix_fmt == avctx->codec->pix_fmts[i])
break;
if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_NONE
&& !((avctx->codec_id == AV_CODEC_ID_MJPEG || avctx->codec_id == AV_CODEC_ID_LJPEG)
&& avctx->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL)) {
char buf[128];
snprintf(buf, sizeof(buf), "%d", avctx->pix_fmt);
av_log(avctx, AV_LOG_ERROR, "Specified pixel format %s is invalid or not supported
",
(char *)av_x_if_null(av_get_pix_fmt_name(avctx->pix_fmt), buf));
ret = AVERROR(EINVAL);
goto free_and_end;
}
if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ420P ||
avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ411P ||
avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ422P ||
avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ440P ||
avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ444P)
avctx->color_range = AVCOL_RANGE_JPEG;
}
//检查采样率
if (avctx->codec->supported_samplerates) {
for (i = 0; avctx->codec->supported_samplerates[i] != 0; i++)
if (avctx->sample_rate == avctx->codec->supported_samplerates[i])
break;
if (avctx->codec->supported_samplerates[i] == 0) {
av_log(avctx, AV_LOG_ERROR, "Specified sample rate %d is not supported
",
avctx->sample_rate);
ret = AVERROR(EINVAL);
goto free_and_end;
}
}
//检查声道布局
if (avctx->codec->channel_layouts) {
if (!avctx->channel_layout) {
av_log(avctx, AV_LOG_WARNING, "Channel layout not specified
");
} else {
for (i = 0; avctx->codec->channel_layouts[i] != 0; i++)
if (avctx->channel_layout == avctx->codec->channel_layouts[i])
break;
if (avctx->codec->channel_layouts[i] == 0) {
char buf[512];
av_get_channel_layout_string(buf, sizeof(buf), -1, avctx->channel_layout);
av_log(avctx, AV_LOG_ERROR, "Specified channel layout '%s' is not supported
", buf);
ret = AVERROR(EINVAL);
goto free_and_end;
}
}
}
//检查声道数
if (avctx->channel_layout && avctx->channels) {
int channels = av_get_channel_layout_nb_channels(avctx->channel_layout);
if (channels != avctx->channels) {
char buf[512];
av_get_channel_layout_string(buf, sizeof(buf), -1, avctx->channel_layout);
av_log(avctx, AV_LOG_ERROR,
"Channel layout '%s' with %d channels does not match number of specified channels %d
",
buf, channels, avctx->channels);
ret = AVERROR(EINVAL);
goto free_and_end;
}
} else if (avctx->channel_layout) {
avctx->channels = av_get_channel_layout_nb_channels(avctx->channel_layout);
}
//检查宽高
if(avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
if (avctx->width <= 0 || avctx->height <= 0) {
av_log(avctx, AV_LOG_ERROR, "dimensions not set
");
ret = AVERROR(EINVAL);
goto free_and_end;
}
}
//检查码率
if ( (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO)
&& avctx->bit_rate>0 && avctx->bit_rate<1000) {
av_log(avctx, AV_LOG_WARNING, "Bitrate %d is extremely low, maybe you mean %dk
", avctx->bit_rate, avctx->bit_rate);
}
if (!avctx->rc_initial_buffer_occupancy)
avctx->rc_initial_buffer_occupancy = avctx->rc_buffer_size * 3 / 4;
}
avcodec_send_packet() 输入AVPacket到解码器
/**
* Supply raw packet data as input to a decoder.
*
* Internally, this call will copy relevant AVCodecContext fields, which can
* influence decoding per-packet, and apply them when the packet is actually
* decoded. (For example AVCodecContext.skip_frame, which might direct the
* decoder to drop the frame contained by the packet sent with this function.)
*
* @warning The input buffer, avpkt->data must be AV_INPUT_BUFFER_PADDING_SIZE
* larger than the actual read bytes because some optimized bitstream
* readers read 32 or 64 bits at once and could read over the end.
*
* @warning Do not mix this API with the legacy API (like avcodec_decode_video2())
* on the same AVCodecContext. It will return unexpected results now
* or in future libavcodec versions.
*
* @note The AVCodecContext MUST have been opened with @ref avcodec_open2()
* before packets may be fed to the decoder.
*
* @param avctx codec context
* @param[in] avpkt The input AVPacket. Usually, this will be a single video
* frame, or several complete audio frames.
* Ownership of the packet remains with the caller, and the
* decoder will not write to the packet. The decoder may create
* a reference to the packet data (or copy it if the packet is
* not reference-counted).
* Unlike with older APIs, the packet is always fully consumed,
* and if it contains multiple frames (e.g. some audio codecs),
* will require you to call avcodec_receive_frame() multiple
* times afterwards before you can send a new packet.
* It can be NULL (or an AVPacket with data set to NULL and
* size set to 0); in this case, it is considered a flush
* packet, which signals the end of the stream. Sending the
* first flush packet will return success. Subsequent ones are
* unnecessary and will return AVERROR_EOF. If the decoder
* still has frames buffered, it will return them after sending
* a flush packet.
*
* @return 0 on success, otherwise negative error code:
* AVERROR(EAGAIN): input is not accepted in the current state - user
* must read output with avcodec_receive_frame() (once
* all output is read, the packet should be resent, and
* the call will not fail with EAGAIN).
* AVERROR_EOF: the decoder has been flushed, and no new packets can
* be sent to it (also returned if more than 1 flush
* packet is sent)
* AVERROR(EINVAL): codec not opened, it is an encoder, or requires flush
* AVERROR(ENOMEM): failed to add packet to internal queue, or similar
* other errors: legitimate decoding errors
*/
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
avcodec_receive_frame() 返回解码的数据AVFrame
/**
* Return decoded output data from a decoder.
*
* @param avctx codec context
* @param frame This will be set to a reference-counted video or audio
* frame (depending on the decoder type) allocated by the
* decoder. Note that the function will always call
* av_frame_unref(frame) before doing anything else.
*
* @return
* 0: success, a frame was returned
* AVERROR(EAGAIN): output is not available in this state - user must try
* to send new input
* AVERROR_EOF: the decoder has been fully flushed, and there will be
* no more output frames
* AVERROR(EINVAL): codec not opened, or it is an encoder
* AVERROR_INPUT_CHANGED: current decoded frame has changed parameters
* with respect to first decoded frame. Applicable
* when flag AV_CODEC_FLAG_DROPCHANGED is set.
* other negative values: legitimate decoding errors
*/
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);
sws_getCachedContext() 生成像素格式转换上下文
/**
* Check if context can be reused, otherwise reallocate a new one.
*
* If context is NULL, just calls sws_getContext() to get a new
* context. Otherwise, checks if the parameters are the ones already
* saved in context. If that is the case, returns the current
* context. Otherwise, frees context and gets a new context with
* the new parameters.
*
* Be warned that srcFilter and dstFilter are not checked, they
* are assumed to remain the same.
*/
struct SwsContext *sws_getCachedContext(struct SwsContext *context,
int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
int flags, SwsFilter *srcFilter,
SwsFilter *dstFilter, const double *param);
int srcW, /* 输入图像的宽度 */
int srcH, /* 输入图像的高度 */
enum AVPixelFormat srcFormat, /* 输入图像的像素格式 */
int dstW, /* 输出图像的宽度 */
int dstH, /* 输出图像的高度 */
enum AVPixelFormat dstFormat, /* 输出图像的像素格式 */
int flags,/* 选择缩放算法(只有当输入输出图像大小不同时有效),一般选择SWS_FAST_BILINEAR */
SwsFilter *srcFilter, /* 输入图像的滤波器信息, 若不需要传NULL */
SwsFilter *dstFilter, /* 输出图像的滤波器信息, 若不需要传NULL */
const double *param /* 特定缩放算法需要的参数(?),默认为NULL */
与其类似的函数还有: sws_getContext(),区别在于:sws_getContext()可以用于多路码流转换,为每个不同的码流都指定一个不同的转换上下文,而sws_getCachedContext只能用于一路码流转换
sws_scale() 视频像素格式和分辨率的转换
其优势在于:可以在同一个函数里实现:1.图像色彩空间转换, 2:分辨率缩放,3:前后图像滤波处理。不足之处在于:效率相对较低,不如libyuv或shader。
int sws_scale(struct SwsContext *c,
const uint8_t *const srcSlice[],
const int srcStride[],
int srcSliceY, int srcSliceH,
uint8_t *const dst[], const int dstStride[]);
下面对其函数参数进行详细说明:
1.参数 SwsContext *c, 转换格式的上下文。也就是 sws_getContext 函数返回的结果。
2.参数 const uint8_t *const srcSlice[], 输入图像的每个颜色通道的数据指针。其实就是解码后的AVFrame中的data[]数组。因为不同像素的存储格式不同,所以srcSlice[]维数
也有可能不同。
以YUV420P为例,它是planar格式,它的内存中的排布如下:
YYYYYYYY UUUU VVVV
使用FFmpeg解码后存储在AVFrame的data[]数组中时:
data[0]——-Y分量, Y1, Y2, Y3, Y4, Y5, Y6, Y7, Y8……
data[1]——-U分量, U1, U2, U3, U4……
data[2]——-V分量, V1, V2, V3, V4……
linesize[]数组中保存的是对应通道的数据宽度 ,
linesize[0]——-Y分量的宽度
linesize[1]——-U分量的宽度
linesize[2]——-V分量的宽度
而RGB24,它是packed格式,它在data[]数组中则只有一维,它在存储方式如下:
data[0]: R1, G1, B1, R2, G2, B2, R3, G3, B3, R4, G4, B4……
这里要特别注意,linesize[0]的值并不一定等于图片的宽度,有时候为了对齐各解码器的CPU,实际尺寸会大于图片的宽度,这点在我们编程时(比如OpengGL硬件转换/渲染)要特别注意,否则解码出来的图像会异常。
3.参数const int srcStride[],输入图像的每个颜色通道的跨度。也就是每个通道的行字节数,对应的是解码后的AVFrame中的linesize[]数组。根据它可以确立下一行的起始位置,不过stride和width不一定相同,这是因为:
a.由于数据帧存储的对齐,有可能会向每行后面增加一些填充字节这样 stride = width + N;
b.packet色彩空间下,每个像素几个通道数据混合在一起,例如RGB24,每个像素3字节连续存放,因此下一行的位置需要跳过3*width字节。
4.参数int srcSliceY, int srcSliceH,定义在输入图像上处理区域,srcSliceY是起始位置,srcSliceH是处理多少行。
如果srcSliceY=0,srcSliceH=height,表示一次性处理完整个图像。这种设置是为了多线程并行,例如可以创建两个线程,第一个线程处理 [0, h/2-1]行,第二个线程处理 [h/2, h-1]行。并行处理加快速度。
5.参数uint8_t *const dst[], const int dstStride[]定义输出图像信息(输出的每个颜色通道数据指针,每个颜色通道行字节数)//AV_NUM_DATA_POINTERS宏定义为8
参考:FFmpeg: FFmepg中的sws_scale() 函数分析
av_image_fill_arrays() 填充给定图像,avpicture_fill()已被废弃实际调用此函数
使用指向图像数据缓冲区的src地址填充给定图像的字段,av_image_fill_arrays()的声明位于libavutil/imgutils.h中.
/**
* Setup the data pointers and linesizes based on the specified image
* parameters and the provided array.
*
* 使用指向图像数据缓冲区的src地址填充给定图像的字段。
* 根据指定的像素格式,将设置一个或多个图像数据指针和行大小。
* 如果指定了平面格式,则将设置多个指向不同图片平面的指针,
* 并且不同平面的线条大小将存储在lines_sizes数组中。
*
* To allocate the buffer and fill in the dst_data and dst_linesize in
* one call, use av_image_alloc().
*
* @param dst_data data pointers to be filled in
* @param dst_linesizes linesizes for the image in dst_data to be filled in
* @param src buffer which will contain or contains the actual image data, can be NULL
* @param pix_fmt the pixel format of the image
* @param width the width of the image in pixels
* @param height the height of the image in pixels
* @param align the value used in src for linesize alignment
* @return the size in bytes required for src, a negative error code
* in case of failure
*/
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],
const uint8_t *src, enum AVPixelFormat pix_fmt,
int width, int height, int align)
{
int ret, i;
// 检查输入的width和height是否可用.
ret = av_image_check_size(width, height, 0, NULL);
if (ret < 0)
return ret;
/**
* 1. memset(linesizes, 0, 4*sizeof(linesizes[0])); 将dst_linesize数组的内容清零;
* 2. 利用av_pix_fmt_desc_get函数得到输入格式的AVPixFmtDescriptor指针;
* 3. 最后利用image_get_linesize函数设置linesizes数组中每个元素的值.
*
* 可见该函数是用于填充dst_linesize数组.
*/
ret = av_image_fill_linesizes(dst_linesize, pix_fmt, width);
if (ret < 0)
return ret;
for (i = 0; i < 4; i++)
/**
* 由上面的第一节avpicture_fill的实现中可知align=1, 故该宏返回的值还是linesizes[i].
*/
dst_linesize[i] = FFALIGN(dst_linesize[i], align);
/**
* 1. memset(data, 0, sizeof(data[0])*4); 将data数组内的指针置空
* 2. data[i] = data[i-1] + size[i-1]; 将data数组内的指针分别指向ptr内的数据
*/
return av_image_fill_pointers(dst_data, pix_fmt, height, (uint8_t *)src, dst_linesize);
}
swr_alloc_set_opts() 配置音频转换上下文
int swr_init(struct SwrContext *s); // 初始化上下文
void swr_free(struct SwrContext **s); // 释放上下文空间
/**
* SwrContext if needed and set/reset common parameters.
*
* This function does not require s to be allocated with swr_alloc(). On the
* other hand, swr_alloc() can use swr_alloc_set_opts() to set the parameters
* on the allocated context.
*
* @param s Swr context非空检查是否合法可用, 为空则进行空间分配if(!s) s= swr_alloc();
* @param out_ch_layout 输出的layout (AV_CH_LAYOUT_*)
* @param out_sample_fmt 输出的样本格式 (AV_SAMPLE_FMT_*).
* @param out_sample_rate 输出的样本率 (frequency in Hz)
* @param in_ch_layout input channel layout (AV_CH_LAYOUT_*)
* @param in_sample_fmt input sample format (AV_SAMPLE_FMT_*).
* @param in_sample_rate input sample rate (frequency in Hz)
* @param log_offset logging level offset
* @param log_ctx parent logging context, can be NULL
*
* @see swr_init(), swr_free()
* @return NULL on error, allocated context otherwise
*/
struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate,
int log_offset, void *log_ctx);
swr_init() 初始化SwrContext上下文
/**
* Initialize context after user parameters have been set.
* @note The context must be configured using the AVOption API.
*
* @see av_opt_set_int()
* @see av_opt_set_dict()
*
* @param[in,out] s Swr context to initialize
* @return AVERROR error code in case of failure.
*/
int swr_init(struct SwrContext *s);
swr_convert() 音频重采样
[全网最深刻的理解音频转码swr_convert(2019/10/29已补充修正)]
/** Convert audio.
*
* in and in_count can be set to 0 to flush the last few samples out at the
* end.
*
* If more input is provided than output space, then the input will be buffered.
* You can avoid this buffering by using swr_get_out_samples() to retrieve an
* upper bound on the required number of output samples for the given number of
* input samples. Conversion will run directly without copying whenever possible.
*
* @param s 分配Swr context,并设置参数
* @param out output buffers,如果是压缩音频,只需设置第一个缓冲区
* @param out_count 可用于输出的空间量(每个通道的采样数)
* @param in input buffers, 如果是压缩音频,只需设置第一个
* @param in_count 一个通道中可用的输入样本数
*
* @return 每个通道输出的样本数,出错时为负值
*/
int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
const uint8_t **in , int in_count);
编码
libavdevice 多媒体设备交互的类库
使用这个库可以读取电脑(或者其他设备上)的多媒体设备的数据,或者输出数据到指定的多媒体设备上,Libavdevice支持以下设备作为输入端:
alsa
avfoundation
bktr
dshow
dv1394
fbdev
gdigrab
iec61883
jack
lavfi
libcdio
libdc1394
openal
oss
pulse
qtkit
sndio
video4linux2, v4l2
vfwcap
x11grab
decklink
Libavdevice支持以下设备作为输出端:
alsa
caca
decklink
fbdev
opengl
oss
pulse
sdl
sndio
xv
使用libavdevice的时候,唯一的不同在于需要首先查找用于输入的设备。在这里使用av_find_input_format()完成:
AVFormatContext *pFormatCtx = avformat_alloc_context();
AVInputFormat *ifmt=av_find_input_format("vfwcap");
avformat_open_input(&pFormatCtx, 0, ifmt,NULL);
avdevice_register_all() 初始化libavdevice并注册所有输入输出设备
//Initialize libavdevice and register all the input and output devices.
avdevice_register_all();
avformat_alloc_context() 创建和分配AVFormatContext地址空间
/**
* Allocate an AVFormatContext.
* avformat_free_context() can be used to free the context and everything
* allocated by the framework within it.
*/
AVFormatContext *avformat_alloc_context(void);
avformat_alloc_output_context2() 初始化输出格式的AVFormatContext结构体
声明位于libavformatavformat.h,定义在libavformataviobuf.c
/**
* Allocate an AVFormatContext for an output format.
* avformat_free_context() can be used to free the context and
* everything allocated by the framework within it.
*
* @param *avctx 函数调用成功之后创建的AVFormatContext结构体, 失败为 NULL
* @param 指定AVFormatContext中的AVOutputFormat,用于确定输出格式。如果指定为NULL
可以设定后两个参数(format_name或者filename)由FFmpeg猜测输出格式
* @param 指定输出格式的名称。根据格式名称,FFmpeg会推测输出格式。输出格式可以是“flv”,“mkv”等等
* @return >= 0 成功, a negative AVERROR code 失败
*/
int avformat_alloc_output_context2(AVFormatContext **avctx, AVOutputFormat *oformat,
const char *format, const char *filename)
{
//分配AVFormatContext 结构,并设置默认的AVClass接口
AVFormatContext *s = avformat_alloc_context();
int ret = 0;
*avctx = NULL;
if (!s)
goto nomem;
//(根据文件名)找到合适的AVOutputFormat 结构
if (!oformat) {
if (format) {
oformat = av_guess_format(format, NULL, NULL);
if (!oformat) {
av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format
", format);
ret = AVERROR(EINVAL);
goto error;
}
} else {
oformat = av_guess_format(NULL, filename, NULL);
if (!oformat) {
ret = AVERROR(EINVAL);
av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'
",
filename);
goto error;
}
}
}
//拷贝保存AVOutputFormat 的私有AVClass后续使用
s->oformat = oformat;
if (s->oformat->priv_data_size > 0) {
s->priv_data = av_mallocz(s->oformat->priv_data_size);
if (!s->priv_data)
goto nomem;
if (s->oformat->priv_class) {
*(const AVClass**)s->priv_data= s->oformat->priv_class;
av_opt_set_defaults(s->priv_data);
}
} else
s->priv_data = NULL;
//记录输出文件名
if (filename)
av_strlcpy(s->filename, filename, sizeof(s->filename));
*avctx = s;
return 0;
nomem:
av_log(s, AV_LOG_ERROR, "Out of memory
");
ret = AVERROR(ENOMEM);
error:
avformat_free_context(s);
return ret;
}
/*AVCodecContext 相当于虚基类,需要用具体的编码器实现来给他赋值*/
pCodecCtxEnc = video_st->codec;
//编码器的ID号,这里我们自行指定为264编码器,实际上也可以根据video_st里的codecID 参数赋值
pCodecCtxEnc->codec_id = AV_CODEC_ID_H264;
//编码器编码的数据类型
pCodecCtxEnc->codec_type = AVMEDIA_TYPE_VIDEO;
//目标的码率,即采样的码率;显然,采样码率越大,视频大小越大
pCodecCtxEnc->bit_rate = 200000;
//固定允许的码率误差,数值越大,视频越小
pCodecCtxEnc->bit_rate_tolerance = 4000000;
//编码目标的视频帧大小,以像素为单位
pCodecCtxEnc->width = 640;
pCodecCtxEnc->height = 480;
//帧率的基本单位,我们用分数来表示,
//用分数来表示的原因是,有很多视频的帧率是带小数的eg:NTSC 使用的帧率是29.97
pCodecCtxEnc->time_base.den = 30;
pCodecCtxEnc->time_base = (AVRational){1,25};
pCodecCtxEnc->time_base.num = 1;
//像素的格式,也就是说采用什么样的色彩空间来表明一个像素点
pCodecCtxEnc->pix_fmt = PIX_FMT_YUV420P;
//每250帧插入1个I帧,I帧越少,视频越小
pCodecCtxEnc->gop_size = 250;
//两个非B帧之间允许出现多少个B帧数
//设置0表示不使用B帧
//b 帧越多,图片越小
pCodecCtxEnc->max_b_frames = 0;
//运动估计
pCodecCtxEnc->pre_me = 2;
//设置最小和最大拉格朗日乘数
//拉格朗日乘数 是统计学用来检测瞬间平均值的一种方法
pCodecCtxEnc->lmin = 1;
pCodecCtxEnc->lmax = 5;
//最大和最小量化系数
pCodecCtxEnc->qmin = 10;
pCodecCtxEnc->qmax = 50;
//因为我们的量化系数q是在qmin和qmax之间浮动的,
//qblur表示这种浮动变化的变化程度,取值范围0.0~1.0,取0表示不削减
pCodecCtxEnc->qblur = 0.0;
//空间复杂度的masking力度,取值范围 0.0-1.0
pCodecCtxEnc->spatial_cplx_masking = 0.3;
//运动场景预判功能的力度,数值越大编码时间越长
pCodecCtxEnc->me_pre_cmp = 2;
//采用(qmin/qmax的比值来控制码率,1表示局部采用此方法,)
pCodecCtxEnc->rc_qsquish = 1;
//设置 i帧、p帧与B帧之间的量化系数q比例因子,这个值越大,B帧越不清楚
//B帧量化系数 = 前一个P帧的量化系数q * b_quant_factor + b_quant_offset
pCodecCtxEnc->b_quant_factor = 1.25;
//i帧、p帧与B帧的量化系数便宜量,便宜越大,B帧越不清楚
pCodecCtxEnc->b_quant_offset = 1.25;
//p和i的量化系数比例因子,越接近1,P帧越清楚
//p的量化系数 = I帧的量化系数 * i_quant_factor + i_quant_offset
pCodecCtxEnc->i_quant_factor = 0.8;
pCodecCtxEnc->i_quant_offset = 0.0;
//码率控制测率,宏定义,查API
pCodecCtxEnc->rc_strategy = 2;
//b帧的生成策略
pCodecCtxEnc->b_frame_strategy = 0;
//消除亮度和色度门限
pCodecCtxEnc->luma_elim_threshold = 0;
pCodecCtxEnc->chroma_elim_threshold = 0;
//DCT变换算法的设置,有7种设置,这个算法的设置是根据不同的CPU指令集来优化的取值范围在0-7之间
pCodecCtxEnc->dct_algo = 0;
//这两个参数表示对过亮或过暗的场景作masking的力度,0表示不作
pCodecCtxEnc->lumi_masking = 0.0;
pCodecCtxEnc->dark_masking = 0.0;
av_find_input_format()函数
根据名称查找链表当中的AVInputFormat,在文件avformat.h中。
枚举采集设备和采集数据
//DirectShow是微软公司提供的一套在Windows平台上进行流媒体处理的开发包,9.0之前与DirectX开发包一起发布,之后包含在windows SDK中。
//运用DirectShow,我们可以很方便地从支持WDM驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。
//它广泛地支持各种媒体格式,包括Asf、Mpeg、Avi、Dv、Mp3、Wave等等,使得多媒体数据的回放变得轻而易举
ffmpeg -list_devices true -f dshow -i dummy //枚举采集设备和采集数据
//这个命令行的作用是获取指定视频采集设备支持的分辨率、帧率和像素格式等属性
ffmpeg -list_options true -f dshow -i video="USB 2861 Device"
//ffmpeg在Linux下用X11grab进行屏幕录像
//在Windows下用DirectShow滤镜,使用dshow抓屏需要安装抓屏软件:screen-capture-recorder
[dshow @ 002db420] DirectShow video devices
[dshow @ 002db420] "screen-capture-recorder" //这个就是桌面捕获设备,如果电脑上连有摄像头,也会列出来
[dshow @ 002db420] DirectShow audio devices
[dshow @ 002db420] "virtual-audio-capturer" //这个是音频捕获设备
ffmpeg -f dshow -i video="screen-capture-recorder" -f dshow -i audio="virtual-audio-capturer"
-pix_fmt yuv420p -vcodec libx264 -acodec libvo_aacenc -s 1280x720 -r 25 -q 10 -ar 44100 -ac 2 -tune zerolatency -preset ultrafast
-f mpegts - | ffmpeg -f mpegts -i - -c copy -bsf:a aac_adtstoasc -f flv temp.flv
AVInputFormat *av_find_input_format(const char *short_name)
{
AVInputFormat *fmt = NULL;
while ((fmt = av_iformat_next(fmt)))
if (av_match_name(short_name, fmt->name))
return fmt;
return NULL;
}
avformat_new_stream() 创建输出码流的AVStream
在AVFormatContext结构体中创建一个流,声明在libavformatavformat.h中
/**
* Add a new stream to a media file.
*
* 如果是解封装在 read_header()之前使用. If the
* flag AVFMTCTX_NOHEADER is set in s.ctx_flags, then it may also
* be called in read_packet().
*
* 如果是封装音视频需要在 avformat_write_header()调用前使用
*
* 用户需要使用 avcodec_close() and avformat_free_context() 清除 avformat_new_stream() 分配的空间
*
* @param s media file handle
* @param c非空, 新流相对应的avcodeContext会使用c进行初始化
* This is needed for e.g. codec-specific
* defaults to be set, so codec should be provided if it is known.
*
* @return newly created stream or NULL on error.
*/
AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c);
av_opt_set() 设置AVOption参数
在使用FFmpeg库进行编码的时候,由于需要设置AVCodecContext的参数值,需要使用到av_opt_set()这个函数,该函数被声明在libavutilopt.h中,用来设置AVOption.参考:FFmpeg中av_opt_set()的使用。
int av_opt_set (void *obj, const char *name, const char *val, int search_flags);
int av_opt_set_int (void *obj, const char *name, int64_t val, int search_flags);
int av_opt_set_double (void *obj, const char *name, double val, int search_flags);
int av_opt_set_q (void *obj, const char *name, AVRational val, int search_flags);
int av_opt_set_bin (void *obj, const char *name, const uint8_t *val, int size, int search_flags);
int av_opt_set_image_size(void *obj, const char *name, int w, int h, int search_flags);
int av_opt_set_pixel_fmt (void *obj, const char *name, enum AVPixelFormat fmt, int search_flags);
int av_opt_set_sample_fmt(void *obj, const char *name, enum AVSampleFormat fmt, int search_flags);
int av_opt_set_video_rate(void *obj, const char *name, AVRational val, int search_flags);
int av_opt_set_channel_layout(void *obj, const char *name, int64_t ch_layout, int search_flags);
他们之间的不同是第三个参数,av_opt_set()中第三个参数是一个字符串,值为val, 用来设置obj中的name值,而其他函数多了一些后缀比如int double,使用这些函数设置参数值的时候不需要使用字符串,使用对用的变量类型就可以。
AVOption是存储在AVClass结构体中的,AVClass中的成员变量option必须指向一个AVOption类型的静态数组。如果一个结构体要支持AVOption的话,它的第一个成员必须是一个指向AVClass结构体的指针,比如上面提到的AVCodecContext,以及AVFormatContext等等。AVClass的作用就是给结构体如AVCodecContext增加AVOption功能的支持,起到了AVCodecContext与AVOption之间的桥梁作用,即:AVCodecContext中包含AVClass,AVClass中包含AVOption。
AVOption除了可以对常用的结构体AVFormatContext、AVCodecContext等进行设置之外,还可以对它们的私有数据priv_data进行赋值。这些字段里面通常存储了各种编码器特有的结构体。如使用libx264进行编码的时候,通过AVCodecContext的priv_data字段可以对X264Context(libavcodeclibx264.c)结构体中的变量进行赋值,设置preset、profile等。
avcodec_parameters_from_context() 从AVCodecContext填充AVStream的AVCodecParameters
/**
* Fill the parameters struct based on the values from the supplied codec
* context. Any allocated fields in par are freed and replaced with duplicates
* of the corresponding fields in codec.
*
* @return >= 0 on success, a negative AVERROR code on failure
*/
int avcodec_parameters_from_context(AVCodecParameters *par,
const AVCodecContext *codec);
av_frame_get_buffer() 为音频或视频数据AVFrame.data and AVFrame.buf数组填配新的缓冲区并进行填充
/**
* Allocate new buffer(s) for audio or video data.
*
* 调用此函数之前,必须在frame上设置以下字段:
* - 视频像素格式,音频采样格式 (pixel format for video, sample format for audio)
* - 视频长和宽
* - 音频的nb_样本和频道布局
*
* This function will fill AVFrame.data and AVFrame.buf arrays and, if
* necessary, allocate and fill AVFrame.extended_data and AVFrame.extended_buf.
* For planar formats, one buffer will be allocated for each plane.
*
* @warning: 如果已经分配了frame,调用这个函数会泄漏内存。此外,在某些情况下可能会发生未定义的行为
*
* @param frame 要在其中存储新缓冲区的帧.
* @param align 所需的缓冲区大小对齐。如果等于0,将自动为当前CPU选择对齐方式。
* 强烈建议在此处传递0,除非您知道自己在做什么.
*
* @return 0 on success, a negative AVERROR on error.
*/
int av_frame_get_buffer(AVFrame *frame, int align);
avio_open2()函数 打开输入输出文件,创建AVFormatContext中的AVIOContext
声明位于libavformat/avio.h文件中
/**
* Create and initialize a AVIOContext for accessing the
* resource indicated by url.
* @note When the resource indicated by url has been opened in
* read+write mode, the AVIOContext can be used only for writing.
*
* @param s 函数调用成功之后创建的AVIOContext结构体.
* In case of failure the pointed to value is set to NULL.
* @param url 输入输出协议的地址(文件也是一种“广义”的协议,对于文件来说就是文件的路径)
* @param flags 打开地址的方式。可以选择只读,只写,或者读写。取值如下。
AVIO_FLAG_READ:只读。
AVIO_FLAG_WRITE:只写。
AVIO_FLAG_READ_WRITE:读写。
* is to be opened
* @param int_cb an interrupt callback to be used at the protocols level
* @param options A dictionary filled with protocol-private options. On return
* this parameter will be destroyed and replaced with a dict containing options
* that were not found. May be NULL.
* @return >= 0 in case of success, a negative value corresponding to an
* AVERROR code in case of failure
*/
//libavformat/aviobuf.c源码
int avio_open2(AVIOContext **s, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options)
{
URLContext *h;
int err;
err = ffurl_open(&h, filename, flags, int_cb, options);//用于初始化URLContext
if (err < 0)
return err;
err = ffio_fdopen(s, h);//用于根据URLContext初始化AVIOContext
if (err < 0) {
ffurl_close(h);
return err;
}
return 0;
}
avformat_write_header() 写AVStream流的文件头
声明在libavformatavformat.h中,对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS
avformat_write_header()完成了以下工作:
(1)调用init_muxer()初始化复用器
(2)调用AVOutputFormat的write_header()
init_muxer()代码很长,但是它所做的工作比较简单,可以概括成两个字:检查。函数的流程可以概括成以下几步:
(1)将传入的AVDictionary形式的选项设置到AVFormatContext
(2)遍历AVFormatContext中的每个AVStream,并作如下检查:
a)AVStream的time_base是否正确设置。如果发现AVStream的time_base没有设置,则会调用avpriv_set_pts_info()进行设置。
b)对于音频,检查采样率设置是否正确;对于视频,检查宽、高、宽高比。
c)其他一些检查,不再详述。
/**
* Allocate the stream private data and write the stream header to
* an output media file.
*
* @param s 需要已经创建avformat_alloc_context().
* Its oformat field must be set to the desired output format;
* Its pb field must be set to an already opened AVIOContext.
* @param options An AVDictionary filled with AVFormatContext and muxer-private options.
* On return this parameter will be destroyed and replaced with a dict containing
* options that were not found. May be NULL.
*
* @return AVSTREAM_INIT_IN_WRITE_HEADER on success if the codec had not already been fully initialized in avformat_init,
* AVSTREAM_INIT_IN_INIT_OUTPUT on success if the codec had already been fully initialized in avformat_init,
* negative AVERROR on failure.
*
* @see av_opt_find, av_dict_set, avio_open, av_oformat_next, avformat_init_output.
*/
av_warn_unused_result
int avformat_write_header(AVFormatContext *s, AVDictionary **options);
avcodec_send_frame()
avcodec_receive_packet()
av_compare_ts() 比较时间决定该写入视频还是音频
在libavutilmathematics.h声明
/**
* Compare two timestamps each in its own time base.
*
* @return One of the following values:
* - -1 if `ts_a` is before `ts_b`
* - 1 if `ts_a` is after `ts_b`
* - 0 if they represent the same position
*
* @warning
* The result of the function is undefined if one of the timestamps is outside
* the `int64_t` range when represented in the other's timebase.
*/
int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b);
av_interleaved_write_frame()函数
声明在libavformatavformat.h中,将对 packet 进行缓存和 pts 检查,av_write_frame 直接将包写进Mux,没有缓存和重新排序,一切都需要用户自己设置。 ffmpeg——av_write_frame/av_interleaved_write_frame写文件包
/**
* Write a packet to an output media file ensuring correct interleaving.
*
* This function will buffer the packets internally as needed to make sure the
* packets in the output file are properly interleaved in the order of
* increasing dts. Callers doing their own interleaving should call
* av_write_frame() instead of this function.
*
* Using this function instead of av_write_frame() can give muxers advance
* knowledge of future packets, improving e.g. the behaviour of the mp4
* muxer for VFR content in fragmenting mode.
*
* @param s media file handle
* @param pkt The packet containing the data to be written.
* <br>
* If the packet is reference-counted, this function will take
* ownership of this reference and unreference it later when it sees
* fit.
* The caller must not access the data through this reference after
* this function returns. If the packet is not reference-counted,
* libavformat will make a copy.
* <br>
* This parameter can be NULL (at any time, not just at the end), to
* flush the interleaving queues.
* <br>
* Packet's @ref AVPacket.stream_index "stream_index" field must be
* set to the index of the corresponding stream in @ref
* AVFormatContext.streams "s->streams".
* <br>
* The timestamps (@ref AVPacket.pts "pts", @ref AVPacket.dts "dts")
* must be set to correct values in the stream's timebase (unless the
* output format is flagged with the AVFMT_NOTIMESTAMPS flag, then
* they can be set to AV_NOPTS_VALUE).
* The dts for subsequent packets in one stream must be strictly
* increasing (unless the output format is flagged with the
* AVFMT_TS_NONSTRICT, then they merely have to be nondecreasing).
* @ref AVPacket.duration "duration") should also be set if known.
*
* @return 0 on success, a negative AVERROR on error. Libavformat will always
* take care of freeing the packet, even if this function fails.
*
* @see av_write_frame(), AVFormatContext.max_interleave_delta
*/
int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt);
av_write_frame() 将AVPacket写进Mux
声明在libavformatavformat.h中,的定义位于libavformatmux.c直接将包写进Mux,没有缓存和重新排序,一切都需要用户自己设置
/**
* Write a packet to an output media file.
*
* This function passes the packet directly to the muxer, without any buffering
* or reordering. The caller is responsible for correctly interleaving the
* packets if the format requires it. Callers that want libavformat to handle
* the interleaving should call av_interleaved_write_frame() instead of this
* function.
*
* @param s media file handle
* @param pkt The packet containing the data to be written. Note that unlike
* av_interleaved_write_frame(), this function does not take
* ownership of the packet passed to it (though some muxers may make
* an internal reference to the input packet).
* <br>
* This parameter can be NULL (at any time, not just at the end), in
* order to immediately flush data buffered within the muxer, for
* muxers that buffer up data internally before writing it to the
* output.
* <br>
* Packet's @ref AVPacket.stream_index "stream_index" field must be
* set to the index of the corresponding stream in @ref
* AVFormatContext.streams "s->streams".
* <br>
* The timestamps (@ref AVPacket.pts "pts", @ref AVPacket.dts "dts")
* must be set to correct values in the stream's timebase (unless the
* output format is flagged with the AVFMT_NOTIMESTAMPS flag, then
* they can be set to AV_NOPTS_VALUE).
* The dts for subsequent packets passed to this function must be strictly
* increasing when compared in their respective timebases (unless the
* output format is flagged with the AVFMT_TS_NONSTRICT, then they
* merely have to be nondecreasing). @ref AVPacket.duration
* "duration") should also be set if known.
* @return < 0 on error, = 0 if OK, 1 if flushed and there is no more data to flush
*
* @see av_interleaved_write_frame()
*/
int av_write_frame(AVFormatContext *s, AVPacket *pkt);
av_write_trailer() 写文件尾,写入时长信息等
声明在libavformatavformat.h中,对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS
av_write_trailer()主要完成了以下两步工作:
(1)循环调用interleave_packet()以及write_packet(),将还未输出的AVPacket输出出来。
(2)调用AVOutputFormat的write_trailer(),输出文件尾。
/**
* Write the stream trailer to an output media file and free the
* file private data.
*
* May only be called after a successful call to avformat_write_header.
*
* @param s media file handle
* @return 0 if OK, AVERROR_xxx on error
*/
int av_write_trailer(AVFormatContext *s);
视音频滤镜
avfilter_register_all() 已废弃
AVFilterContext 滤镜实例的结构体
一个滤镜实例,即使是同一个滤镜,但是在进行实际的滤波时,也会由于输入的参数不同而有不同的滤波效果,AVFilterContext就是在实际进行滤波时用于维护滤波相关信息的实体。
/** An instance of a filter */
struct AVFilterContext {
const AVClass *av_class; ///< needed for av_log() and filters common options
const AVFilter *filter; ///< 指明本实例是哪个AVFilter的实例
char *name; ///< 当前滤镜实例的名称
AVFilterPad *input_pads; ///< 输入Pad的数组 array of input pads
AVFilterLink **inputs; ///< 输入link的指针数组
unsigned nb_inputs; ///< 输入Pad的个数 number of input pads
AVFilterPad *output_pads; ///< 输出Pad的数组 array of output pads
AVFilterLink **outputs; ///< 输出link的指针数组
unsigned nb_outputs; ///< 输出Pad的个数 number of output pads
void *priv; ///< 本滤镜使用的私有数据
struct AVFilterGraph *graph; ///< 指明本滤镜是属于哪个filtergraph
/**
* Type of multithreading being allowed/used. A combination of
* AVFILTER_THREAD_* flags.
*
* May be set by the caller before initializing the filter to forbid some
* or all kinds of multithreading for this filter. The default is allowing
* everything.
*
* When the filter is initialized, this field is combined using bit AND with
* AVFilterGraph.thread_type to get the final mask used for determining
* allowed threading types. I.e. a threading type needs to be set in both
* to be allowed.
*
* After the filter is initialized, libavfilter sets this field to the
* threading type that is actually used (0 for no multithreading).
*/
int thread_type;
/**
* An opaque struct for libavfilter internal use.
*/
AVFilterInternal *internal;
struct AVFilterCommand *command_queue;
char *enable_str; ///< enable expression string
void *enable; ///< parsed expression (AVExpr*)
double *var_values; ///< variable values for the enable expression
int is_disabled; ///< the enabled state from the last expression evaluation
/**
* For filters which will create hardware frames, sets the device the
* filter should create them in. All other filters will ignore this field:
* in particular, a filter which consumes or processes hardware frames will
* instead use the hw_frames_ctx field in AVFilterLink to carry the
* hardware context information.
*/
AVBufferRef *hw_device_ctx;
/**
* Max number of threads allowed in this filter instance.
* If <= 0, its value is ignored.
* Overrides global number of threads set per filter graph.
*/
int nb_threads;
/**
* Ready status of the filter.
* A non-0 value means that the filter needs activating;
* a higher value suggests a more urgent activation.
*/
unsigned ready;
/**
* Sets the number of extra hardware frames which the filter will
* allocate on its output links for use in following filters or by
* the caller.
*
* Some hardware filters require all frames that they will use for
* output to be defined in advance before filtering starts. For such
* filters, any hardware frame pools used for output must therefore be
* of fixed size. The extra frames set here are on top of any number
* that the filter needs internally in order to operate normally.
*
* This field must be set before the graph containing this filter is
* configured.
*/
int extra_hw_frames;
};
AVFilter 结构体
struct AVFilter{
/**
* 滤镜定义。
* 它定义了滤镜与外部进行交互的pad(管脚,这个概念来自集成电脑,表示滤镜和外部交互的接口),
* 以及和本滤镜进行交互的所有回调函数;
*/
typedef struct AVFilter {
/* 滤镜名。必须是非空且唯一的; */
const char *name;
/* 滤镜的描述,可以为空。 */
const char *description;
/**
* Pad : 输入Pad列表,以零元素结束, 它是和它的上一个AVFilterLink的 dstpad 相连的;
* 如果没有(静态的)输入,则将其设置为空。
* 如果滤镜是 AVFILTER_FLAG_DYNAMIC_INPUTS 类型的实例,则在这个列表中可能有多个输入
*/
const AVFilterPad *inputs;
/**
* Pad : 输出Pad列表,以零元素结束, 它是和它的下一个AVFilterLink的 srcpad 相连的;
* 如果没有(静态的)输出,则将其设置为空。
* 如果滤镜是 AVFILTER_FLAG_DYNAMIC_OUTPUTS 类型的实例,则在这个列表中可能有多个输出
*/
const AVFilterPad *outputs;
/**
* 私有数据类,用于声明滤镜私有的 AVoptions 结构体;
* 如果滤镜没有任何可选项则将这个值初始化为NULL;
* 如果这个值不为空,那么滤镜私有数据的第一个成员变量必须是 AVClass指针;
*/
const AVClass *priv_class;
/**
* 滤镜类型标志 , 其值为 AVFILTER_FLAG_* 宏定义
*/
int flags;
/*****************************************************************
* 下面的变量不是公开的API,它们不能被libavfilter的外部使用
*****************************************************************/
/**
* 回调函数: 滤镜预初始化函数
* 当滤镜的 context 被分配后,将立即调用这个回调函数,来分配空间和初始化子对象。
* 如果 这个回调函数不为空, 当遇到空间分配失败时将调用 uninit 回调函数;
* 返回值: 0, 表示成功
* AVERROR,表示失败,但是这个错误码会被删除,并被调用代码视为 ENOMEM 。
*/
int (*preinit)(AVFilterContext *ctx);
/**
* 回调函数: 滤镜初始化函数
* 在滤镜的整个生命周期中,这个回调函数只会在 所有可选项被设置后,滤镜链建立及格式协调之前 被调用 一次。
* 滤镜本身的基本初始化可以放在这里,
* ----- 意思是只是初始化滤镜本身的状态,结构体,及本滤镜专用的参数(如输入参数再计算得到的参数)可以放在这里;
* 如果 这些初始化需要使用到本滤镜外的(如上下游滤镜,系统)参数,则需要把这样的初始化放在 config_input()中;
* NOTE:输入参数的默认值是在 static const AVOption xxxx_options[] 中设置的,不需要再单独设置;
*
* 如果滤镜是多输入/输出类型的,那么应该在这里基于提供的可选项创建这些 inputs/outputs。
* 对于滤镜来说,当这个回调被调用后,就不能再有滤镜的inputs/outputs的改变了。
*
* 这个回调函数它假设滤镜链是有效的,或者帧参数是有效的
* AVFilter.uninit 指针指向的 uninit 函数 保证了即使初始化失败时 ,这个函数将会被调用 , 因此失败时这个回调函数不需要清空
*
* 返回值: 0, 表示成功; 负数AVERROR, 表示失败。
*/
int (*init)(AVFilterContext *ctx);
/**
* Should be set instead of @ref AVFilter.init "init" by the filters that
* want to pass a dictionary of AVOptions to nested contexts that are
* allocated during init.
*
* On return, the options dict should be freed and replaced with one that
* contains all the options which could not be processed by this filter (or
* with NULL if all the options were processed).
*
* Otherwise the semantics is the same as for @ref AVFilter.init "init".
* 回调函数: 这个变量是被滤镜设置,用来代替 AVFilter.init "init" 回调函数,
* 它会传递一个 AVOptions的字典 给 嵌套的 context (它是在初始化进分配的)
* 返回时,这个字典选项应当被释放,且被一个包含所有选项的
*/
int (*init_dict)(AVFilterContext *ctx, AVDictionary **options);
/**
* 回调函数: 滤镜释放函数 , 它必须在滤镜释放前仅调用一次。
* 在这个回调函数中,应当释放滤镜的所有申请的内存,指针引用等。
* 但它不需要去释放 AVFilterContext.priv 内存空间本身;
* 即便 AVFilter.init "init" 函数没有被调用或调用失败,这个函数也可能会被调用。
* 因此,它必须能处理这种情况
*/
void (*uninit)(AVFilterContext *ctx);
/**
* 回调函数: 检查本滤镜输入列表和输出列表支持的格式。
* 它是在滤镜初始化后(这时输入列表和输出列表都固定了),格式协商之前被调用。它可以被调用多次。
*
* 在这个回调函数中, 必须对每一个输入link 设置 AVFilterLink.out_formats, 对每一个输出link 设置 AVFilterLink.in_formats,
* 列出这个滤镜的link 支持的 像素/样本格式列表。
* 对于音频 link, 这个滤镜还需要设置的参数有:
* @ref AVFilterLink.in_samplerates "in_samplerates" /
* @ref AVFilterLink.out_samplerates "out_samplerates" and
* @ref AVFilterLink.in_channel_layouts "in_channel_layouts" /
* @ref AVFilterLink.out_channel_layouts "out_channel_layouts"
*
* 如果滤镜只有一个输入,那这个回调函数可以设置为空,
* 在这种情况下, libavfilter假设它支持所有的输入格式,并在输出时保留它们
*
* 返回值: 0, 表示成功; 负数,对应为AVERROR 码
*/
int (*query_formats)(AVFilterContext *);
int priv_size; ///< 滤镜分配的私有数据的大小
int flags_internal; ///< avfilter内部使用的额外的标志
/**
* 它是被滤镜注册系统使用的,任何其它代码都不需要去操作它
*/
struct AVFilter *next;
/**
* 让滤镜实例执行一个命令;
* 参数: cmd , 要执行的命令,为简化处理,所有命令必须仅为字母数字;
* arg , 命令的参数
* res , 大小为 res_size的buffer, 用于存放滤镜的返回。当命令不支持时,不需要改变它。
* flags , 如果设置为 AVFILTER_CMD_FLAG_FAST, 且这个命令是有时间消耗的时,这个滤镜将会不执行命令
* 返回值: >=0, 表示成功。 否则为错误码,其中,AVERROR(ENOSYS) 为滤镜不支持这个命令
*/
int (*process_command)(AVFilterContext *, const char *cmd, const char *arg, char *res, int res_len, int flags);
/**
* 回调函数: 滤镜初始化函数,它用来替代 init()回调函数。
* 参数中可以包含用户提供的参数 : opaque是用于传输二进制数据
*/
int (*init_opaque)(AVFilterContext *ctx, void *opaque);
/**
* Filter activation function.
*
* Called when any processing is needed from the filter, instead of any
* filter_frame and request_frame on pads.
*
* The function must examine inlinks and outlinks and perform a single
* step of processing. If there is nothing to do, the function must do
* nothing and not return an error. If more steps are or may be
* possible, it must use ff_filter_set_ready() to schedule another
* activation.
* 回调函数: 滤镜激活函数
* 当有处理需要这个滤镜时,就会调用这个函数。
*/
int (*activate)(AVFilterContext *ctx);
} AVFilter;
}
AVFilterPad 结构体
AVFilterPad是Pad类。一个AVFilterContext实例包括AVFilterPad的一组In Pad实例和一组Out Pad实例。AVFilterLink是Pad Link类,它连接两个AVFilterPad实例的输入输出端口,一个滤波器可以有多个输入以及多个输出端口,相邻滤镜之间是通过AVFilterLink来串联的,而位于AVFilterLink两端的分别就是前一个滤镜的输出端口以及后一个滤镜的输入端口。
/**
* @addtogroup lavu_media Media Type
* @brief Media Type
*/
enum AVMediaType {
AVMEDIA_TYPE_UNKNOWN = -1, ///< Usually treated as AVMEDIA_TYPE_DATA
AVMEDIA_TYPE_VIDEO,
AVMEDIA_TYPE_AUDIO,
AVMEDIA_TYPE_DATA, ///< Opaque data information usually continuous
AVMEDIA_TYPE_SUBTITLE,
AVMEDIA_TYPE_ATTACHMENT, ///< Opaque data information usually sparse
AVMEDIA_TYPE_NB
};
/**
* 一个 filter pad 就是一个滤镜的输入/输出端口
*/
struct AVFilterPad {
/**
* Pad名称, 在输入列表,或输出列表内部必须是唯一的,但输入列表,输出列表之间可以重名。
* 如果 这个 Pad 不会被以名称引用,则其值可以为NULL。
*/
const char *name;
/**
* AVFilterPad 类型
*/
enum AVMediaType type;
/**
* 获得一个视频 buffer的回调函数,如果为空,则滤镜系统会使用默认的 ff_default_get_video_buffer();
* 只对输入的视频 Pad 有效
*/
AVFrame *(*get_video_buffer)(AVFilterLink *link, int w, int h);
/**
* 获得一个音频 buffer的回调函数,如果为空,则滤镜系统会使用默认的 ff_default_get_audio_buffer();
* 只对输入的 音频 Pad 有效
*/
AVFrame *(*get_audio_buffer)(AVFilterLink *link, int nb_samples);
/**
* 调用滤镜进行处理的回调函数:当滤镜收到一帧 音频/视频 数据时,就要调用它进行处理。
* 它是滤镜处理的真正入口 , 只在输入 pad中使用;
*
* 返回值: >=0, 表示成功,负数,为AVERROR的错误。
* 这个函数必须确保 当滤镜的处理出现错误时未被不合适地引用,且不会将错误传递到下一个滤镜
*/
int (*filter_frame)(AVFilterLink *link, AVFrame *frame);
/**
* Frame poll callback. This returns the number of immediately available
* samples. It should return a positive value if the next request_frame()
* is guaranteed to return one frame (with no delay).
*
* Defaults to just calling the source poll_frame() method.
*
* Output pads only.
* 回调函数: 帧轮询的回调函数, 它返回当前有效的样本个数; 只对输出Pad有效;
* 如果下一个 request_frame()函数是有帧数据返回,则它应该返回一个正数值;
*/
int (*poll_frame)(AVFilterLink *link);
/**
* Frame request callback. A call to this should result in some progress
* towards producing output over the given link. This should return zero
* on success, and another value on error.
*
* Output pads only.
* 回调函数: 帧请求回调函数。只对输出Pad有效;
* 如果调用 了这个回调函数,应当在给定的link上生成经过处理后的输出数据;
* 返回值: 0,表示成功;否则为错误值;
*/
int (*request_frame)(AVFilterLink *link);
/**
* 回调函数: 用于link的配置的回调函数
* 对于 输出 Pad, 它应当设置 link的属性,如 width/height等。
* 不要在这里设置 格式 属性;
* 因为格式属性是由滤镜系统调用这个回调函数之前,调用query_formats() 回调函数在滤镜间进行协调得到的。
* 对于输入 pad, 这个回调函数是用检查 link的属性,并由此更新滤镜内部的相关状态;
*
* 对于既有输入,又有输出的滤镜,这个回调函数在成功时返回零,其它返回值为出错。
*/
int (*config_props)(AVFilterLink *link);
/**
* 这个滤镜需要有一个 FIFO插入它的输入link,通常是因为这个滤镜会有一个延迟的动作。
* 只在输入pad中使用
*/
int needs_fifo;
/**
* 这个滤镜需要从它的输入 link中得到一个可写的帧,如果有需要,会复制数据buffer
* 只在输入pad中使用
*/
int needs_writable;
};
AVFilterInOut 结构体:滤镜链输入/输出的链接列表
/**
* A linked-list of the inputs/outputs of the filter chain.
*
* 这主要适用于avfilter_graph_parse() / avfilter_graph_parse2(),
* where it is used to communicate open (unlinked) inputs and outputs from and
* to the caller.
* 这个struct为图中包含的每个未连接的pad指定建立链接所需的筛选器上下文和pad索引
*/
typedef struct AVFilterInOut {
//列表中此输入/输出的唯一名称
char *name;
//与此输入/输出关联的过滤器上下文
AVFilterContext *filter_ctx;
//用于链接的filt_ctx pad的索引
int pad_idx;
//列表中下一个输入, NULL表示最后一个
struct AVFilterInOut *next;
} AVFilterInOut;
AVFilterGraph 结构体
typedef struct AVFilterGraph {
const AVClass *av_class;
AVFilterContext **filters;
unsigned nb_filters;
char *scale_sws_opts; ///< sws options to use for the auto-inserted scale filters
#if FF_API_LAVR_OPTS
attribute_deprecated char *resample_lavr_opts; ///< libavresample options to use for the auto-inserted resample filters
#endif
/**
* Type of multithreading allowed for filters in this graph. A combination
* of AVFILTER_THREAD_* flags.
*
* May be set by the caller at any point, the setting will apply to all
* filters initialized after that. The default is allowing everything.
*
* When a filter in this graph is initialized, this field is combined using
* bit AND with AVFilterContext.thread_type to get the final mask used for
* determining allowed threading types. I.e. a threading type needs to be
* set in both to be allowed.
*/
int thread_type;
/**
* Maximum number of threads used by filters in this graph. May be set by
* the caller before adding any filters to the filtergraph. Zero (the
* default) means that the number of threads is determined automatically.
*/
int nb_threads;
/**
* Opaque object for libavfilter internal use.
*/
AVFilterGraphInternal *internal;
/**
* Opaque user data. May be set by the caller to an arbitrary value, e.g. to
* be used from callbacks like @ref AVFilterGraph.execute.
* Libavfilter will not touch this field in any way.
*/
void *opaque;
/**
* This callback may be set by the caller immediately after allocating the
* graph and before adding any filters to it, to provide a custom
* multithreading implementation.
*
* If set, filters with slice threading capability will call this callback
* to execute multiple jobs in parallel.
*
* If this field is left unset, libavfilter will use its internal
* implementation, which may or may not be multithreaded depending on the
* platform and build options.
*/
avfilter_execute_func *execute;
char *aresample_swr_opts; ///< swr options to use for the auto-inserted aresample filters, Access ONLY through AVOptions
/**
* Private fields
*
* The following fields are for internal use only.
* Their type, offset, number and semantic can change without notice.
*/
AVFilterLink **sink_links;
int sink_links_count;
unsigned disable_auto_convert;
} AVFilterGraph;
AVFilterLink 链接结构体定义
滤镜链,作用主要是用于连接相邻的两个AVFilterContext。为了实现一个滤波过程,可能会需要多个滤波器协同完成,即一个滤镜的输出可能会是另一个滤波器的输入,AVFilterLink的作用是串联两个相邻的滤镜实例,形成两个滤镜之间的通道。
libavfilter/avfilter.h
/**
* 两个滤镜之间的链接;
* 如果两个滤镜是有链接的,则这个链接结构体包含有指向源滤镜与目的滤镜的指针,
* 及 源滤镜的输出Pad, 和 目的滤镜的输入Pad 指针;
* 另外,这个链接还包含有这个两个滤镜经过协商并匹配成功的参数,如图像的分辨率,格式等;
*
* 应用程序不能直接去访问链接结构体,而是使用 buffersrc 和 buffersink API 来访问,如:
*
*/
struct AVFilterLink {
AVFilterContext *src; ///< 指向和它相连的上游的源滤镜
AVFilterPad *srcpad; ///< 源Pad , 和上游源滤镜的 output_pad 相连
AVFilterContext *dst; ///< 指向和它相连的下游的目的滤镜
AVFilterPad *dstpad; ///< 目的Pad, 和下游目目的滤镜的 innput_pad 相连
enum AVMediaType type; ///< Link的媒体类型
/* 视频相关参数 */
int w; ///< agreed upon image width
int h; ///< agreed upon image height
AVRational sample_aspect_ratio; ///< agreed upon sample aspect ratio
/* 音频相关参数 */
uint64_t channel_layout; ///< channel layout of current buffer (see libavutil/channel_layout.h)
int sample_rate; ///< samples per second
int format; ///< 协商后并匹配成功的媒体格式 ID
/**
* 定义将通过此链接的 帧/样本的PTS表示用的 时钟基,
* 在配置阶段,每个滤镜应当只能改变输出的时钟基,而输入的时钟基是不可以更改的
*/
AVRational time_base;
/*****************************************************************
* 下面的所有成员属性与函数都不是公开API,它们不能被 libavfilter的外部使用
*****************************************************************
*/
/**
* 输入滤镜与输出滤镜支持的格式、通道布局、采样率列表,这些列表用是用于实际的格式协商。
* 当协商成功后,匹配的格式与通道布局将会更新上面的 format 、channel_layout 、sample_rate 成员属性;
*/
AVFilterFormats *in_formats;
AVFilterFormats *out_formats;
AVFilterFormats *in_samplerates;
AVFilterFormats *out_samplerates;
struct AVFilterChannelLayouts *in_channel_layouts;
struct AVFilterChannelLayouts *out_channel_layouts;
/**
* 仅用于音频;
* 输出滤镜会设置它为一个非零值,用于请求将有设定数量的样本缓冲区发送给它。
* 此时,对应输入Pad的 AVFilterPad.needs_fifo 也要做设置;
* EOF之前的最后一个buffer将会使用静音数据填充;
*/
int request_samples;
/** stage of the initialization of the link properties (dimensions, etc) */
enum {
AVLINK_UNINIT = 0, ///< not started
AVLINK_STARTINIT, ///< started, but incomplete
AVLINK_INIT ///< complete
} init_state;
/**
* 滤镜所属的滤镜图;
*/
struct AVFilterGraph *graph;
/**
* 本链接的当前时间戳,由最近的帧定义,单位是 上面的 time_base;
*/
int64_t current_pts;
/**
* 本链接的当前时间戳,由最近的帧定义,单位是 AV_TIME_BASE (即ffmpeg内部使用的时钟基)
*/
int64_t current_pts_us;
/**
* Index in the age array.
*/
int age_index;
/**
* 本滤镜链的流的帧率,当帧率是未知或可变时,设为 {1/0};
* 如果设置为 {0/0}, 将会自动从源滤镜的输入复制得到;
*
* 源滤镜应当将其设置为实际帧率的最佳估值。
* 如果源滤镜帧率是未知或是可变的,则将其设置为 {1/0}.
* 如果有必要,滤镜应当在它的处理函数中更新这个值;
* Sink型滤镜可以用它来设置默认的输出帧率;
* 它和 AVStream中 的 r_frame_rate 类似;
*/
AVRational frame_rate;
/**
* Buffer partially filled with samples to achieve a fixed/minimum size.
*
*/
AVFrame *partial_buf;
/**
* Size of the partial buffer to allocate.Must be between min_samples and max_samples.
*
*/
int partial_buf_size;
/**
* 滤镜一次能处理的最小样本数。
* 如果调用 filter_frame() 函数 时 的样本数小于这个值时,那这个函数将会在 partial_buf中将这些样本累积起来。
* 这个属性值 及 相关的属性在滤镜初始化完成后就不能再更改了;
* 如果设置为零,则所有相关的属性都被忽略;
*/
int min_samples;
/**
* 滤镜一次能处理的最大样本数。
* 如果调用 filter_frame() 函数时的 样本数大于这个值时,那这个函数要将样本切分后再处理;
*/
int max_samples;
/**
* 音频通道的个数
*/
int channels;
/**
* Link processing flags. 链接处理标志
*/
unsigned flags;
/**
* 通过链接的输入的帧数,输出的帧数;
*/
int64_t frame_count_in, frame_count_out;
/**
* A pointer to a FFFramePool struct.
*/
void *frame_pool;
/**
* True if a frame is currently wanted on the output of this filter.
* Set when ff_request_frame() is called by the output,
* cleared when a frame is filtered.
* 如果当前帧希望从滤镜输出,则此值为真。
*/
int frame_wanted_out;
/**
* For hwaccel pixel formats, this should be a reference to the
* AVHWFramesContext describing the frames.
*/
AVBufferRef *hw_frames_ctx;
#ifndef FF_INTERNAL_FIELDS
/**
* Internal structure members.
* The fields below this limit are internal for libavfilter's use
* and must in no way be accessed by applications.
*/
char reserved[0xF000];
#else /* FF_INTERNAL_FIELDS */
/**
* Queue of frames waiting to be filtered.
*/
FFFrameQueue fifo;
/**
* If set, the source filter can not generate a frame as is.
* The goal is to avoid repeatedly calling the request_frame() method on
* the same link.
*/
int frame_blocked_in;
/**
* Link input status.
* If not zero, all attempts of filter_frame will fail with the
* corresponding code.
*/
int status_in;
/**
* Timestamp of the input status change.
*/
int64_t status_in_pts;
/**
* Link output status.
* If not zero, all attempts of request_frame will fail with the
* corresponding code.
*/
int status_out;
#endif /* FF_INTERNAL_FIELDS */
};
avfilter_graph_alloc() 分配filter graph空间,返回一个AVFilterGraph
/**
* Allocate a filter graph.
*
* @return the allocated filter graph on success or NULL.
*/
AVFilterGraph *avfilter_graph_alloc(void);
avfilter_get_by_name() 根据过滤器名字返回匹配的过滤器对象
定义在libavfilterallfilters.c中
/**
* Get a filter definition matching the given name.
*
* @param name the filter name to find
* @return the filter definition, if any matching one is registered.
* NULL if none found.
*/
const AVFilter *avfilter_get_by_name(const char *name);
avfilter_graph_create_filter() 创建一个滤镜实例AVFilterContext,并添加到AVFilterGraph中
/**
* Create and add a filter instance into an existing graph.
* 过滤器实例是从filter filt创建的,并用参数args和opaque初始化.
*
* 如果成功,请将指向所创建滤镜实例的指针放入*filt_ctx,否则将*filt_ctx设置为NULL.
*
* @param name 定义滤镜的实例名称,对各个结构体中name属性
* @param graph_ctx the filter graph
* @return a negative AVERROR error code in case of failure, a non
* negative value otherwise
*/
int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *filt,
const char *name, const char *args, void *opaque,
AVFilterGraph *graph_ctx);
avfilter_link() 连接两个滤镜节点
/**
* Link two filters together.
*
* @param src the source filter
* @param srcpad 源filter的输出pad的编号
* @param dst the destination filter
* @param dstpad 目的filter的输入pad编号
* @return zero on success
*/
int avfilter_link(AVFilterContext *src, unsigned srcpad,
AVFilterContext *dst, unsigned dstpad);
avfilter_graph_config() 检查有效性并配置图中的所有链接和格式
/**
* Check validity and configure all the links and formats in the graph.
*
* @param graphctx the filter graph
* @param log_ctx context used for logging
* @return >= 0 in case of success, a negative AVERROR code otherwise
*/
int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx);
avfilter_graph_dump() 将filter graph转换成人类可读的字符串表示
/**
* Dump a graph into a human-readable string representation.
*
* @param graph the graph to dump
* @param options formatting options; currently ignored
* @return a string, or NULL in case of memory allocation failure;
* the string must be freed using av_free
*/
char *avfilter_graph_dump(AVFilterGraph *graph, const char *options);
buffersrc
一个特殊的滤波器,这个滤波器的作用就是充当整个滤波过程的入口,通过调用该滤波器提供的函数(如av_buffersrc_add_frame)可以把需要滤波的帧传输进入滤波过程。在创建该滤波器实例的时候需要提供一些关于所输入的帧的格式的必要参数(如:time_base、图像的宽高、图像像素格式等)。
av_buffersrc_add_frame() 向FilterGraph中加入一个AVFrame
/**
* Add a frame to the buffer source.
*
* @param ctx buffersrc filter的实例
* @param frame frame to be added. If the frame is reference counted, this
* function will take ownership of the reference(s) and reset the frame.
* Otherwise the frame data will be copied. If this function returns an error,
* the input frame is not touched.
* 如果帧被引用计数,此函数将获得引用的所有权并重置帧。
* 否则将复制帧数据。如果此函数返回错误,则不接触输入帧
*
* @return 0 on success, a negative AVERROR on error.
*
* @注意此函数与av_buffersrc_write_frame()的不同之处在于,
* av_buffersrc_write_frame()会创建一个对输入帧的新引用,而此函数将拥有传递给它的引用的所有权。
*
* This function is equivalent to av_buffersrc_add_frame_flags() without the
* AV_BUFFERSRC_FLAG_KEEP_REF flag.
*/
av_warn_unused_result
int av_buffersrc_add_frame(AVFilterContext *ctx, AVFrame *frame);
buffersink
一个特殊的滤波器,这个滤波器的作用就是充当整个滤波过程的出口,通过调用该滤波器提供的函数(如av_buffersink_get_frame)可以提取出被滤波过程滤波完成后的帧。
av_buffersink_get_frame() 从buffersink中取出一个AVFrame
/**
* Get a frame with filtered data from sink and put it in frame.
*
* @param ctx pointer to a context of a buffersink or abuffersink AVFilter.
* @param frame pointer to an allocated frame that will be filled with data.
* The data must be freed using av_frame_unref() / av_frame_free()
*
* @return
* - >= 0 if a frame was successfully returned.
* - AVERROR(EAGAIN) if no frames are available at this point; more
* input frames must be added to the filtergraph to get more output.
* - AVERROR_EOF if there will be no more output frames on this sink.
* - A different negative AVERROR code in other failure cases.
*/
int av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame);
avfilter_graph_parse2()
avfilter_graph_parse_ptr() 将一串通过字符串描述的Graph添加到FilterGraph中多个滤镜字符串描述
/**
* Add a graph described by a string to a graph.
*
* In the graph filters description, if the input label of the first
* filter is not specified, "in" is assumed; if the output label of
* the last filter is not specified, "out" is assumed.
*
* @param graph the filter graph where to link the parsed graph context
* @param filters string to be parsed
* @param inputs pointer to a linked list to the inputs of the graph, may be NULL.
* If non-NULL, *inputs is updated to contain the list of open inputs
* after the parsing, should be freed with avfilter_inout_free().
* @param outputs pointer to a linked list to the outputs of the graph, may be NULL.
* If non-NULL, *outputs is updated to contain the list of open outputs
* after the parsing, should be freed with avfilter_inout_free().
* @return non negative on success, a negative AVERROR code on error
*/
int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters,
AVFilterInOut **inputs, AVFilterInOut **outputs,
void *log_ctx);
这种复杂的滤波器过程创建方式要求用户以字符串的方式描述各个滤波器之间的关系。如下是一个描述复杂滤波过程的字符串的例子:
[0]trim=start_frame=10:end_frame=20[v0];
[0]trim=start_frame=30:end_frame=40[v1];
[v0][v1]concat=n=2[v2];
[1]hflip[v3];
[v2][v3]overlay=eof_action=repeat[v4];
[v4]drawbox=50:50:120:120:red:t=5[v5]
以上是一个连续的字符串,为了方便分析我们把该字符串进行了划分,每一行都是一个滤波器实例,对于一行:
- 开头是一对中括号,中括号内的是输入的标识名0。
- 中括号后面接着的是滤波器名称trim。
- 名称后的第一个等号后面是滤波器参数start_frame=10:end_frame=20,这里有两组参数,两组参数用冒号分开。
- 第一组参数名称为start_frame,参数值为10,中间用等号分开。
- 第二组参数名称为end_frame,参数值为20,中间用等号分开。
- 最后也有一对中括号,中括号内的是输出的标识名v0。
- 如果一个滤波实例的输入标识名与另一个滤波实例的输出标识名相同,则表示这两个滤波实例构成滤波链。
- 如果一个滤波实例的输入标识名或者输出标识名一直没有与其它滤波实例的输出标识名或者输入标识名相同,则表明这些为外部的输入输出,通常我们会为其接上buffersrc以及buffersink。
1、解析字符串,并构建该字符串所描述的滤波图
avfilter_graph_parse_ptr(filter_graph, graph_desc, &inputs, &outputs,0);
2、inputs与outputs分别为输入与输出的接口集合,我们需要为这些接口接上输入以及输出。
for (cur = inputs, i = 0; cur; cur = cur->next, i++) {
const AVFilter *buffersrc = avfilter_get_by_name("buffer");
avfilter_graph_create_filter(&filter, buffersrc, name, args, NULL, filter_graph);
avfilter_link(filter, 0, cur->filter_ctx, cur->pad_idx);
}
avfilter_inout_free(&inputs);
for (cur = outputs, i = 0; cur; cur = cur->next, i++) {
const AVFilter *buffersink = avfilter_get_by_name("buffersink");
avfilter_graph_create_filter(&filter, buffersink, name, NULL, NULL, filter_graph);
avfilter_link(cur->filter_ctx, cur->pad_idx, filter, 0);
}
avfilter_inout_free(&outputs);
ERRORCODE
libavutilerror.h中定义了相关的错误码
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* error code definitions
*/
#ifndef AVUTIL_ERROR_H
#define AVUTIL_ERROR_H
#include <errno.h>
#include <stddef.h>
/**
* @addtogroup lavu_error
*
* @{
*/
/* error handling */
#if EDOM > 0
#define AVERROR(e) (-(e)) ///< Returns a negative error code from a POSIX error code, to return from library functions.
#define AVUNERROR(e) (-(e)) ///< Returns a POSIX error code from a library function error return value.
#else
/* Some platforms have E* and errno already negated. */
#define AVERROR(e) (e)
#define AVUNERROR(e) (e)
#endif
#define FFERRTAG(a, b, c, d) (-(int)MKTAG(a, b, c, d))
#define AVERROR_BSF_NOT_FOUND FFERRTAG(0xF8,'B','S','F') ///< Bitstream filter not found
#define AVERROR_BUG FFERRTAG( 'B','U','G','!') ///< Internal bug, also see AVERROR_BUG2
#define AVERROR_BUFFER_TOO_SMALL FFERRTAG( 'B','U','F','S') ///< Buffer too small
#define AVERROR_DECODER_NOT_FOUND FFERRTAG(0xF8,'D','E','C') ///< Decoder not found
#define AVERROR_DEMUXER_NOT_FOUND FFERRTAG(0xF8,'D','E','M') ///< Demuxer not found
#define AVERROR_ENCODER_NOT_FOUND FFERRTAG(0xF8,'E','N','C') ///< Encoder not found
#define AVERROR_EOF FFERRTAG( 'E','O','F',' ') ///< End of file
#define AVERROR_EXIT FFERRTAG( 'E','X','I','T') ///< Immediate exit was requested; the called function should not be restarted
#define AVERROR_EXTERNAL FFERRTAG( 'E','X','T',' ') ///< Generic error in an external library
#define AVERROR_FILTER_NOT_FOUND FFERRTAG(0xF8,'F','I','L') ///< Filter not found
#define AVERROR_INVALIDDATA FFERRTAG( 'I','N','D','A') ///< Invalid data found when processing input
#define AVERROR_MUXER_NOT_FOUND FFERRTAG(0xF8,'M','U','X') ///< Muxer not found
#define AVERROR_OPTION_NOT_FOUND FFERRTAG(0xF8,'O','P','T') ///< Option not found
#define AVERROR_PATCHWELCOME FFERRTAG( 'P','A','W','E') ///< Not yet implemented in FFmpeg, patches welcome
#define AVERROR_PROTOCOL_NOT_FOUND FFERRTAG(0xF8,'P','R','O') ///< Protocol not found
#define AVERROR_STREAM_NOT_FOUND FFERRTAG(0xF8,'S','T','R') ///< Stream not found
/**
* This is semantically identical to AVERROR_BUG
* it has been introduced in Libav after our AVERROR_BUG and with a modified value.
*/
#define AVERROR_BUG2 FFERRTAG( 'B','U','G',' ')
#define AVERROR_UNKNOWN FFERRTAG( 'U','N','K','N') ///< Unknown error, typically from an external library
#define AVERROR_EXPERIMENTAL (-0x2bb2afa8) ///< Requested feature is flagged experimental. Set strict_std_compliance if you really want to use it.
#define AVERROR_INPUT_CHANGED (-0x636e6701) ///< Input changed between calls. Reconfiguration is required. (can be OR-ed with AVERROR_OUTPUT_CHANGED)
#define AVERROR_OUTPUT_CHANGED (-0x636e6702) ///< Output changed between calls. Reconfiguration is required. (can be OR-ed with AVERROR_INPUT_CHANGED)
/* HTTP & RTSP errors */
#define AVERROR_HTTP_BAD_REQUEST FFERRTAG(0xF8,'4','0','0')
#define AVERROR_HTTP_UNAUTHORIZED FFERRTAG(0xF8,'4','0','1')
#define AVERROR_HTTP_FORBIDDEN FFERRTAG(0xF8,'4','0','3')
#define AVERROR_HTTP_NOT_FOUND FFERRTAG(0xF8,'4','0','4')
#define AVERROR_HTTP_OTHER_4XX FFERRTAG(0xF8,'4','X','X')
#define AVERROR_HTTP_SERVER_ERROR FFERRTAG(0xF8,'5','X','X')
#define AV_ERROR_MAX_STRING_SIZE 64
/**
* Put a description of the AVERROR code errnum in errbuf.
* In case of failure the global variable errno is set to indicate the
* error. Even in case of failure av_strerror() will print a generic
* error message indicating the errnum provided to errbuf.
*
* @param errnum error code to describe
* @param errbuf buffer to which description is written
* @param errbuf_size the size in bytes of errbuf
* @return 0 on success, a negative value if a description for errnum
* cannot be found
*/
int av_strerror(int errnum, char *errbuf, size_t errbuf_size);
/**
* Fill the provided buffer with a string containing an error string
* corresponding to the AVERROR code errnum.
*
* @param errbuf a buffer
* @param errbuf_size size in bytes of errbuf
* @param errnum error code to describe
* @return the buffer in input, filled with the error description
* @see av_strerror()
*/
static inline char *av_make_error_string(char *errbuf, size_t errbuf_size, int errnum)
{
av_strerror(errnum, errbuf, errbuf_size);
return errbuf;
}
/**
* Convenience macro, the return value should be used only directly in
* function arguments but never stand-alone.
*/
#define av_err2str(errnum)
av_make_error_string((char[AV_ERROR_MAX_STRING_SIZE]){0}, AV_ERROR_MAX_STRING_SIZE, errnum)
/**
* @}
*/
#endif /* AVUTIL_ERROR_H */
往mp3封装格式写入头信息,报错误码-22
0 describe:Immediate exit
知识点
音视频同步
时间基准
AV_TIME_BASE:ffmpeg中的内部计时单位(内部时间基)
FFmpeg内部的AVFormatContext时间单位其实是微秒(μs,百万分之一秒),AVFormatContext、的duration即以为着这个流的长度为duration个AV_TIME_BASE。
#define AV_TIME_BASE 1000000
AV_TIME_BASE_Q:ffmpeg内部时间基的分数表示,是AV_TIME_BASE的倒数
相当于是 1/1000000 秒,也就是把1s分成1000000份
#define AV_TIME_BASE_Q (AVRational){1, AV_TIME_BASE}
typedef struct AVRational{
int num; //numerator 分子
int den; //denominator 分母
} AVRational;
double av_q2d(AVRational a): 把AVRatioal结构转换成double
static inline double av_q2d(AVRational a){
/**
* Convert rational to double.
* @param a rational to convert
**/
return a.num / (double) a.den;
}
time_base :FFmpeg时间戳详解
不同的封装格式具有不同的时间基。在FFmpeg处理音视频过程中的不同阶段,也会采用不同的时间基。
FFmepg中有三种时间基,命令行中tbn、tbc和tbr的打印值就是这三种时间基的倒数:
- tbn:对应容器中的时间基。值是AVStream.time_base的倒数
- tbc:对应编解码器中的时间基。值是AVCodecContext.time_base的倒数
- tbr:从视频流中猜算得到,可能是帧率或场率(帧率的2倍)
- AVStream的time_base的单位是秒。每种格式的time_base的值不一样,根据采样来计算,比如mpeg的pts、dts都是以90kHz来采样的,所以采样间隔(time_base)就是1/900000秒;flv,MP4等一般是1:1000。
- AVCodecContext的time_base单位同样为秒,不过精度没有AVStream->time_base高,大小为1/framerate。
- AVPacket下的pts和dts以AVStream->time_base为单位(数值比较大),时间间隔就是AVStream->time_base。
- AVFrame里面的pkt_pts和pkt_dts是拷贝自AVPacket,同样以AVStream->time_base为单位;而pts是为输出(显示)准备的,以AVCodecContex->time_base为单位,输出以毫秒为单位。
- 输入流InputStream下的pts和dts以AV_TIME_BASE为单位(微秒),至于为什么要转化为微秒,可能是为了避免使用浮点数。
- 输出流OutputStream涉及音视频同步,结构和InputStream不同,暂时只作记录,不分析。
av_rescale_q() 将时间值从一种时间基转换为另一种时间基
av_rescale_q(a,b,c)作用相当于执行a*b/c,通过设置b,c的值,可以很方便的实现time_base之间转换
av_rescale_rnd() 计算 “a * b / c” 的值并分五种方式来取整
函数定义(见于libavutil/mathematics.c)
AV_ROUND_ZERO = 0, // Round toward zero. 趋近于0
AV_ROUND_INF = 1, // Round away from zero. 趋远于0
AV_ROUND_DOWN = 2, // Round toward -infinity. 趋于更小的整数
AV_ROUND_UP = 3, // Round toward +infinity. 趋于更大的整数
AV_ROUND_NEAR_INF = 5, // Round to nearest and halfway cases away from zero.
// 四舍五入,小于0.5取值趋向0,大于0.5取值趋远于0
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd);
av_packet_rescale_ts() 将AVPacket中时间值从一种时间基转换为另一种时间基
/**
* Convert valid timing fields (timestamps / durations) in a packet from one
* timebase to another. Timestamps with unknown values (AV_NOPTS_VALUE) will be
* ignored.
*
* @param pkt packet on which the conversion will be performed
* @param tb_src source timebase, in which the timing fields in pkt are
* expressed
* @param tb_dst destination timebase, to which the timing fields will be
* converted
*/
void av_packet_rescale_ts(AVPacket *pkt, AVRational tb_src, AVRational tb_dst);
举例
- 计算视频总时长
AVFormatContext *ifmt_ctx = NULL;
avformat_open_input(&ifmt_ctx, filename, NULL, NULL);
double totle_seconds = ifmt_ctx->duration * av_q2d(AV_TIME_BASE_Q);
- 根据PTS求出一帧在视频中对应的秒数位置
double sec = enc_pkt.pts * av_q2d(ofmt_ctx->streams[stream_index]->time_base);
- ffmpeg内部的时间戳与标准的时间转换方法
timestamp(ffmpeg内部时间戳) = AV_TIME_BASE * time(秒)
time(秒) = AV_TIME_BASE_Q * timestamp(ffmpeg内部时间戳)
- 当需要把视频Seek到N秒的时候
// 指定流索引
int pos = 20 * r2d(ic->streams[videoStream]->time_base);
av_seek_frame(ic,videoStream, pos, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME );
// 未指定指定流索引
int64_t timestamp = N * AV_TIME_BASE;
av_seek_frame(fmtctx, -1, timestamp, AVSEEK_FLAG_BACKWARD);
- InputStream(AV_TIME_BASE)到AVPacket(AVStream->time_base)
static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output)
{
pkt->dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ist->st->time_base);
}
- AVPacket(AVStream->time_base)到InputStream(AV_TIME_BASE)
static int process_input_packet(InputStream *ist, const AVPacket *pkt)
{
if (pkt->dts != AV_NOPTS_VALUE)
{
ist->next_dts = ist->dts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q);
}
}
音频通道相关转换
相关函数在libavutilchannel_layout.h中声明,在libavutilchannel_layout.c中定义
常见的声道有:
- 单声道,mono
- 双声道,stereo,最常见的类型,包含左声道以及右声道
- 2.1声道,在双声道基础上加入一个低音声道
- 5.1声道,包含一个正面声道、左前方声道、右前方声道、左环绕声道、右环绕声道、一个低音声道,最早应用于早期的电影院
- 7.1声道,在5.1声道的基础上,把左右的环绕声道拆分为左右环绕声道以及左右后置声道,主要应用于BD以及现代的电影院
channels 为 音频的 通道数 1 2 3 4 5.....
channel_layout 为音频 通道格式类型 如 单通道 双通道 .....
static const struct {
const char *name;
int nb_channels;
uint64_t layout;
} channel_layout_map[] = {
{ "mono", 1, AV_CH_LAYOUT_MONO },
{ "stereo", 2, AV_CH_LAYOUT_STEREO },
{ "2.1", 3, AV_CH_LAYOUT_2POINT1 },
{ "3.0", 3, AV_CH_LAYOUT_SURROUND },
{ "3.0(back)", 3, AV_CH_LAYOUT_2_1 },
{ "4.0", 4, AV_CH_LAYOUT_4POINT0 },
{ "quad", 4, AV_CH_LAYOUT_QUAD },
{ "quad(side)", 4, AV_CH_LAYOUT_2_2 },
{ "3.1", 4, AV_CH_LAYOUT_3POINT1 },
{ "5.0", 5, AV_CH_LAYOUT_5POINT0_BACK },
{ "5.0(side)", 5, AV_CH_LAYOUT_5POINT0 },
{ "4.1", 5, AV_CH_LAYOUT_4POINT1 },
{ "5.1", 6, AV_CH_LAYOUT_5POINT1_BACK },
{ "5.1(side)", 6, AV_CH_LAYOUT_5POINT1 },
{ "6.0", 6, AV_CH_LAYOUT_6POINT0 },
{ "6.0(front)", 6, AV_CH_LAYOUT_6POINT0_FRONT },
{ "hexagonal", 6, AV_CH_LAYOUT_HEXAGONAL },
{ "6.1", 7, AV_CH_LAYOUT_6POINT1 },
{ "6.1(back)", 7, AV_CH_LAYOUT_6POINT1_BACK },
{ "6.1(front)", 7, AV_CH_LAYOUT_6POINT1_FRONT },
{ "7.0", 7, AV_CH_LAYOUT_7POINT0 },
{ "7.0(front)", 7, AV_CH_LAYOUT_7POINT0_FRONT },
{ "7.1", 8, AV_CH_LAYOUT_7POINT1 },
{ "7.1(wide)", 8, AV_CH_LAYOUT_7POINT1_WIDE_BACK },
{ "7.1(wide-side)", 8, AV_CH_LAYOUT_7POINT1_WIDE },
{ "octagonal", 8, AV_CH_LAYOUT_OCTAGONAL },
{ "hexadecagonal", 16, AV_CH_LAYOUT_HEXADECAGONAL },
{ "downmix", 2, AV_CH_LAYOUT_STEREO_DOWNMIX, },
};
av_get_channel_layout_nb_channels() 由音频声道id获取声道通道数
/**
* Return the number of channels in the channel layout.
*/
int av_get_channel_layout_nb_channels(uint64_t channel_layout);
av_get_default_channel_layout() 由音频声道通道数返回取默认的声道id
/**
* Return default channel layout for a given number of channels.
*/
int64_t av_get_default_channel_layout(int nb_channels);
av_get_channel_layout() 由声道名称获取声道id
/**
* 返回与名称匹配的声道id,如果未找到匹配项,则返回0.
*
* name can be one or several of the following notations,
* separated by '+' or '|':
* - 声道方式名字 (mono, stereo, 4.0, quad, 5.0,
* 5.0(side), 5.1, 5.1(side), 7.1, 7.1(wide), downmix);
* - the name of a single channel (FL, FR, FC, LFE, BL, BR, FLC, FRC, BC,
* SL, SR, TC, TFL, TFC, TFR, TBL, TBC, TBR, DL, DR);
* - 多个通道,以十进制表示, 以 'c'字符结尾, yielding
* the default channel layout for that number of channels (@see
* av_get_default_channel_layout);
* - a channel layout mask, in hexadecimal starting with "0x" (see the
* AV_CH_* macros).
*
* Example: "stereo+FC" = "2c+FC" = "2c+1c" = "0x7"
*/
uint64_t av_get_channel_layout(const char *name);
av_get_channel_layout_string() 由声道通道数和声id道返回声道字符串描述
/**
* Return a description of a channel layout.
* If nb_channels is <= 0, it is guessed from the channel_layout.
*
* @param buf put here the string containing the channel layout
* @param buf_size size in bytes of the buffer
*/
void av_get_channel_layout_string(char *buf, int buf_size, int nb_channels, uint64_t channel_layout);
AVStream->codecpar->codec_tag = 0 的作用
在编码中,对AVStream的参数进行设置,avformat_write_header写入封装容器的头信息时,会检查codec_tag:若AVStream->codecpar->codec_tag值不为0,则会校验AVStream->codecpar->codec_tag是否在封装格式支持的codec_tag列表中,若不在,就会打印错误信息;若AVStream->codecpar->codec_tag为0,则会根据AVCodecID从封装格式的codec_tag列表中,找一个匹配的codec_tag。参考:FFmpeg hevc codec_tag兼容问题
// par是hevc流的编码参数:AVStream->codecpar
// st是hevc流:AVStream
// s是AVFormatContext
// of表示封装格式,这里是mp4:AVFormatContext->oformat;
if (par->codec_tag) { // 如果AVStream->codecpar->codec_tag不为0
// 检查AVStream->codecpar->codec_tag是否在封装容器支持的codec_tag列表中,即:AVOutputFormat->codec_tag列表
if (!validate_codec_tag(s, st)) {
// 从AVOutputFormat->codec_tag列表中找出编码ID对应的codec_tag
const uint32_t otag = av_codec_get_tag(s->oformat->codec_tag, par->codec_id);
// 现在设置的codec_tag校验失败,与codec_id对应的codec_tag是otag
av_log(s, AV_LOG_ERROR, "Tag %s incompatible with output codec id '%d' (%s)
", av_fourcc2str(par->codec_tag), par->codec_id, av_fourcc2str(otag));
ret = AVERROR_INVALIDDATA;
goto fail;
}
} else {
// 若AVStream->codecpar->codec_tag为0(默认情况下为0),则FFmpeg会根据编码ID(AVCodecID)从封装格式的codec_tag列表中,找到一个codec_tag。
par->codec_tag = av_codec_get_tag(of->codec_tag, par->codec_id);
}
创建自定义AVFilter滤镜
在源码目录文件ffmpegdocwriting_filters.txt中有相关说明
- 定义自己的AVFilter ff_my_filter
- 在libavfilter/Makefile添加自定义的滤镜
- 在libavfilter/allfilters.c中添加extern AVFilter ff_my_filter;
ffmpeg中编码类型为rawvideo
在linux下使用x11grab抓屏进行录像,获取到的流解封装后AVCOdecID为AV_CODEC_ID_RAWVIDEO(codec id = 14),此时像素格式为AV_PIX_FMT_YUYV422,编码类型为rawvideo的视频流可以不经过解码操作直接就可进行显示。
ffmpeg参数设置
libavformat tsp.c的option设置
const AVOption ff_rtsp_options[] = {
{ "initial_pause", "do not start playing the stream immediately", OFFSET(initial_pause), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DEC },FF_RTP_FLAG_OPTS(RTSPState, rtp_muxer_flags),
{ "rtsp_transport", "set RTSP transport protocols", OFFSET(lower_transport_mask), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, DEC|ENC, "rtsp_transport" },
{ "udp", "UDP", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_UDP}, 0, 0, DEC|ENC, "rtsp_transport" },
{ "tcp", "TCP", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_TCP}, 0, 0, DEC|ENC, "rtsp_transport" },
{ "udp_multicast", "UDP multicast", 0, AV_OPT_TYPE_CONST, {.i64 = 1 << RTSP_LOWER_TRANSPORT_UDP_MULTICAST}, 0, 0, DEC, "rtsp_transport" },
{ "http", "HTTP tunneling", 0, AV_OPT_TYPE_CONST, {.i64 = (1 << RTSP_LOWER_TRANSPORT_HTTP)}, 0, 0, DEC, "rtsp_transport" },
RTSP_FLAG_OPTS("rtsp_flags", "set RTSP flags"),
{ "listen", "wait for incoming connections", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_LISTEN}, 0, 0, DEC, "rtsp_flags" },
{ "prefer_tcp", "try RTP via TCP first, if available", 0, AV_OPT_TYPE_CONST, {.i64 = RTSP_FLAG_PREFER_TCP}, 0, 0, DEC|ENC, "rtsp_flags" },
RTSP_MEDIATYPE_OPTS("allowed_media_types", "set media types to accept from the server"),
{ "min_port", "set minimum local UDP port", OFFSET(rtp_port_min), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MIN}, 0, 65535, DEC|ENC },
{ "max_port", "set maximum local UDP port", OFFSET(rtp_port_max), AV_OPT_TYPE_INT, {.i64 = RTSP_RTP_PORT_MAX}, 0, 65535, DEC|ENC },
{ "listen_timeout", "set maximum timeout (in seconds) to wait for incoming connections (-1 is infinite, imply flag listen)", OFFSET(initial_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, DEC },
#if FF_API_OLD_RTSP_OPTIONS
{ "timeout", "set maximum timeout (in seconds) to wait for incoming connections (-1 is infinite, imply flag listen) (deprecated, use listen_timeout)", OFFSET(initial_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, DEC },
{ "stimeout", "set timeout (in microseconds) of socket TCP I/O operations", OFFSET(stimeout), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC },
#else
{ "timeout", "set timeout (in microseconds) of socket TCP I/O operations", OFFSET(stimeout), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC },
#endif
COMMON_OPTS(),
{ "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = LIBAVFORMAT_IDENT}, 0, 0, DEC },
#if FF_API_OLD_RTSP_OPTIONS
{ "
libavformatoptions_table.h 参数设置
static const AVOption avformat_options[] = {
{"avioflags", NULL, OFFSET(avio_flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "avioflags"},
{"direct", "reduce buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVIO_FLAG_DIRECT }, INT_MIN, INT_MAX, D|E, "avioflags"},
{"probesize", "set probing size", OFFSET(probesize), AV_OPT_TYPE_INT64, {.i64 = 5000000 }, 32, INT64_MAX, D},
{"formatprobesize", "number of bytes to probe file format", OFFSET(format_probesize), AV_OPT_TYPE_INT, {.i64 = PROBE_BUF_MAX}, 0, INT_MAX-1, D},
{"packetsize", "set packet size", OFFSET(packet_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, E},
{"fflags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = AVFMT_FLAG_AUTO_BSF }, INT_MIN, INT_MAX, D|E, "fflags"},
{"flush_packets", "reduce the latency by flushing out packets immediately", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_FLUSH_PACKETS }, INT_MIN, INT_MAX, E, "fflags"},
{"ignidx", "ignore index", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNIDX }, INT_MIN, INT_MAX, D, "fflags"},
{"genpts", "generate pts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_GENPTS }, INT_MIN, INT_MAX, D, "fflags"},
{"nofillin", "do not fill in missing values that can be exactly calculated", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOFILLIN }, INT_MIN, INT_MAX, D, "fflags"},
{"noparse", "disable AVParsers, this needs nofillin too", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOPARSE }, INT_MIN, INT_MAX, D, "fflags"},
{"igndts", "ignore dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNDTS }, INT_MIN, INT_MAX, D, "fflags"},
{"discardcorrupt", "discard corrupted frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_DISCARD_CORRUPT }, INT_MIN, INT_MAX, D, "fflags"},
{"sortdts", "try to interleave outputted packets by dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_SORT_DTS }, INT_MIN, INT_MAX, D, "fflags"},
#if FF_API_LAVF_KEEPSIDE_FLAG
{"keepside", "deprecated, does nothing", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_KEEP_SIDE_DATA }, INT_MIN, INT_MAX, D, "fflags"},
#endif
{"fastseek", "fast but inaccurate seeks", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_FAST_SEEK }, INT_MIN, INT_MAX, D, "fflags"},
#if FF_API_LAVF_MP4A_LATM
{"latm", "deprecated, does nothing", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_MP4A_LATM }, INT_MIN, INT_MAX, E, "fflags"},
#endif
{"nobuffer", "reduce the latency introduced by optional buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOBUFFER }, 0, INT_MAX, D, "fflags"},
{"bitexact", "do not write random/volatile data", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_BITEXACT }, 0, 0, E, "fflags" },
{"shortest", "stop muxing with the shortest stream", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_SHORTEST }, 0, 0, E, "fflags" },
{"autobsf", "add needed bsfs automatically", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_AUTO_BSF }, 0, 0, E, "fflags" },
{"seek2any", "allow seeking to non-keyframes on demuxer level when supported", OFFSET(seek2any), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, D},
{"analyzeduration", "specify how many microseconds are analyzed to probe the input", OFFSET(max_analyze_duration), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, D},
{"cryptokey", "decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, {.dbl = 0}, 0, 0, D},
{"indexmem", "max memory used for timestamp index (per stream)", OFFSET(max_index_size), AV_OPT_TYPE_INT, {.i64 = 1<<20 }, 0, INT_MAX, D},
{"rtbufsize", "max memory used for buffering real-time frames", OFFSET(max_picture_buffer), AV_OPT_TYPE_INT, {.i64 = 3041280 }, 0, INT_MAX, D}, /* defaults to 1s of 15fps 352x288 YUYV422 video */
{"fdebug", "print specific debug info", OFFSET(debug), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, INT_MAX, E|D, "fdebug"},
{"ts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_FDEBUG_TS }, INT_MIN, INT_MAX, E|D, "fdebug"},
{"max_delay", "maximum muxing or demuxing delay in microseconds", OFFSET(max_delay), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, E|D},
{"start_time_realtime", "wall-clock time when stream begins (PTS==0)", OFFSET(start_time_realtime), AV_OPT_TYPE_INT64, {.i64 = AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX, E},
{"fpsprobesize", "number of frames used to probe fps", OFFSET(fps_probe_size), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX-1, D},
{"audio_preload", "microseconds by which audio packets should be interleaved earlier", OFFSET(audio_preload), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E},
{"chunk_duration", "microseconds for each chunk", OFFSET(max_chunk_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E},
{"chunk_size", "size in bytes for each chunk", OFFSET(max_chunk_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E},
/* this is a crutch for avconv, since it cannot deal with identically named options in different contexts.
* to be removed when avconv is fixed */
{"f_err_detect", "set error detection flags (deprecated; use err_detect, save via avconv)", OFFSET(error_recognition), AV_OPT_TYPE_FLAGS, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, "err_detect"},
{"err_detect", "set error detection flags", OFFSET(error_recognition), AV_OPT_TYPE_FLAGS, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, "err_detect"},
{"crccheck", "verify embedded CRCs", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, "err_detect"},
{"bitstream", "detect bitstream specification deviations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BITSTREAM }, INT_MIN, INT_MAX, D, "err_detect"},
{"buffer", "detect improper bitstream length", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BUFFER }, INT_MIN, INT_MAX, D, "err_detect"},
{"explode", "abort decoding on minor error detection", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_EXPLODE }, INT_MIN, INT_MAX, D, "err_detect"},
{"ignore_err", "ignore errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_IGNORE_ERR }, INT_MIN, INT_MAX, D, "err_detect"},
{"careful", "consider things that violate the spec, are fast to check and have not been seen in the wild as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CAREFUL }, INT_MIN, INT_MAX, D, "err_detect"},
{"compliant", "consider all spec non compliancies as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_COMPLIANT }, INT_MIN, INT_MAX, D, "err_detect"},
{"aggressive", "consider things that a sane encoder shouldn't do as an error", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_AGGRESSIVE }, INT_MIN, INT_MAX, D, "err_detect"},
{"use_wallclock_as_timestamps", "use wallclock as timestamps", OFFSET(use_wallclock_as_timestamps), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, D},
{"skip_initial_bytes", "set number of bytes to skip before reading header and frames", OFFSET(skip_initial_bytes), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX-1, D},
{"correct_ts_overflow", "correct single timestamp overflows", OFFSET(correct_ts_overflow), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, D},
{"flush_packets", "enable flushing of the I/O context after each packet", OFFSET(flush_packets), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, E},
{"metadata_header_padding", "set number of bytes to be written as padding in a metadata header", OFFSET(metadata_header_padding), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, E},
{"output_ts_offset", "set output timestamp offset", OFFSET(output_ts_offset), AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, INT64_MAX, E},
{"max_interleave_delta", "maximum buffering duration for interleaving", OFFSET(max_interleave_delta), AV_OPT_TYPE_INT64, { .i64 = 10000000 }, 0, INT64_MAX, E },
{"f_strict", "how strictly to follow the standards (deprecated; use strict, save via avconv)", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "strict"},
{"strict", "how strictly to follow the standards", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "strict"},
{"very", "strictly conform to a older more strict version of the spec or reference software", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_VERY_STRICT }, INT_MIN, INT_MAX, D|E, "strict"},
{"strict", "strictly conform to all the things in the spec no matter what the consequences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_STRICT }, INT_MIN, INT_MAX, D|E, "strict"},
{"normal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_NORMAL }, INT_MIN, INT_MAX, D|E, "strict"},
{"unofficial", "allow unofficial extensions", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_UNOFFICIAL }, INT_MIN, INT_MAX, D|E, "strict"},
{"experimental", "allow non-standardized experimental variants", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_EXPERIMENTAL }, INT_MIN, INT_MAX, D|E, "strict"},
{"max_ts_probe", "maximum number of packets to read while waiting for the first timestamp", OFFSET(max_ts_probe), AV_OPT_TYPE_INT, { .i64 = 50 }, 0, INT_MAX, D },
{"avoid_negative_ts", "shift timestamps so they start at 0", OFFSET(avoid_negative_ts), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, E, "avoid_negative_ts"},
{"auto", "enabled when required by target format", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_AUTO }, INT_MIN, INT_MAX, E, "avoid_negative_ts"},
{"disabled", "do not change timestamps", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, INT_MIN, INT_MAX, E, "avoid_negative_ts"},
{"make_non_negative", "shift timestamps so they are non negative", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE }, INT_MIN, INT_MAX, E, "avoid_negative_ts"},
{"make_zero", "shift timestamps so they start at 0", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_ZERO }, INT_MIN, INT_MAX, E, "avoid_negative_ts"},
{"dump_separator", "set information dump field separator", OFFSET(dump_separator), AV_OPT_TYPE_STRING, {.str = ", "}, CHAR_MIN, CHAR_MAX, D|E},
{"codec_whitelist", "List of decoders that are allowed to be used", OFFSET(codec_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D },
{"format_whitelist", "List of demuxers that are allowed to be used", OFFSET(format_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D },
{"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D },
{"protocol_blacklist", "List of protocols that are not allowed to be used", OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D },
{"max_streams", "maximum number of streams", OFFSET(max_streams), AV_OPT_TYPE_INT, { .i64 = 1000 }, 0, INT_MAX, D },
{"skip_estimate_duration_from_pts", "skip duration calculation in estimate_timings_from_pts", OFFSET(skip_estimate_duration_from_pts), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, D},
{NULL},
};