Coverage Report

Created: 2026-01-25 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavformat/utils.c
Line
Count
Source
1
/*
2
 * various utility functions for use within FFmpeg
3
 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
22
#include <stdint.h>
23
#include <time.h>
24
25
#include "config.h"
26
27
#include "libavutil/avassert.h"
28
#include "libavutil/avstring.h"
29
#include "libavutil/bprint.h"
30
#include "libavutil/dict.h"
31
#include "libavutil/internal.h"
32
#include "libavutil/mem.h"
33
#include "libavutil/opt.h"
34
#include "libavutil/parseutils.h"
35
#include "libavutil/time.h"
36
#include "libavutil/time_internal.h"
37
38
#include "libavcodec/internal.h"
39
40
#include "avformat.h"
41
#include "avio_internal.h"
42
#include "internal.h"
43
#if CONFIG_NETWORK
44
#include "network.h"
45
#endif
46
#include "os_support.h"
47
#include "urldecode.h"
48
49
/**
50
 * @file
51
 * various utility functions for use within FFmpeg
52
 */
53
54
/* an arbitrarily chosen "sane" max packet size -- 50M */
55
0
#define SANE_CHUNK_SIZE (50000000)
56
57
/* Read the data in sane-sized chunks and append to pkt.
58
 * Return the number of bytes read or an error. */
59
static int append_packet_chunked(AVIOContext *s, AVPacket *pkt, int size)
60
0
{
61
0
    int orig_size      = pkt->size;
62
0
    int ret;
63
64
0
    do {
65
0
        int prev_size = pkt->size;
66
0
        int read_size;
67
68
        /* When the caller requests a lot of data, limit it to the amount
69
         * left in file or SANE_CHUNK_SIZE when it is not known. */
70
0
        read_size = size;
71
0
        if (read_size > SANE_CHUNK_SIZE/10) {
72
0
            read_size = ffio_limit(s, read_size);
73
            // If filesize/maxsize is unknown, limit to SANE_CHUNK_SIZE
74
0
            if (ffiocontext(s)->maxsize < 0)
75
0
                read_size = FFMIN(read_size, SANE_CHUNK_SIZE);
76
0
        }
77
78
0
        ret = av_grow_packet(pkt, read_size);
79
0
        if (ret < 0)
80
0
            break;
81
82
0
        ret = avio_read(s, pkt->data + prev_size, read_size);
83
0
        if (ret != read_size) {
84
0
            av_shrink_packet(pkt, prev_size + FFMAX(ret, 0));
85
0
            break;
86
0
        }
87
88
0
        size -= read_size;
89
0
    } while (size > 0);
90
0
    if (size > 0)
91
0
        pkt->flags |= AV_PKT_FLAG_CORRUPT;
92
93
0
    if (!pkt->size)
94
0
        av_packet_unref(pkt);
95
0
    return pkt->size > orig_size ? pkt->size - orig_size : ret;
96
0
}
97
98
int av_get_packet(AVIOContext *s, AVPacket *pkt, int size)
99
0
{
100
0
#if FF_API_INIT_PACKET
101
0
FF_DISABLE_DEPRECATION_WARNINGS
102
0
    av_init_packet(pkt);
103
0
    pkt->data = NULL;
104
0
    pkt->size = 0;
105
0
FF_ENABLE_DEPRECATION_WARNINGS
106
#else
107
    av_packet_unref(pkt);
108
#endif
109
0
    pkt->pos  = avio_tell(s);
110
111
0
    return append_packet_chunked(s, pkt, size);
112
0
}
113
114
int av_append_packet(AVIOContext *s, AVPacket *pkt, int size)
115
0
{
116
0
    if (!pkt->size)
117
0
        return av_get_packet(s, pkt, size);
118
0
    return append_packet_chunked(s, pkt, size);
119
0
}
120
121
int av_filename_number_test(const char *filename)
122
0
{
123
0
    AVBPrint bp;
124
125
0
    if (!filename)
126
0
        return 0;
127
0
    av_bprint_init(&bp, 0, AV_BPRINT_SIZE_COUNT_ONLY);
128
0
    return (ff_bprint_get_frame_filename(&bp, filename, 1, AV_FRAME_FILENAME_FLAGS_IGNORE_TRUNCATION) >= 0);
129
0
}
130
131
/**********************************************************/
132
133
unsigned int ff_codec_get_tag(const AVCodecTag *tags, enum AVCodecID id)
134
0
{
135
0
    while (tags->id != AV_CODEC_ID_NONE) {
136
0
        if (tags->id == id)
137
0
            return tags->tag;
138
0
        tags++;
139
0
    }
140
0
    return 0;
141
0
}
142
143
enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag)
144
0
{
145
0
    for (int i = 0; tags[i].id != AV_CODEC_ID_NONE; i++)
146
0
        if (tag == tags[i].tag)
147
0
            return tags[i].id;
148
0
    for (int i = 0; tags[i].id != AV_CODEC_ID_NONE; i++)
149
0
        if (ff_toupper4(tag) == ff_toupper4(tags[i].tag))
150
0
            return tags[i].id;
151
0
    return AV_CODEC_ID_NONE;
152
0
}
153
154
enum AVCodecID ff_get_pcm_codec_id(int bps, int flt, int be, int sflags)
155
0
{
156
0
    if (bps <= 0 || bps > 64)
157
0
        return AV_CODEC_ID_NONE;
158
159
0
    if (flt) {
160
0
        switch (bps) {
161
0
        case 32:
162
0
            return be ? AV_CODEC_ID_PCM_F32BE : AV_CODEC_ID_PCM_F32LE;
163
0
        case 64:
164
0
            return be ? AV_CODEC_ID_PCM_F64BE : AV_CODEC_ID_PCM_F64LE;
165
0
        default:
166
0
            return AV_CODEC_ID_NONE;
167
0
        }
168
0
    } else {
169
0
        bps  += 7;
170
0
        bps >>= 3;
171
0
        if (sflags & (1 << (bps - 1))) {
172
0
            switch (bps) {
173
0
            case 1:
174
0
                return AV_CODEC_ID_PCM_S8;
175
0
            case 2:
176
0
                return be ? AV_CODEC_ID_PCM_S16BE : AV_CODEC_ID_PCM_S16LE;
177
0
            case 3:
178
0
                return be ? AV_CODEC_ID_PCM_S24BE : AV_CODEC_ID_PCM_S24LE;
179
0
            case 4:
180
0
                return be ? AV_CODEC_ID_PCM_S32BE : AV_CODEC_ID_PCM_S32LE;
181
0
            case 8:
182
0
                return be ? AV_CODEC_ID_PCM_S64BE : AV_CODEC_ID_PCM_S64LE;
183
0
            default:
184
0
                return AV_CODEC_ID_NONE;
185
0
            }
186
0
        } else {
187
0
            switch (bps) {
188
0
            case 1:
189
0
                return AV_CODEC_ID_PCM_U8;
190
0
            case 2:
191
0
                return be ? AV_CODEC_ID_PCM_U16BE : AV_CODEC_ID_PCM_U16LE;
192
0
            case 3:
193
0
                return be ? AV_CODEC_ID_PCM_U24BE : AV_CODEC_ID_PCM_U24LE;
194
0
            case 4:
195
0
                return be ? AV_CODEC_ID_PCM_U32BE : AV_CODEC_ID_PCM_U32LE;
196
0
            default:
197
0
                return AV_CODEC_ID_NONE;
198
0
            }
199
0
        }
200
0
    }
201
0
}
202
203
unsigned int av_codec_get_tag(const AVCodecTag *const *tags, enum AVCodecID id)
204
0
{
205
0
    unsigned int tag;
206
0
    if (!av_codec_get_tag2(tags, id, &tag))
207
0
        return 0;
208
0
    return tag;
209
0
}
210
211
int av_codec_get_tag2(const AVCodecTag * const *tags, enum AVCodecID id,
212
                      unsigned int *tag)
213
0
{
214
0
    for (int i = 0; tags && tags[i]; i++) {
215
0
        const AVCodecTag *codec_tags = tags[i];
216
0
        while (codec_tags->id != AV_CODEC_ID_NONE) {
217
0
            if (codec_tags->id == id) {
218
0
                *tag = codec_tags->tag;
219
0
                return 1;
220
0
            }
221
0
            codec_tags++;
222
0
        }
223
0
    }
224
0
    return 0;
225
0
}
226
227
enum AVCodecID av_codec_get_id(const AVCodecTag *const *tags, unsigned int tag)
228
0
{
229
0
    for (int i = 0; tags && tags[i]; i++) {
230
0
        enum AVCodecID id = ff_codec_get_id(tags[i], tag);
231
0
        if (id != AV_CODEC_ID_NONE)
232
0
            return id;
233
0
    }
234
0
    return AV_CODEC_ID_NONE;
235
0
}
236
237
int ff_alloc_extradata(AVCodecParameters *par, int size)
238
0
{
239
0
    av_freep(&par->extradata);
240
0
    par->extradata_size = 0;
241
242
0
    if (size < 0 || size >= INT32_MAX - AV_INPUT_BUFFER_PADDING_SIZE)
243
0
        return AVERROR(EINVAL);
244
245
0
    par->extradata = av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
246
0
    if (!par->extradata)
247
0
        return AVERROR(ENOMEM);
248
249
0
    memset(par->extradata + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
250
0
    par->extradata_size = size;
251
252
0
    return 0;
253
0
}
254
255
/*******************************************************/
256
257
uint64_t ff_ntp_time(void)
258
0
{
259
0
    return (av_gettime() / 1000) * 1000 + NTP_OFFSET_US;
260
0
}
261
262
uint64_t ff_get_formatted_ntp_time(uint64_t ntp_time_us)
263
0
{
264
0
    uint64_t ntp_ts, frac_part, sec;
265
0
    uint32_t usec;
266
267
    //current ntp time in seconds and micro seconds
268
0
    sec = ntp_time_us / 1000000;
269
0
    usec = ntp_time_us % 1000000;
270
271
    //encoding in ntp timestamp format
272
0
    frac_part = usec * 0xFFFFFFFFULL;
273
0
    frac_part /= 1000000;
274
275
0
    if (sec > 0xFFFFFFFFULL)
276
0
        av_log(NULL, AV_LOG_WARNING, "NTP time format roll over detected\n");
277
278
0
    ntp_ts = sec << 32;
279
0
    ntp_ts |= frac_part;
280
281
0
    return ntp_ts;
282
0
}
283
284
uint64_t ff_parse_ntp_time(uint64_t ntp_ts)
285
0
{
286
0
    uint64_t sec = ntp_ts >> 32;
287
0
    uint64_t frac_part = ntp_ts & 0xFFFFFFFFULL;
288
0
    uint64_t usec = (frac_part * 1000000) / 0xFFFFFFFFULL;
289
290
0
    return (sec * 1000000) + usec;
291
0
}
292
293
int ff_bprint_get_frame_filename(struct AVBPrint *buf, const char *path, int64_t number, int flags)
294
0
{
295
0
    const char *p;
296
0
    char c;
297
0
    int nd, percentd_found;
298
299
0
    p = path;
300
0
    percentd_found = 0;
301
0
    for (;;) {
302
0
        c = *p++;
303
0
        if (c == '\0')
304
0
            break;
305
0
        if (c == '%') {
306
0
            do {
307
0
                nd = 0;
308
0
                while (av_isdigit(*p)) {
309
0
                    if (nd >= INT_MAX / 10 - 255)
310
0
                        goto fail;
311
0
                    nd = nd * 10 + *p++ - '0';
312
0
                }
313
0
                c = *p++;
314
0
            } while (av_isdigit(c));
315
316
0
            switch (c) {
317
0
            case '%':
318
0
                goto addchar;
319
0
            case 'd':
320
0
                if (!(flags & AV_FRAME_FILENAME_FLAGS_MULTIPLE) && percentd_found)
321
0
                    goto fail;
322
0
                percentd_found = 1;
323
0
                if (number < 0)
324
0
                    nd += 1;
325
0
                av_bprintf(buf, "%0*" PRId64, nd, number);
326
0
                break;
327
0
            default:
328
0
                goto fail;
329
0
            }
330
0
        } else {
331
0
addchar:
332
0
            av_bprint_chars(buf, c, 1);
333
0
        }
334
0
    }
335
0
    if (!percentd_found)
336
0
        goto fail;
337
0
    if (!(flags & AV_FRAME_FILENAME_FLAGS_IGNORE_TRUNCATION) && !av_bprint_is_complete(buf))
338
0
        return AVERROR(ENOMEM);
339
0
    return 0;
340
0
fail:
341
0
    return AVERROR(EINVAL);
342
0
}
343
344
static int get_frame_filename(char *buf, int buf_size, const char *path, int64_t number, int flags)
345
0
{
346
0
    AVBPrint bp;
347
0
    av_bprint_init_for_buffer(&bp, buf, buf_size);
348
0
    return ff_bprint_get_frame_filename(&bp, path, number, flags) < 0 ? -1 : 0;
349
0
}
350
351
int av_get_frame_filename2(char *buf, int buf_size, const char *path, int number, int flags)
352
0
{
353
0
    return get_frame_filename(buf, buf_size, path, number, flags);
354
0
}
355
356
int av_get_frame_filename(char *buf, int buf_size, const char *path, int number)
357
0
{
358
0
    return get_frame_filename(buf, buf_size, path, number, 0);
359
0
}
360
361
void av_url_split(char *proto, int proto_size,
362
                  char *authorization, int authorization_size,
363
                  char *hostname, int hostname_size,
364
                  int *port_ptr, char *path, int path_size, const char *url)
365
0
{
366
0
    const char *p, *ls, *at, *at2, *col, *brk;
367
368
0
    if (port_ptr)
369
0
        *port_ptr = -1;
370
0
    if (proto_size > 0)
371
0
        proto[0] = 0;
372
0
    if (authorization_size > 0)
373
0
        authorization[0] = 0;
374
0
    if (hostname_size > 0)
375
0
        hostname[0] = 0;
376
0
    if (path_size > 0)
377
0
        path[0] = 0;
378
379
    /* parse protocol */
380
0
    if ((p = strchr(url, ':'))) {
381
0
        av_strlcpy(proto, url, FFMIN(proto_size, p + 1 - url));
382
0
        p++; /* skip ':' */
383
0
        if (*p == '/')
384
0
            p++;
385
0
        if (*p == '/')
386
0
            p++;
387
0
    } else {
388
        /* no protocol means plain filename */
389
0
        av_strlcpy(path, url, path_size);
390
0
        return;
391
0
    }
392
393
    /* separate path from hostname */
394
0
    ls = p + strcspn(p, "/?#");
395
0
    av_strlcpy(path, ls, path_size);
396
397
    /* the rest is hostname, use that to parse auth/port */
398
0
    if (ls != p) {
399
        /* authorization (user[:pass]@hostname) */
400
0
        at2 = p;
401
0
        while ((at = strchr(p, '@')) && at < ls) {
402
0
            av_strlcpy(authorization, at2,
403
0
                       FFMIN(authorization_size, at + 1 - at2));
404
0
            p = at + 1; /* skip '@' */
405
0
        }
406
407
0
        if (*p == '[' && (brk = strchr(p, ']')) && brk < ls) {
408
            /* [host]:port */
409
0
            av_strlcpy(hostname, p + 1,
410
0
                       FFMIN(hostname_size, brk - p));
411
0
            if (brk[1] == ':' && port_ptr)
412
0
                *port_ptr = atoi(brk + 2);
413
0
        } else if ((col = strchr(p, ':')) && col < ls) {
414
0
            av_strlcpy(hostname, p,
415
0
                       FFMIN(col + 1 - p, hostname_size));
416
0
            if (port_ptr)
417
0
                *port_ptr = atoi(col + 1);
418
0
        } else
419
0
            av_strlcpy(hostname, p,
420
0
                       FFMIN(ls + 1 - p, hostname_size));
421
0
    }
422
0
}
423
424
int ff_mkdir_p(const char *path)
425
0
{
426
0
    int ret = 0;
427
0
    char *temp = av_strdup(path);
428
0
    char *pos = temp;
429
0
    char tmp_ch = '\0';
430
431
0
    if (!path || !temp) {
432
0
        return -1;
433
0
    }
434
435
0
    if (!av_strncasecmp(temp, "/", 1) || !av_strncasecmp(temp, "\\", 1)) {
436
0
        pos++;
437
0
    } else if (!av_strncasecmp(temp, "./", 2) || !av_strncasecmp(temp, ".\\", 2)) {
438
0
        pos += 2;
439
0
    }
440
441
0
    for ( ; *pos != '\0'; ++pos) {
442
0
        if (*pos == '/' || *pos == '\\') {
443
0
            tmp_ch = *pos;
444
0
            *pos = '\0';
445
0
            ret = mkdir(temp, 0755);
446
0
            *pos = tmp_ch;
447
0
        }
448
0
    }
449
450
0
    if ((*(pos - 1) != '/') && (*(pos - 1) != '\\')) {
451
0
        ret = mkdir(temp, 0755);
452
0
    }
453
454
0
    av_free(temp);
455
0
    return ret;
456
0
}
457
458
char *ff_data_to_hex(char *buff, const uint8_t *src, int s, int lowercase)
459
0
{
460
0
    static const char hex_table_uc[16] = { '0', '1', '2', '3',
461
0
                                           '4', '5', '6', '7',
462
0
                                           '8', '9', 'A', 'B',
463
0
                                           'C', 'D', 'E', 'F' };
464
0
    static const char hex_table_lc[16] = { '0', '1', '2', '3',
465
0
                                           '4', '5', '6', '7',
466
0
                                           '8', '9', 'a', 'b',
467
0
                                           'c', 'd', 'e', 'f' };
468
0
    const char *hex_table = lowercase ? hex_table_lc : hex_table_uc;
469
470
0
    for (int i = 0; i < s; i++) {
471
0
        buff[i * 2]     = hex_table[src[i] >> 4];
472
0
        buff[i * 2 + 1] = hex_table[src[i] & 0xF];
473
0
    }
474
0
    buff[2 * s] = '\0';
475
476
0
    return buff;
477
0
}
478
479
int ff_hex_to_data(uint8_t *data, const char *p)
480
0
{
481
0
    int c, len, v;
482
483
0
    len = 0;
484
0
    v   = 1;
485
0
    for (;;) {
486
0
        p += strspn(p, SPACE_CHARS);
487
0
        if (*p == '\0')
488
0
            break;
489
0
        c = av_toupper((unsigned char) *p++);
490
0
        if (c >= '0' && c <= '9')
491
0
            c = c - '0';
492
0
        else if (c >= 'A' && c <= 'F')
493
0
            c = c - 'A' + 10;
494
0
        else
495
0
            break;
496
0
        v = (v << 4) | c;
497
0
        if (v & 0x100) {
498
0
            if (data)
499
0
                data[len] = v;
500
0
            len++;
501
0
            v = 1;
502
0
        }
503
0
    }
504
0
    return len;
505
0
}
506
507
void ff_parse_key_value(const char *str, ff_parse_key_val_cb callback_get_buf,
508
                        void *context)
509
0
{
510
0
    const char *ptr = str;
511
512
    /* Parse key=value pairs. */
513
0
    for (;;) {
514
0
        const char *key;
515
0
        char *dest = NULL, *dest_end;
516
0
        int key_len, dest_len = 0;
517
518
        /* Skip whitespace and potential commas. */
519
0
        while (*ptr && (av_isspace(*ptr) || *ptr == ','))
520
0
            ptr++;
521
0
        if (!*ptr)
522
0
            break;
523
524
0
        key = ptr;
525
526
0
        if (!(ptr = strchr(key, '=')))
527
0
            break;
528
0
        ptr++;
529
0
        key_len = ptr - key;
530
531
0
        callback_get_buf(context, key, key_len, &dest, &dest_len);
532
0
        dest_end = dest ? dest + dest_len - 1 : NULL;
533
534
0
        if (*ptr == '\"') {
535
0
            ptr++;
536
0
            while (*ptr && *ptr != '\"') {
537
0
                if (*ptr == '\\') {
538
0
                    if (!ptr[1])
539
0
                        break;
540
0
                    if (dest && dest < dest_end)
541
0
                        *dest++ = ptr[1];
542
0
                    ptr += 2;
543
0
                } else {
544
0
                    if (dest && dest < dest_end)
545
0
                        *dest++ = *ptr;
546
0
                    ptr++;
547
0
                }
548
0
            }
549
0
            if (*ptr == '\"')
550
0
                ptr++;
551
0
        } else {
552
0
            for (; *ptr && !(av_isspace(*ptr) || *ptr == ','); ptr++)
553
0
                if (dest && dest < dest_end)
554
0
                    *dest++ = *ptr;
555
0
        }
556
0
        if (dest)
557
0
            *dest = 0;
558
0
    }
559
0
}
560
561
int avformat_network_init(void)
562
0
{
563
0
#if CONFIG_NETWORK
564
0
    int ret;
565
0
    if ((ret = ff_network_init()) < 0)
566
0
        return ret;
567
0
    if ((ret = ff_tls_init()) < 0)
568
0
        return ret;
569
0
#endif
570
0
    return 0;
571
0
}
572
573
int avformat_network_deinit(void)
574
0
{
575
0
#if CONFIG_NETWORK
576
0
    ff_network_close();
577
0
    ff_tls_deinit();
578
0
#endif
579
0
    return 0;
580
0
}
581
582
0
int ff_is_http_proto(const char *filename) {
583
0
    const char *proto = avio_find_protocol_name(filename);
584
0
    return proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0;
585
0
}
586
587
int ff_bprint_to_codecpar_extradata(AVCodecParameters *par, struct AVBPrint *buf)
588
0
{
589
0
    int ret;
590
0
    char *str;
591
592
0
    ret = av_bprint_finalize(buf, &str);
593
0
    if (ret < 0)
594
0
        return ret;
595
0
    if (!av_bprint_is_complete(buf)) {
596
0
        av_free(str);
597
0
        return AVERROR(ENOMEM);
598
0
    }
599
600
0
    par->extradata = str;
601
    /* Note: the string is NUL terminated (so extradata can be read as a
602
     * string), but the ending character is not accounted in the size (in
603
     * binary formats you are likely not supposed to mux that character). When
604
     * extradata is copied, it is also padded with AV_INPUT_BUFFER_PADDING_SIZE
605
     * zeros. */
606
0
    par->extradata_size = buf->len;
607
0
    return 0;
608
0
}
609
610
int ff_dict_set_timestamp(AVDictionary **dict, const char *key, int64_t timestamp)
611
0
{
612
0
    time_t seconds = timestamp / 1000000;
613
0
    struct tm *ptm, tmbuf;
614
0
    ptm = gmtime_r(&seconds, &tmbuf);
615
0
    if (ptm) {
616
0
        char buf[32];
617
0
        if (!strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", ptm))
618
0
            return AVERROR_EXTERNAL;
619
0
        av_strlcatf(buf, sizeof(buf), ".%06dZ", (int)(timestamp % 1000000));
620
0
        return av_dict_set(dict, key, buf, 0);
621
0
    } else {
622
0
        return AVERROR_EXTERNAL;
623
0
    }
624
0
}
625
626
static const AVOption* find_opt(void *obj, const char *name, size_t len)
627
0
{
628
0
    char decoded_name[128];
629
630
0
    if (ff_urldecode_len(decoded_name, sizeof(decoded_name), name, len, 1) < 0)
631
0
        return NULL;
632
633
0
    return av_opt_find(obj, decoded_name, NULL, 0, 0);
634
0
}
635
636
int ff_parse_opts_from_query_string(void *obj, const char *str, int allow_unknown)
637
0
{
638
0
    const AVOption *opt;
639
0
    char optval[512];
640
0
    int ret;
641
642
0
    if (*str == '?')
643
0
        str++;
644
0
    while (*str) {
645
0
        size_t len = strcspn(str, "=&");
646
0
        opt = find_opt(obj, str, len);
647
0
        if (!opt) {
648
0
            if (!allow_unknown) {
649
0
                av_log(obj, AV_LOG_ERROR, "Query string option '%.*s' does not exist\n", (int)len, str);
650
0
                return AVERROR_OPTION_NOT_FOUND;
651
0
            }
652
0
            av_log(obj, AV_LOG_VERBOSE, "Ignoring unknown query string option '%.*s'\n", (int)len, str);
653
0
        }
654
0
        str += len;
655
0
        if (!opt) {
656
0
            len = strcspn(str, "&");
657
0
            str += len;
658
0
        } else if (*str == '&' || *str == '\0') {
659
            /* Check for bool options without value, e.g. "?verify".
660
             * Unfortunately "listen" is a tri-state INT for some protocols so
661
             * we also have to allow that for backward compatibility. */
662
0
            if (opt->type != AV_OPT_TYPE_BOOL && strcmp(opt->name, "listen")) {
663
0
                av_log(obj, AV_LOG_ERROR, "Non-bool query string option '%s' has no value\n", opt->name);
664
0
                return AVERROR(EINVAL);
665
0
            }
666
0
            ret = av_opt_set_int(obj, opt->name, 1, 0);
667
0
            if (ret < 0)
668
0
                return ret;
669
0
        } else {
670
0
            av_assert2(*str == '=');
671
0
            str++;
672
0
            len = strcspn(str, "&");
673
0
            if (ff_urldecode_len(optval, sizeof(optval), str, len, 1) < 0) {
674
0
                av_log(obj, AV_LOG_ERROR, "Query string option '%s' value is too long\n", opt->name);
675
0
                return AVERROR(EINVAL);
676
0
            }
677
0
            ret = av_opt_set(obj, opt->name, optval, 0);
678
0
            if (ret < 0)
679
0
                return ret;
680
0
            str += len;
681
0
        }
682
0
        if (*str)
683
0
            str++;
684
0
    }
685
0
    return 0;
686
0
}