FFmpegKit iOS / macOS / tvOS API 5.1
fftools_cmdutils.c
Go to the documentation of this file.
1/*
2 * Various utilities for command line tools
3 * Copyright (c) 2000-2003 Fabrice Bellard
4 * copyright (c) 2018 Taner Sener ( tanersener gmail com )
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23/*
24 * This file is the modified version of cmdutils.c file living in ffmpeg source code under the fftools folder. We
25 * manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
26 * by us to develop mobile-ffmpeg and later ffmpeg-kit libraries.
27 *
28 * mobile-ffmpeg / ffmpeg-kit changes by Taner Sener
29 *
30 * 09.2022
31 * --------------------------------------------------------
32 * - Dropped the prototypes of init_report, ffmpegkit_log_callback_function and report_callback functions
33 * - volatile dropped from thread local variables
34 *
35 * 01.2020
36 * --------------------------------------------------------
37 * - ffprobe support added (variables used by ffprobe marked with "__thread" specifier, logs with AV_LOG_INFO level
38 * migrated to use AV_LOG_STDERR)
39 * - (optindex < argc) validation in parse_options() method updated with (optindex >= argc) check
40 *
41 * 12.2019
42 * --------------------------------------------------------
43 * - concurrent execution support ("__thread" specifier added to variables used by multiple threads)
44 * - log_callback_report method re-added to fix -report option issues
45 *
46 * 08.2018
47 * --------------------------------------------------------
48 * - fftools_ prefix added to the file name and parent header
49 *
50 * 07.2018
51 * --------------------------------------------------------
52 * - unused headers removed
53 * - parentheses placed around assignments in conditions to prevent -Wparentheses warning
54 * - exit_program updated with longjmp, disabling exit
55 * - longjmp_value added to store exit code
56 * - (optindex < argc) validation added before accessing argv[optindex] inside split_commandline()
57 * and parse_options()
58 * - all av_log_set_callback invocations updated to set ffmpegkit_log_callback_function from FFmpegKitConfig.m
59 * - unused log_callback_help method removed
60 * - (idx + 1 < argc) validation added in parse_loglevel()
61 */
62
63#include <string.h>
64#include <stdint.h>
65#include <stdlib.h>
66#include <errno.h>
67#include <math.h>
68
69/* Include only the enabled headers since some compilers (namely, Sun
70 Studio) will not omit unused inline functions and create undefined
71 references to libraries that are not being built. */
72
73#include "config.h"
74#include "libavformat/avformat.h"
75#include "libswscale/swscale.h"
76#include "libswresample/swresample.h"
77#include "libavutil/avassert.h"
78#include "libavutil/avstring.h"
79#include "libavutil/channel_layout.h"
80#include "libavutil/display.h"
81#include "libavutil/getenv_utf8.h"
82#include "libavutil/mathematics.h"
83#include "libavutil/imgutils.h"
84#include "libavutil/libm.h"
85#include "libavutil/parseutils.h"
86#include "libavutil/eval.h"
87#include "libavutil/dict.h"
88#include "libavutil/opt.h"
89#include "fftools_cmdutils.h"
90#include "fftools_fopen_utf8.h"
91#include "fftools_opt_common.h"
92#include "ffmpegkit_exception.h"
93#ifdef _WIN32
94#include <windows.h>
95#include "compat/w32dlfcn.h"
96#endif
97
98__thread char *program_name;
99__thread int program_birth_year;
100
101__thread AVDictionary *sws_dict;
102__thread AVDictionary *swr_opts;
103__thread AVDictionary *format_opts, *codec_opts;
104
105__thread int hide_banner = 0;
106__thread int longjmp_value = 0;
107
108void uninit_opts(void)
109{
110 av_dict_free(&swr_opts);
111 av_dict_free(&sws_dict);
112 av_dict_free(&format_opts);
113 av_dict_free(&codec_opts);
114}
115
116void init_dynload(void)
117{
118#if HAVE_SETDLLDIRECTORY && defined(_WIN32)
119 /* Calling SetDllDirectory with the empty string (but not NULL) removes the
120 * current working directory from the DLL search path as a security pre-caution. */
121 SetDllDirectory("");
122#endif
123}
124
125static __thread void (*program_exit)(int ret);
126
127void register_exit(void (*cb)(int ret))
128{
129 program_exit = cb;
130}
131
132void exit_program(int ret)
133{
134 if (program_exit)
135 program_exit(ret);
136
137 // exit disabled and replaced with longjmp, exit value stored in longjmp_value
138 // exit(ret);
139 longjmp_value = ret;
140 longjmp(ex_buf__, ret);
141}
142
143double parse_number_or_die(const char *context, const char *numstr, int type,
144 double min, double max)
145{
146 char *tail;
147 const char *error;
148 double d = av_strtod(numstr, &tail);
149 if (*tail)
150 error = "Expected number for %s but found: %s\n";
151 else if (d < min || d > max)
152 error = "The value for %s was %s which is not within %f - %f\n";
153 else if (type == OPT_INT64 && (int64_t)d != d)
154 error = "Expected int64 for %s but found %s\n";
155 else if (type == OPT_INT && (int)d != d)
156 error = "Expected int for %s but found %s\n";
157 else
158 return d;
159 av_log(NULL, AV_LOG_FATAL, error, context, numstr, min, max);
160 exit_program(1);
161 return 0;
162}
163
164int64_t parse_time_or_die(const char *context, const char *timestr,
165 int is_duration)
166{
167 int64_t us;
168 if (av_parse_time(&us, timestr, is_duration) < 0) {
169 av_log(NULL, AV_LOG_FATAL, "Invalid %s specification for %s: %s\n",
170 is_duration ? "duration" : "date", context, timestr);
171 exit_program(1);
172 }
173 return us;
174}
175
176void show_help_options(const OptionDef *options, const char *msg, int req_flags,
177 int rej_flags, int alt_flags)
178{
179 const OptionDef *po;
180 int first;
181
182 first = 1;
183 for (po = options; po->name; po++) {
184 char buf[128];
185
186 if (((po->flags & req_flags) != req_flags) ||
187 (alt_flags && !(po->flags & alt_flags)) ||
188 (po->flags & rej_flags))
189 continue;
190
191 if (first) {
192 av_log(NULL, AV_LOG_STDERR, "%s\n", msg);
193 first = 0;
194 }
195 av_strlcpy(buf, po->name, sizeof(buf));
196 if (po->argname) {
197 av_strlcat(buf, " ", sizeof(buf));
198 av_strlcat(buf, po->argname, sizeof(buf));
199 }
200 av_log(NULL, AV_LOG_STDERR, "-%-17s %s\n", buf, po->help);
201 }
202 av_log(NULL, AV_LOG_STDERR, "\n");
203}
204
205void show_help_children(const AVClass *class, int flags)
206{
207 void *iter = NULL;
208 const AVClass *child;
209 if (class->option) {
210 av_opt_show2(&class, NULL, flags, 0);
211 av_log(NULL, AV_LOG_STDERR, "\n");
212 }
213
214 while ((child = av_opt_child_class_iterate(class, &iter)))
215 show_help_children(child, flags);
216}
217
218static const OptionDef *find_option(const OptionDef *po, const char *name)
219{
220 while (po->name) {
221 const char *end;
222 if (av_strstart(name, po->name, &end) && (!*end || *end == ':'))
223 break;
224 po++;
225 }
226 return po;
227}
228
229/* _WIN32 means using the windows libc - cygwin doesn't define that
230 * by default. HAVE_COMMANDLINETOARGVW is true on cygwin, while
231 * it doesn't provide the actual command line via GetCommandLineW(). */
232#if HAVE_COMMANDLINETOARGVW && defined(_WIN32)
233#include <shellapi.h>
234/* Will be leaked on exit */
235static char** win32_argv_utf8 = NULL;
236static int win32_argc = 0;
237
245static void prepare_app_arguments(int *argc_ptr, char ***argv_ptr)
246{
247 char *argstr_flat;
248 wchar_t **argv_w;
249 int i, buffsize = 0, offset = 0;
250
251 if (win32_argv_utf8) {
252 *argc_ptr = win32_argc;
253 *argv_ptr = win32_argv_utf8;
254 return;
255 }
256
257 win32_argc = 0;
258 argv_w = CommandLineToArgvW(GetCommandLineW(), &win32_argc);
259 if (win32_argc <= 0 || !argv_w)
260 return;
261
262 /* determine the UTF-8 buffer size (including NULL-termination symbols) */
263 for (i = 0; i < win32_argc; i++)
264 buffsize += WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1,
265 NULL, 0, NULL, NULL);
266
267 win32_argv_utf8 = av_mallocz(sizeof(char *) * (win32_argc + 1) + buffsize);
268 argstr_flat = (char *)win32_argv_utf8 + sizeof(char *) * (win32_argc + 1);
269 if (!win32_argv_utf8) {
270 LocalFree(argv_w);
271 return;
272 }
273
274 for (i = 0; i < win32_argc; i++) {
275 win32_argv_utf8[i] = &argstr_flat[offset];
276 offset += WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1,
277 &argstr_flat[offset],
278 buffsize - offset, NULL, NULL);
279 }
280 win32_argv_utf8[i] = NULL;
281 LocalFree(argv_w);
282
283 *argc_ptr = win32_argc;
284 *argv_ptr = win32_argv_utf8;
285}
286#else
287static inline void prepare_app_arguments(int *argc_ptr, char ***argv_ptr)
288{
289 /* nothing to do */
290}
291#endif /* HAVE_COMMANDLINETOARGVW */
292
293static int write_option(void *optctx, const OptionDef *po, const char *opt,
294 const char *arg)
295{
296 /* new-style options contain an offset into optctx, old-style address of
297 * a global var*/
298 void *dst = po->flags & (OPT_OFFSET | OPT_SPEC) ?
299 (uint8_t *)optctx + po->u.off : po->u.dst_ptr;
300 int *dstcount;
301
302 if (po->flags & OPT_SPEC) {
303 SpecifierOpt **so = dst;
304 char *p = strchr(opt, ':');
305 char *str;
306
307 dstcount = (int *)(so + 1);
308 *so = grow_array(*so, sizeof(**so), dstcount, *dstcount + 1);
309 str = av_strdup(p ? p + 1 : "");
310 if (!str)
311 return AVERROR(ENOMEM);
312 (*so)[*dstcount - 1].specifier = str;
313 dst = &(*so)[*dstcount - 1].u;
314 }
315
316 if (po->flags & OPT_STRING) {
317 char *str;
318 str = av_strdup(arg);
319 av_freep(dst);
320 if (!str)
321 return AVERROR(ENOMEM);
322 *(char **)dst = str;
323 } else if (po->flags & OPT_BOOL || po->flags & OPT_INT) {
324 *(int *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT_MIN, INT_MAX);
325 } else if (po->flags & OPT_INT64) {
326 *(int64_t *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT64_MIN, (double)INT64_MAX);
327 } else if (po->flags & OPT_TIME) {
328 *(int64_t *)dst = parse_time_or_die(opt, arg, 1);
329 } else if (po->flags & OPT_FLOAT) {
330 *(float *)dst = parse_number_or_die(opt, arg, OPT_FLOAT, -INFINITY, INFINITY);
331 } else if (po->flags & OPT_DOUBLE) {
332 *(double *)dst = parse_number_or_die(opt, arg, OPT_DOUBLE, -INFINITY, INFINITY);
333 } else if (po->u.func_arg) {
334 int ret = po->u.func_arg(optctx, opt, arg);
335 if (ret < 0) {
336 av_log(NULL, AV_LOG_ERROR,
337 "Failed to set value '%s' for option '%s': %s\n",
338 arg, opt, av_err2str(ret));
339 return ret;
340 }
341 }
342 if (po->flags & OPT_EXIT)
343 exit_program(0);
344
345 return 0;
346}
347
348int parse_option(void *optctx, const char *opt, const char *arg,
349 const OptionDef *options)
350{
351 static const OptionDef opt_avoptions = {
352 .name = "AVOption passthrough",
353 .flags = HAS_ARG,
354 .u.func_arg = opt_default,
355 };
356
357 const OptionDef *po;
358 int ret;
359
360 po = find_option(options, opt);
361 if (!po->name && opt[0] == 'n' && opt[1] == 'o') {
362 /* handle 'no' bool option */
363 po = find_option(options, opt + 2);
364 if ((po->name && (po->flags & OPT_BOOL)))
365 arg = "0";
366 } else if (po->flags & OPT_BOOL)
367 arg = "1";
368
369 if (!po->name)
370 po = &opt_avoptions;
371 if (!po->name) {
372 av_log(NULL, AV_LOG_ERROR, "Unrecognized option '%s'\n", opt);
373 return AVERROR(EINVAL);
374 }
375 if (po->flags & HAS_ARG && !arg) {
376 av_log(NULL, AV_LOG_ERROR, "Missing argument for option '%s'\n", opt);
377 return AVERROR(EINVAL);
378 }
379
380 ret = write_option(optctx, po, opt, arg);
381 if (ret < 0)
382 return ret;
383
384 return !!(po->flags & HAS_ARG);
385}
386
387void parse_options(void *optctx, int argc, char **argv, const OptionDef *options,
388 void (*parse_arg_function)(void *, const char*))
389{
390 const char *opt;
391 int optindex, handleoptions = 1, ret;
392
393 /* perform system-dependent conversions for arguments list */
394 prepare_app_arguments(&argc, &argv);
395
396 /* parse options */
397 optindex = 1;
398 while (optindex < argc) {
399 opt = argv[optindex++];
400
401 if (handleoptions && opt[0] == '-' && opt[1] != '\0') {
402 if (opt[1] == '-' && opt[2] == '\0') {
403 handleoptions = 0;
404 continue;
405 }
406 opt++;
407 if (optindex >= argc) {
408 if ((ret = parse_option(optctx, opt, NULL, options)) < 0)
409 exit_program(1);
410 } else {
411 if ((ret = parse_option(optctx, opt, argv[optindex], options)) < 0)
412 exit_program(1);
413 }
414 optindex += ret;
415 } else {
416 if (parse_arg_function)
417 parse_arg_function(optctx, opt);
418 }
419 }
420}
421
422int parse_optgroup(void *optctx, OptionGroup *g)
423{
424 int i, ret;
425
426 av_log(NULL, AV_LOG_DEBUG, "Parsing a group of options: %s %s.\n",
427 g->group_def->name, g->arg);
428
429 for (i = 0; i < g->nb_opts; i++) {
430 Option *o = &g->opts[i];
431
432 if (g->group_def->flags &&
433 !(g->group_def->flags & o->opt->flags)) {
434 av_log(NULL, AV_LOG_ERROR, "Option %s (%s) cannot be applied to "
435 "%s %s -- you are trying to apply an input option to an "
436 "output file or vice versa. Move this option before the "
437 "file it belongs to.\n", o->key, o->opt->help,
438 g->group_def->name, g->arg);
439 return AVERROR(EINVAL);
440 }
441
442 av_log(NULL, AV_LOG_DEBUG, "Applying option %s (%s) with argument %s.\n",
443 o->key, o->opt->help, o->val);
444
445 ret = write_option(optctx, o->opt, o->key, o->val);
446 if (ret < 0)
447 return ret;
448 }
449
450 av_log(NULL, AV_LOG_DEBUG, "Successfully parsed a group of options.\n");
451
452 return 0;
453}
454
455int locate_option(int argc, char **argv, const OptionDef *options,
456 const char *optname)
457{
458 const OptionDef *po;
459 int i;
460
461 for (i = 1; i < argc; i++) {
462 const char *cur_opt = argv[i];
463
464 if (*cur_opt++ != '-')
465 continue;
466
467 po = find_option(options, cur_opt);
468 if (!po->name && cur_opt[0] == 'n' && cur_opt[1] == 'o')
469 po = find_option(options, cur_opt + 2);
470
471 if ((!po->name && !strcmp(cur_opt, optname)) ||
472 (po->name && !strcmp(optname, po->name)))
473 return i;
474
475 if (!po->name || po->flags & HAS_ARG)
476 i++;
477 }
478 return 0;
479}
480
481static void dump_argument(FILE *report_file, const char *a)
482{
483 const unsigned char *p;
484
485 for (p = a; *p; p++)
486 if (!((*p >= '+' && *p <= ':') || (*p >= '@' && *p <= 'Z') ||
487 *p == '_' || (*p >= 'a' && *p <= 'z')))
488 break;
489 if (!*p) {
490 fputs(a, report_file);
491 return;
492 }
493 fputc('"', report_file);
494 for (p = a; *p; p++) {
495 if (*p == '\\' || *p == '"' || *p == '$' || *p == '`')
496 fprintf(report_file, "\\%c", *p);
497 else if (*p < ' ' || *p > '~')
498 fprintf(report_file, "\\x%02x", *p);
499 else
500 fputc(*p, report_file);
501 }
502 fputc('"', report_file);
503}
504
505static void check_options(const OptionDef *po)
506{
507 while (po->name) {
508 if (po->flags & OPT_PERFILE)
509 av_assert0(po->flags & (OPT_INPUT | OPT_OUTPUT));
510 po++;
511 }
512}
513
514void parse_loglevel(int argc, char **argv, const OptionDef *options)
515{
516 int idx = locate_option(argc, argv, options, "loglevel");
517 char *env;
518
519 check_options(options);
520
521 if (!idx)
522 idx = locate_option(argc, argv, options, "v");
523 if (idx && (idx + 1 < argc) && argv[idx + 1])
524 opt_loglevel(NULL, "loglevel", argv[idx + 1]);
525 idx = locate_option(argc, argv, options, "report");
526 env = getenv_utf8("FFREPORT");
527 if (env || idx) {
528 FILE *report_file = NULL;
530 if (report_file) {
531 int i;
532 fprintf(report_file, "Command line:\n");
533 for (i = 0; i < argc; i++) {
534 dump_argument(report_file, argv[i]);
535 fputc(i < argc - 1 ? ' ' : '\n', report_file);
536 }
537 fflush(report_file);
538 }
539 }
540 freeenv_utf8(env);
541 idx = locate_option(argc, argv, options, "hide_banner");
542 if (idx)
543 hide_banner = 1;
544}
545
546static const AVOption *opt_find(void *obj, const char *name, const char *unit,
547 int opt_flags, int search_flags)
548{
549 const AVOption *o = av_opt_find(obj, name, unit, opt_flags, search_flags);
550 if(o && !o->flags)
551 return NULL;
552 return o;
553}
554
555#define FLAGS (o->type == AV_OPT_TYPE_FLAGS && (arg[0]=='-' || arg[0]=='+')) ? AV_DICT_APPEND : 0
556int opt_default(void *optctx, const char *opt, const char *arg)
557{
558 const AVOption *o;
559 int consumed = 0;
560 char opt_stripped[128];
561 const char *p;
562 const AVClass *cc = avcodec_get_class(), *fc = avformat_get_class();
563#if CONFIG_SWSCALE
564 const AVClass *sc = sws_get_class();
565#endif
566#if CONFIG_SWRESAMPLE
567 const AVClass *swr_class = swr_get_class();
568#endif
569
570 if (!strcmp(opt, "debug") || !strcmp(opt, "fdebug"))
571 av_log_set_level(AV_LOG_DEBUG);
572
573 if (!(p = strchr(opt, ':')))
574 p = opt + strlen(opt);
575 av_strlcpy(opt_stripped, opt, FFMIN(sizeof(opt_stripped), p - opt + 1));
576
577 if ((o = opt_find(&cc, opt_stripped, NULL, 0,
578 AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ)) ||
579 ((opt[0] == 'v' || opt[0] == 'a' || opt[0] == 's') &&
580 (o = opt_find(&cc, opt + 1, NULL, 0, AV_OPT_SEARCH_FAKE_OBJ)))) {
581 av_dict_set(&codec_opts, opt, arg, FLAGS);
582 consumed = 1;
583 }
584 if ((o = opt_find(&fc, opt, NULL, 0,
585 AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ))) {
586 av_dict_set(&format_opts, opt, arg, FLAGS);
587 if (consumed)
588 av_log(NULL, AV_LOG_VERBOSE, "Routing option %s to both codec and muxer layer\n", opt);
589 consumed = 1;
590 }
591#if CONFIG_SWSCALE
592 if (!consumed && (o = opt_find(&sc, opt, NULL, 0,
593 AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ))) {
594 if (!strcmp(opt, "srcw") || !strcmp(opt, "srch") ||
595 !strcmp(opt, "dstw") || !strcmp(opt, "dsth") ||
596 !strcmp(opt, "src_format") || !strcmp(opt, "dst_format")) {
597 av_log(NULL, AV_LOG_ERROR, "Directly using swscale dimensions/format options is not supported, please use the -s or -pix_fmt options\n");
598 return AVERROR(EINVAL);
599 }
600 av_dict_set(&sws_dict, opt, arg, FLAGS);
601
602 consumed = 1;
603 }
604#else
605 if (!consumed && !strcmp(opt, "sws_flags")) {
606 av_log(NULL, AV_LOG_WARNING, "Ignoring %s %s, due to disabled swscale\n", opt, arg);
607 consumed = 1;
608 }
609#endif
610#if CONFIG_SWRESAMPLE
611 if (!consumed && (o=opt_find(&swr_class, opt, NULL, 0,
612 AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ))) {
613 av_dict_set(&swr_opts, opt, arg, FLAGS);
614 consumed = 1;
615 }
616#endif
617
618 if (consumed)
619 return 0;
620 return AVERROR_OPTION_NOT_FOUND;
621}
622
623/*
624 * Check whether given option is a group separator.
625 *
626 * @return index of the group definition that matched or -1 if none
627 */
628static int match_group_separator(const OptionGroupDef *groups, int nb_groups,
629 const char *opt)
630{
631 int i;
632
633 for (i = 0; i < nb_groups; i++) {
634 const OptionGroupDef *p = &groups[i];
635 if (p->sep && !strcmp(p->sep, opt))
636 return i;
637 }
638
639 return -1;
640}
641
642/*
643 * Finish parsing an option group.
644 *
645 * @param group_idx which group definition should this group belong to
646 * @param arg argument of the group delimiting option
647 */
648static void finish_group(OptionParseContext *octx, int group_idx,
649 const char *arg)
650{
651 OptionGroupList *l = &octx->groups[group_idx];
652 OptionGroup *g;
653
655 g = &l->groups[l->nb_groups - 1];
656
657 *g = octx->cur_group;
658 g->arg = arg;
659 g->group_def = l->group_def;
660 g->sws_dict = sws_dict;
661 g->swr_opts = swr_opts;
664
665 codec_opts = NULL;
666 format_opts = NULL;
667 sws_dict = NULL;
668 swr_opts = NULL;
669
670 memset(&octx->cur_group, 0, sizeof(octx->cur_group));
671}
672
673/*
674 * Add an option instance to currently parsed group.
675 */
676static void add_opt(OptionParseContext *octx, const OptionDef *opt,
677 const char *key, const char *val)
678{
679 int global = !(opt->flags & (OPT_PERFILE | OPT_SPEC | OPT_OFFSET));
680 OptionGroup *g = global ? &octx->global_opts : &octx->cur_group;
681
682 GROW_ARRAY(g->opts, g->nb_opts);
683 g->opts[g->nb_opts - 1].opt = opt;
684 g->opts[g->nb_opts - 1].key = key;
685 g->opts[g->nb_opts - 1].val = val;
686}
687
689 const OptionGroupDef *groups, int nb_groups)
690{
691 static const OptionGroupDef global_group = { "global" };
692 int i;
693
694 memset(octx, 0, sizeof(*octx));
695
696 octx->nb_groups = nb_groups;
697 octx->groups = av_calloc(octx->nb_groups, sizeof(*octx->groups));
698 if (!octx->groups)
699 exit_program(1);
700
701 for (i = 0; i < octx->nb_groups; i++)
702 octx->groups[i].group_def = &groups[i];
703
704 octx->global_opts.group_def = &global_group;
705 octx->global_opts.arg = "";
706}
707
709{
710 int i, j;
711
712 for (i = 0; i < octx->nb_groups; i++) {
713 OptionGroupList *l = &octx->groups[i];
714
715 for (j = 0; j < l->nb_groups; j++) {
716 av_freep(&l->groups[j].opts);
717 av_dict_free(&l->groups[j].codec_opts);
718 av_dict_free(&l->groups[j].format_opts);
719
720 av_dict_free(&l->groups[j].sws_dict);
721 av_dict_free(&l->groups[j].swr_opts);
722 }
723 av_freep(&l->groups);
724 }
725 av_freep(&octx->groups);
726
727 av_freep(&octx->cur_group.opts);
728 av_freep(&octx->global_opts.opts);
729
730 uninit_opts();
731}
732
733int split_commandline(OptionParseContext *octx, int argc, char *argv[],
734 const OptionDef *options,
735 const OptionGroupDef *groups, int nb_groups)
736{
737 int optindex = 1;
738 int dashdash = -2;
739
740 /* perform system-dependent conversions for arguments list */
741 prepare_app_arguments(&argc, &argv);
742
743 init_parse_context(octx, groups, nb_groups);
744 av_log(NULL, AV_LOG_DEBUG, "Splitting the commandline.\n");
745
746 while (optindex < argc) {
747 const char *opt = argv[optindex++], *arg;
748 const OptionDef *po;
749 int ret;
750
751 av_log(NULL, AV_LOG_DEBUG, "Reading option '%s' ...", opt);
752
753 if (opt[0] == '-' && opt[1] == '-' && !opt[2]) {
754 dashdash = optindex;
755 continue;
756 }
757 /* unnamed group separators, e.g. output filename */
758 if (opt[0] != '-' || !opt[1] || dashdash+1 == optindex) {
759 finish_group(octx, 0, opt);
760 av_log(NULL, AV_LOG_DEBUG, " matched as %s.\n", groups[0].name);
761 continue;
762 }
763 opt++;
764
765#define GET_ARG(arg) \
766do { \
767 if (optindex < argc) { \
768 arg = argv[optindex++]; \
769 } else { \
770 av_log(NULL, AV_LOG_ERROR, "Missing argument for option '%s'.\n", opt);\
771 return AVERROR(EINVAL); \
772 } \
773} while (0)
774
775 /* named group separators, e.g. -i */
776 if ((ret = match_group_separator(groups, nb_groups, opt)) >= 0) {
777 GET_ARG(arg);
778 finish_group(octx, ret, arg);
779 av_log(NULL, AV_LOG_DEBUG, " matched as %s with argument '%s'.\n",
780 groups[ret].name, arg);
781 continue;
782 }
783
784 /* normal options */
785 po = find_option(options, opt);
786 if (po->name) {
787 if (po->flags & OPT_EXIT) {
788 /* optional argument, e.g. -h */
789 if (optindex < argc) {
790 arg = argv[optindex++];
791 } else {
792 arg = NULL;
793 }
794 } else if (po->flags & HAS_ARG) {
795 GET_ARG(arg);
796 } else {
797 arg = "1";
798 }
799
800 add_opt(octx, po, opt, arg);
801 av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
802 "argument '%s'.\n", po->name, po->help, arg);
803 continue;
804 }
805
806 /* AVOptions */
807 if ((optindex < argc) && argv[optindex]) {
808 ret = opt_default(NULL, opt, argv[optindex]);
809 if (ret >= 0) {
810 av_log(NULL, AV_LOG_DEBUG, " matched as AVOption '%s' with "
811 "argument '%s'.\n", opt, argv[optindex]);
812 optindex++;
813 continue;
814 } else if (ret != AVERROR_OPTION_NOT_FOUND) {
815 av_log(NULL, AV_LOG_ERROR, "Error parsing option '%s' "
816 "with argument '%s'.\n", opt, argv[optindex]);
817 return ret;
818 }
819 }
820
821 /* boolean -nofoo options */
822 if (opt[0] == 'n' && opt[1] == 'o' &&
823 (po = find_option(options, opt + 2)) &&
824 po->name && po->flags & OPT_BOOL) {
825 add_opt(octx, po, opt, "0");
826 av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
827 "argument 0.\n", po->name, po->help);
828 continue;
829 }
830
831 av_log(NULL, AV_LOG_ERROR, "Unrecognized option '%s'.\n", opt);
832 return AVERROR_OPTION_NOT_FOUND;
833 }
834
835 if (octx->cur_group.nb_opts || codec_opts || format_opts)
836 av_log(NULL, AV_LOG_WARNING, "Trailing option(s) found in the "
837 "command: may be ignored.\n");
838
839 av_log(NULL, AV_LOG_DEBUG, "Finished splitting the commandline.\n");
840
841 return 0;
842}
843
844void print_error(const char *filename, int err)
845{
846 char errbuf[128];
847 const char *errbuf_ptr = errbuf;
848
849 if (av_strerror(err, errbuf, sizeof(errbuf)) < 0)
850 errbuf_ptr = strerror(AVUNERROR(err));
851 av_log(NULL, AV_LOG_ERROR, "%s: %s\n", filename, errbuf_ptr);
852}
853
854int read_yesno(void)
855{
856 int c = getchar();
857 int yesno = (av_toupper(c) == 'Y');
858
859 while (c != '\n' && c != EOF)
860 c = getchar();
861
862 return yesno;
863}
864
865FILE *get_preset_file(char *filename, size_t filename_size,
866 const char *preset_name, int is_path,
867 const char *codec_name)
868{
869 FILE *f = NULL;
870 int i;
871#if HAVE_GETMODULEHANDLE && defined(_WIN32)
872 char *datadir = NULL;
873#endif
874 char *env_home = getenv_utf8("HOME");
875 char *env_ffmpeg_datadir = getenv_utf8("FFMPEG_DATADIR");
876 const char *base[3] = { env_ffmpeg_datadir,
877 env_home, /* index=1(HOME) is special: search in a .ffmpeg subfolder */
878 FFMPEG_DATADIR, };
879
880 if (is_path) {
881 av_strlcpy(filename, preset_name, filename_size);
882 f = fopen_utf8(filename, "r");
883 } else {
884#if HAVE_GETMODULEHANDLE && defined(_WIN32)
885 wchar_t *datadir_w = get_module_filename(NULL);
886 base[2] = NULL;
887
888 if (wchartoutf8(datadir_w, &datadir))
889 datadir = NULL;
890 av_free(datadir_w);
891
892 if (datadir)
893 {
894 char *ls;
895 for (ls = datadir; *ls; ls++)
896 if (*ls == '\\') *ls = '/';
897
898 if (ls = strrchr(datadir, '/'))
899 {
900 ptrdiff_t datadir_len = ls - datadir;
901 size_t desired_size = datadir_len + strlen("/ffpresets") + 1;
902 char *new_datadir = av_realloc_array(
903 datadir, desired_size, sizeof *datadir);
904 if (new_datadir) {
905 datadir = new_datadir;
906 datadir[datadir_len] = 0;
907 strncat(datadir, "/ffpresets", desired_size - 1 - datadir_len);
908 base[2] = datadir;
909 }
910 }
911 }
912#endif
913 for (i = 0; i < 3 && !f; i++) {
914 if (!base[i])
915 continue;
916 snprintf(filename, filename_size, "%s%s/%s.ffpreset", base[i],
917 i != 1 ? "" : "/.ffmpeg", preset_name);
918 f = fopen_utf8(filename, "r");
919 if (!f && codec_name) {
920 snprintf(filename, filename_size,
921 "%s%s/%s-%s.ffpreset",
922 base[i], i != 1 ? "" : "/.ffmpeg", codec_name,
923 preset_name);
924 f = fopen_utf8(filename, "r");
925 }
926 }
927 }
928
929#if HAVE_GETMODULEHANDLE && defined(_WIN32)
930 av_free(datadir);
931#endif
932 freeenv_utf8(env_ffmpeg_datadir);
933 freeenv_utf8(env_home);
934 return f;
935}
936
937int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec)
938{
939 int ret = avformat_match_stream_specifier(s, st, spec);
940 if (ret < 0)
941 av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec);
942 return ret;
943}
944
945AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id,
946 AVFormatContext *s, AVStream *st, const AVCodec *codec)
947{
948 AVDictionary *ret = NULL;
949 const AVDictionaryEntry *t = NULL;
950 int flags = s->oformat ? AV_OPT_FLAG_ENCODING_PARAM
951 : AV_OPT_FLAG_DECODING_PARAM;
952 char prefix = 0;
953 const AVClass *cc = avcodec_get_class();
954
955 if (!codec)
956 codec = s->oformat ? avcodec_find_encoder(codec_id)
957 : avcodec_find_decoder(codec_id);
958
959 switch (st->codecpar->codec_type) {
960 case AVMEDIA_TYPE_VIDEO:
961 prefix = 'v';
962 flags |= AV_OPT_FLAG_VIDEO_PARAM;
963 break;
964 case AVMEDIA_TYPE_AUDIO:
965 prefix = 'a';
966 flags |= AV_OPT_FLAG_AUDIO_PARAM;
967 break;
968 case AVMEDIA_TYPE_SUBTITLE:
969 prefix = 's';
970 flags |= AV_OPT_FLAG_SUBTITLE_PARAM;
971 break;
972 }
973
974 while ((t = av_dict_get(opts, "", t, AV_DICT_IGNORE_SUFFIX))) {
975 const AVClass *priv_class;
976 char *p = strchr(t->key, ':');
977
978 /* check stream specification in opt name */
979 if (p)
980 switch (check_stream_specifier(s, st, p + 1)) {
981 case 1: *p = 0; break;
982 case 0: continue;
983 default: exit_program(1);
984 }
985
986 if (av_opt_find(&cc, t->key, NULL, flags, AV_OPT_SEARCH_FAKE_OBJ) ||
987 !codec ||
988 ((priv_class = codec->priv_class) &&
989 av_opt_find(&priv_class, t->key, NULL, flags,
990 AV_OPT_SEARCH_FAKE_OBJ)))
991 av_dict_set(&ret, t->key, t->value, 0);
992 else if (t->key[0] == prefix &&
993 av_opt_find(&cc, t->key + 1, NULL, flags,
994 AV_OPT_SEARCH_FAKE_OBJ))
995 av_dict_set(&ret, t->key + 1, t->value, 0);
996
997 if (p)
998 *p = ':';
999 }
1000 return ret;
1001}
1002
1003AVDictionary **setup_find_stream_info_opts(AVFormatContext *s,
1004 AVDictionary *codec_opts)
1005{
1006 int i;
1007 AVDictionary **opts;
1008
1009 if (!s->nb_streams)
1010 return NULL;
1011 opts = av_calloc(s->nb_streams, sizeof(*opts));
1012 if (!opts) {
1013 av_log(NULL, AV_LOG_ERROR,
1014 "Could not alloc memory for stream options.\n");
1015 exit_program(1);
1016 }
1017 for (i = 0; i < s->nb_streams; i++)
1018 opts[i] = filter_codec_opts(codec_opts, s->streams[i]->codecpar->codec_id,
1019 s, s->streams[i], NULL);
1020 return opts;
1021}
1022
1023void *grow_array(void *array, int elem_size, int *size, int new_size)
1024{
1025 if (new_size >= INT_MAX / elem_size) {
1026 av_log(NULL, AV_LOG_ERROR, "Array too big.\n");
1027 exit_program(1);
1028 }
1029 if (*size < new_size) {
1030 uint8_t *tmp = av_realloc_array(array, new_size, elem_size);
1031 if (!tmp) {
1032 av_log(NULL, AV_LOG_ERROR, "Could not alloc buffer.\n");
1033 exit_program(1);
1034 }
1035 memset(tmp + *size*elem_size, 0, (new_size-*size) * elem_size);
1036 *size = new_size;
1037 return tmp;
1038 }
1039 return array;
1040}
1041
1042void *allocate_array_elem(void *ptr, size_t elem_size, int *nb_elems)
1043{
1044 void *new_elem;
1045
1046 if (!(new_elem = av_mallocz(elem_size)) ||
1047 av_dynarray_add_nofree(ptr, nb_elems, new_elem) < 0) {
1048 av_log(NULL, AV_LOG_ERROR, "Could not alloc buffer.\n");
1049 exit_program(1);
1050 }
1051 return new_elem;
1052}
1053
1054double get_rotation(int32_t *displaymatrix)
1055{
1056 double theta = 0;
1057 if (displaymatrix)
1058 theta = -round(av_display_rotation_get((int32_t*) displaymatrix));
1059
1060 theta -= 360*floor(theta/360 + 0.9/360);
1061
1062 if (fabs(theta - 90*round(theta/90)) > 2)
1063 av_log(NULL, AV_LOG_WARNING, "Odd rotation angle.\n"
1064 "If you want to help, upload a sample "
1065 "of this file to https://streams.videolan.org/upload/ "
1066 "and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)");
1067
1068 return theta;
1069}
__thread jmp_buf ex_buf__
void exit_program(int ret)
__thread AVDictionary * swr_opts
static int match_group_separator(const OptionGroupDef *groups, int nb_groups, const char *opt)
void show_help_children(const AVClass *class, int flags)
__thread AVDictionary * codec_opts
static void prepare_app_arguments(int *argc_ptr, char ***argv_ptr)
void init_dynload(void)
int parse_option(void *optctx, const char *opt, const char *arg, const OptionDef *options)
void show_help_options(const OptionDef *options, const char *msg, int req_flags, int rej_flags, int alt_flags)
__thread AVDictionary * format_opts
int opt_default(void *optctx, const char *opt, const char *arg)
void print_error(const char *filename, int err)
double get_rotation(int32_t *displaymatrix)
int read_yesno(void)
int locate_option(int argc, char **argv, const OptionDef *options, const char *optname)
static void dump_argument(FILE *report_file, const char *a)
static const AVOption * opt_find(void *obj, const char *name, const char *unit, int opt_flags, int search_flags)
static int write_option(void *optctx, const OptionDef *po, const char *opt, const char *arg)
int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec)
static void init_parse_context(OptionParseContext *octx, const OptionGroupDef *groups, int nb_groups)
static void add_opt(OptionParseContext *octx, const OptionDef *opt, const char *key, const char *val)
__thread char * program_name
#define FLAGS
static __thread void(* program_exit)(int ret)
#define GET_ARG(arg)
static void finish_group(OptionParseContext *octx, int group_idx, const char *arg)
__thread int longjmp_value
void parse_loglevel(int argc, char **argv, const OptionDef *options)
__thread int program_birth_year
void parse_options(void *optctx, int argc, char **argv, const OptionDef *options, void(*parse_arg_function)(void *, const char *))
void uninit_parse_context(OptionParseContext *octx)
__thread AVDictionary * sws_dict
int split_commandline(OptionParseContext *octx, int argc, char *argv[], const OptionDef *options, const OptionGroupDef *groups, int nb_groups)
void * allocate_array_elem(void *ptr, size_t elem_size, int *nb_elems)
int64_t parse_time_or_die(const char *context, const char *timestr, int is_duration)
void register_exit(void(*cb)(int ret))
FILE * get_preset_file(char *filename, size_t filename_size, const char *preset_name, int is_path, const char *codec_name)
void uninit_opts(void)
void * grow_array(void *array, int elem_size, int *size, int new_size)
AVDictionary * filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, AVFormatContext *s, AVStream *st, const AVCodec *codec)
static const OptionDef * find_option(const OptionDef *po, const char *name)
__thread int hide_banner
int parse_optgroup(void *optctx, OptionGroup *g)
static void check_options(const OptionDef *po)
double parse_number_or_die(const char *context, const char *numstr, int type, double min, double max)
AVDictionary ** setup_find_stream_info_opts(AVFormatContext *s, AVDictionary *codec_opts)
#define OPT_SPEC
#define OPT_BOOL
#define OPT_INT64
#define OPT_PERFILE
#define OPT_INT
#define OPT_FLOAT
#define AV_LOG_STDERR
#define OPT_INPUT
#define OPT_DOUBLE
#define OPT_STRING
#define GROW_ARRAY(array, nb_elems)
#define OPT_EXIT
#define OPT_OUTPUT
#define OPT_TIME
#define OPT_OFFSET
#define HAS_ARG
static const OptionGroupDef groups[]
static FILE * fopen_utf8(const char *path, const char *mode)
int opt_loglevel(void *optctx, const char *opt, const char *arg)
static __thread FILE * report_file
int init_report(const char *env, FILE **file)
union OptionDef::@1 u
const char * name
const char * argname
const char * help
int(* func_arg)(void *, const char *, const char *)
const char * name
const char * sep
const OptionGroupDef * group_def
AVDictionary * codec_opts
AVDictionary * swr_opts
AVDictionary * sws_dict
const char * arg
AVDictionary * format_opts
OptionGroup * groups
const OptionGroupDef * group_def
const char * key
const OptionDef * opt
const char * val
OptionGroupList * groups