static int ffm_write_recommended_config(AVIOContext *pb, AVCodecContext *ctx, unsigned tag, const char *configuration) { int ret; const AVCodec *enc = ctx->codec ? ctx->codec : avcodec_find_encoder(ctx->codec_id); AVIOContext *tmp; AVDictionaryEntry *t = NULL; AVDictionary *all = NULL, *comm = NULL, *prv = NULL; char *buf = NULL; if (!enc || !enc->priv_class || !enc->priv_data_size) { /* codec is not known/has no private options, so save everything as common options */ if (avio_open_dyn_buf(&tmp) < 0) return AVERROR(ENOMEM); avio_put_str(tmp, configuration); write_header_chunk(pb, tmp, tag); return 0; } if ((ret = av_dict_parse_string(&all, configuration, "=", ",", 0)) < 0) return ret; while ((t = av_dict_get(all, "", t, AV_DICT_IGNORE_SUFFIX))) { if (av_opt_find((void *)&enc->priv_class, t->key, NULL, 0, AV_OPT_SEARCH_FAKE_OBJ)) { if ((ret = av_dict_set(&prv, t->key, t->value, 0)) < 0) goto fail; } else if ((ret = av_dict_set(&comm, t->key, t->value, 0)) < 0) goto fail; } if (comm) { if ((ret = av_dict_get_string(comm, &buf, '=', ',')) < 0 || (ret = avio_open_dyn_buf(&tmp)) < 0) goto fail; avio_put_str(tmp, buf); av_freep(&buf); write_header_chunk(pb, tmp, tag); } if (prv) { if ((ret = av_dict_get_string(prv, &buf, '=', ',')) < 0 || (ret = avio_open_dyn_buf(&tmp)) < 0) goto fail; avio_put_str(tmp, buf); write_header_chunk(pb, tmp, MKBETAG('C', 'P', 'R', 'V')); } fail: av_free(buf); av_dict_free(&all); av_dict_free(&comm); av_dict_free(&prv); return ret; }
int main(void) { AVDictionary *dict = NULL; char *buffer = NULL; printf("Testing av_dict_get_string() and av_dict_parse_string()\n"); av_dict_get_string(dict, &buffer, '=', ','); printf("%s\n", buffer); av_freep(&buffer); av_dict_set(&dict, "aaa", "aaa", 0); av_dict_set(&dict, "b,b", "bbb", 0); av_dict_set(&dict, "c=c", "ccc", 0); av_dict_set(&dict, "ddd", "d,d", 0); av_dict_set(&dict, "eee", "e=e", 0); av_dict_set(&dict, "f,f", "f=f", 0); av_dict_set(&dict, "g=g", "g,g", 0); test_separators(dict, ',', '='); av_dict_free(&dict); av_dict_set(&dict, "aaa", "aaa", 0); av_dict_set(&dict, "bbb", "bbb", 0); av_dict_set(&dict, "ccc", "ccc", 0); av_dict_set(&dict, "\\,=\'\"", "\\,=\'\"", 0); test_separators(dict, '"', '='); test_separators(dict, '\'', '='); test_separators(dict, ',', '"'); test_separators(dict, ',', '\''); test_separators(dict, '\'', '"'); test_separators(dict, '"', '\''); av_dict_free(&dict); return 0; }
static void test_separators(const AVDictionary *m, const char pair, const char val) { AVDictionary *dict = NULL; char pairs[] = {pair , '\0'}; char vals[] = {val, '\0'}; char *buffer = NULL; av_dict_copy(&dict, m, 0); print_dict(dict); av_dict_get_string(dict, &buffer, val, pair); printf("%s\n", buffer); av_dict_free(&dict); av_dict_parse_string(&dict, buffer, vals, pairs, 0); av_freep(&buffer); print_dict(dict); av_dict_free(&dict); }
int main(void) { AVDictionary *dict = NULL; AVDictionaryEntry *e; char *buffer = NULL; printf("Testing av_dict_get_string() and av_dict_parse_string()\n"); av_dict_get_string(dict, &buffer, '=', ','); printf("%s\n", buffer); av_freep(&buffer); av_dict_set(&dict, "aaa", "aaa", 0); av_dict_set(&dict, "b,b", "bbb", 0); av_dict_set(&dict, "c=c", "ccc", 0); av_dict_set(&dict, "ddd", "d,d", 0); av_dict_set(&dict, "eee", "e=e", 0); av_dict_set(&dict, "f,f", "f=f", 0); av_dict_set(&dict, "g=g", "g,g", 0); test_separators(dict, ',', '='); av_dict_free(&dict); av_dict_set(&dict, "aaa", "aaa", 0); av_dict_set(&dict, "bbb", "bbb", 0); av_dict_set(&dict, "ccc", "ccc", 0); av_dict_set(&dict, "\\,=\'\"", "\\,=\'\"", 0); test_separators(dict, '"', '='); test_separators(dict, '\'', '='); test_separators(dict, ',', '"'); test_separators(dict, ',', '\''); test_separators(dict, '\'', '"'); test_separators(dict, '"', '\''); av_dict_free(&dict); printf("\nTesting av_dict_set()\n"); av_dict_set(&dict, "a", "a", 0); av_dict_set(&dict, "b", av_strdup("b"), AV_DICT_DONT_STRDUP_VAL); av_dict_set(&dict, av_strdup("c"), "c", AV_DICT_DONT_STRDUP_KEY); av_dict_set(&dict, av_strdup("d"), av_strdup("d"), AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); av_dict_set(&dict, "e", "e", AV_DICT_DONT_OVERWRITE); av_dict_set(&dict, "e", "f", AV_DICT_DONT_OVERWRITE); av_dict_set(&dict, "f", "f", 0); av_dict_set(&dict, "f", NULL, 0); av_dict_set(&dict, "ff", "f", 0); av_dict_set(&dict, "ff", "f", AV_DICT_APPEND); e = NULL; while ((e = av_dict_get(dict, "", e, AV_DICT_IGNORE_SUFFIX))) printf("%s %s\n", e->key, e->value); av_dict_free(&dict); av_dict_set(&dict, NULL, "a", 0); av_dict_set(&dict, NULL, "b", 0); av_dict_get(dict, NULL, NULL, 0); e = NULL; while ((e = av_dict_get(dict, "", e, AV_DICT_IGNORE_SUFFIX))) printf("'%s' '%s'\n", e->key, e->value); av_dict_free(&dict); //valgrind sensible test printf("\nTesting av_dict_set_int()\n"); av_dict_set_int(&dict, "1", 1, AV_DICT_DONT_STRDUP_VAL); av_dict_set_int(&dict, av_strdup("2"), 2, AV_DICT_DONT_STRDUP_KEY); av_dict_set_int(&dict, av_strdup("3"), 3, AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL); av_dict_set_int(&dict, "4", 4, 0); av_dict_set_int(&dict, "5", 5, AV_DICT_DONT_OVERWRITE); av_dict_set_int(&dict, "5", 6, AV_DICT_DONT_OVERWRITE); av_dict_set_int(&dict, "12", 1, 0); av_dict_set_int(&dict, "12", 2, AV_DICT_APPEND); e = NULL; while ((e = av_dict_get(dict, "", e, AV_DICT_IGNORE_SUFFIX))) printf("%s %s\n", e->key, e->value); av_dict_free(&dict); //valgrind sensible test printf("\nTesting av_dict_set() with existing AVDictionaryEntry.key as key\n"); av_dict_set(&dict, "key", "old", 0); e = av_dict_get(dict, "key", NULL, 0); av_dict_set(&dict, e->key, "new val OK", 0); e = av_dict_get(dict, "key", NULL, 0); printf("%s\n", e->value); av_dict_set(&dict, e->key, e->value, 0); e = av_dict_get(dict, "key", NULL, 0); printf("%s\n", e->value); av_dict_free(&dict); return 0; }
static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) { int i, ret; AVDictionary *options = NULL; AVDictionaryEntry *entry; char *filename; char *format = NULL, *select = NULL, *on_fail = NULL; char *use_fifo = NULL, *fifo_options_str = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int stream_count; int fullret; char *subselect = NULL, *next_subselect = NULL, *first_subselect = NULL, *tmp_select = NULL; if ((ret = ff_tee_parse_slave_options(avf, slave, &options, &filename)) < 0) return ret; #define STEAL_OPTION(option, field) do { \ if ((entry = av_dict_get(options, option, NULL, 0))) { \ field = entry->value; \ entry->value = NULL; /* prevent it from being freed */ \ av_dict_set(&options, option, NULL, 0); \ } \ } while (0) STEAL_OPTION("f", format); STEAL_OPTION("select", select); STEAL_OPTION("onfail", on_fail); STEAL_OPTION("use_fifo", use_fifo); STEAL_OPTION("fifo_options", fifo_options_str); ret = parse_slave_failure_policy_option(on_fail, tee_slave); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Invalid onfail option value, valid options are 'abort' and 'ignore'\n"); goto end; } ret = parse_slave_fifo_options(use_fifo, fifo_options_str, tee_slave); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Error parsing fifo options: %s\n", av_err2str(ret)); goto end; } if (tee_slave->use_fifo) { if (options) { char *format_options_str = NULL; ret = av_dict_get_string(options, &format_options_str, '=', ':'); if (ret < 0) goto end; ret = av_dict_set(&tee_slave->fifo_options, "format_options", format_options_str, AV_DICT_DONT_STRDUP_VAL); if (ret < 0) goto end; } if (format) { ret = av_dict_set(&tee_slave->fifo_options, "fifo_format", format, AV_DICT_DONT_STRDUP_VAL); format = NULL; if (ret < 0) goto end; } av_dict_free(&options); options = tee_slave->fifo_options; } ret = avformat_alloc_output_context2(&avf2, NULL, tee_slave->use_fifo ? "fifo" :format, filename); if (ret < 0) goto end; tee_slave->avf = avf2; av_dict_copy(&avf2->metadata, avf->metadata, 0); avf2->opaque = avf->opaque; avf2->io_open = avf->io_open; avf2->io_close = avf->io_close; avf2->interrupt_callback = avf->interrupt_callback; avf2->flags = avf->flags; tee_slave->stream_map = av_calloc(avf->nb_streams, sizeof(*tee_slave->stream_map)); if (!tee_slave->stream_map) { ret = AVERROR(ENOMEM); goto end; } stream_count = 0; for (i = 0; i < avf->nb_streams; i++) { st = avf->streams[i]; if (select) { tmp_select = av_strdup(select); // av_strtok is destructive so we regenerate it in each loop if (!tmp_select) { ret = AVERROR(ENOMEM); goto end; } fullret = 0; first_subselect = tmp_select; next_subselect = NULL; while (subselect = av_strtok(first_subselect, slave_select_sep, &next_subselect)) { first_subselect = NULL; ret = avformat_match_stream_specifier(avf, avf->streams[i], subselect); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Invalid stream specifier '%s' for output '%s'\n", subselect, slave); goto end; } if (ret != 0) { fullret = 1; // match break; } } av_freep(&tmp_select); if (fullret == 0) { /* no match */ tee_slave->stream_map[i] = -1; continue; } } tee_slave->stream_map[i] = stream_count++; if (!(st2 = avformat_new_stream(avf2, NULL))) { ret = AVERROR(ENOMEM); goto end; } ret = ff_stream_encode_params_copy(st2, st); if (ret < 0) goto end; } ret = ff_format_output_open(avf2, filename, NULL); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Slave '%s': error opening: %s\n", slave, av_err2str(ret)); goto end; } if ((ret = avformat_write_header(avf2, &options)) < 0) { av_log(avf, AV_LOG_ERROR, "Slave '%s': error writing header: %s\n", slave, av_err2str(ret)); goto end; } tee_slave->header_written = 1; tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(*tee_slave->bsfs)); if (!tee_slave->bsfs) { ret = AVERROR(ENOMEM); goto end; } entry = NULL; while (entry = av_dict_get(options, "bsfs", NULL, AV_DICT_IGNORE_SUFFIX)) { const char *spec = entry->key + strlen("bsfs"); if (*spec) { if (strspn(spec, slave_bsfs_spec_sep) != 1) { av_log(avf, AV_LOG_ERROR, "Specifier separator in '%s' is '%c', but only characters '%s' " "are allowed\n", entry->key, *spec, slave_bsfs_spec_sep); ret = AVERROR(EINVAL); goto end; } spec++; /* consume separator */ } for (i = 0; i < avf2->nb_streams; i++) { ret = avformat_match_stream_specifier(avf2, avf2->streams[i], spec); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Invalid stream specifier '%s' in bsfs option '%s' for slave " "output '%s'\n", spec, entry->key, filename); goto end; } if (ret > 0) { av_log(avf, AV_LOG_DEBUG, "spec:%s bsfs:%s matches stream %d of slave " "output '%s'\n", spec, entry->value, i, filename); if (tee_slave->bsfs[i]) { av_log(avf, AV_LOG_WARNING, "Duplicate bsfs specification associated to stream %d of slave " "output '%s', filters will be ignored\n", i, filename); continue; } ret = av_bsf_list_parse_str(entry->value, &tee_slave->bsfs[i]); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Error parsing bitstream filter sequence '%s' associated to " "stream %d of slave output '%s'\n", entry->value, i, filename); goto end; } } } av_dict_set(&options, entry->key, NULL, 0); } for (i = 0; i < avf->nb_streams; i++){ int target_stream = tee_slave->stream_map[i]; if (target_stream < 0) continue; if (!tee_slave->bsfs[target_stream]) { /* Add pass-through bitstream filter */ ret = av_bsf_get_null_filter(&tee_slave->bsfs[target_stream]); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Failed to create pass-through bitstream filter: %s\n", av_err2str(ret)); goto end; } } tee_slave->bsfs[target_stream]->time_base_in = avf->streams[i]->time_base; ret = avcodec_parameters_copy(tee_slave->bsfs[target_stream]->par_in, avf->streams[i]->codecpar); if (ret < 0) goto end; ret = av_bsf_init(tee_slave->bsfs[target_stream]); if (ret < 0) { av_log(avf, AV_LOG_ERROR, "Failed to initialize bitstream filter(s): %s\n", av_err2str(ret)); goto end; } } if (options) { entry = NULL; while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX))) av_log(avf2, AV_LOG_ERROR, "Unknown option '%s'\n", entry->key); ret = AVERROR_OPTION_NOT_FOUND; goto end; } end: av_free(format); av_free(select); av_free(on_fail); av_dict_free(&options); av_freep(&tmp_select); return ret; }
/* add a codec and set the default parameters */ static void add_codec(FFServerStream *stream, AVCodecContext *av, FFServerConfig *config) { AVStream *st; AVDictionary **opts, *recommended = NULL; char *enc_config; if(stream->nb_streams >= FF_ARRAY_ELEMS(stream->streams)) return; opts = av->codec_type == AVMEDIA_TYPE_AUDIO ? &config->audio_opts : &config->video_opts; av_dict_copy(&recommended, *opts, 0); av_opt_set_dict2(av->priv_data, opts, AV_OPT_SEARCH_CHILDREN); av_opt_set_dict2(av, opts, AV_OPT_SEARCH_CHILDREN); if (av_dict_count(*opts)) av_log(NULL, AV_LOG_WARNING, "Something is wrong, %d options are not set!\n", av_dict_count(*opts)); if (!config->stream_use_defaults) { switch(av->codec_type) { case AVMEDIA_TYPE_AUDIO: if (av->bit_rate == 0) report_config_error(config->filename, config->line_num, AV_LOG_ERROR, &config->errors, "audio bit rate is not set\n"); if (av->sample_rate == 0) report_config_error(config->filename, config->line_num, AV_LOG_ERROR, &config->errors, "audio sample rate is not set\n"); break; case AVMEDIA_TYPE_VIDEO: if (av->width == 0 || av->height == 0) report_config_error(config->filename, config->line_num, AV_LOG_ERROR, &config->errors, "video size is not set\n"); break; default: av_assert0(0); } goto done; } /* stream_use_defaults = true */ /* compute default parameters */ switch(av->codec_type) { case AVMEDIA_TYPE_AUDIO: if (!av_dict_get(recommended, "b", NULL, 0)) { av->bit_rate = 64000; av_dict_set_int(&recommended, "b", av->bit_rate, 0); WARNING("Setting default value for audio bit rate = %d. " "Use NoDefaults to disable it.\n", av->bit_rate); } if (!av_dict_get(recommended, "ar", NULL, 0)) { av->sample_rate = 22050; av_dict_set_int(&recommended, "ar", av->sample_rate, 0); WARNING("Setting default value for audio sample rate = %d. " "Use NoDefaults to disable it.\n", av->sample_rate); } if (!av_dict_get(recommended, "ac", NULL, 0)) { av->channels = 1; av_dict_set_int(&recommended, "ac", av->channels, 0); WARNING("Setting default value for audio channel count = %d. " "Use NoDefaults to disable it.\n", av->channels); } break; case AVMEDIA_TYPE_VIDEO: if (!av_dict_get(recommended, "b", NULL, 0)) { av->bit_rate = 64000; av_dict_set_int(&recommended, "b", av->bit_rate, 0); WARNING("Setting default value for video bit rate = %d. " "Use NoDefaults to disable it.\n", av->bit_rate); } if (!av_dict_get(recommended, "time_base", NULL, 0)) { av->time_base.den = 5; av->time_base.num = 1; av_dict_set(&recommended, "time_base", "1/5", 0); WARNING("Setting default value for video frame rate = %d. " "Use NoDefaults to disable it.\n", av->time_base.den); } if (!av_dict_get(recommended, "video_size", NULL, 0)) { av->width = 160; av->height = 128; av_dict_set(&recommended, "video_size", "160x128", 0); WARNING("Setting default value for video size = %dx%d. " "Use NoDefaults to disable it.\n", av->width, av->height); } /* Bitrate tolerance is less for streaming */ if (!av_dict_get(recommended, "bt", NULL, 0)) { av->bit_rate_tolerance = FFMAX(av->bit_rate / 4, (int64_t)av->bit_rate*av->time_base.num/av->time_base.den); av_dict_set_int(&recommended, "bt", av->bit_rate_tolerance, 0); WARNING("Setting default value for video bit rate tolerance = %d. " "Use NoDefaults to disable it.\n", av->bit_rate_tolerance); } if (!av_dict_get(recommended, "rc_eq", NULL, 0)) { av->rc_eq = av_strdup("tex^qComp"); av_dict_set(&recommended, "rc_eq", "tex^qComp", 0); WARNING("Setting default value for video rate control equation = " "%s. Use NoDefaults to disable it.\n", av->rc_eq); } if (!av_dict_get(recommended, "maxrate", NULL, 0)) { av->rc_max_rate = av->bit_rate * 2; av_dict_set_int(&recommended, "maxrate", av->rc_max_rate, 0); WARNING("Setting default value for video max rate = %d. " "Use NoDefaults to disable it.\n", av->rc_max_rate); } if (av->rc_max_rate && !av_dict_get(recommended, "bufsize", NULL, 0)) { av->rc_buffer_size = av->rc_max_rate; av_dict_set_int(&recommended, "bufsize", av->rc_buffer_size, 0); WARNING("Setting default value for video buffer size = %d. " "Use NoDefaults to disable it.\n", av->rc_buffer_size); } break; default: abort(); } done: st = av_mallocz(sizeof(AVStream)); if (!st) return; av_dict_get_string(recommended, &enc_config, '=', ','); av_dict_free(&recommended); av_stream_set_recommended_encoder_configuration(st, enc_config); st->codec = av; stream->streams[stream->nb_streams++] = st; }