FFmpeg 解析命令列引數
阿新 • • 發佈:2018-12-02
FFmpeg 命令列基礎語法:
ffmpeg [global_options] {[input_file_options] -i input_file}...{[output_file_options] output_file}...
- global_options:全域性引數
- input_file_options:輸入檔案相關引數
- output_file_options:輸出檔案相關引數
如下為一個簡單的 FFmpeg 命令,將 input.avi 視訊檔案轉換為 640kbps 位元速率的 output.avi
ffmpeg -i input.avi -b:v 640k output.avi
當我們使用命令列來呼叫 FFmpeg 時,當命令列傳入 FFmpeg 時,FFmpeg內部是如何識別這些命令並進行解析和賦值的呢?
首先簡單看下流程圖:
總結起來,解析命令列的大致流程就是:
- 跳過 “–xx xxx” 引數
- “-xx xxx” 格式的預設引數存入全域性引數陣列或臨時引數陣列
- “-noxx xxx”格式的引數,即預設值為“0”,將值存入全域性引數陣列或臨時引數陣列
- 解析專屬引數,並存入專屬陣列結構體(AVDictionary)
- “-i xxx” 格式的輸入檔案路徑引數,將臨時引數陣列的值、輸入檔案路徑以及專屬引數存入輸入相關引數結構體,並清空臨時引數陣列
- “xxx” 格式的輸出檔案路徑引數,將臨時引數陣列的值、輸出檔案路徑以及專屬引數存入輸出相關引數結構體,並清空臨時引數陣列
引數解析後存放在哪?
-
有關全域性引數、輸入引數、輸出引數都儲存到
OptionParseContext *octx
中typedef struct OptionParseContext { // 全域性命令分組 OptionGroup global_opts; // 輸入和輸出的命令分組 (groups[0] 儲存與輸出檔案相關引數,groups[1] 儲存與輸入檔案相關引數) OptionGroupList *groups;
-
專屬引數會先儲存到 AVDictionary
AVDictionary *codec_opts; AVDictionary *format_opts; AVDictionary *resample_opts; AVDictionary *sws_dict; AVDictionary *swr_opts;
下面通過程式碼來分析 split_commandline 是如何解析命令列的:
/**
* 解析命令列引數
* @param octx: 用來將命令列中的引數通過分析分別存入此結構對應變數中
* @param options: 預設引數的定義 (具體參見ffmpeg_opt.c檔案中的 const OptionDef options[] 定義)
* @param groups: 儲存輸入檔案和輸出檔案相關引數
* @param nb_groups: 2
*/
int split_commandline(OptionParseContext *octx, int argc, char *argv[],
const OptionDef *options,
const OptionGroupDef *groups, int nb_groups)
{
int optindex = 1;
int dashdash = -2;
/* perform system-dependent conversions for arguments list */
prepare_app_arguments(&argc, &argv);
/* 基本的記憶體分配,OptionGroupDef 與 OptionParseContext 建立聯絡 */
init_parse_context(octx, groups, nb_groups, data);
av_log(NULL, AV_LOG_DEBUG, "Splitting the commandline.\n");
/* 迴圈取出引數 */
while (optindex < argc) {
// 取引數佇列
const char *opt = argv[optindex++], *arg;
const OptionDef *po;
int ret;
av_log(NULL, AV_LOG_DEBUG, "Reading option '%s' ...", opt);
/* 跳過引數格式 --xx xxx */
if (opt[0] == '-' && opt[1] == '-' && !opt[2]) {
dashdash = optindex;
continue;
}
/* 如果不是以 - 開頭,另外一種情況,就是定義輸出檔案 */
if (opt[0] != '-' || !opt[1] || dashdash+1 == optindex) {
/**
* 將輸出的路徑、 octx->cur_group陣列的值以及專屬引數,儲存到 * octx->groups[0]結構體中,並清空 octx->cur_group 陣列的值
*/
finish_group(octx, 0, opt);
av_log(NULL, AV_LOG_DEBUG, " matched as %s.\n", groups[0].name);
continue;
}
/* 排除以 -- 開頭 & 輸出檔案定義,剩下的就是標準引數形式
將引數的型別指標 +1,把 - 去掉 */
opt++;
#define GET_ARG(arg) \
do { \
arg = argv[optindex++]; \
if (!arg) { \
av_log(NULL, AV_LOG_ERROR, "Missing argument for option '%s'.\n", opt);\
return AVERROR(EINVAL); \
} \
} while (0)
/* 引數格式是否為“-i xxx”,如果匹配,返回1 */
if ((ret = match_group_separator(groups, nb_groups, opt)) >= 0) {
// 獲取引數對應的值
GET_ARG(arg);
/**
* 將輸入的路徑、octx->cur_group陣列的值以及專屬引數,儲存到 * octx->groups[0]結構體中,並清空 octx->cur_group 陣列的值
*/
finish_group(octx, ret, arg);
av_log(NULL, AV_LOG_DEBUG, " matched as %s with argument '%s'.\n",
groups[ret].name, arg);
continue;
}
// 判斷此 opt 是否為 options 中定義的引數
po = find_option(options, opt);
if (po->name) {
if (po->flags & OPT_EXIT) {
arg = argv[optindex++];
} else if (po->flags & HAS_ARG) {
/* 獲取引數 */
GET_ARG(arg);
} else {
// 允許引數後面的值缺失,直接設定為1
arg = "1";
}
/**
* 判斷引數型別(opt->flags),存入 octx->global_opts(全域性引數) 或
* octx->cur_group(臨時陣列)
*/
add_opt(octx, po, opt, arg);
av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
"argument '%s'.\n", po->name, po->help, arg);
continue;
}
if (argv[optindex]) {
/**
* 在 avcodec_options、avformat_options、avresample_options、 * swscale_options、swresample 中的 options 引數中不斷查詢,查詢專屬 * 並存入 AVDictionary
*/
ret = opt_default(NULL, opt, argv[optindex]);
if (ret >= 0) {
av_log(NULL, AV_LOG_DEBUG, " matched as AVOption '%s' with "
"argument '%s'.\n", opt, argv[optindex]);
optindex++;
continue;
} else if (ret != AVERROR_OPTION_NOT_FOUND) {
// 引數格式錯誤
av_log(NULL, AV_LOG_ERROR, "Error parsing option '%s' "
"with argument '%s'.\n", opt, argv[optindex]);
return ret;
}
}
/* 處理 “-noxx" 格式引數 */
if (opt[0] == 'n' && opt[1] == 'o' &&
(po = find_option(options, opt + 2)) &&
po->name && po->flags & OPT_BOOL) {
// 存入預設值 ”0“
add_opt(octx, po, opt, "0");
av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
"argument 0.\n", po->name, po->help);
continue;
}
// 沒有匹配的引數,返回錯誤
av_log(NULL, AV_LOG_ERROR, "Unrecognized option '%s'.\n", opt);
return AVERROR_OPTION_NOT_FOUND;
}
// 迴圈結束後,臨時引數及專屬引數還存在值,沒有儲存到輸入&輸出相關引數陣列中
if (octx->cur_group.nb_opts || codec_opts || format_opts || resample_opts)
av_log(NULL, AV_LOG_WARNING, "Trailing options were found on the "
"commandline.\n");
av_log(NULL, AV_LOG_DEBUG, "Finished splitting the commandline.\n");
return 0;
}