ffmpeg 中 write_option()函數詳細注釋


author: hjjdebug
date: 2025年 07月 11日 星期五 10:51:23 CST
descrip: ffmpeg 中 write_option()函數詳細注釋


文章目錄

  • 1. 函數原型
    • 1.1 參數說明
    • 1.2 SpecifierOpt 說明符選項結構
  • 2. write_option 代碼注釋
    • 2.1 誰調用了write_option 函數?
  • 3. 小結:

write_option()不僅在ffmpeg 中, ffplay,ffprobe 都調用了該函數,
write_option是處理命令行參數解析的核心函數,
負責將參數值寫入目標地址(全局變量或結構體字段)
所以這里著重介紹一下:

1. 函數原型

static int write_option(void *optctx,
const OptionDef *opt,
const char *key,
const char *val)

1.1 參數說明

optctx:目標上下文(NULL時寫入全局變量)
opt:選項實例指針,"選項定義OptionDef"完整定義如下

typedef struct OptionDef {const char *name; //這里的name 就是 查找時的key值, 由此找到實例指針int flags; // val的解釋由flags來定義,它還有一個功能確定目標類型union { //目標位置,函數指針,偏移量共用一個union, 到底是什么由flags確定void *dst_ptr;int (*func_arg)(void *, const char *, const char *);size_t off;} u; 	 //下面2個字符串在幫助信息中使用const char *help;    //幫助的提示信息const char *argname; //參數的名稱信息 } OptionDef;

key:原始命令行鍵字符串
由key 值找到的OptionDef 實例指針 opt, 但key在這里不是多余的,因為key可能還帶有:及附加信息
val 鍵對應的值, 是一個字符串.
通過opt->flags 可以將其轉換為多種數據類型.
OPT_BOOL: 轉換為bool值
OPT_STRING:分配內存并拷貝字符串
OPT_INT64:用strtol轉換成數值
OPT_OFFSET|OPT_SPEC 決定ctx中的偏移位置
目標地址由opt->u決定,可能是全局固定地址,或者optctx中的一個偏移位置,或者全局函數

舉幾個OptionDef 實例的例子.

    -f 選項,有參數,是OFFSET,可為輸入或輸出參數,偏移量OFFSET(format),幫助信息 "force format", 幫助參數名 "fmt"{ "f",              HAS_ARG | OPT_STRING | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT,{ .off       = OFFSET(format) }, "force format", "fmt" }-y 選項, 布爾值, 直接給全局變量file_overwrite賦值,幫助信息:"overwrite output files", 參數名稱沒有設置{ "y",              OPT_BOOL,                                    {              &file_overwrite }, "overwrite output files" }-c 選項, 帶參數,是字符串, 是OPT_SPEC, 可為輸入或輸出參數,偏移位置在OFFSET(codec_names)幫助信息是 "codec name", 幫助參數是"codec"{ "c",          HAS_ARG | OPT_STRING | OPT_SPEC | OPT_INPUT | OPT_OUTPUT,  { .off       = OFFSET(codec_names) }, "codec name", "codec" },-vcodec key,帶參數是視頻參數,輸入或輸出參數,屬于文件范圍.沒有說明值值是什么類型,目標位置是函數{ "vcodec",       OPT_VIDEO | HAS_ARG  | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT,{ .func_arg = opt_video_codec }, "force video codec ('copy' to copy stream)", "codec" },

OPT_SPEC 的意思是說明符選項,在目標位置,存儲的是一個數組指針,可以保存很多條信息.
此例codec_name 可以保留-c:v h264 -c:a aac 等, 要把"v"對應的"h264","a"對應的"aac"這些信息都保留下來
與上面類似的還有-b:v -b:a 設置等.
其實SPEC 的使用還有很多,例如 -r frame_rate; -aspect aspect 設置; -pix_fmt pixel 設置
他們都是一個鍵對應著若干個值. 這些值可以作為候選值.

1.2 SpecifierOpt 說明符選項結構

OPT_SEC 對應的是一個SpecifierOpt 選項數組, 這里給出 SpecifierOpt的結構定義:

typedef struct SpecifierOpt {char *specifier;    /**< stream/chapter/program/... specifier,一般是"v","a","s","d" 字符串 */union {uint8_t *str;int        i;int64_t  i64;uint64_t ui64;float      f;double   dbl;} u; } SpecifierOpt;

2. write_option 代碼注釋

鋪墊差不多了,把代碼copy 來標注一下:

static int write_option(void *optctx, const OptionDef *po, const char *opt, const char *arg)
{//確定目標地址,帶OPT_OFFSET或OPT_SPEC, 地址在optctx結構內部(optctx+offset),否則是全局變量地址void *dst = po->flags & (OPT_OFFSET | OPT_SPEC) ?(uint8_t *)optctx + po->u.off : po->u.dst_ptr;int *dstcount;double num;int ret;if (po->flags & OPT_SPEC) { //有SPEC 標志SpecifierOpt **so = dst;  //目標位置是一個SpecifierOpt數組的地址char *p = strchr(opt, ':'); //把key值用:分割char *str;dstcount = (int *)(so + 1); //數組地址下面so+1是一個整數地址,這里將要存儲數組的大小ret = grow_array((void**)so, sizeof(**so), dstcount, *dstcount + 1);//把數組擴大1個if (ret < 0)return ret;str = av_strdup(p ? p + 1 : ""); //有:復制:后面部分,無冒號不復制(復制空)if (!str)return AVERROR(ENOMEM);
//dup的字符串就是specifier,翻譯為"說明符",一般是"v""a""s""d"(*so)[*dstcount - 1].specifier = str; dst = &(*so)[*dstcount - 1].u; //調整dst 為新分配的SpecifierOpt的u位置,把值存到這里}if (po->flags & OPT_STRING) { //如果是字符串,把字符串dup,地址放到目標處char *str;str = av_strdup(arg);av_freep(dst);if (!str)return AVERROR(ENOMEM);*(char **)dst = str;} else if (po->flags & OPT_BOOL || po->flags & OPT_INT) {ret = parse_number(opt, arg, OPT_INT64, INT_MIN, INT_MAX, &num);if (ret < 0)return ret;*(int *)dst = num; //bool值或int,將參數變成數值,保存到目標} else if (po->flags & OPT_INT64) {ret = parse_number(opt, arg, OPT_INT64, INT64_MIN, INT64_MAX, &num);if (ret < 0)return ret;*(int64_t *)dst = num; //64位,那就按64位存儲} else if (po->flags & OPT_TIME) {ret = av_parse_time(dst, arg, 1); //目標存時間if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Invalid duration for option %s: %s\n",opt, arg);return ret;}} else if (po->flags & OPT_FLOAT) {ret = parse_number(opt, arg, OPT_FLOAT, -INFINITY, INFINITY, &num);if (ret < 0)return ret;*(float *)dst = num; //目標存float值} else if (po->flags & OPT_DOUBLE) {ret = parse_number(opt, arg, OPT_DOUBLE, -INFINITY, INFINITY, &num);if (ret < 0)return ret;*(double *)dst = num; //目標存float值} else if (po->u.func_arg) { //如果實例設置了函數指針,則執行該函數int ret = po->u.func_arg(optctx, opt, arg);if (ret < 0) {av_log(NULL, AV_LOG_ERROR,"Failed to set value '%s' for option '%s': %s\n",arg, opt, av_err2str(ret));return ret;}}if (po->flags & OPT_EXIT) //選項實例要求退出,那就返回退出碼return AVERROR_EXIT;return 0;
}

2.1 誰調用了write_option 函數?

1. ffplay ffprobe 有 parse_options -> parse_option ->write_option 調用
int parse_option(void *optctx, const char *opt, const char *arg, const OptionDef *options)
它們定義的OptionDef表options 基本上都是直接分析到全局變量.
optctx 就是空,沒有使用.
opt 是key, arg 是value
其功能是依據options 表, 根據key值 opt,將val值 arg寫入目的地址.

其再上一層parse_options() 其輸入參數直接就是命令行參數. 就是循環調用parse_option
void parse_options(void *optctx, int argc, char **argv, const OptionDef *options,
void (*parse_arg_function)(void , const char))
這2個函數都比較簡單,就不多做分析了.
整體的功能是把命令行參數寫到指定的全局變量中以供使用

2. ffmpeg 中調用 parse_optgroup() -> write_option()
int parse_optgroup(void *optctx, OptionGroup *g)
這一次較為復雜,因為它使用了optctx, 不再是空了,
其2引入了OptionGroup 概念. 什么是OptionGroup? 就是選項組.
我們看看它的簡化定義,忽略暫時未使用部分.

typedef struct OptionGroup {Option *opts;  //OptionGroup 就是 Option 動態數組,Option就是3個指針int  nb_opts;... //忽略暫時不關注部分
} OptionGroup;

parse_optgroup()功能也很好理解, write_option 寫了一條option,
parse_optgroup就是循環調用write_option, 把group中所有的option都寫出去.

那OptionGroup 從哪里來? 誰調用了parse_optgroup? 有2處
在ffmpeg_parse_options() 函數中, 有調用分析選項組函數parse_optgroup,全局選項組為參數,
這些分析的結果都為寫到全局變量中,比較簡單

int ffmpeg_parse_options(int argc, char **argv)
{...ret = parse_optgroup(NULL, &octx.global_opts);ret = open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);ret = open_files(&octx.groups[GROUP_OUTFILE], "output", open_output_file);...
}

還有一處,在打開文件的時候. 實際是2次,打開輸入文件列表,打開輸出文件列表
static int open_files(OptionGroupList *l, const char inout,
int (open_file)(OptionsContext, const char
))
看參數我們知道,它又引入了2個概念. 先看第一個OptionGroupList

typedef struct OptionGroupList {const OptionGroupDef *group_def; //不重要,主要是名稱,分割符等OptionGroup *groups; //選項組的數組int       nb_groups;
} OptionGroupList;

既然是選項組的數組,那就循環調用parse_optgroup(),把結果存到OptionsContext 結構中 o
一個文件對應一個選項組,多個文件對應多個選項組,多個選項組組織成一個表叫選項組列表

這個函數的關鍵是調用回調函數open_file,傳遞參數OptionContext &o 和 文件名g->arg
ret = open_file(&o, g->arg);
那OptionsContext 是什么結構呢?
這個很復雜,就不用copy了,它是ffmpeg 特有的,ffplay,ffprobe 都不使用這個結構.
該context定義了ffmpeg options中所有的選項值的存儲位置, 并為后面的open_file服務.
這就是ffmpeg 為什么命令行參數多樣化的來歷.

3. 小結:

  1. 選項key/value的引入
    本來選項用argv[i]訪問挺好的.
    但是由于參數變得復雜,我們引入了key/value的概念.
    只所以引入key, 就是因為這樣val不再依賴位置i來確定了,而是依賴key來確定.
    例如原來是arg[3]為輸入文件名, 現在規定-i 選項后面跟文件名,
    顯然后者更靈活.

  2. 選線組的引入
    一個選項只能說明一個屬性,多個選項說明多個屬性,所以選線組是很自然的. 一個文件對應一個選項組

  3. 選項組列表的引入
    一個文件對應一個選項組,那多個文件對應多個選項組,把多個"選項組"組織成表叫選項組列表,
    我們把多個文件按輸入文件,輸出文件分組. 則可以分成2個文件組,
    輸入文件組對應輸入選項組列表,
    輸出文件組對應輸出選項組列表,

好了,有了這些概念,您大概可以讀懂ffmpeg 的ffmpeg_parse_options 函數了.
至于它的split_commandline()及open_files() 這里就不展開了. 保持簡潔和高效!
讀懂了ffmpeg_parse_options函數,就讀懂了框架的1/2.

作為實戰,不妨分析一下下面還算簡單的命令行參數
ffmpeg -v debug -c:v h264 -i ./1.ts -f null -

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/914062.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/914062.shtml
英文地址,請注明出處:http://en.pswp.cn/news/914062.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PandaCoder重大產品更新-引入Jenkinsfile文件支持

寫在前面 安裝這個插件可以直接平替 Jenkinsfile Pro &#xff0c;節省200元關于插件介紹的處女篇&#xff1a;https://mp.weixin.qq.com/s/fwMEhmx8vxVlvfnipx09Ag為什么叫「熊貓編碼助手」&#xff1f; 熊貓是中國的國寶&#xff0c;備受世界喜愛&#xff0c;代表著中國特色和…

鏈表算法之【判斷鏈表中是否有環】

目錄 LeetCode-141題 LeetCode-141題 給定一個鏈表的頭節點&#xff0c;判斷鏈表中是否存在環 class Solution {public boolean hasCycle(ListNode head) {// checkif (head null || head.next null)return false;// 定義兩個指針&#xff0c;一個快指針[fast]&#xff0c…

Ubuntu 22.04安裝SQL Server指南

看起來在安裝過程中出現了問題&#xff0c;導致 mssql-server 沒有正確安裝。以下是排查和修復步驟&#xff1a;1. 檢查是否成功安裝了 mssql-server 運行以下命令&#xff0c;確認是否已安裝&#xff1a; dpkg -l | grep mssql-server如果沒有任何輸出&#xff0c;說明 mssql-…

Vue+ElementUI聊天室開發指南

Hi&#xff0c;我是布蘭妮甜 &#xff01;在現代Web應用中&#xff0c;實時聊天功能已成為許多社交平臺、協作工具和客戶支持系統的核心需求。本文將詳細介紹如何使用Vue.js框架配合ElementUI組件庫實現一個功能完整的聊天室應用。我們將從項目搭建開始&#xff0c;逐步實現用戶…

提升你的AI交互技能:使用Anthropic互動提示教程

探索Anthropic的互動式提示工程教程&#xff1a;讓Claude與你更默契 在當今人工智能世界中&#xff0c;熟練掌握有效的提示工程成為了與AI進行高效溝通的關鍵。Anthropic推出了一款全面且互動性強的教程&#xff0c;名為“Prompt Engineering Interactive Tutorial”&#xff0…

從 JavaFX WebView 遷移至 JxBrowser

長久以來&#xff0c;JavaFX 一直包含一個內置的 WebView 組件&#xff0c;這是在 Java 應用中渲染 Web 內容的一個穩定方案。然而&#xff0c;在更復雜或要求更高的使用場景中&#xff0c;它可能就不夠用了。因此&#xff0c;許多開發者轉向了像 JxBrowser 這樣的替代方案。 …

將 Go 應用從 x86 平臺遷移至 Amazon Graviton:場景剖析與最佳實踐

簡介 近年來&#xff0c;Amazon Graviton 處理器以其優越的性價比和強勁的性能&#xff0c;成為了構建高效、可擴展云原生應用的重要選擇。Graviton 采用基于 Arm64 架構的芯片&#xff0c;與傳統的 x86 架構相比存在不少架構差異。雖然 Go 天生對 Arm64 具有良好支持&#xf…

arcgis api for js 設置地圖服務請求帶有請求頭信息

通過地圖的config模塊的請求攔截器來設置請求頭信息&#xff0c;如下示例&#xff1a; 1、引入&#xff1a;‘esri/config’ 1、設置請求頭信息 import { loadArcgisModules } from /utils/map/mapLoadUtil export default { mounted() {this.loadMap()}, methods: {/** ****…

工業通信升級新選擇:耐達訊CCLINKIE轉Modbus TCP網關

在工業自動化系統中&#xff0c;協議轉換網關的選擇直接影響系統穩定性與通信效率。對于CCLINKIE轉Modbus TCP場景&#xff0c;耐達訊通信技術網關憑借以下特性&#xff0c;成為多個項目中的優選方案。技術選型要點協議兼容性支持CCLINKIE的令牌環機制與Modbus TCP的TCP/IP協議…

使用python的 FastApi框架開發圖書管理系統-前后端分離項目分享

今天給大家分享一個 我最近使用python 框架 fastapi 寫的一個web項目 &#xff0c;叫圖書管理系統。項目主要是來鞏固 python的編程技術。使用的是前端后 分離開發。 主要實現的功能&#xff1a; 1、用戶管理&#xff1a;可以新增、編輯、刪除用戶信息。 2、圖書管理&#xff1…

上位機知識篇---Docker

Docker 詳細介紹 一、Docker 是什么 Docker 是一個開源的容器化平臺&#xff0c;它允許開發者將應用程序及其依賴項打包到一個標準化的單元&#xff08;稱為容器&#xff09;中&#xff0c;確保應用在任何環境中都能以相同的方式運行。 簡單來說&#xff0c;Docker 解決了 &…

藍橋杯第十六屆(2025)真題深度解析:思路復盤與代碼實戰

> 省一選手的血淚經驗:**避免這些坑,你也能沖進國賽!** 2025年藍橋杯省賽已落下帷幕,作為近年來**難度最高的一屆競賽**,不少選手在考場上遭遇了“滑鐵盧”。本文將以C++ B組真題為例,逐題解析解題思路,并提供**優化后的AC代碼與詳細注釋**。筆者最終排名省一前40%,…

使用gdal讀取shp及filegdb文件

一、使用qgis開源工具構建兩個文件&#xff0c;分別是filegdb和shp&#xff0c;每個文件包含一個圖層&#xff0c;圖層內容只包含一個字段&#xff1a;id&#xff0c;有兩個數據行&#xff0c;圖層幾何為多邊形&#xff0c;圖層都是如下的效果。二、使用rust讀取上述文件 rust依…

從0開始學習R語言--Day44--LR檢驗

之前我們提到用LM檢驗的方式&#xff0c;來判斷數據在空間上是否受到鄰近數據及其殘差的影響&#xff0c;但是LM檢驗是采用直接計算的方式&#xff0c;只關注了數據的殘差平方和&#xff0c;沒有數據關于依賴項的考慮&#xff0c;容易被結果誤導。而LR檢驗雖然在結果上有時候跟…

openEuler 24.03 (LTS-SP1) 下私有鏡像倉庫部署與自簽 SSL 全流程目標

目錄 openEuler 24.03 (LTS-SP1) 下私有鏡像倉庫部署與自簽 SSL 全流程 1 創建根 CA 與服務器證書&#xff08;修正版&#xff1a;SAN 寫法兼容所有 OpenSSL&#xff09; 2 配置 Docker Compose 文件 3 客戶端節點信任 CA 3.1 Docker 3.2 containerd 4 推送 / 拉取測試 …

mysql的LIMIT 用法

常見用法1. 限制返回行數-- 返回前5條記錄 SELECT * FROM products LIMIT 5;2. 分頁查詢&#xff08;帶偏移量&#xff09;-- 跳過前10條&#xff0c;返回接下來的5條記錄&#xff08;第11-15條&#xff09; SELECT * FROM products LIMIT 10, 5;-- MySQL 8.0 也支持這種語法 S…

maven 發布到中央倉庫之持續集成-03

maven 系列 maven-01-發布到中央倉庫概覽 maven-02-發布到中央倉庫常用腳本 maven-03-發布到中央倉庫之持續集成 maven-04-發布到中央倉庫之 Ignore Licence maven-05-maven 配置進階學習 maven-06-maven 中央倉庫 OSSRH 停止服務&#xff0c;Central Publishing Portal …

(補充)RS422

RS4221. 基本定義與定位 官方名稱&#xff1a; EIA/TIA-422&#xff08;電子工業協會/電信工業協會標準422&#xff09;。類型&#xff1a; 一種定義了電氣特性的 平衡式差分 串行通信標準。目的&#xff1a; 克服 RS-232 在傳輸距離、速率和抗干擾能力上的嚴重局限性。核心思想…

自建ELK vs 云商日志服務:成本對比分析

在當今數據驅動的時代&#xff0c;日志管理已成為企業IT基礎設施中不可或缺的一部分。面對日益增長的日志數據&#xff0c;許多團隊都在糾結&#xff1a;是自建ELK&#xff08;Elasticsearch、Logstash、Kibana&#xff09;堆棧&#xff0c;還是直接使用云服務商提供的日志服務…

Eigen 幾何模塊深拆:Isometry3d vs Affine3d + 變換矩陣本質詳解

文章目錄0 寫在前面1 數學背景對比2 Eigen 實現差異3 Isometry3d 是不是 4 4 矩陣&#xff1f;4 核心 API 速查5 實戰示例5.1 SLAM 位姿鏈&#xff1a;相機點 → 世界點5.2 體素濾波&#xff1a;各向異性縮放&#xff08;X/Y → 5 cm&#xff0c;Z → 10 cm&#xff09;5.3 把…