opt.c 19.4 KB
Newer Older
1 2 3 4
/*
 * AVOptions
 * Copyright (c) 2005 Michael Niedermayer <michaelni@gmx.at>
 *
5
 * This file is part of Libav.
6
 *
7
 * Libav is free software; you can redistribute it and/or
8 9 10 11
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
12
 * Libav is distributed in the hope that it will be useful,
13 14 15 16 17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with Libav; if not, write to the Free Software
19 20 21 22 23 24 25 26 27 28
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file
 * AVOptions
 * @author Michael Niedermayer <michaelni@gmx.at>
 */

#include "avutil.h"
29
#include "avstring.h"
30
#include "opt.h"
31
#include "eval.h"
32 33

//FIXME order them and do a bin search
34 35
const AVOption *av_find_opt(void *v, const char *name, const char *unit, int mask, int flags)
{
36 37 38
    AVClass *c= *(AVClass**)v; //FIXME silly way of storing AVClass
    const AVOption *o= c->option;

39 40
    for (; o && o->name; o++) {
        if (!strcmp(o->name, name) && (!unit || (o->unit && !strcmp(o->unit, unit))) && (o->flags & mask) == flags)
41 42 43 44 45
            return o;
    }
    return NULL;
}

46 47 48 49 50
const AVOption *av_next_option(void *obj, const AVOption *last)
{
    if (last && last[1].name) return ++last;
    else if (last)            return NULL;
    else                      return (*(AVClass**)obj)->option;
51 52
}

53 54
static int av_set_number2(void *obj, const char *name, double num, int den, int64_t intnum, const AVOption **o_out)
{
55 56
    const AVOption *o= av_find_opt(obj, name, NULL, 0, 0);
    void *dst;
57
    if (o_out)
58
        *o_out= o;
59
    if (!o || o->offset<=0)
60
        return AVERROR_OPTION_NOT_FOUND;
61

62
    if (o->max*den < num*intnum || o->min*den > num*intnum) {
63 64 65 66 67 68
        av_log(obj, AV_LOG_ERROR, "Value %lf for parameter '%s' out of range\n", num, name);
        return AVERROR(ERANGE);
    }

    dst= ((uint8_t*)obj) + o->offset;

69
    switch (o->type) {
70 71 72 73 74 75
    case FF_OPT_TYPE_FLAGS:
    case FF_OPT_TYPE_INT:   *(int       *)dst= llrint(num/den)*intnum; break;
    case FF_OPT_TYPE_INT64: *(int64_t   *)dst= llrint(num/den)*intnum; break;
    case FF_OPT_TYPE_FLOAT: *(float     *)dst= num*intnum/den;         break;
    case FF_OPT_TYPE_DOUBLE:*(double    *)dst= num*intnum/den;         break;
    case FF_OPT_TYPE_RATIONAL:
76 77
        if ((int)num == num) *(AVRational*)dst= (AVRational){num*intnum, den};
        else                 *(AVRational*)dst= av_d2q(num*intnum/den, 1<<24);
78 79 80 81 82 83 84
        break;
    default:
        return AVERROR(EINVAL);
    }
    return 0;
}

85 86
static const AVOption *av_set_number(void *obj, const char *name, double num, int den, int64_t intnum)
{
87 88 89 90 91 92 93
    const AVOption *o = NULL;
    if (av_set_number2(obj, name, num, den, intnum, &o) < 0)
        return NULL;
    else
        return o;
}

94
static const double const_values[] = {
95 96 97 98 99 100
    M_PI,
    M_E,
    FF_QP2LAMBDA,
    0
};

101
static const char * const const_names[] = {
102 103 104 105 106 107 108 109 110 111 112 113 114
    "PI",
    "E",
    "QP2LAMBDA",
    0
};

static int hexchar2int(char c) {
    if (c >= '0' && c <= '9') return c - '0';
    if (c >= 'a' && c <= 'f') return c - 'a' + 10;
    if (c >= 'A' && c <= 'F') return c - 'A' + 10;
    return -1;
}

115 116
int av_set_string3(void *obj, const char *name, const char *val, int alloc, const AVOption **o_out)
{
117 118 119 120
    int ret;
    const AVOption *o= av_find_opt(obj, name, NULL, 0, 0);
    if (o_out)
        *o_out = o;
121
    if (!o)
122
        return AVERROR_OPTION_NOT_FOUND;
123
    if (!val || o->offset<=0)
124 125
        return AVERROR(EINVAL);

126
    if (o->type == FF_OPT_TYPE_BINARY) {
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
        uint8_t **dst = (uint8_t **)(((uint8_t*)obj) + o->offset);
        int *lendst = (int *)(dst + 1);
        uint8_t *bin, *ptr;
        int len = strlen(val);
        av_freep(dst);
        *lendst = 0;
        if (len & 1) return AVERROR(EINVAL);
        len /= 2;
        ptr = bin = av_malloc(len);
        while (*val) {
            int a = hexchar2int(*val++);
            int b = hexchar2int(*val++);
            if (a < 0 || b < 0) {
                av_free(bin);
                return AVERROR(EINVAL);
            }
            *ptr++ = (a << 4) | b;
        }
        *dst = bin;
        *lendst = len;
        return 0;
    }
149
    if (o->type != FF_OPT_TYPE_STRING) {
150
        int notfirst=0;
151
        for (;;) {
152 153 154 155 156
            int i;
            char buf[256];
            int cmd=0;
            double d;

157
            if (*val == '+' || *val == '-')
158 159
                cmd= *(val++);

160
            for (i=0; i<sizeof(buf)-1 && val[i] && val[i]!='+' && val[i]!='-'; i++)
161 162 163 164 165
                buf[i]= val[i];
            buf[i]=0;

            {
                const AVOption *o_named= av_find_opt(obj, buf, o->unit, 0, 0);
166
                if (o_named && o_named->type == FF_OPT_TYPE_CONST)
167
                    d= o_named->default_val;
168 169 170 171 172
                else if (!strcmp(buf, "default")) d= o->default_val;
                else if (!strcmp(buf, "max"    )) d= o->max;
                else if (!strcmp(buf, "min"    )) d= o->min;
                else if (!strcmp(buf, "none"   )) d= 0;
                else if (!strcmp(buf, "all"    )) d= ~0;
173
                else {
174
                    int res = av_expr_parse_and_eval(&d, buf, const_names, const_values, NULL, NULL, NULL, NULL, NULL, 0, obj);
175 176 177 178 179 180
                    if (res < 0) {
                        av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\"\n", val);
                        return res;
                    }
                }
            }
181 182 183 184 185 186
            if (o->type == FF_OPT_TYPE_FLAGS) {
                if      (cmd=='+') d= av_get_int(obj, name, NULL) | (int64_t)d;
                else if (cmd=='-') d= av_get_int(obj, name, NULL) &~(int64_t)d;
            } else {
                if      (cmd=='+') d= notfirst*av_get_double(obj, name, NULL) + d;
                else if (cmd=='-') d= notfirst*av_get_double(obj, name, NULL) - d;
187 188 189 190 191
            }

            if ((ret = av_set_number2(obj, name, d, 1, 1, o_out)) < 0)
                return ret;
            val+= i;
192
            if (!*val)
193 194 195 196 197 198
                return 0;
            notfirst=1;
        }
        return AVERROR(EINVAL);
    }

199
    if (alloc) {
200 201 202 203 204 205 206 207
        av_free(*(void**)(((uint8_t*)obj) + o->offset));
        val= av_strdup(val);
    }

    memcpy(((uint8_t*)obj) + o->offset, &val, sizeof(val));
    return 0;
}

208 209
const AVOption *av_set_double(void *obj, const char *name, double n)
{
210 211 212
    return av_set_number(obj, name, n, 1, 1);
}

213 214
const AVOption *av_set_q(void *obj, const char *name, AVRational n)
{
215 216 217
    return av_set_number(obj, name, n.num, n.den, 1);
}

218 219
const AVOption *av_set_int(void *obj, const char *name, int64_t n)
{
220 221 222 223 224 225 226 227
    return av_set_number(obj, name, 1, 1, n);
}

/**
 *
 * @param buf a buffer which is used for returning non string values as strings, can be NULL
 * @param buf_len allocated length in bytes of buf
 */
228 229
const char *av_get_string(void *obj, const char *name, const AVOption **o_out, char *buf, int buf_len)
{
230 231 232 233
    const AVOption *o= av_find_opt(obj, name, NULL, 0, 0);
    void *dst;
    uint8_t *bin;
    int len, i;
234
    if (!o || o->offset<=0)
235
        return NULL;
236
    if (o->type != FF_OPT_TYPE_STRING && (!buf || !buf_len))
237 238 239
        return NULL;

    dst= ((uint8_t*)obj) + o->offset;
240
    if (o_out) *o_out= o;
241

242
    switch (o->type) {
243 244 245 246 247 248 249 250 251
    case FF_OPT_TYPE_FLAGS:     snprintf(buf, buf_len, "0x%08X",*(int    *)dst);break;
    case FF_OPT_TYPE_INT:       snprintf(buf, buf_len, "%d" , *(int    *)dst);break;
    case FF_OPT_TYPE_INT64:     snprintf(buf, buf_len, "%"PRId64, *(int64_t*)dst);break;
    case FF_OPT_TYPE_FLOAT:     snprintf(buf, buf_len, "%f" , *(float  *)dst);break;
    case FF_OPT_TYPE_DOUBLE:    snprintf(buf, buf_len, "%f" , *(double *)dst);break;
    case FF_OPT_TYPE_RATIONAL:  snprintf(buf, buf_len, "%d/%d", ((AVRational*)dst)->num, ((AVRational*)dst)->den);break;
    case FF_OPT_TYPE_STRING:    return *(void**)dst;
    case FF_OPT_TYPE_BINARY:
        len = *(int*)(((uint8_t *)dst) + sizeof(uint8_t *));
252
        if (len >= (buf_len + 1)/2) return NULL;
253
        bin = *(uint8_t**)dst;
254
        for (i = 0; i < len; i++) snprintf(buf + i*2, 3, "%02X", bin[i]);
255 256 257 258 259 260
        break;
    default: return NULL;
    }
    return buf;
}

261 262
static int av_get_number(void *obj, const char *name, const AVOption **o_out, double *num, int *den, int64_t *intnum)
{
263 264
    const AVOption *o= av_find_opt(obj, name, NULL, 0, 0);
    void *dst;
265
    if (!o || o->offset<=0)
266 267 268 269
        goto error;

    dst= ((uint8_t*)obj) + o->offset;

270
    if (o_out) *o_out= o;
271

272
    switch (o->type) {
273 274 275 276 277 278 279 280 281 282 283 284 285 286
    case FF_OPT_TYPE_FLAGS:     *intnum= *(unsigned int*)dst;return 0;
    case FF_OPT_TYPE_INT:       *intnum= *(int    *)dst;return 0;
    case FF_OPT_TYPE_INT64:     *intnum= *(int64_t*)dst;return 0;
    case FF_OPT_TYPE_FLOAT:     *num=    *(float  *)dst;return 0;
    case FF_OPT_TYPE_DOUBLE:    *num=    *(double *)dst;return 0;
    case FF_OPT_TYPE_RATIONAL:  *intnum= ((AVRational*)dst)->num;
                                *den   = ((AVRational*)dst)->den;
                                                        return 0;
    }
error:
    *den=*intnum=0;
    return -1;
}

287 288
double av_get_double(void *obj, const char *name, const AVOption **o_out)
{
289 290 291 292 293 294 295 296
    int64_t intnum=1;
    double num=1;
    int den=1;

    av_get_number(obj, name, o_out, &num, &den, &intnum);
    return num*intnum/den;
}

297 298
AVRational av_get_q(void *obj, const char *name, const AVOption **o_out)
{
299 300 301 302 303
    int64_t intnum=1;
    double num=1;
    int den=1;

    av_get_number(obj, name, o_out, &num, &den, &intnum);
304
    if (num == 1.0 && (int)intnum == intnum)
305 306 307 308 309
        return (AVRational){intnum, den};
    else
        return av_d2q(num*intnum/den, 1<<24);
}

310 311
int64_t av_get_int(void *obj, const char *name, const AVOption **o_out)
{
312 313 314 315 316 317 318 319 320 321 322 323 324
    int64_t intnum=1;
    double num=1;
    int den=1;

    av_get_number(obj, name, o_out, &num, &den, &intnum);
    return num*intnum/den;
}

static void opt_list(void *obj, void *av_log_obj, const char *unit,
                     int req_flags, int rej_flags)
{
    const AVOption *opt=NULL;

325
    while ((opt= av_next_option(obj, opt))) {
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
        if (!(opt->flags & req_flags) || (opt->flags & rej_flags))
            continue;

        /* Don't print CONST's on level one.
         * Don't print anything but CONST's on level two.
         * Only print items from the requested unit.
         */
        if (!unit && opt->type==FF_OPT_TYPE_CONST)
            continue;
        else if (unit && opt->type!=FF_OPT_TYPE_CONST)
            continue;
        else if (unit && opt->type==FF_OPT_TYPE_CONST && strcmp(unit, opt->unit))
            continue;
        else if (unit && opt->type == FF_OPT_TYPE_CONST)
            av_log(av_log_obj, AV_LOG_INFO, "   %-15s ", opt->name);
        else
            av_log(av_log_obj, AV_LOG_INFO, "-%-17s ", opt->name);

344
        switch (opt->type) {
345
            case FF_OPT_TYPE_FLAGS:
346
                av_log(av_log_obj, AV_LOG_INFO, "%-7s ", "<flags>");
347 348
                break;
            case FF_OPT_TYPE_INT:
349
                av_log(av_log_obj, AV_LOG_INFO, "%-7s ", "<int>");
350 351
                break;
            case FF_OPT_TYPE_INT64:
352
                av_log(av_log_obj, AV_LOG_INFO, "%-7s ", "<int64>");
353 354
                break;
            case FF_OPT_TYPE_DOUBLE:
355
                av_log(av_log_obj, AV_LOG_INFO, "%-7s ", "<double>");
356 357
                break;
            case FF_OPT_TYPE_FLOAT:
358
                av_log(av_log_obj, AV_LOG_INFO, "%-7s ", "<float>");
359 360
                break;
            case FF_OPT_TYPE_STRING:
361
                av_log(av_log_obj, AV_LOG_INFO, "%-7s ", "<string>");
362 363
                break;
            case FF_OPT_TYPE_RATIONAL:
364
                av_log(av_log_obj, AV_LOG_INFO, "%-7s ", "<rational>");
365 366
                break;
            case FF_OPT_TYPE_BINARY:
367
                av_log(av_log_obj, AV_LOG_INFO, "%-7s ", "<binary>");
368 369 370
                break;
            case FF_OPT_TYPE_CONST:
            default:
371
                av_log(av_log_obj, AV_LOG_INFO, "%-7s ", "");
372 373 374 375 376 377 378 379
                break;
        }
        av_log(av_log_obj, AV_LOG_INFO, "%c", (opt->flags & AV_OPT_FLAG_ENCODING_PARAM) ? 'E' : '.');
        av_log(av_log_obj, AV_LOG_INFO, "%c", (opt->flags & AV_OPT_FLAG_DECODING_PARAM) ? 'D' : '.');
        av_log(av_log_obj, AV_LOG_INFO, "%c", (opt->flags & AV_OPT_FLAG_VIDEO_PARAM   ) ? 'V' : '.');
        av_log(av_log_obj, AV_LOG_INFO, "%c", (opt->flags & AV_OPT_FLAG_AUDIO_PARAM   ) ? 'A' : '.');
        av_log(av_log_obj, AV_LOG_INFO, "%c", (opt->flags & AV_OPT_FLAG_SUBTITLE_PARAM) ? 'S' : '.');

380
        if (opt->help)
381 382 383 384 385 386 387 388 389 390
            av_log(av_log_obj, AV_LOG_INFO, " %s", opt->help);
        av_log(av_log_obj, AV_LOG_INFO, "\n");
        if (opt->unit && opt->type != FF_OPT_TYPE_CONST) {
            opt_list(obj, av_log_obj, opt->unit, req_flags, rej_flags);
        }
    }
}

int av_opt_show2(void *obj, void *av_log_obj, int req_flags, int rej_flags)
{
391
    if (!obj)
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
        return -1;

    av_log(av_log_obj, AV_LOG_INFO, "%s AVOptions:\n", (*(AVClass**)obj)->class_name);

    opt_list(obj, av_log_obj, NULL, req_flags, rej_flags);

    return 0;
}

/** Set the values of the AVCodecContext or AVFormatContext structure.
 * They are set to the defaults specified in the according AVOption options
 * array default_val field.
 *
 * @param s AVCodecContext or AVFormatContext for which the defaults will be set
 */
void av_opt_set_defaults2(void *s, int mask, int flags)
{
    const AVOption *opt = NULL;
    while ((opt = av_next_option(s, opt)) != NULL) {
411
        if ((opt->flags & mask) != flags)
412
            continue;
413
        switch (opt->type) {
414 415 416 417 418 419 420 421 422 423 424
            case FF_OPT_TYPE_CONST:
                /* Nothing to be done here */
            break;
            case FF_OPT_TYPE_FLAGS:
            case FF_OPT_TYPE_INT: {
                int val;
                val = opt->default_val;
                av_set_int(s, opt->name, val);
            }
            break;
            case FF_OPT_TYPE_INT64:
425
                if ((double)(opt->default_val+0.6) == opt->default_val)
426 427 428
                    av_log(s, AV_LOG_DEBUG, "loss of precision in default of %s\n", opt->name);
                av_set_int(s, opt->name, opt->default_val);
            break;
429
            case FF_OPT_TYPE_DOUBLE:
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
            case FF_OPT_TYPE_FLOAT: {
                double val;
                val = opt->default_val;
                av_set_double(s, opt->name, val);
            }
            break;
            case FF_OPT_TYPE_RATIONAL: {
                AVRational val;
                val = av_d2q(opt->default_val, INT_MAX);
                av_set_q(s, opt->name, val);
            }
            break;
            case FF_OPT_TYPE_STRING:
            case FF_OPT_TYPE_BINARY:
                /* Cannot set default for string as default_val is of type * double */
            break;
            default:
                av_log(s, AV_LOG_DEBUG, "AVOption type %d of option %s not implemented yet\n", opt->type, opt->name);
        }
    }
}

452 453
void av_opt_set_defaults(void *s)
{
454 455 456
    av_opt_set_defaults2(s, 0, 0);
}

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
/**
 * Store the value in the field in ctx that is named like key.
 * ctx must be an AVClass context, storing is done using AVOptions.
 *
 * @param buf the string to parse, buf will be updated to point at the
 * separator just after the parsed key/value pair
 * @param key_val_sep a 0-terminated list of characters used to
 * separate key from value
 * @param pairs_sep a 0-terminated list of characters used to separate
 * two pairs from each other
 * @return 0 if the key/value pair has been successfully parsed and
 * set, or a negative value corresponding to an AVERROR code in case
 * of error:
 * AVERROR(EINVAL) if the key/value pair cannot be parsed,
 * the error code issued by av_set_string3() if the key/value pair
 * cannot be set
 */
static int parse_key_value_pair(void *ctx, const char **buf,
                                const char *key_val_sep, const char *pairs_sep)
{
    char *key = av_get_token(buf, key_val_sep);
    char *val;
    int ret;

    if (*key && strspn(*buf, key_val_sep)) {
        (*buf)++;
        val = av_get_token(buf, pairs_sep);
    } else {
        av_log(ctx, AV_LOG_ERROR, "Missing key or no key/value separator found after key '%s'\n", key);
        av_free(key);
        return AVERROR(EINVAL);
    }

    av_log(ctx, AV_LOG_DEBUG, "Setting value '%s' for key '%s'\n", val, key);

    ret = av_set_string3(ctx, key, val, 1, NULL);
493
    if (ret == AVERROR_OPTION_NOT_FOUND)
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
        av_log(ctx, AV_LOG_ERROR, "Key '%s' not found.\n", key);

    av_free(key);
    av_free(val);
    return ret;
}

int av_set_options_string(void *ctx, const char *opts,
                          const char *key_val_sep, const char *pairs_sep)
{
    int ret, count = 0;

    while (*opts) {
        if ((ret = parse_key_value_pair(ctx, &opts, key_val_sep, pairs_sep)) < 0)
            return ret;
        count++;

        if (*opts)
            opts++;
    }

    return count;
}

#ifdef TEST

#undef printf

typedef struct TestContext
{
    const AVClass *class;
    int num;
    int toggle;
    char *string;
    int flags;
    AVRational rational;
} TestContext;

#define OFFSET(x) offsetof(TestContext, x)

#define TEST_FLAG_COOL 01
#define TEST_FLAG_LAME 02
#define TEST_FLAG_MU   04

static const AVOption test_options[]= {
{"num",      "set num",        OFFSET(num),      FF_OPT_TYPE_INT,      0,              0,        100                 },
{"toggle",   "set toggle",     OFFSET(toggle),   FF_OPT_TYPE_INT,      0,              0,        1                   },
{"rational", "set rational",   OFFSET(rational), FF_OPT_TYPE_RATIONAL, 0,              0,        10                  },
{"string",   "set string",     OFFSET(string),   FF_OPT_TYPE_STRING,   0,              CHAR_MIN, CHAR_MAX            },
{"flags",    "set flags",      OFFSET(flags),    FF_OPT_TYPE_FLAGS,    0,              0,        INT_MAX, 0, "flags" },
{"cool",     "set cool flag ", 0,                FF_OPT_TYPE_CONST,    TEST_FLAG_COOL, INT_MIN,  INT_MAX, 0, "flags" },
{"lame",     "set lame flag ", 0,                FF_OPT_TYPE_CONST,    TEST_FLAG_LAME, INT_MIN,  INT_MAX, 0, "flags" },
{"mu",       "set mu flag ",   0,                FF_OPT_TYPE_CONST,    TEST_FLAG_MU,   INT_MIN,  INT_MAX, 0, "flags" },
{NULL},
};

static const char *test_get_name(void *ctx)
{
    return "test";
}

static const AVClass test_class = {
    "TestContext",
    test_get_name,
    test_options
};

int main(void)
{
    int i;

    printf("\nTesting av_set_options_string()\n");
    {
        TestContext test_ctx;
        const char *options[] = {
            "",
            ":",
            "=",
            "foo=:",
            ":=foo",
            "=foo",
            "foo=",
            "foo",
            "foo=val",
            "foo==val",
            "toggle=:",
            "string=:",
            "toggle=1 : foo",
            "toggle=100",
            "toggle==1",
            "flags=+mu-lame : num=42: toggle=0",
            "num=42 : string=blahblah",
            "rational=0 : rational=1/2 : rational=1/-1",
            "rational=-1/0",
        };

        test_ctx.class = &test_class;
        av_opt_set_defaults2(&test_ctx, 0, 0);
        test_ctx.string = av_strdup("default");

        av_log_set_level(AV_LOG_DEBUG);

        for (i=0; i < FF_ARRAY_ELEMS(options); i++) {
            av_log(&test_ctx, AV_LOG_DEBUG, "Setting options string '%s'\n", options[i]);
            if (av_set_options_string(&test_ctx, options[i], "=", ":") < 0)
                av_log(&test_ctx, AV_LOG_ERROR, "Error setting options string: '%s'\n", options[i]);
            printf("\n");
        }
    }

    return 0;
}

#endif