You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +02:00 
			
		
		
		
	fftools/ffmpeg_opt: add helpers to match stream groups
Will be used to check for specifiers that match a given stream group and not a stream within one. Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
		| @@ -1350,6 +1350,77 @@ int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec) | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| unsigned stream_group_specifier_match(const StreamSpecifier *ss, | ||||
|                                       const AVFormatContext *s, const AVStreamGroup *stg, | ||||
|                                       void *logctx) | ||||
| { | ||||
|     int start_stream_group = 0, nb_stream_groups; | ||||
|     int nb_matched = 0; | ||||
|  | ||||
|     if (ss->idx >= 0) | ||||
|         return 0; | ||||
|  | ||||
|     switch (ss->stream_list) { | ||||
|     case STREAM_LIST_STREAM_ID: | ||||
|     case STREAM_LIST_ALL: | ||||
|     case STREAM_LIST_PROGRAM: | ||||
|         return 0; | ||||
|     case STREAM_LIST_GROUP_ID: | ||||
|         // <n-th> stream with given ID makes no sense and should be impossible to request | ||||
|         av_assert0(ss->idx < 0); | ||||
|         // return early if we know for sure the stream does not match | ||||
|         if (stg->id != ss->list_id) | ||||
|             return 0; | ||||
|         start_stream_group = stg->index; | ||||
|         nb_stream_groups   = stg->index + 1; | ||||
|         break; | ||||
|     case STREAM_LIST_GROUP_IDX: | ||||
|         start_stream_group = ss->list_id >= 0 ? 0 : stg->index; | ||||
|         nb_stream_groups   = stg->index + 1; | ||||
|         break; | ||||
|     default: av_assert0(0); | ||||
|     } | ||||
|  | ||||
|     for (int i = start_stream_group; i < nb_stream_groups; i++) { | ||||
|         const AVStreamGroup *candidate = s->stream_groups[i]; | ||||
|  | ||||
|         if (ss->meta_key) { | ||||
|             const AVDictionaryEntry *tag = av_dict_get(candidate->metadata, | ||||
|                                                        ss->meta_key, NULL, 0); | ||||
|  | ||||
|             if (!tag) | ||||
|                 continue; | ||||
|             if (ss->meta_val && strcmp(tag->value, ss->meta_val)) | ||||
|                 continue; | ||||
|         } | ||||
|  | ||||
|         if (ss->usable_only) { | ||||
|             switch (candidate->type) { | ||||
|             case AV_STREAM_GROUP_PARAMS_TILE_GRID: { | ||||
|                 const AVStreamGroupTileGrid *tg = candidate->params.tile_grid; | ||||
|                 if (!tg->coded_width || !tg->coded_height || !tg->nb_tiles || | ||||
|                     !tg->width       || !tg->height       || !tg->nb_tiles) | ||||
|                     continue; | ||||
|                 break; | ||||
|             } | ||||
|             default: | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (ss->disposition && | ||||
|             (candidate->disposition & ss->disposition) != ss->disposition) | ||||
|             continue; | ||||
|  | ||||
|         if (stg == candidate) | ||||
|             return ss->list_id < 0 || ss->list_id == nb_matched; | ||||
|  | ||||
|         nb_matched++; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id, | ||||
|                       AVFormatContext *s, AVStream *st, const AVCodec *codec, | ||||
|                       AVDictionary **dst, AVDictionary **opts_used) | ||||
|   | ||||
| @@ -158,6 +158,10 @@ unsigned stream_specifier_match(const StreamSpecifier *ss, | ||||
|                                 const AVFormatContext *s, const AVStream *st, | ||||
|                                 void *logctx); | ||||
|  | ||||
| unsigned stream_group_specifier_match(const StreamSpecifier *ss, | ||||
|                                       const AVFormatContext *s, const AVStreamGroup *stg, | ||||
|                                       void *logctx); | ||||
|  | ||||
| void stream_specifier_uninit(StreamSpecifier *ss); | ||||
|  | ||||
| typedef struct SpecifierOpt { | ||||
|   | ||||
| @@ -136,6 +136,7 @@ typedef struct StreamMap { | ||||
|     int disabled;           /* 1 is this mapping is disabled by a negative map */ | ||||
|     int file_index; | ||||
|     int stream_index; | ||||
|     int group_index; | ||||
|     char *linklabel;       /* name of an output link, for mapping lavfi outputs */ | ||||
|  | ||||
|     ViewSpecifier vs; | ||||
| @@ -932,6 +933,15 @@ void opt_match_per_stream_int64(void *logctx, const SpecifierOptList *sol, | ||||
| void opt_match_per_stream_dbl(void *logctx, const SpecifierOptList *sol, | ||||
|                               AVFormatContext *fc, AVStream *st, double *out); | ||||
|  | ||||
| void opt_match_per_stream_group_str(void *logctx, const SpecifierOptList *sol, | ||||
|                                     AVFormatContext *fc, AVStreamGroup *stg, const char **out); | ||||
| void opt_match_per_stream_group_int(void *logctx, const SpecifierOptList *sol, | ||||
|                                     AVFormatContext *fc, AVStreamGroup *stg, int *out); | ||||
| void opt_match_per_stream_group_int64(void *logctx, const SpecifierOptList *sol, | ||||
|                                       AVFormatContext *fc, AVStreamGroup *stg, int64_t *out); | ||||
| void opt_match_per_stream_group_dbl(void *logctx, const SpecifierOptList *sol, | ||||
|                                     AVFormatContext *fc, AVStreamGroup *stg, double *out); | ||||
|  | ||||
| int view_specifier_parse(const char **pspec, ViewSpecifier *vs); | ||||
|  | ||||
| int muxer_thread(void *arg); | ||||
|   | ||||
| @@ -242,6 +242,70 @@ OPT_MATCH_PER_STREAM(int,   int,          OPT_TYPE_INT,    i); | ||||
| OPT_MATCH_PER_STREAM(int64, int64_t,      OPT_TYPE_INT64,  i64); | ||||
| OPT_MATCH_PER_STREAM(dbl,   double,       OPT_TYPE_DOUBLE, dbl); | ||||
|  | ||||
| static unsigned opt_match_per_stream_group(void *logctx, enum OptionType type, | ||||
|                                            const SpecifierOptList *sol, | ||||
|                                            AVFormatContext *fc, AVStreamGroup *stg) | ||||
| { | ||||
|     int matches = 0, match_idx = -1; | ||||
|  | ||||
|     av_assert0((type == sol->type) || !sol->nb_opt); | ||||
|  | ||||
|     for (int i = 0; i < sol->nb_opt; i++) { | ||||
|         const StreamSpecifier *ss = &sol->opt[i].stream_spec; | ||||
|  | ||||
|         if (stream_group_specifier_match(ss, fc, stg, logctx)) { | ||||
|             match_idx = i; | ||||
|             matches++; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (matches > 1 && sol->opt_canon) { | ||||
|         const SpecifierOpt *so = &sol->opt[match_idx]; | ||||
|         const char *spec = so->specifier && so->specifier[0] ? so->specifier : ""; | ||||
|  | ||||
|         char namestr[128] = ""; | ||||
|         char optval_buf[32]; | ||||
|         const char *optval = optval_buf; | ||||
|  | ||||
|         snprintf(namestr, sizeof(namestr), "-%s", sol->opt_canon->name); | ||||
|         if (sol->opt_canon->flags & OPT_HAS_ALT) { | ||||
|             const char * const *names_alt = sol->opt_canon->u1.names_alt; | ||||
|             for (int i = 0; names_alt[i]; i++) | ||||
|                 av_strlcatf(namestr, sizeof(namestr), "/-%s", names_alt[i]); | ||||
|         } | ||||
|  | ||||
|         switch (sol->type) { | ||||
|         case OPT_TYPE_STRING: optval = so->u.str;                                             break; | ||||
|         case OPT_TYPE_INT:    snprintf(optval_buf, sizeof(optval_buf), "%d", so->u.i);        break; | ||||
|         case OPT_TYPE_INT64:  snprintf(optval_buf, sizeof(optval_buf), "%"PRId64, so->u.i64); break; | ||||
|         case OPT_TYPE_FLOAT:  snprintf(optval_buf, sizeof(optval_buf), "%f", so->u.f);        break; | ||||
|         case OPT_TYPE_DOUBLE: snprintf(optval_buf, sizeof(optval_buf), "%f", so->u.dbl);      break; | ||||
|         default: av_assert0(0); | ||||
|         } | ||||
|  | ||||
|         av_log(logctx, AV_LOG_WARNING, "Multiple %s options specified for " | ||||
|                "stream group %d, only the last option '-%s%s%s %s' will be used.\n", | ||||
|                namestr, stg->index, sol->opt_canon->name, spec[0] ? ":" : "", | ||||
|                spec, optval); | ||||
|     } | ||||
|  | ||||
|     return match_idx + 1; | ||||
| } | ||||
|  | ||||
| #define OPT_MATCH_PER_STREAM_GROUP(name, type, opt_type, m)                                  \ | ||||
| void opt_match_per_stream_group_ ## name(void *logctx, const SpecifierOptList *sol,          \ | ||||
|                                          AVFormatContext *fc, AVStreamGroup *stg, type *out) \ | ||||
| {                                                                                            \ | ||||
|     unsigned ret = opt_match_per_stream_group(logctx, opt_type, sol, fc, stg);               \ | ||||
|     if (ret > 0)                                                                             \ | ||||
|         *out = sol->opt[ret - 1].u.m;                                                        \ | ||||
| } | ||||
|  | ||||
| OPT_MATCH_PER_STREAM_GROUP(str,   const char *, OPT_TYPE_STRING, str); | ||||
| OPT_MATCH_PER_STREAM_GROUP(int,   int,          OPT_TYPE_INT,    i); | ||||
| OPT_MATCH_PER_STREAM_GROUP(int64, int64_t,      OPT_TYPE_INT64,  i64); | ||||
| OPT_MATCH_PER_STREAM_GROUP(dbl,   double,       OPT_TYPE_DOUBLE, dbl); | ||||
|  | ||||
| int view_specifier_parse(const char **pspec, ViewSpecifier *vs) | ||||
| { | ||||
|     const char *spec = *pspec; | ||||
| @@ -504,8 +568,10 @@ static int opt_map(void *optctx, const char *opt, const char *arg) | ||||
|     } | ||||
|  | ||||
|     if (arg[0] == '[') { | ||||
|         ViewSpecifier vs; | ||||
|         /* this mapping refers to lavfi output */ | ||||
|         const char *c = arg + 1; | ||||
|         char *endptr; | ||||
|  | ||||
|         ret = GROW_ARRAY(o->stream_maps, o->nb_stream_maps); | ||||
|         if (ret < 0) | ||||
| @@ -518,6 +584,27 @@ static int opt_map(void *optctx, const char *opt, const char *arg) | ||||
|             ret = AVERROR(EINVAL); | ||||
|             goto fail; | ||||
|         } | ||||
|  | ||||
|         arg++; | ||||
|  | ||||
|         m->group_index = -1; | ||||
|         file_idx = strtol(arg, &endptr, 0); | ||||
|         if (file_idx >= nb_input_files || file_idx < 0) | ||||
|             goto end; | ||||
|  | ||||
|         arg = endptr; | ||||
|         ret = stream_specifier_parse(&ss, *arg == ':' ? arg + 1 : arg, 1, NULL); | ||||
|         if (ret < 0) | ||||
|             goto end; | ||||
|  | ||||
|         arg = ss.remainder ? ss.remainder : ""; | ||||
|         ret = view_specifier_parse(&arg, &vs); | ||||
|         if (ret < 0 || (*arg && strcmp(arg, "]"))) | ||||
|             goto end; | ||||
|  | ||||
|         m->file_index  = file_idx; | ||||
|         m->stream_index = ss.idx; | ||||
|         m->group_index = ss.stream_list == STREAM_LIST_GROUP_IDX ? ss.list_id : -1; | ||||
|     } else { | ||||
|         ViewSpecifier vs; | ||||
|         char *endptr; | ||||
| @@ -583,6 +670,7 @@ static int opt_map(void *optctx, const char *opt, const char *arg) | ||||
|  | ||||
|                 m->file_index   = file_idx; | ||||
|                 m->stream_index = i; | ||||
|                 m->group_index  = ss.stream_list == STREAM_LIST_GROUP_IDX ? ss.list_id : -1; | ||||
|                 m->vs           = vs; | ||||
|             } | ||||
|     } | ||||
| @@ -602,6 +690,7 @@ static int opt_map(void *optctx, const char *opt, const char *arg) | ||||
|             goto fail; | ||||
|         } | ||||
|     } | ||||
| end: | ||||
|     ret = 0; | ||||
| fail: | ||||
|     stream_specifier_uninit(&ss); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user