/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 | } |