FFMPeg代码分析:AVFormatContext结构体

2023-11-23 04:58

本文主要是介绍FFMPeg代码分析:AVFormatContext结构体,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

从先前的demo中可以看到,进入main函数所定义的第一个变量就是AVFormatContext的指针:

int main(int argc, char *argv[])
{AVFormatContext *pFormatCtx = NULL;  ....
}
而且,往下看就会知道这个结构体将贯穿函数始终,avformat_open_input、av_find_stream_info、av_read_frame、av_close_input_file都需要这个结构作为参数。该结构在FFMPEG中的作用是解封装,即根据视频文件容器的后缀名(.mp4, .avi, .flv等)分析其中编码过的音频和视频流的保存方式并将其提取以供解码器进行解码。该结构的部分定义如下:

typedef struct AVFormatContext
{    const AVClass *av_class;struct AVInputFormat *iformat;struct AVOutputFormat *oformat;    void *priv_data;AVIOContext *pb;int ctx_flags;unsigned int nb_streams;AVStream **streams;char filename[1024];int64_t start_time;int64_t duration;int bit_rate;unsigned int packet_size;int max_delay;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.
#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)
#define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000 ///< Don't merge side data but keep it separate.unsigned int probesize;int max_analyze_duration;const uint8_t *key;int keylen;unsigned int nb_programs;AVProgram **programs;enum AVCodecID video_codec_id;enum AVCodecID audio_codec_id;enum AVCodecID subtitle_codec_id;unsigned int max_index_size;unsigned int max_picture_buffer;unsigned int nb_chapters;AVChapter **chapters;AVDictionary *metadata;int64_t start_time_realtime;int fps_probe_size;int error_recognition;AVIOInterruptCB interrupt_callback;int debug;
#define FF_FDEBUG_TS        0x0001int ts_id;int audio_preload;int max_chunk_duration;int max_chunk_size;int use_wallclock_as_timestamps;int avoid_negative_ts;int avio_flags;enum AVDurationEstimationMethod duration_estimation_method;unsigned int skip_initial_bytes;unsigned int correct_ts_overflow;int seek2any;int flush_packets;int probe_score;
} AVFormatContext;
该定义中部分成员变量的作用,其中与解码相关的由红色标出:
const AVClass *av_class:用于记录日志和设置AVOption的类,由avformat_alloc_context()设置。AVClass类中保存了包含该成员的类名、对象名和AVOption实例等信息;

AVInputFormat *iformat / AVOutputFormat *oformat:输入/输出格式,或为输入或为输出,这两个不能同时存在;分别用于编码和解码。AVOutputFormat类保存了文件名、扩展名、默认音视频和字幕解码器等信息;

void *priv_data:私有数据,只有iformat/oformat.priv_class非0时有效;

AVIOContext *pb:指向IO数据的缓存的指针;

int ctx_flags:媒体流信息;

unsigned int nb_streams:包含的媒体流的数量

AVStream **streams:指向实际输入/输出数据的指针

char filename[1024]:输入或输出的文件名

int64_t start_time / int64_t duration:媒体的开始时间和持续时长,一般由AVStream **streams导出;

int bit_rate:视频码率,由ffmpeg自动计算;

unsigned int packet_size:包尺寸;

int max_delay:最大延迟;

unsigned int probesize:在解码时用于探测的数据的大小,编码时不使用;

int max_analyze_duration:允许输入数据在avformat_find_stream_info()中分析的最长时间;

const uint8_t *key:关键字;

int keylen:关键字长;

unsigned int nb_programs:包含节目的数量;

AVProgram **programs:指向实际节目的指针;

enum AVCodecID video_codec_id:指定的视频解码器ID;由用户指定;

enum AVCodecID audio_codec_id:指定的音频解码器ID;由用户指定;

enum AVCodecID subtitle_codec_id:指定的字幕解码器ID;由用户指定;

unsigned int max_index_size:用于检索码流的索引值的最大字长;

unsigned int max_picture_buffer:图像缓存的最大尺寸;

unsigned int nb_chapters:章节数目;

AVChapter **chapters:指向章节数据的指针;

AVDictionary *metadata:元数据;

int64_t start_time_realtime;:码流开始的实际时间,以毫秒为单位;

int fps_probe_size:表明使用了多少帧用于格式检测;

int error_recognition:错误检测的阈值;

AVIOInterruptCB interrupt_callback:在IO层用户自定义的回调结构;

int ts_id:传输流的ID;

unsigned int skip_initial_bytes:打开媒体流时跳过的字节数;

int seek2any:是否可以移动到任意一帧的位置;

int flush_packets:每处理一个packet之后刷新io上下文;

int probe_score:格式探测的分值,上限为AVPROBE_SCORE_MAX;


指向元数据的结构体AVDictionary由两部分组成:统计计数和指向AVDictionaryEntry的指针,也就是说AVDictionary可以说是AVDictionaryEntry的封装。

struct AVDictionary 
{int count;AVDictionaryEntry *elems;
};
typedef struct AVDictionaryEntry{char *key;char *value;
} AVDictionaryEntry;

了解了AVFormatContext的构造之后,可以再来看看它是如何被初始化的了。在函数avformat_open_input中,AVFormatContext的初始化由avformat_alloc_context()及其调用的函数实现:

AVFormatContext *avformat_alloc_context(void)
{AVFormatContext *ic;ic = av_malloc(sizeof(AVFormatContext));if (!ic) return ic;avformat_get_context_defaults(ic);return ic;
}
static void avformat_get_context_defaults(AVFormatContext *s)
{memset(s, 0, sizeof(AVFormatContext));s->av_class = &av_format_context_class;av_opt_set_defaults(s);
}
void av_opt_set_defaults(void *s)
{
#if FF_API_OLD_AVOPTIONSav_opt_set_defaults2(s, 0, 0);
}
void av_opt_set_defaults2(void *s, int mask, int flags)
{
#endifconst AVClass *class = *(AVClass **)s;const AVOption *opt = NULL;while ((opt = av_opt_next(s, opt)) != NULL) {
#if FF_API_OLD_AVOPTIONSif ((opt->flags & mask) != flags)continue;
#endifswitch (opt->type) {case AV_OPT_TYPE_CONST:/* Nothing to be done here */break;case AV_OPT_TYPE_FLAGS:case AV_OPT_TYPE_INT:case AV_OPT_TYPE_INT64:case AV_OPT_TYPE_DURATION:case AV_OPT_TYPE_CHANNEL_LAYOUT:av_opt_set_int(s, opt->name, opt->default_val.i64, 0);break;case AV_OPT_TYPE_DOUBLE:case AV_OPT_TYPE_FLOAT: {double val;val = opt->default_val.dbl;av_opt_set_double(s, opt->name, val, 0);}break;case AV_OPT_TYPE_RATIONAL: {AVRational val;val = av_d2q(opt->default_val.dbl, INT_MAX);av_opt_set_q(s, opt->name, val, 0);}break;case AV_OPT_TYPE_COLOR:case AV_OPT_TYPE_STRING:case AV_OPT_TYPE_IMAGE_SIZE:case AV_OPT_TYPE_VIDEO_RATE:av_opt_set(s, opt->name, opt->default_val.str, 0);break;case AV_OPT_TYPE_PIXEL_FMT:
#if LIBAVUTIL_VERSION_MAJOR < 53if (class->version && class->version < AV_VERSION_INT(52, 10, 100))av_opt_set(s, opt->name, opt->default_val.str, 0);else
#endifav_opt_set_pixel_fmt(s, opt->name, opt->default_val.i64, 0);break;case AV_OPT_TYPE_SAMPLE_FMT:
#if LIBAVUTIL_VERSION_MAJOR < 53if (class->version && class->version < AV_VERSION_INT(52, 10, 100))av_opt_set(s, opt->name, opt->default_val.str, 0);else
#endifav_opt_set_sample_fmt(s, opt->name, opt->default_val.i64, 0);break;case AV_OPT_TYPE_BINARY:/* Cannot set default for binary */break;default:av_log(s, AV_LOG_DEBUG, "AVOption type %d of option %s not implemented yet\n", opt->type, opt->name);}}
}

可以看到,在av_opt_set_defaults2()函数中,首先会调用av_opt_next(s, opt),并根据opt->type的不同值来进行下一步的设置。av_opt_next()的代码:

const AVOption *av_opt_next(void *obj, const AVOption *last)
{AVClass *class = *(AVClass**)obj;if (!last && class && class->option && class->option[0].name)return class->option;if (last && last[1].name)return ++last;return NULL;
}
由于初始化时opt=NULL,因此该函数返回AVFormatContext::AVClass::option,并在下一次调用的时候作为参数传入。在options_table.h中可以看出该函数将avformat_options[]表中的设置数据逐条地返回给调用者,并通过其type进行下一步操作。以av_opt_set_int()为例:

int av_opt_set_int(void *obj, const char *name, int64_t val, int search_flags)
{return set_number(obj, name, 1, 1, val, search_flags);
}
static int set_number(void *obj, const char *name, double num, int den, int64_t intnum, int search_flags)
{void *dst, *target_obj;const AVOption *o = av_opt_find2(obj, name, NULL, 0, search_flags, &target_obj);if (!o || !target_obj)return AVERROR_OPTION_NOT_FOUND;dst = ((uint8_t*)target_obj) + o->offset;return write_number(obj, o, dst, num, den, intnum);
}
由此我们知道了,AVFormatContext结构体中AVOption的元素通过这些函数,由options_table.h中的预定义表生成。


这篇关于FFMPeg代码分析:AVFormatContext结构体的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/415496

相关文章

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

Python实现Excel批量样式修改器(附完整代码)

《Python实现Excel批量样式修改器(附完整代码)》这篇文章主要为大家详细介绍了如何使用Python实现一个Excel批量样式修改器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录前言功能特性核心功能界面特性系统要求安装说明使用指南基本操作流程高级功能技术实现核心技术栈关键函

Vite 打包目录结构自定义配置小结

《Vite打包目录结构自定义配置小结》在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义... 目录一、实现原理二、具体配置步骤1. 基础配置文件2. 配置说明(1)js 资源分离(2)非 JS 资

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

Python 基于http.server模块实现简单http服务的代码举例

《Python基于http.server模块实现简单http服务的代码举例》Pythonhttp.server模块通过继承BaseHTTPRequestHandler处理HTTP请求,使用Threa... 目录测试环境代码实现相关介绍模块简介类及相关函数简介参考链接测试环境win11专业版python

Python从Word文档中提取图片并生成PPT的操作代码

《Python从Word文档中提取图片并生成PPT的操作代码》在日常办公场景中,我们经常需要从Word文档中提取图片,并将这些图片整理到PowerPoint幻灯片中,手动完成这一任务既耗时又容易出错,... 目录引言背景与需求解决方案概述代码解析代码核心逻辑说明总结引言在日常办公场景中,我们经常需要从 W

使用Spring Cache本地缓存示例代码

《使用SpringCache本地缓存示例代码》缓存是提高应用程序性能的重要手段,通过将频繁访问的数据存储在内存中,可以减少数据库访问次数,从而加速数据读取,:本文主要介绍使用SpringCac... 目录一、Spring Cache简介核心特点:二、基础配置1. 添加依赖2. 启用缓存3. 缓存配置方案方案

MySQL的配置文件详解及实例代码

《MySQL的配置文件详解及实例代码》MySQL的配置文件是服务器运行的重要组成部分,用于设置服务器操作的各种参数,下面:本文主要介绍MySQL配置文件的相关资料,文中通过代码介绍的非常详细,需要... 目录前言一、配置文件结构1.[mysqld]2.[client]3.[mysql]4.[mysqldum

基于Java和FFmpeg实现视频压缩和剪辑功能

《基于Java和FFmpeg实现视频压缩和剪辑功能》在视频处理开发中,压缩和剪辑是常见的需求,本文将介绍如何使用Java结合FFmpeg实现视频压缩和剪辑功能,同时去除数据库操作,仅专注于视频处理,需... 目录引言1. 环境准备1.1 项目依赖1.2 安装 FFmpeg2. 视频压缩功能实现2.1 主要功