当前位置: 首页 > news >正文

ffplay源码分析-main函数入口分析

ffplay源码分析-main函数入口分析

基于ffmpeg6.0源码分析。

流程

使用ffplay播放视频文件,会触发main函数的调用。main函数中会进行以下操作:

  1. 从命令行中解析日志级别、日志是否需要落文件、是否要输出banner信息。banner信息包含版权、库的版本。
  2. 注册解码器、复用器、协议。
  • avdevice_register_all会将输出formats和输入formats注册到allformats.c的变量中。
    allformat.c
void avpriv_register_devices(const FFOutputFormat * const o[], const AVInputFormat * const i[])
{// 还是个原子操作赋值。atomic_store_explicit(&outdev_list_intptr, (uintptr_t)o, memory_order_relaxed);atomic_store_explicit(&indev_list_intptr,  (uintptr_t)i, memory_order_relaxed);
}

alldevices.c

void avdevice_register_all(void)
{avpriv_register_devices(outdev_list, indev_list);
}

其中outdev_list来自outdev_list.c文件,idev_list来自indev_list.c文件。这2个文件是自动生成的。所以我猜它是根据我们不同的配置来生成不同的内容。
我的indev_list.c

static const AVInputFormat * const indev_list[] = {
&ff_avfoundation_demuxer,
&ff_lavfi_demuxer,
NULL };

我的outdev_list.c

static const FFOutputFormat * const outdev_list[] = {
&ff_audiotoolbox_muxer,
&ff_sdl2_muxer,
NULL };
  1. 初始化网络,会根据使用的是openssl还是guntls进行初始化,对于window,有可能需要调用一下WSAStartup,ms-socket。
void ff_tls_deinit(void)
{
#if CONFIG_TLS_PROTOCOL
#if CONFIG_OPENSSLff_openssl_deinit();
#endif
#if CONFIG_GNUTLSff_gnutls_deinit();
#endif
#endif
}int ff_network_init(void)
{
#if HAVE_WINSOCK2_HWSADATA wsaData;// Windowsif (WSAStartup(MAKEWORD(1,1), &wsaData))return 0;
#endifreturn 1;
}
  1. 监听程序退出信号,这一步是为了能响应用户关闭程序的操作。比如在播放过程中,在命令行中按ctrl-c就会退出程序。
static void sigterm_handler(int sig)
{exit(123);
}
  1. 显示banner信息,banner信息包含版本、配置等。
void show_banner(int argc, char **argv, const OptionDef *options)
{int idx = locate_option(argc, argv, options, "version");if (hide_banner || idx)return;// 打印ffmpeg的版本,版权信息、配置信息print_program_info (INDENT|SHOW_COPYRIGHT, AV_LOG_INFO);// 打印子库的配置信息print_all_libs_info(INDENT|SHOW_CONFIG,  AV_LOG_INFO);// 打印字库的版本信息print_all_libs_info(INDENT|SHOW_VERSION, AV_LOG_INFO);
}
  1. 解析命令行参数,得到需要播放的文件,解析成功之后全局变量input_filename存储的就是输入的文件。
void parse_options(void *optctx, int argc, char **argv, const OptionDef *options,void (*parse_arg_function)(void *, const char*))
{const char *opt;int optindex, handleoptions = 1, ret;prepare_app_arguments(&argc, &argv); // 空的,不用管optindex = 1;while (optindex < argc) {opt = argv[optindex++];if (handleoptions && opt[0] == '-' && opt[1] != '\0') {if (opt[1] == '-' && opt[2] == '\0') {handleoptions = 0; // 如果参数是"--" ,即没有拼参数名,则跳过。continue;}opt++;// 如果小于0,说明解析参数发生了错误。if ((ret = parse_option(optctx, opt, argv[optindex], options)) < 0)exit_program(1); // 退出程序。optindex += ret;} else {if (parse_arg_function)  // 如果上一个参数是跳过的(handleoptiongs是0),或者参数不是"-"开始的一个字符串,不能只是一个"-",则执行这个函数。parse_arg_function(optctx, opt); // 这种情况opt可能是要处理文件。}}
}
// parse_arg_function 就指向这个函数:
static void opt_input_file(void *optctx, const char *filename)
{if (input_filename) {av_log(NULL, AV_LOG_FATAL,"Argument '%s' provided as input filename, but '%s' was already specified.\n",filename, input_filename);exit(1);}if (!strcmp(filename, "-"))filename = "fd:";input_filename = filename; // 要播放的输入文件。
}
  1. 如果没解析到要播放的文件,输出帮助信息,并退出程序。
  2. 初始化SDL库,ffplay播放音频、视频都用到SDL库。
  3. 根据用户的配置、SDL的版本、来设置SDL显示窗口的配置,比如是否显示窗口边界。使用默认大小来创建一个SDL窗口。
 int flags = SDL_WINDOW_HIDDEN;if (alwaysontop)
#if SDL_VERSION_ATLEAST(2,0,5)flags |= SDL_WINDOW_ALWAYS_ON_TOP;
#elseav_log(NULL, AV_LOG_WARNING, "Your SDL version doesn't support SDL_WINDOW_ALWAYS_ON_TOP. Feature will be inactive.\n");
#endifif (borderless) // 命令参数可以控制flags |= SDL_WINDOW_BORDERLESS; // 控制SDL显示的窗口是否需要边界elseflags |= SDL_WINDOW_RESIZABLE;#ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITORSDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
#endif// 使用默认大小、窗口配置flags创建窗口。window = SDL_CreateWindow(program_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, default_width, default_height, flags);SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // https://wiki.libsdl.org/SDL2/SDL_HINT_RENDER_SCALE_QUALITY
  1. 创建renderer,renderer是SDK绘制图形必须要用到的东西。以及是否有可用的texture。
 if (window) {// 创建renderer, 可以用这个画图形。renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);if (!renderer) {av_log(NULL, AV_LOG_WARNING, "Failed to initialize a hardware accelerated renderer: %s\n", SDL_GetError());renderer = SDL_CreateRenderer(window, -1, 0);}if (renderer) {if (!SDL_GetRendererInfo(renderer, &renderer_info)) // 获取renderer的信息。av_log(NULL, AV_LOG_VERBOSE, "Initialized %s renderer.\n", renderer_info.name);}}if (!window || !renderer || !renderer_info.num_texture_formats) { // 如果没有渲染的条件,则退出程序。av_log(NULL, AV_LOG_FATAL, "Failed to create window or renderer: %s", SDL_GetError());do_exit(NULL);}... 
  1. 打开输入文件。
  2. 进入循环,监听键盘、鼠标事件,循环渲染视频。

代码

int main(int argc, char **argv)
{int flags;VideoState *is; // 用来保存全局状态init_dynload(); // 仅window会有用,安全问题,会将当前目录从DLL库搜索目录中删除。av_log_set_flags(AV_LOG_SKIP_REPEATED); // 设置日志如果是重复文案是否跳过parse_loglevel(argc, argv, options); // 从参数中解析日志级别、日志是否要落文件、是否要输出banner信息。/* register all codecs, demux and protocols */
#if CONFIG_AVDEVICEavdevice_register_all();
#endifavformat_network_init();// 处理用户退出程序操作。 https://zh.m.wikipedia.org/wiki/Unix%E4%BF%A1%E5%8F%B7#SIGTERMsignal(SIGINT , sigterm_handler); /* Interrupt (ANSI).  Ctrl-C   */signal(SIGTERM, sigterm_handler); /* Termination (ANSI). when user terminate  */// 显示banner, 就是打印动态库的版权、版本等信息。show_banner(argc, argv, options);parse_options(NULL, argc, argv, options, opt_input_file);if (!input_filename) {show_usage(); // 如果没有从参数中解析出要播放的文件,输出错误日志提示用户,并退出程序。av_log(NULL, AV_LOG_FATAL, "An input file must be specified\n");av_log(NULL, AV_LOG_FATAL,"Use -h to get full help or, even better, run 'man %s'\n", program_name);exit(1);}if (display_disable) { // 用户参数可以控制这个开关,默认关闭。video_disable = 1;}flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER; // 设置SDL初始化时的flags。包含了SDL音频子系统、SDL视频子系统、SDL时间子系统、事件子系统。if (audio_disable) // // 用户参数可以控制这个开关,默认关闭。flags &= ~SDL_INIT_AUDIO;else {/* Try to work around an occasional ALSA buffer underflow issue when the* period size is NPOT due to ALSA resampling by forcing the buffer size. */if (!SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE")) // 奇怪的fix.SDL_setenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE","1", 1);}if (display_disable)flags &= ~SDL_INIT_VIDEO;if (SDL_Init (flags)) { // 初始化SDL,https://wiki.libsdl.org/SDL2/SDL_Initav_log(NULL, AV_LOG_FATAL, "Could not initialize SDL - %s\n", SDL_GetError());av_log(NULL, AV_LOG_FATAL, "(Did you set the DISPLAY variable?)\n");exit(1);}// 不监听系统事件和用户自定义事件。https://wiki.libsdl.org/SDL2/SDL_EventSDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);SDL_EventState(SDL_USEREVENT, SDL_IGNORE);if (!display_disable) {int flags = SDL_WINDOW_HIDDEN;if (alwaysontop)
#if SDL_VERSION_ATLEAST(2,0,5)flags |= SDL_WINDOW_ALWAYS_ON_TOP;
#elseav_log(NULL, AV_LOG_WARNING, "Your SDL version doesn't support SDL_WINDOW_ALWAYS_ON_TOP. Feature will be inactive.\n");
#endifif (borderless)flags |= SDL_WINDOW_BORDERLESS; // 控制SDL显示的窗口是否需要边界elseflags |= SDL_WINDOW_RESIZABLE;#ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITORSDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
#endif// 使用默认大小、窗口配置flags创建窗口。window = SDL_CreateWindow(program_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, default_width, default_height, flags);SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // https://wiki.libsdl.org/SDL2/SDL_HINT_RENDER_SCALE_QUALITYif (window) {// 创建renderer, 可以用这个画图形。renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);if (!renderer) {av_log(NULL, AV_LOG_WARNING, "Failed to initialize a hardware accelerated renderer: %s\n", SDL_GetError());renderer = SDL_CreateRenderer(window, -1, 0);}if (renderer) {if (!SDL_GetRendererInfo(renderer, &renderer_info)) // 获取renderer的信息。av_log(NULL, AV_LOG_VERBOSE, "Initialized %s renderer.\n", renderer_info.name);}}if (!window || !renderer || !renderer_info.num_texture_formats) { // 如果没有渲染的条件,则退出程序。av_log(NULL, AV_LOG_FATAL, "Failed to create window or renderer: %s", SDL_GetError());do_exit(NULL);}}// 打开输入文件, 如果用户强制设置了fmt file_iformat则不为nullis = stream_open(input_filename, file_iformat);if (!is) {av_log(NULL, AV_LOG_FATAL, "Failed to initialize VideoState!\n");do_exit(NULL);}// 循环,监听键盘事件、刷新界面。event_loop(is); /* never returns */return 0;
}

相关文章:

ffplay源码分析-main函数入口分析

ffplay源码分析-main函数入口分析 基于ffmpeg6.0源码分析。 流程 使用ffplay播放视频文件&#xff0c;会触发main函数的调用。main函数中会进行以下操作&#xff1a; 从命令行中解析日志级别、日志是否需要落文件、是否要输出banner信息。banner信息包含版权、库的版本。注…...

C++三种继承方式

C继承的一般语法为&#xff1a;class 派生类名:&#xff3b;继承方式&#xff3d; 基类名{派生类新增加的成员};继承方式限定了基类成员在派生类中的访问权限&#xff0c;包括 public&#xff08;公有的&#xff09;、private&#xff08;私有的&#xff09;和 protected&#…...

【Android -- 软技能】《软技能:代码之外的生存指南》之好书推荐(一)

前言 这是一本由美国的一个软件开发人员写的&#xff0c;但书中除了有 Java 、C# 几个单词外&#xff0c;没有一行代码。 因为这本书讲的是代码之外的东西。 文章目录结构&#xff1a; 1. 职业 从业心态&#xff1a;说白了就是要有责任心&#xff0c;把每份工作要当成是自…...

Nginx可视化管理工具 - Nginx Proxy Manager

一、介绍 nginx-proxy-manager 是一个反向代理管理系统,它基于Nginx,具有漂亮干净的 Web UI。还可以获得受信任的 SSL 证书,并通过单独的配置、自定义和入侵保护来管理多个代理。 其官网地址如下: https://nginxproxymanager.com/ 二、安装 第一步:192.168.1.108服务…...

https是如何保证安全的

在学习http与https的区别的时候&#xff0c;我们通常从以下几点出发&#xff1a;http是超文本传输协议&#xff0c;是明文传输&#xff0c;有安全风险&#xff0c;https在TCP和http网络层之间加入了SSL/TLS安全协议&#xff0c;使得报文能够加密传输http连接简单&#xff0c;三…...

ubuntu下使用GCC开发单片机的过程

以下是一个简单的单片机C程序示例,实现的功能是控制LED灯的闪烁: #include <reg52.h> // 导入单片机的寄存器定义void main() {while(1) { // 无限循环P1 = 0x00; // P1口输出低电平delay(1000); // 延时1秒P1 = 0xff; // P1口输出高电平delay(1000); // 延时1秒…...

人工智能能否取代软硬件开发工程师

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 人工智能发展趋势 随着AI技术的不断发展&#xff0c;它正在改变我们的生活方式、商业模式和工作方式。人工智能技术的发展一直处于快速变化和持续创新的状态&#xff0c;以下…...

BPI-R3开发板 - uboot编译

一. 获取源码 https://github.com/mtk-openwrt/u-boot 二. 编译步骤 编译环境为ubuntu 18.04。交叉编译工具链我用的是openwrt编译生成的工具链&#xff0c;并设置到环境变量&#xff0c;如下&#xff1a; export PATH$PATH:/root/mt8976/BPI-R3-OPENWRT-V21.02.3-main/staging…...

优秀程序员的5个特征,你在第几层?

每个人程序员都对未来的职业发展有着憧憬和规划&#xff0c;要做架构师、要做技术总监、要做CTO。但现实总是复杂的&#xff0c;日复一日的工作与生活总能让人一次又一次地陷入迷茫。大部分原因就是对职业发展轨迹和自我能力提升的一般规律缺乏认识&#xff0c;做事找不到方向或…...

JAVA Session会话 Thymeleaf - 视图模板技术配置步骤

JAVAWebSession会话会话跟踪技术session保存作用域Thymeleaf - 视图模板技术配置过程Session会话 HTTP是无状态的&#xff1a;服务器无法区分这两个请求是同一个客户端发过来的&#xff0c;还是不同的客户端发过来的 现实问题&#xff1a;第一次请求是添加商品到购物车&#x…...

Linux编译cpprestsdk库

本文用的Linux系统为Ubuntu22.04&#xff0c;自带GCC11.3.0。 依赖 ①编译需要boost库&#xff0c;本文用的库版本为boost-1.82.0.beta1.tar.gz。 ②编译需要openssl库&#xff0c;这里使用的版本为openssl-1.1.1s.tar.gz。 ③编译需要cmake库&#xff0c;本文使用的是cmake-3…...

算法的时间复杂度和空间复杂度

目录 1 如何衡量一个算法的好坏 2.时间复杂度 2.1 时间复杂度的概念 2.2 大O的渐进表示法 2.3常见代码举例 2.3.1 Func2 O(N) 2.3.2 Func3 O(MN) 2.3.3 Func4 O(1) 2.3.4 Func5 strchr O(N) 2.3.5 Func6 冒泡排序 O(N^2) 2.3.6 Func7 二分…...

基本认识vue3

一、基本搭建 项目搭建 使用 最新的 Vue3 TS Vite项目 执行命令 &#xff08;本项目采用如下方式&#xff09; npm create vitelatest my-vite-app --template vue-ts或者 运行项目 npm install npm run dev项目搭建初始化目录 新搭建的项目可能会遇到个问题&#xf…...

HTTP/HTTPS协议认识

写在前面 这个博客我们要要讨论的是协议,主要是应用层.今天我们将正式认识HTTP和HTTPS,也要认识序列化和反序列化,内容比较多,但是不难 再谈协议 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层,我们要完成下面三个步骤. sock的使用 定制…...

【VScode】远程连接Linux

目录标题1. 安装扩展插件2. 在Linux上操作3. 确定Linux的IP地址4. 远程连接到Linux5. 实现免密码登录使用 VScode 远程编程与调试的时有会用到插件 Remote Development&#xff0c;使用这个插件可以在很多情况下代替 vim 直接远程修改与调试服务器上的代码&#xff0c;同时具备…...

QT/C++调试技巧:内存泄漏检测

文章目录内存泄漏方案一方案二&#xff1a;CRT调试定位代码位置方法1方法2其它问题方案三&#xff1a;使用vs诊断工具方案四&#xff1a;使用工具VLD&#xff08;Visio Leak Detector&#xff09;方案五Cppcheck内存泄漏 内存泄漏&#xff1a;指的是在程序里动态申请的内存在使…...

【贪心算法】一文让你学会“贪心”(贪心算法详解及经典案例)

文章目录前言如何理解“贪心算法”&#xff1f;贪心算法实战分析1.分糖果2.钱币找零3.区间覆盖内容小结最后说一句&#x1f431;‍&#x1f409;作者简介&#xff1a;大家好&#xff0c;我是黑洞晓威&#xff0c;一名大二学生&#xff0c;希望和大家一起进步。 &#x1f47f;本…...

【字体图标iconfont】字体图标部署流程+项目源码分析

今日&#xff0c;心情甚是烦闷&#xff0c;原由… 公司项目需要将字体图标做一些细微的调整&#xff0c;我一人分析了许久&#xff0c;看不大懂源码的逻辑&#xff0c;产生了自我怀疑。深吸一口气&#xff0c;重新鼓起勇气&#xff0c;调整心境&#xff0c;一下子豁然开朗&…...

2023最全的Web自动化测试介绍(建议收藏)

做测试的同学们都了解&#xff0c;做Web自动化&#xff0c;我们主要用Selenium或者是QTP。 有的人可能就会说&#xff0c;我没这个Java基础&#xff0c;没有Selenium基础&#xff0c;能行吗&#xff1f;测试虽然属于计算机行业&#xff0c;但其实并不需要太深入的编程知识&…...

jvm_根节点枚举安全点安全区域

1、可达性分析可以分成两个阶段 根节点枚举 从根节点开始遍历对象图 前文我们在介绍垃圾收集算法的时候&#xff0c;简单提到过&#xff1a;标记-整理算法(Mark-Compact)中的移动存活对象操作是一种极为负重的操作&#xff0c;必须全程暂停用户应用程序才能进行&#xff0c;像这…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...