FFmpeg
Loading...
Searching...
No Matches
libcurl.c
Go to the documentation of this file.
1/*
2 * libcurl based HTTP(S) protocol
3 * Copyright (C) 2026 Kacper Michajłow
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 "config_components.h"
23
24#include <curl/curl.h>
25#include <errno.h>
26#include <inttypes.h>
27#include <limits.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include "libavutil/avstring.h"
32#include "libavutil/bprint.h"
33#include "libavutil/dict.h"
34#include "libavutil/error.h"
35#include "libavutil/fifo.h"
36#include "libavutil/log.h"
37#include "libavutil/macros.h"
38#include "libavutil/mem.h"
39#include "libavutil/opt.h"
40#include "libavutil/thread.h"
41#include "libavutil/time.h"
42
43#include "avformat.h"
44#include "http.h"
45#include "internal.h"
46#include "network.h"
47#include "url.h"
48#include "version.h"
49
50#define DEFAULT_USER_AGENT "Lavf/" AV_STRINGIFY(LIBAVFORMAT_VERSION)
51#define CURL_DEFAULT_BUFFER_SIZE (4 << 20)
52
53/* Blocking waits wake up this often so url_read()/open can poll the interrupt
54 * callback. */
55#define CURL_WAIT_US 100000
56
57/* Cap on the accumulated "Icy-*" reply headers exported to the caller. */
58#define ICY_MAX_HEADERS 65536
59
60/* Largest in-band metadata block: the length byte counts 16 byte units. */
61#define ICY_MAX_BLOCK (255 * 16)
62
63/* How many microseconds to wait before retrying a failed request; grows
64 * exponentially (2^) with each consecutive failure up to MAX_US. Note that the
65 * first retry is always immediate. */
66#define CURL_RETRY_BASE_US 1000000
67#define CURL_RETRY_MAX_US 60000000
68
69typedef struct CurlContext CurlContext;
70
72 CMD_ADD, /* add the easy handle to the multi and start the transfer */
73 CMD_REMOVE, /* remove the easy handle from the multi */
74 CMD_UNPAUSE, /* resume a transfer paused because the FIFO was full */
75 CMD_SEEK, /* restart the transfer at a new byte offset */
76};
77
78typedef struct CurlCmd {
81 int64_t pos; /* CMD_SEEK target offset */
82 int sync; /* caller waits for completion */
83 int done;
84 struct CurlCmd *next;
85} CurlCmd;
86
87typedef struct CurlLoop {
88 AVFormatContext *avfc; /* owning context (if any) */
89
91 CURLM *multi;
92 CURLSH *share; /* shared cookies/HSTS */
93
94 pthread_mutex_t mutex; /* guards the command queue, exit, share and cmd->done */
95 pthread_cond_t cond; /* signaled when a sync command completes */
97 int exit;
98
99 /* Connection statistics (updated by loop thread) */
106} CurlLoop;
107
109 const AVClass *class;
111
113 int private_loop; /* loop is owned by this context (not shared) */
114 CURL *easy;
115 struct curl_slist *header_list;
116 struct curl_slist *alias_list;
117
118 /* AVOptions. */
120 char *referer;
121 char *headers;
123 char *cookies;
124 char *ca_file;
126 char *key_file;
127 char *location; /* effective URL after redirects (output) */
128 int64_t off; /* initial byte offset */
129 int64_t end_off; /* exclusive upper byte bound (0 = none) */
141 int icy;
143 char *icy_metadata_headers; /* "Icy-*" reply headers (output) */
144 char *icy_metadata_packet; /* last in-band block (output) */
145 AVDictionary *metadata; /* ICY metadata (output) */
146
147 /* URL thread bookkeeping, not touched by loop thread */
148 int64_t logical_pos; /* next byte url_read() will return, caller side */
149 int retry_count; /* consecutive recoverable failures */
150 int64_t retry_time; /* timestamp of next retry */
151 int64_t icy_data_read; /* payload bytes since the last block, caller side */
152 int icy_block_len; /* -1 while the length byte is pending */
155
156 /* Producer bookkeeping, touched only by the loop thread. */
157 int active; /* currently added to the multi */
158 int64_t request_start; /* absolute offset the current request began at */
159 int64_t request_received;/* bytes delivered in the current request */
160 int64_t request_end; /* expected end of request, or -1 if unknown */
161 int is_initial; /* using reduced request size */
162 int seek_queued; /* soft seeking; drain remaining bytes until done */
163
164 /* Per-response-block header scratch, loop thread only. */
167 int64_t hdr_content_start; /* inclusive start, or -1 */
168 int64_t hdr_content_end; /* inclusive end, or -1 */
169 int64_t hdr_content_total; /* if known, or -1 */
170 int64_t hdr_content_length; /* if known, or -1 */
171 AVDictionary *hdr_icy; /* "Icy-*" headers of this block */
172 int64_t hdr_icy_metaint; /* in-band metadata interval, or -1 */
173
174 /* Probe result. Set by the loop thread, read by url_open() once probed. */
179
180 /* Shared transfer state, guarded by mutex. */
184 int paused; /* write callback paused, FIFO was full */
185 int aborted; /* transfer should stop (open was interrupted) */
186 int status; /* current stream status (AVERROR code) */
187 CURLcode curl_status; /* corresponding libcurl status code */
188 int64_t icy_metaint; /* in-band metadata interval, 0 if none */
189};
190
191/* Guards lazy creation of a format context's shared loop. */
193
194static int curlcode_to_averror(CURLcode code)
195{
196 switch (code) {
197 case CURLE_OK: return 0;
198 case CURLE_URL_MALFORMAT:
199 case CURLE_UNSUPPORTED_PROTOCOL:
200 case CURLE_BAD_FUNCTION_ARGUMENT: return AVERROR(EINVAL);
201 case CURLE_UNKNOWN_OPTION:
202 case CURLE_NOT_BUILT_IN: return AVERROR(ENOSYS);
203 case CURLE_COULDNT_RESOLVE_PROXY:
204 case CURLE_COULDNT_RESOLVE_HOST: return AVERROR(EHOSTUNREACH);
205 case CURLE_COULDNT_CONNECT: return AVERROR(ECONNREFUSED);
206 case CURLE_OPERATION_TIMEDOUT: return AVERROR(ETIMEDOUT);
207 case CURLE_LOGIN_DENIED:
208 case CURLE_REMOTE_ACCESS_DENIED: return AVERROR(EACCES);
209 case CURLE_OUT_OF_MEMORY: return AVERROR(ENOMEM);
210 case CURLE_PEER_FAILED_VERIFICATION:
211 case CURLE_SSL_CACERT_BADFILE: return AVERROR_INVALIDDATA;
212 default: return AVERROR(EIO);
213 }
214}
215
216static int curl_setopt_checked(CurlContext *c, const char *name, CURLcode code)
217{
218 if (code == CURLE_OK)
219 return 0;
220
221 av_log(c->h, AV_LOG_ERROR, "Could not set %s: %s\n",
222 name, curl_easy_strerror(code));
224}
225
226static int curlmcode_to_averror(CURLMcode code)
227{
228 switch (code) {
229 case CURLM_OK: return 0;
230 case CURLM_UNKNOWN_OPTION: return AVERROR(EINVAL);
231 case CURLM_OUT_OF_MEMORY: return AVERROR(ENOMEM);
232 default: return AVERROR(EIO);
233 }
234}
235
236static int curlmcode_to_curlcode(CURLMcode code)
237{
238 switch (code) {
239 case CURLM_OK: return CURLE_OK;
240 case CURLM_UNKNOWN_OPTION: return CURLE_UNKNOWN_OPTION;
241 case CURLM_OUT_OF_MEMORY: return CURLE_OUT_OF_MEMORY;
242 case CURLM_ABORTED_BY_CALLBACK: return CURLE_ABORTED_BY_CALLBACK;
243 case CURLM_UNRECOVERABLE_POLL: return CURLE_UNRECOVERABLE_POLL;
244 default: return CURLE_FAILED_INIT;
245 }
246}
247
248static int is_recoverable(CURLcode code)
249{
250 switch (code) {
251 case CURLE_RECV_ERROR:
252 case CURLE_SEND_ERROR:
253 case CURLE_PARTIAL_FILE:
254 case CURLE_OPERATION_TIMEDOUT:
255 case CURLE_GOT_NOTHING:
256 case CURLE_COULDNT_CONNECT:
257 case CURLE_COULDNT_RESOLVE_HOST:
258 case CURLE_HTTP2:
259 case CURLE_HTTP2_STREAM:
260 return 1;
261 default:
262 return 0;
263 }
264}
265
266/* ------------------------------------------------------------------------- */
267/* curl callbacks (run on the loop thread) */
268/* ------------------------------------------------------------------------- */
269
270static void update_status_locked(CurlContext *c, int status, CURLcode code)
271{
272 if (c->status)
273 return;
274
275 c->status = status;
276 c->curl_status = code;
277}
278
279static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
280{
281 CurlContext *c = userdata;
282 size_t bytes = size * nmemb;
283 size_t space;
284
285 pthread_mutex_lock(&c->mutex);
286
287 if (c->aborted || !c->stream_ok) {
288 pthread_mutex_unlock(&c->mutex);
289 return CURL_WRITEFUNC_ERROR;
290 }
291
292 if (c->seek_queued) {
293 pthread_mutex_unlock(&c->mutex);
294 return bytes; /* discard */
295 }
296
297 /* Prevent overflow / non-addressable byte ranges */
298 if (bytes > INT64_MAX - c->request_start - c->request_received) {
299 av_log(c->h, AV_LOG_ERROR, "Server sent back more data than addressable "
300 "at offset %"PRId64"\n", c->request_start);
301 c->loop->num_errors++;
302 c->stream_ok = 0;
303 if (!c->status)
304 c->status = AVERROR(ERANGE);
306 pthread_mutex_unlock(&c->mutex);
307 return CURL_WRITEFUNC_ERROR;
308 }
309
310 space = av_fifo_can_write(c->fifo);
311 if (space < bytes) {
312 /* pause the transfer and wait for the consumer to drain. */
313 c->paused = 1;
314 pthread_mutex_unlock(&c->mutex);
315 return CURL_WRITEFUNC_PAUSE;
316 }
317
318 av_fifo_write(c->fifo, ptr, bytes);
319 c->paused = 0;
320 c->request_received += bytes;
322 pthread_mutex_unlock(&c->mutex);
323
324 return bytes;
325}
326
327/* Return 1 if an offset was successfully parsed, 0 otherwise */
328static int parse_offset(const char *str, int64_t *out, const char **ptr)
329{
330 if (!av_isdigit(str[0]))
331 return 0;
332
333 errno = 0;
334 char *end;
335 int64_t val = strtoll(str, &end, 10);
336 if (errno == ERANGE)
337 return 0;
338
339 *out = val;
340 *ptr = end;
341 return 1;
342}
343
344/* "bytes $from-$to/$document_size" */
345static void parse_content_range(CurlContext *c, const char *v)
346{
347 int64_t start = -1, end = -1, total = -1;
348 while (av_isspace(*v))
349 v++;
350
351 if (!av_stristart(v, "bytes ", &v))
352 return;
353
354 if (!av_strstart(v, "*", &v)) {
355 if (!parse_offset(v, &start, &v) ||
356 !av_strstart(v, "-", &v) ||
357 !parse_offset(v, &end, &v))
358 return;
359 }
360
361 if (!av_strstart(v, "/", &v))
362 return;
363 if (!av_strstart(v, "*", &v) && !parse_offset(v, &total, &v))
364 return;
365
366 while (av_isspace(*v))
367 v++;
368
369 if (v[0] || (total < 0 && start < 0))
370 return; // reject trailing bytes or "*/*"
371
372 /* only set these fields if the header was recognized; ignore otherwise */
373 c->hdr_content_start = start;
374 c->hdr_content_end = end;
375 c->hdr_content_total = total;
376}
377
378/* Parse a decimal header value, bounded by len since curl does not promise a
379 * NUL terminated header buffer. Returns -1 if absent, malformed or too large. */
380static int64_t parse_metaint(const char *p, size_t len)
381{
382 int64_t v = 0;
383 size_t i = 0;
384
385 while (i < len && av_isspace(p[i]))
386 i++;
387 if (i == len || !av_isdigit(p[i]))
388 return -1;
389
390 for (; i < len && av_isdigit(p[i]); i++) {
391 if (v > (INT_MAX - (p[i] - '0')) / 10)
392 return -1;
393 v = v * 10 + (p[i] - '0');
394 }
395
396 // Reject 4junk
397 while (i < len && av_isspace(p[i]))
398 i++;
399 if (i != len)
400 return -1;
401
402 return v;
403}
404
405/* Store one "Tag: value" reply header, tolerating a missing space after the
406 * colon and the CRLF curl leaves on the line. */
407static void store_icy_header(AVDictionary **dict, const char *ptr, size_t len)
408{
409 const char *colon = memchr(ptr, ':', len);
410 const char *val, *end = ptr + len;
411 char *key, *value;
412
413 if (!colon)
414 return;
415
416 val = colon + 1;
417 while (val < end && av_isspace(*val))
418 val++;
419 while (end > val && (end[-1] == '\r' || end[-1] == '\n'))
420 end--;
421
422 key = av_strndup(ptr, colon - ptr);
423 value = av_strndup(val, end - val);
424 /* Multikey keeps repeated headers in arrival order, as http.c reports them. */
425 if (key && value)
428 else {
429 av_free(key);
430 av_free(value);
431 }
432}
433
434/* Export the reply's "Icy-*" headers. Called once, before probed is set, so
435 * that from then on only the caller thread touches the exported metadata. */
437{
438 const AVDictionaryEntry *e = NULL;
439 AVBPrint bp;
440
441 if (!c->hdr_icy)
442 return;
443
445 while ((e = av_dict_iterate(c->hdr_icy, e)))
446 av_bprintf(&bp, "%s: %s\n", e->key, e->value);
447
448 av_freep(&c->icy_metadata_headers);
449 if (av_bprint_finalize(&bp, &c->icy_metadata_headers) < 0)
450 c->icy_metadata_headers = NULL;
451 av_dict_copy(&c->metadata, c->hdr_icy, 0);
452}
453
455 int64_t total)
456{
457 if (start < 0 || start != c->request_start) {
458 av_log(c->h, AV_LOG_ERROR, "Server sent back unexpected reply "
459 "with offset %"PRId64" (expected %"PRId64")\n",
460 start, c->request_start);
461 return 0;
462 }
463
464 if (end >= 0 && end < start) {
465 av_log(c->h, AV_LOG_ERROR, "Server sent back backwards content range "
466 "%"PRId64"-%"PRId64"\n", start, end);
467 return 0;
468 }
469
470 if (total >= 0 && (start >= total || end >= total)) {
471 av_log(c->h, AV_LOG_ERROR, "Server sent back content range "
472 "%"PRId64"-%"PRId64" that exceeds the total size "
473 "%"PRId64"\n", start, end, total);
474 return 0;
475 }
476
477 if (c->hdr_content_length >= 0 && end >= 0 &&
478 (c->hdr_content_length - 1 > INT64_MAX - start ||
479 start + (c->hdr_content_length - 1) != end))
480 {
481 av_log(c->h, AV_LOG_ERROR, "Server sent back content range "
482 "%"PRId64"-%"PRId64" that doesn't match the content length "
483 "%"PRId64"\n", start, end, c->hdr_content_length);
484 return 0;
485 }
486
487 return 1;
488}
489
490static size_t header_callback(char *ptr, size_t size, size_t nitems, void *userdata)
491{
492 CurlContext *c = userdata;
493 size_t len = size * nitems;
494 size_t n = len;
495 long status = 0;
496
497 /* Only "ICY 200" is aliased, so these two prefixes cover every status line
498 * curl lets through. */
499 if (av_strncasecmp(ptr, "HTTP/", 5) == 0 ||
500 av_strncasecmp(ptr, "ICY ", 4) == 0) {
501 c->hdr_accept_ranges = 0;
502 c->hdr_compressed = 0;
503 c->hdr_content_start = -1;
504 c->hdr_content_end = -1;
505 c->hdr_content_total = -1;
506 c->hdr_content_length = -1;
507 c->hdr_icy_metaint = -1;
508 av_dict_free(&c->hdr_icy);
509 return len;
510 }
511 if (av_strncasecmp(ptr, "Accept-Ranges:", 14) == 0) {
512 c->hdr_accept_ranges = !!av_stristr(ptr + 14, "bytes");
513 return len;
514 }
515 if (av_strncasecmp(ptr, "Content-Encoding:", 17) == 0) {
516 c->hdr_compressed = !av_stristr(ptr + 17, "identity");
517 return len;
518 }
519 if (av_strncasecmp(ptr, "Content-Range:", 14) == 0) {
520 parse_content_range(c, ptr + 14);
521 return len;
522 }
523 if (av_strncasecmp(ptr, "icy-metaint:", 12) == 0) {
524 c->hdr_icy_metaint = parse_metaint(ptr + 12, len - 12);
525 return len;
526 }
527 /* Collected per block so that headers from a redirect hop, which the
528 * interim early return below discards, do not reach the caller. */
529 if (av_strncasecmp(ptr, "icy-", 4) == 0) {
530 store_icy_header(&c->hdr_icy, ptr, len);
531 return len;
532 }
533
534 /* Otherwise act only on the blank line that terminates the header block. */
535 while (n && (ptr[n - 1] == '\r' || ptr[n - 1] == '\n'))
536 n--;
537 if (n)
538 return len;
539
540 curl_easy_getinfo(c->easy, CURLINFO_RESPONSE_CODE, &status);
541
542 /* Interim (1xx) and redirect (3xx) responses produce an intermediate header
543 * block, wait for the final one. */
544 if (status < 200 || (status >= 300 && status < 400))
545 return len;
546
547 curl_off_t cl = -1;
548 if (curl_easy_getinfo(c->easy, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cl) == CURLE_OK)
549 c->hdr_content_length = cl >= 0 ? cl : -1;
550
551 pthread_mutex_lock(&c->mutex);
552 if (status >= 200 && status < 300) {
553 int64_t content_start = status == 206 ? c->hdr_content_start : 0;
554 int64_t content_end = status == 206 ? c->hdr_content_end : -1;
555 int64_t content_total = status == 206 ? c->hdr_content_total : c->hdr_content_length;
556 if (content_end < 0 && content_total > 0)
557 content_end = content_total - 1;
558
559 if (!verify_content_range(c, content_start, content_end, content_total)) {
560 c->loop->num_errors++;
561 c->stream_ok = 0;
562 update_status_locked(c, AVERROR(EIO), CURLE_OK);
564 pthread_mutex_unlock(&c->mutex);
565 return len;
566 }
567
568 /* Don't unlearn a known size when a reply omits it. */
569 if (!c->hdr_compressed && content_total >= 0)
570 c->content_size = content_total;
571
572 if (!c->hdr_compressed) {
573 int64_t total = content_total;
574 if (total < 0 && status != 206)
575 total = c->hdr_content_length;
576 /* Don't unlearn a known size when a reply omits it. */
577 if (total >= 0)
578 c->content_size = total;
579 if (content_end < 0)
580 content_end = c->content_size > 0 ? c->content_size - 1 : -1;
581 }
582
583 c->stream_ok = 1;
584 c->request_end = content_end;
585 /* Capture the post-redirect URL, this is exposed as "location" AVOption
586 * for compatibility with http.c. */
587 if (!c->probed) {
588 const char *eff = NULL;
589 if (curl_easy_getinfo(c->easy, CURLINFO_EFFECTIVE_URL, &eff) == CURLE_OK
590 && eff) {
591 char *dup = av_strdup(eff);
592 if (dup) {
593 av_free(c->location);
594 c->location = dup;
595 }
596 }
598 }
599 /* A compressed body is addressed in encoded form, so byte offsets are
600 * meaningless: not seekable. Note that we prefer compression over
601 * seekability, servers don't offer media in compressed form, so it
602 * gives us free compression for other payloads like text playlist. */
603 c->seekable = !c->hdr_compressed &&
604 (status == 206 || c->hdr_accept_ranges);
605 /* Apply the user override on every reply so re-evaluation of a
606 * follow-up reply doesn't clobber it. */
607 if (c->seekable_opt >= 0)
608 c->seekable = c->seekable_opt;
609
610 c->icy_metaint = 0;
611 if (c->hdr_icy_metaint > 0) {
612 c->icy_metaint = c->hdr_icy_metaint;
613 if (c->seekable_opt > 0)
614 av_log(c->h, AV_LOG_WARNING, "Ignoring seekable=1, the "
615 "reply carries in-band ICY metadata\n");
616 /* In-band metadata makes byte offsets meaningless. Staying
617 * non-seekable also keeps on_done() from restarting a request
618 * without draining the FIFO, which would desync the
619 * interleave. */
620 c->seekable = 0;
621 c->content_size = -1;
622 }
623 } else {
624 c->loop->num_errors++;
625 c->stream_ok = 0;
626 update_status_locked(c, ff_http_averror(status, AVERROR(EIO)), CURLE_OK);
627 }
628 c->probed = 1;
630 pthread_mutex_unlock(&c->mutex);
631
632 return len;
633}
634
635static int xferinfo_callback(void *userdata, curl_off_t dltotal, curl_off_t dlnow,
636 curl_off_t ultotal, curl_off_t ulnow)
637{
638 CurlContext *c = userdata;
639 int aborted;
640 pthread_mutex_lock(&c->mutex);
641 aborted = c->aborted;
642 pthread_mutex_unlock(&c->mutex);
643 return aborted; /* non-zero aborts the transfer */
644}
645
646/* (Re)issue the request for the current offset and add it to the multi. Loop
647 * thread only. */
649{
650 char range[48];
651 const char *range_value = NULL;
652 CURLcode cc;
653 int ret;
654
655 if (!c->probed || c->seekable) {
656 int64_t start = c->request_start;
657 int64_t request_size = c->request_size;
658 if (c->is_initial && c->initial_request_size > 0)
659 request_size = c->initial_request_size;
660 if (request_size > 0 || c->end_off > 0) {
661 int64_t end = INT64_MAX;
662 if (request_size > 0 && start <= INT64_MAX - request_size)
663 end = start + request_size - 1;
664 if (c->content_size > 0)
665 end = FFMIN(end, c->content_size - 1);
666 if (c->end_off > 0)
667 end = FFMIN(end, c->end_off - 1);
668 snprintf(range, sizeof(range), "%"PRId64"-%"PRId64, start, end);
669 } else {
670 snprintf(range, sizeof(range), "%"PRId64"-", start);
671 }
672 range_value = range;
673 }
674 cc = curl_easy_setopt(c->easy, CURLOPT_RANGE, range_value);
675 if ((ret = curl_setopt_checked(c, "CURLOPT_RANGE", cc)) < 0) {
676 c->loop->num_errors++;
677 pthread_mutex_lock(&c->mutex);
678 update_status_locked(c, ret, cc);
680 pthread_mutex_unlock(&c->mutex);
681 return;
682 }
683 c->loop->num_requests++;
684 c->request_received = 0;
685 c->request_end = -1;
686 c->active = 1;
687 CURLMcode res = curl_multi_add_handle(c->loop->multi, c->easy);
688 if (res != CURLM_OK) {
689 av_log(c->h, AV_LOG_ERROR, "curl_multi_add_handle: %s\n",
690 curl_multi_strerror(res));
691 c->active = 0;
692 pthread_mutex_lock(&c->mutex);
695 pthread_mutex_unlock(&c->mutex);
696 }
697}
698
700{
701 CurlLoop *loop = c->loop;
702 CURL *e = c->easy;
703
704 curl_off_t recv = 0, time = 0;
705 curl_easy_getinfo(e, CURLINFO_SIZE_DOWNLOAD_T, &recv);
706 curl_easy_getinfo(e, CURLINFO_TOTAL_TIME_T, &time);
707
708 if (recv) {
709 av_log(c->h, AV_LOG_DEBUG, "%"PRId64" bytes received in %"PRId64" ms\n",
710 (int64_t) recv, (int64_t) time / 1000);
711
712 loop->total_bytes += recv;
713 loop->total_time_us += time;
714 }
715
716 long num_conns = 0, num_redirs = 0;
717 curl_easy_getinfo(e, CURLINFO_NUM_CONNECTS, &num_conns);
718 curl_easy_getinfo(e, CURLINFO_REDIRECT_COUNT, &num_redirs);
719 loop->num_connections += (int) num_conns;
720 loop->num_redirects += (int) num_redirs;
721}
722
723/* Transfer finished (or failed) */
724static void on_done(CurlContext *c, CURLcode code)
725{
726 int64_t received;
727 int aborted;
728
729 pthread_mutex_lock(&c->mutex);
730 aborted = c->aborted;
731 received = c->request_received;
732 /* Advance past delivered bytes so a retry or seek resumes at the right offset. */
733 if (received > INT64_MAX - c->request_start) {
734 received = 0;
735 aborted = 1;
738 }
739 c->request_start += received;
740 c->request_received = 0;
741 pthread_mutex_unlock(&c->mutex);
743
744 if (!c->probed) {
745 /* Connection died before any usable header arrived. */
746 pthread_mutex_lock(&c->mutex);
747 c->probed = 1;
748 c->stream_ok = 0;
749 c->loop->num_errors++;
752 pthread_mutex_unlock(&c->mutex);
753 return;
754 }
755
756 if (aborted)
757 return;
758
759 if (c->seek_queued) {
760 /* previous soft seek drain finished; can start new request now */
761 c->seek_queued = 0;
763 return;
764 }
765
766 if (code == CURLE_OK && c->stream_ok) {
767 int64_t file_end = c->content_size > 0 ? c->content_size - 1 : -1;
768 if (c->end_off > 0)
769 file_end = FFMIN(file_end, c->end_off - 1);
770 if (c->seekable && c->request_end >= 0 && c->request_end < file_end) {
771 c->is_initial = 0;
773 return;
774 }
775 pthread_mutex_lock(&c->mutex);
776 c->status = AVERROR_EOF;
777 c->curl_status = CURLE_OK;
779 pthread_mutex_unlock(&c->mutex);
780 return;
781 }
782
783 if (c->stream_ok) {
784 av_log(c->h, AV_LOG_WARNING, "%s\n", curl_easy_strerror(code));
785 c->loop->num_errors++;
786 }
787
788 /* Unhandled generic curl error */
789 pthread_mutex_lock(&c->mutex);
792 pthread_mutex_unlock(&c->mutex);
793}
794
795/* ------------------------------------------------------------------------- */
796/* event loop thread + command queue */
797/* ------------------------------------------------------------------------- */
798
800{
801 if (c->seek_queued)
802 return 1; /* short seek already queued */
803
804 if (c->short_seek_size <= 0 || /* short seek disabled */
805 c->request_end < 0) /* content size not known */
806 return 0;
807
808 const int64_t last = c->request_end - c->request_start;
809 return last - c->request_received < c->short_seek_size;
810}
811
813{
814 CurlContext *c = cmd->ctx;
815
816 switch (cmd->kind) {
817 case CMD_ADD:
819 break;
820 case CMD_REMOVE:
821 if (c->active) {
822 curl_multi_remove_handle(loop->multi, c->easy);
824 c->active = 0;
825 }
826 break;
827 case CMD_UNPAUSE:
828 pthread_mutex_lock(&c->mutex);
829 c->paused = 0;
830 pthread_mutex_unlock(&c->mutex);
831 curl_easy_pause(c->easy, CURLPAUSE_CONT);
832 break;
833 case CMD_SEEK:
834 if (c->active && test_short_seek(c)) {
835 c->seek_queued = 1;
836 } else if (c->active) {
837 curl_multi_remove_handle(loop->multi, c->easy);
838 c->active = 0;
839 }
840 pthread_mutex_lock(&c->mutex);
841 av_fifo_reset2(c->fifo);
842 const int was_paused = c->paused;
843 c->aborted = 0;
844 c->paused = 0;
845 c->status = 0;
846 c->curl_status = 0;
847 pthread_mutex_unlock(&c->mutex);
848 c->request_start = cmd->pos;
849 c->request_received = 0;
850 if (!c->seek_queued)
852 else if (was_paused)
853 curl_easy_pause(c->easy, CURLPAUSE_CONT);
854 break;
855 }
856}
857
858static void *curl_worker(void *arg)
859{
860 CurlLoop *loop = arg;
861
862 ff_thread_setname("curl");
863
864 while (1) {
865 CurlCmd *cmd;
866 CURLMsg *msg;
867 int running = 0, left = 0, do_exit;
868
869 pthread_mutex_lock(&loop->mutex);
870 cmd = loop->cmd_head;
871 if (cmd) {
872 loop->cmd_head = cmd->next;
873 if (!loop->cmd_head)
874 loop->cmd_tail = NULL;
875 }
876 do_exit = loop->exit;
877 pthread_mutex_unlock(&loop->mutex);
878
879 if (cmd) {
880 execute_command(loop, cmd);
881 if (cmd->sync) {
882 pthread_mutex_lock(&loop->mutex);
883 cmd->done = 1;
885 pthread_mutex_unlock(&loop->mutex);
886 } else {
887 av_free(cmd);
888 }
889 continue; /* drain the whole queue before pumping curl */
890 }
891
892 if (do_exit)
893 break;
894
895 curl_multi_perform(loop->multi, &running);
896
897 while ((msg = curl_multi_info_read(loop->multi, &left))) {
898 CurlContext *c = NULL;
899 if (msg->msg != CURLMSG_DONE)
900 continue;
901 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &c);
902 curl_multi_remove_handle(loop->multi, msg->easy_handle);
903 if (c) {
904 c->active = 0;
905 on_done(c, msg->data.result);
906 }
907 }
908
909 curl_multi_poll(loop->multi, NULL, 0, 1000, NULL);
910 }
911
912 return NULL;
913}
914
915/* Dispatch a command to the loop. For sync commands the caller blocks until the
916 * loop thread has executed it. Returns 0 or a negative AVERROR. */
918 int64_t pos, int sync)
919{
920 CurlCmd stackcmd = {0};
921 CurlCmd *cmd = sync ? &stackcmd : av_mallocz(sizeof(*cmd));
922
923 if (!cmd)
924 return AVERROR(ENOMEM);
925
926 cmd->kind = kind;
927 cmd->ctx = c;
928 cmd->pos = pos;
929 cmd->sync = sync;
930
931 pthread_mutex_lock(&loop->mutex);
932 if (loop->cmd_tail)
933 loop->cmd_tail->next = cmd;
934 else
935 loop->cmd_head = cmd;
936 loop->cmd_tail = cmd;
937 curl_multi_wakeup(loop->multi);
938 if (sync) {
939 while (!cmd->done)
940 pthread_cond_wait(&loop->cond, &loop->mutex);
941 }
942 pthread_mutex_unlock(&loop->mutex);
943
944 return 0;
945}
946
947static void share_lock_callback(CURL *handle, curl_lock_data data,
948 curl_lock_access access, void *userdata)
949{
950 CurlLoop *loop = userdata;
951 pthread_mutex_lock(&loop->mutex);
952}
953
954static void share_unlock_callback(CURL *handle, curl_lock_data data,
955 void *userdata)
956{
957 CurlLoop *loop = userdata;
958 pthread_mutex_unlock(&loop->mutex);
959}
960
962{
963 CurlLoop *loop = av_mallocz(sizeof(*loop));
964 if (!loop)
965 return NULL;
966 loop->avfc = avfc;
967
968 if (pthread_mutex_init(&loop->mutex, NULL))
969 goto fail;
970 if (pthread_cond_init(&loop->cond, NULL)) {
972 goto fail;
973 }
974
975 if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
976 goto fail2;
977
978 loop->multi = curl_multi_init();
979 if (!loop->multi)
980 goto fail3;
981 curl_multi_setopt(loop->multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
982
983 loop->share = curl_share_init();
984 if (!loop->share)
985 goto fail3;
986 curl_share_setopt(loop->share, CURLSHOPT_USERDATA, loop);
987 curl_share_setopt(loop->share, CURLSHOPT_LOCKFUNC, share_lock_callback);
988 curl_share_setopt(loop->share, CURLSHOPT_UNLOCKFUNC, share_unlock_callback);
989 curl_share_setopt(loop->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
990 curl_share_setopt(loop->share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
991
992 if (pthread_create(&loop->thread, NULL, curl_worker, loop))
993 goto fail3;
994
995 return loop;
996
997fail3:
998 curl_multi_cleanup(loop->multi);
999 curl_share_cleanup(loop->share);
1000 curl_global_cleanup();
1001fail2:
1002 pthread_cond_destroy(&loop->cond);
1003 pthread_mutex_destroy(&loop->mutex);
1004fail:
1005 av_free(loop);
1006 return NULL;
1007}
1008
1010{
1011 AVFormatContext *avfc = loop->avfc;
1012
1013 if (loop->total_bytes) {
1014 double time = loop->total_time_us / 1000000.0;
1015 double avg = time ? loop->total_bytes / time : 0;
1016 av_log(avfc, AV_LOG_VERBOSE,
1017 "libcurl: Overall %"PRId64" bytes received in %.0f ms = %.0f kB/s\n",
1018 loop->total_bytes, time * 1e3, avg / 1e3);
1019 }
1020
1021 if (loop->num_connections || loop->num_errors) {
1022 av_log(avfc, AV_LOG_VERBOSE,
1023 "libcurl: %d connections, %d redirects, %d requests, %d errors\n",
1024 loop->num_connections, loop->num_redirects, loop->num_requests,
1025 loop->num_errors);
1026 }
1027}
1028
1030{
1031 pthread_mutex_lock(&loop->mutex);
1032 loop->exit = 1;
1033 curl_multi_wakeup(loop->multi);
1034 pthread_mutex_unlock(&loop->mutex);
1035
1036 pthread_join(loop->thread, NULL);
1038
1039 curl_multi_cleanup(loop->multi);
1040 curl_share_cleanup(loop->share);
1041 pthread_cond_destroy(&loop->cond);
1042 pthread_mutex_destroy(&loop->mutex);
1043 av_free(loop);
1044
1045 /* Released after the thread is joined and the multi handle is gone. */
1046 curl_global_cleanup();
1047}
1048
1049/* Attach a context to its event loop. With an owning AVFormatContext the loop is
1050 * created lazily, cached on it, and shared across the demuxer's transfers so curl
1051 * reuses connections; it is freed at format teardown. Without one the context
1052 * gets a private loop freed on close. */
1054{
1055 if (!avfc) {
1056 c->loop = curl_loop_create(NULL);
1057 c->private_loop = 1;
1058 return c->loop ? 0 : AVERROR(ENOMEM);
1059 }
1060
1062 c->loop = ffformatcontext(avfc)->curl_loop;
1063 if (!c->loop) {
1064 c->loop = curl_loop_create(avfc);
1065 ffformatcontext(avfc)->curl_loop = c->loop;
1066 }
1068
1069 return c->loop ? 0 : AVERROR(ENOMEM);
1070}
1071
1073{
1074 if (loop && *loop) {
1076 *loop = NULL;
1077 }
1078}
1079
1080/* ------------------------------------------------------------------------- */
1081/* URLProtocol callbacks */
1082/* ------------------------------------------------------------------------- */
1083
1084static int libcurl_close(URLContext *h);
1085
1086static int debug_callback(CURL *easy, curl_infotype type, char *data,
1087 size_t size, void *userdata)
1088{
1089 CurlContext *c = userdata;
1090 const char *prefix, *p = data, *end = data + size;
1091
1092 switch (type) {
1093 case CURLINFO_TEXT: prefix = "* "; break;
1094 case CURLINFO_HEADER_IN: prefix = "< "; break;
1095 case CURLINFO_HEADER_OUT: prefix = "> "; break;
1096 default: return 0;
1097 }
1098
1099 /* Split multiline payload into each log. */
1100 while (p < end) {
1101 const char *nl = memchr(p, '\n', end - p);
1102 size_t len = (nl ? nl : end) - p;
1103 while (len && p[len - 1] == '\r')
1104 len--;
1105 av_log(c->h, AV_LOG_DEBUG, "%s%.*s\n", prefix, (int)len, p);
1106 if (!nl)
1107 break;
1108 p = nl + 1;
1109 }
1110 return 0;
1111}
1112
1113static int slist_append(struct curl_slist **list, const char *str)
1114{
1115 struct curl_slist *tmp = curl_slist_append(*list, str);
1116 if (!tmp)
1117 return AVERROR(ENOMEM);
1118 *list = tmp;
1119 return 0;
1120}
1121
1122/* Build the custom request header list from the referer and headers options. */
1124{
1125 int user_set_icy = 0, ret = 0;
1126
1127 if (c->referer && c->referer[0]) {
1128 char *h = av_asprintf("Referer: %s", c->referer);
1129 if (!h)
1130 return AVERROR(ENOMEM);
1131 ret = slist_append(&c->header_list, h);
1132 av_free(h);
1133 if (ret < 0)
1134 return ret;
1135 }
1136 if (c->headers && c->headers[0]) {
1137 char *copy = av_strdup(c->headers);
1138 char *line, *saveptr = NULL;
1139 if (!copy)
1140 return AVERROR(ENOMEM);
1141 for (line = av_strtok(copy, "\r\n", &saveptr); line && ret >= 0;
1142 line = av_strtok(NULL, "\r\n", &saveptr)) {
1143 if (!av_strncasecmp(line, "Icy-MetaData:", 13))
1144 user_set_icy = 1;
1145 ret = slist_append(&c->header_list, line);
1146 }
1147 av_free(copy);
1148 if (ret < 0)
1149 return ret;
1150 }
1151 /* libcurl does not deduplicate the list, so only add ours if the user
1152 * did not already ask for one. */
1153 if (c->icy && !user_set_icy)
1154 return slist_append(&c->header_list, "Icy-MetaData: 1");
1155 return 0;
1156}
1157
1159{
1160 const char *wl = c->h->protocol_whitelist;
1161 const char *bl = c->h->protocol_blacklist;
1162 int ret;
1163 if (!wl && !bl)
1164 return 0;
1165
1166 AVBPrint bp;
1168
1169 curl_version_info_data *info = curl_version_info(CURLVERSION_NOW);
1170 for (const char *const *p = info->protocols; *p; p++) {
1171 const char *proto = *p;
1172 if (av_strcasecmp(proto, "http") && av_strcasecmp(proto, "https"))
1173 continue; /* only http(s) are supported by libcurl.c at the moment */
1174 if (wl && av_match_list(proto, wl, ',') <= 0)
1175 continue;
1176 if (bl && av_match_list(proto, bl, ',') > 0)
1177 continue;
1178 if (bp.len)
1179 av_bprint_chars(&bp, ',', 1);
1180 av_bprintf(&bp, "%s", proto);
1181 }
1182
1183 if (!av_bprint_is_complete(&bp)) {
1185 return AVERROR(ENOMEM);
1186 }
1187
1188 if (!bp.len) {
1189 av_log(c->h, AV_LOG_ERROR, "Set of allowed protocols is empty.\n");
1191 return AVERROR(EINVAL);
1192 }
1193
1194 ret = curl_setopt_checked(c, "CURLOPT_PROTOCOLS_STR",
1195 curl_easy_setopt(c->easy, CURLOPT_PROTOCOLS_STR, bp.str));
1196 if (!ret)
1197 ret = curl_setopt_checked(c, "CURLOPT_REDIR_PROTOCOLS_STR",
1198 curl_easy_setopt(c->easy, CURLOPT_REDIR_PROTOCOLS_STR, bp.str));
1200 return ret;
1201}
1202
1203/* A failed setopt used to be ignored, so an unsupported option such as
1204 * -http_version 3 on a libcurl built without HTTP/3 was silently dropped and
1205 * the request went out with curl's default version. */
1206#define CURL_SETOPT(opt, val) \
1207 do { \
1208 int ret_ = curl_setopt_checked(c, #opt, \
1209 curl_easy_setopt(e, opt, val)); \
1210 if (ret_ < 0) \
1211 return ret_; \
1212 } while (0)
1213
1215{
1216 const curl_version_info_data *info = curl_version_info(CURLVERSION_NOW);
1217 CURL *e = c->easy;
1218 CURLcode cc;
1219 int ret;
1220 const char *url = c->h->filename;
1221
1222 /* Drop an optional "libcurl:" prefix that forces this protocol. */
1223 av_strstart(url, "libcurl:", &url);
1224
1225 CURL_SETOPT(CURLOPT_URL, url);
1226 CURL_SETOPT(CURLOPT_PRIVATE, c);
1227 CURL_SETOPT(CURLOPT_NOSIGNAL, 1L);
1228 CURL_SETOPT(CURLOPT_SHARE, c->loop->share);
1229
1230 CURL_SETOPT(CURLOPT_WRITEFUNCTION, write_callback);
1231 CURL_SETOPT(CURLOPT_WRITEDATA, c);
1232 CURL_SETOPT(CURLOPT_HEADERFUNCTION, header_callback);
1233 CURL_SETOPT(CURLOPT_HEADERDATA, c);
1234
1235 CURL_SETOPT(CURLOPT_NOPROGRESS, 0L);
1236 CURL_SETOPT(CURLOPT_XFERINFOFUNCTION, xferinfo_callback);
1237 CURL_SETOPT(CURLOPT_XFERINFODATA, c);
1238
1239 if (av_log_get_level() >= AV_LOG_DEBUG) {
1240 CURL_SETOPT(CURLOPT_VERBOSE, 1L);
1241 CURL_SETOPT(CURLOPT_DEBUGFUNCTION, debug_callback);
1242 CURL_SETOPT(CURLOPT_DEBUGDATA, c);
1243 }
1244
1245 CURL_SETOPT(CURLOPT_FOLLOWLOCATION, 1L);
1246 CURL_SETOPT(CURLOPT_MAXREDIRS, (long)c->max_redirects);
1247 CURL_SETOPT(CURLOPT_HTTP_VERSION, (long)c->http_version);
1248 CURL_SETOPT(CURLOPT_TCP_KEEPALIVE, c->multiple_requests ? 1L : 0L);
1249 CURL_SETOPT(CURLOPT_FORBID_REUSE, c->multiple_requests ? 0L : 1L);
1250 if (info->features & CURL_VERSION_HSTS)
1251 CURL_SETOPT(CURLOPT_HSTS_CTRL, (long)CURLHSTS_ENABLE);
1252 CURL_SETOPT(CURLOPT_ACCEPT_ENCODING,
1253 c->off > 0 || c->end_off > 0 ? "identity" : "");
1254 if (c->connect_timeout > 0)
1255 CURL_SETOPT(CURLOPT_CONNECTTIMEOUT_MS,
1256 (long)c->connect_timeout * 1000);
1257
1258 if (c->user_agent && c->user_agent[0])
1259 CURL_SETOPT(CURLOPT_USERAGENT, c->user_agent);
1260 if (c->http_proxy && c->http_proxy[0])
1261 CURL_SETOPT(CURLOPT_PROXY, c->http_proxy);
1262
1263 if (info->features & CURL_VERSION_SSL)
1264 CURL_SETOPT(CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_NATIVE_CA);
1265 CURL_SETOPT(CURLOPT_SSL_VERIFYPEER, c->tls_verify ? 1L : 0L);
1266 CURL_SETOPT(CURLOPT_SSL_VERIFYHOST, c->tls_verify ? 2L : 0L);
1267 if (c->ca_file)
1268 CURL_SETOPT(CURLOPT_CAINFO, c->ca_file);
1269 if (c->cert_file)
1270 CURL_SETOPT(CURLOPT_SSLCERT, c->cert_file);
1271 if (c->key_file)
1272 CURL_SETOPT(CURLOPT_SSLKEY, c->key_file);
1273
1274 /* The cookie engine is optional unless the user supplied cookies. */
1275 cc = curl_easy_setopt(e, CURLOPT_COOKIEFILE, "");
1276 if ((cc != CURLE_UNKNOWN_OPTION && cc != CURLE_NOT_BUILT_IN) ||
1277 (c->cookies && c->cookies[0])) {
1278 ret = curl_setopt_checked(c, "CURLOPT_COOKIEFILE", cc);
1279 if (ret < 0)
1280 return ret;
1281 }
1282 if (c->cookies && c->cookies[0]) {
1283 char *copy = av_strdup(c->cookies);
1284 char *line, *saveptr = NULL;
1285 if (!copy)
1286 return AVERROR(ENOMEM);
1287 for (line = av_strtok(copy, "\r\n", &saveptr); line;
1288 line = av_strtok(NULL, "\r\n", &saveptr)) {
1289 char *sc = av_asprintf("Set-Cookie: %s", line);
1290
1291 if (!sc) {
1292 av_free(copy);
1293 return AVERROR(ENOMEM);
1294 }
1295 cc = curl_easy_setopt(e, CURLOPT_COOKIELIST, sc);
1296 av_free(sc);
1297 if ((ret = curl_setopt_checked(c, "CURLOPT_COOKIELIST", cc)) < 0) {
1298 av_free(copy);
1299 return ret;
1300 }
1301 }
1302 av_free(copy);
1303 }
1304
1305 if ((ret = build_headers(c)) < 0)
1306 return ret;
1307 if (c->header_list)
1308 CURL_SETOPT(CURLOPT_HTTPHEADER, c->header_list);
1309
1310 /* Shoutcast v1 answers "ICY 200 OK", which curl would otherwise reject as
1311 * HTTP/0.9 before any header reaches header_callback(). */
1312 if (c->icy_status) {
1313 if ((ret = slist_append(&c->alias_list, "ICY 200")) < 0)
1314 return ret;
1315 CURL_SETOPT(CURLOPT_HTTP200ALIASES, c->alias_list);
1316 }
1317
1318 return 0;
1319}
1320
1321#undef CURL_SETOPT
1322
1324{
1326 struct timespec ts = { .tv_sec = t / 1000000,
1327 .tv_nsec = (t % 1000000) * 1000 };
1328 pthread_cond_timedwait(&c->cond, &c->mutex, &ts);
1329}
1330
1331/* Block until the transfer has been probed, the stream errored, or the open was
1332 * interrupted. Returns 0, or a negative AVERROR. */
1334{
1335 URLContext *h = c->h;
1336 int ret = 0;
1337
1338 pthread_mutex_lock(&c->mutex);
1339 while (!c->probed && !c->status) {
1340 if (ff_check_interrupt(&h->interrupt_callback)) {
1341 c->aborted = 1;
1342 ret = AVERROR_EXIT;
1343 break;
1344 }
1346 }
1347 if (!ret) {
1348 if (!c->stream_ok)
1349 ret = c->status ? c->status : AVERROR(EIO);
1350 }
1351 pthread_mutex_unlock(&c->mutex);
1352
1353 return ret;
1354}
1355
1356/* Scales by the recurrence relationship x := 2x + 1, i.e. 2^n - 1 */
1358{
1359 int64_t factor = INT64_MAX >> FFMAX(63 - c->retry_count, 0);
1361 return CURL_RETRY_MAX_US;
1362 return factor * CURL_RETRY_BASE_US;
1363}
1364
1365static int retry_request_locked(URLContext *h, int nonblock)
1366{
1367 CurlContext *c = h->priv_data;
1368 const int status = c->status;
1369 const CURLcode code = c->curl_status;
1370 pthread_mutex_unlock(&c->mutex);
1371
1372 if (!c->retry_time) {
1373 if (c->retry_count >= c->max_retries) {
1374 av_log(h, AV_LOG_ERROR, "Maximum number of retries (%d) reached\n",
1375 c->max_retries);
1376 return status;
1377 }
1378
1379 const int64_t delay = retry_delay(c);
1380 c->retry_time = av_gettime_relative() + delay;
1381 c->retry_count++;
1382
1383 av_log(h, AV_LOG_WARNING, "Retrying (#%d) from %"PRId64" in %.3fs "
1384 "after: %s (%s)\n", c->retry_count, c->logical_pos,
1385 delay * 1e-6, av_err2str(status),
1386 curl_easy_strerror(code));
1387 }
1388
1389 const int64_t sleep_us = c->retry_time - av_gettime_relative();
1390 if (sleep_us > 0 && !nonblock) {
1391 av_usleep(FFMIN(sleep_us, CURL_WAIT_US));
1392 } else if (sleep_us <= 0) {
1393 /* Use a synchronous request to ensure that the seek is registered, and
1394 * the reset of c->state is observable, before the next libcurl_read()
1395 * call, otherwise this might hit the exact same retry path a second
1396 * time. */
1397 int ret = curl_dispatch(c->loop, CMD_SEEK, c, c->logical_pos, 1);
1398 if (ret < 0)
1399 return ret;
1400 c->retry_time = 0;
1401 }
1402
1403 return AVERROR(EAGAIN); /* allow caller to handle interrupts and retry */
1404}
1405
1406static int libcurl_open(URLContext *h, const char *url, int flags,
1408{
1409 /* Guard against non-thread-safe libcurl builds. This should never happen,
1410 * since libcurl is used only on platforms with thread support, and thread
1411 * safety is enabled unconditionally in libcurl when the platform supports
1412 * threads or atomics. */
1413 curl_version_info_data *info = curl_version_info(CURLVERSION_NOW);
1414 if (!(info->features & CURL_VERSION_THREADSAFE))
1415 return AVERROR(ENOSYS);
1416
1417 CurlContext *c = h->priv_data;
1418 const char *eff_url = h->filename;
1419 int ret;
1420
1421 c->h = h;
1422 c->content_size = -1;
1423 c->request_start = c->off;
1424 c->request_end = -1;
1425 c->logical_pos = c->off;
1426 c->is_initial = 1;
1427 c->icy_block_len = -1;
1428
1429 /* Report the request URL until header_callback replaces it post-redirect. */
1430 av_strstart(eff_url, "libcurl:", &eff_url);
1431 av_freep(&c->location);
1432 c->location = av_strdup(eff_url);
1433
1434 ret = pthread_mutex_init(&c->mutex, NULL);
1435 if (ret)
1436 return AVERROR(ret);
1437 ret = pthread_cond_init(&c->cond, NULL);
1438 if (ret) {
1439 pthread_mutex_destroy(&c->mutex);
1440 return AVERROR(ret);
1441 }
1442
1443 c->fifo = av_fifo_alloc2(c->buffer_size, 1, 0);
1444 if (!c->fifo) {
1445 ret = AVERROR(ENOMEM);
1446 goto fail;
1447 }
1448
1449 ret = curl_loop_attach(c, h->avfc);
1450 if (ret < 0)
1451 goto fail;
1452
1453 c->easy = curl_easy_init();
1454 if (!c->easy) {
1455 ret = AVERROR(ENOMEM);
1456 goto fail;
1457 }
1458
1459 ret = setup_protocols(c);
1460 if (ret < 0)
1461 goto fail;
1462
1463 ret = setup_curl(c);
1464 if (ret < 0)
1465 goto fail;
1466
1467 ret = curl_dispatch(c->loop, CMD_ADD, c, 0, 0);
1468 if (ret < 0)
1469 goto fail;
1470
1471 ret = wait_for_probe(c);
1472 if (ret < 0)
1473 goto fail;
1474
1475 pthread_mutex_lock(&c->mutex);
1476 h->is_streamed = !c->seekable;
1477 pthread_mutex_unlock(&c->mutex);
1478
1479 return 0;
1480
1481fail:
1483 return ret;
1484}
1485
1486/* Export the metadata block, which the packet parser splits in place. */
1488{
1489 av_freep(&c->icy_metadata_packet);
1490 c->icy_metadata_packet = av_strdup((char *)c->icy_block);
1491 if (!c->icy_metadata_packet)
1492 return AVERROR(ENOMEM);
1493
1494 ff_http_parse_icy_packet(c->h, &c->metadata, (char *)c->icy_block);
1495 return 0;
1496}
1497
1498/* Consume the metadata block at the current interleave boundary, with the
1499 * mutex held. Returns 1 once a block is complete and ready to export. */
1501{
1502 size_t avail;
1503
1504 while ((avail = av_fifo_can_read(c->fifo))) {
1505 int n;
1506
1507 if (c->icy_block_len < 0) {
1508 uint8_t units;
1509 av_fifo_read(c->fifo, &units, 1);
1510 c->icy_block_len = units * 16;
1511 c->icy_block_filled = 0;
1512 /* A zero length byte means the metadata did not change. */
1513 if (!c->icy_block_len) {
1514 c->icy_block_len = -1;
1515 c->icy_data_read = 0;
1516 return 0;
1517 }
1518 continue;
1519 }
1520
1521 n = FFMIN(avail, (size_t)(c->icy_block_len - c->icy_block_filled));
1522 av_fifo_read(c->fifo, c->icy_block + c->icy_block_filled, n);
1523 c->icy_block_filled += n;
1524 if (c->icy_block_filled < c->icy_block_len)
1525 return 0;
1526
1527 c->icy_block[c->icy_block_len] = 0;
1528 c->icy_block_len = -1;
1529 c->icy_data_read = 0;
1530 return 1;
1531 }
1532 return 0;
1533}
1534
1535static int libcurl_read(URLContext *h, unsigned char *buf, int size)
1536{
1537 CurlContext *c = h->priv_data;
1538 int nonblock = h->flags & AVIO_FLAG_NONBLOCK;
1539 int unpause, ret;
1540
1541 pthread_mutex_lock(&c->mutex);
1542 while (1) {
1543 size_t avail;
1544
1545 /* A failed export loses one metadata update but leaves the interleave
1546 * in sync, since the block is fully consumed either way. */
1547 if (c->icy_metaint > 0 && c->icy_data_read == c->icy_metaint &&
1548 drain_icy_block(c) && (ret = update_icy_metadata(c)) < 0)
1549 break;
1550
1551 avail = av_fifo_can_read(c->fifo);
1552 if (c->icy_metaint > 0)
1553 avail = FFMIN(avail, (size_t)(c->icy_metaint - c->icy_data_read));
1554
1555 if (avail) {
1556 ret = FFMIN(avail, (size_t)size);
1557 av_fifo_read(c->fifo, buf, ret);
1558 c->icy_data_read += ret;
1559 c->logical_pos += ret;
1560 c->retry_count = 0;
1561 c->retry_time = 0;
1562 break;
1563 }
1564
1565 if (c->status) {
1566 if (c->seekable && is_recoverable(c->curl_status))
1567 return retry_request_locked(h, nonblock);
1568 if (c->status == AVERROR_EOF && c->icy_block_len >= 0)
1570 "Stream ended inside an ICY metadata block\n");
1571 ret = c->status;
1572 break;
1573 }
1574 if (nonblock) {
1575 ret = AVERROR(EAGAIN);
1576 break;
1577 }
1579 /* Return to the avio layer so it can poll the interrupt callback. */
1580 nonblock = 1;
1581 }
1582 /* Resume a paused transfer once the FIFO is at least half empty, on every
1583 * exit path because ICY framing can be drained without returning media. */
1584 unpause = c->paused && av_fifo_can_write(c->fifo) * 2 >= c->buffer_size;
1585 pthread_mutex_unlock(&c->mutex);
1586
1587 if (unpause)
1588 curl_dispatch(c->loop, CMD_UNPAUSE, c, 0, 0);
1589
1590 return ret;
1591}
1592
1594{
1595 CurlContext *c = h->priv_data;
1596 int64_t newpos;
1597
1598 pthread_mutex_lock(&c->mutex);
1599 const int64_t content_size = c->content_size;
1600 const int seekable = c->seekable;
1601 pthread_mutex_unlock(&c->mutex);
1602
1603 if (whence == AVSEEK_SIZE)
1604 return content_size >= 0 ? content_size : AVERROR(ENOSYS);
1605
1606 if (!seekable)
1607 return AVERROR(ENOSYS);
1608
1609 switch (whence) {
1610 case SEEK_SET:
1611 newpos = pos;
1612 break;
1613 case SEEK_CUR:
1614 if (pos > INT64_MAX - c->logical_pos)
1615 return AVERROR(ERANGE);
1616 newpos = c->logical_pos + pos;
1617 break;
1618 case SEEK_END:
1619 if (content_size < 0)
1620 return AVERROR(ENOSYS);
1621 if (pos > INT64_MAX - content_size)
1622 return AVERROR(ERANGE);
1623 newpos = content_size + pos;
1624 break;
1625 default:
1626 return AVERROR(EINVAL);
1627 }
1628 if (newpos < 0)
1629 return AVERROR(EINVAL);
1630
1631 if (newpos == c->logical_pos)
1632 return newpos;
1633
1634 /* Restart the transfer at the new offset. Any failure of the new request
1635 * surfaces on the following url_read(). */
1636 curl_dispatch(c->loop, CMD_SEEK, c, newpos, 1);
1637 c->logical_pos = newpos;
1638 c->retry_count = 0;
1639 c->retry_time = 0;
1640
1641 return newpos;
1642}
1643
1645{
1646 CurlContext *c = h->priv_data;
1647
1648 if (c->loop) {
1649 if (c->easy) {
1650 /* Ensure the handle is out of the multi before we free it. */
1651 curl_dispatch(c->loop, CMD_REMOVE, c, 0, 1);
1652 curl_easy_cleanup(c->easy);
1653 c->easy = NULL;
1654 }
1655 /* A shared loop outlives the transfer for connection reuse. */
1656 if (c->private_loop)
1657 curl_loop_destroy(c->loop);
1658 c->loop = NULL;
1659 }
1660
1661 if (c->header_list)
1662 curl_slist_free_all(c->header_list);
1663 if (c->alias_list)
1664 curl_slist_free_all(c->alias_list);
1665 av_dict_free(&c->hdr_icy);
1666 av_fifo_freep2(&c->fifo);
1667 pthread_cond_destroy(&c->cond);
1668 pthread_mutex_destroy(&c->mutex);
1669
1670 return 0;
1671}
1672
1674{
1675 CurlContext *c = h->priv_data;
1676 if (c->short_seek_size >= 1)
1677 return FFMIN(c->short_seek_size, INT_MAX);
1678 return AVERROR(ENOSYS);
1679}
1680
1681#define OFFSET(x) offsetof(CurlContext, x)
1682#define D AV_OPT_FLAG_DECODING_PARAM
1683#define E AV_OPT_FLAG_ENCODING_PARAM
1684static const AVOption options[] = {
1685 { "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, { .str = DEFAULT_USER_AGENT }, 0, 0, D },
1686 { "referer", "override Referer header", OFFSET(referer), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D },
1687 { "headers", "set custom HTTP headers, can override built in default headers", OFFSET(headers), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
1688 { "http_proxy", "set HTTP proxy to tunnel through", OFFSET(http_proxy), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
1689 { "cookies", "set cookies to be sent in applicable future requests, use newline delimited Set-Cookie HTTP field value syntax", OFFSET(cookies), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D },
1690 { "location", "the actual location of the data received", OFFSET(location), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
1691 { "offset", "initial byte offset", OFFSET(off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D },
1692 { "end_offset", "try to limit the request to bytes preceding this offset", OFFSET(end_off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D },
1693 { "seekable", "control seekability of connection", OFFSET(seekable_opt), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, D },
1694 { "tls_verify", "verify the peer certificate and hostname", OFFSET(tls_verify), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D | E },
1695 { "ca_file", "certificate authority bundle file", OFFSET(ca_file), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
1696 { "cert_file", "client certificate file", OFFSET(cert_file), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
1697 { "key_file", "client private key file", OFFSET(key_file), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E },
1698 { "connect_timeout", "connection timeout in seconds (0 = libcurl default)", OFFSET(connect_timeout), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX / 1000, D | E },
1699 { "max_redirects", "maximum number of redirects to follow", OFFSET(max_redirects), AV_OPT_TYPE_INT, { .i64 = 16 }, 0, INT_MAX, D },
1700 { "multiple_requests", "reuse the connection across requests (HTTP keep-alive)", OFFSET(multiple_requests), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D | E },
1701 { "max_retries", "maximum number of retries after a recoverable error", OFFSET(max_retries), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, D },
1702 { "icy", "request ICY metadata", OFFSET(icy), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D },
1703 { "icy_status", "accept legacy Shoutcast \"ICY 200 OK\" status lines", OFFSET(icy_status), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, D },
1704 { "icy_metadata_headers", "return ICY metadata headers", OFFSET(icy_metadata_headers), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT },
1705 { "icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT },
1706 { "metadata", "metadata read from the bitstream", OFFSET(metadata), AV_OPT_TYPE_DICT, {0}, 0, 0, AV_OPT_FLAG_EXPORT },
1707 { "buffer_size", "receive buffer size in bytes", OFFSET(buffer_size), AV_OPT_TYPE_INT64, { .i64 = CURL_DEFAULT_BUFFER_SIZE }, CURL_MAX_WRITE_SIZE, INT_MAX, D },
1708 { "request_size", "split a transfer into ranged requests of at most this many bytes (0 = unlimited)", OFFSET(request_size), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D },
1709 { "initial_request_size", "size (in bytes) of initial requests made during probing / header parsing", OFFSET(initial_request_size), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D },
1710 { "http_version", "HTTP version to use", OFFSET(http_version), AV_OPT_TYPE_INT, { .i64 = CURL_HTTP_VERSION_NONE }, 0, INT_MAX, D, .unit = "http_version" },
1711 { "auto", "negotiate the best supported version", 0, AV_OPT_TYPE_CONST, { .i64 = CURL_HTTP_VERSION_NONE }, 0, 0, D, .unit = "http_version" },
1712 { "1.0", "HTTP/1.0", 0, AV_OPT_TYPE_CONST, { .i64 = CURL_HTTP_VERSION_1_0 }, 0, 0, D, .unit = "http_version" },
1713 { "1.1", "HTTP/1.1", 0, AV_OPT_TYPE_CONST, { .i64 = CURL_HTTP_VERSION_1_1 }, 0, 0, D, .unit = "http_version" },
1714 { "2", "HTTP/2", 0, AV_OPT_TYPE_CONST, { .i64 = CURL_HTTP_VERSION_2 }, 0, 0, D, .unit = "http_version" },
1715 { "2tls", "HTTP/2 over TLS only", 0, AV_OPT_TYPE_CONST, { .i64 = CURL_HTTP_VERSION_2TLS }, 0, 0, D, .unit = "http_version" },
1716 { "2-prior-knowledge", "HTTP/2 without an upgrade handshake", 0, AV_OPT_TYPE_CONST, { .i64 = CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE }, 0, 0, D, .unit = "http_version" },
1717 { "3", "HTTP/3, fall back to earlier versions", 0, AV_OPT_TYPE_CONST, { .i64 = CURL_HTTP_VERSION_3 }, 0, 0, D, .unit = "http_version" },
1718 { "3only", "HTTP/3 only", 0, AV_OPT_TYPE_CONST, { .i64 = CURL_HTTP_VERSION_3ONLY }, 0, 0, D, .unit = "http_version" },
1719 { "short_seek_size", "threshold to favor readahead over seek", OFFSET(short_seek_size), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D },
1720 { NULL }
1721};
1722
1724 .class_name = "libcurl",
1725 .item_name = av_default_item_name,
1726 .option = options,
1727 .version = LIBAVUTIL_VERSION_INT,
1728};
1729
1731 .name = "libcurl",
1732 .url_open2 = libcurl_open,
1733 .url_read = libcurl_read,
1734 .url_seek = libcurl_seek,
1735 .url_close = libcurl_close,
1736 .url_get_short_seek = libcurl_get_short_seek,
1737 .priv_data_size = sizeof(CurlContext),
1738 .priv_data_class = &libcurl_context_class,
1740 .default_whitelist = "http,https,libcurl",
1741};
static double val(void *priv, double ch)
Definition aeval.c:77
static FILE * out
#define L(x)
Definition vpx_arith.h:36
#define E
Definition avdct.c:34
#define D
Definition avdct.c:35
Main libavformat public API header.
int ff_check_interrupt(AVIOInterruptCB *cb)
Check if the user has requested to interrupt a blocking function associated with cb.
Definition avio.c:929
#define AVSEEK_SIZE
Passing this as the "whence" parameter to a seek function causes it to return the filesize without se...
Definition avio.h:468
#define AVIO_FLAG_NONBLOCK
Use non-blocking mode.
Definition avio.h:636
char * av_asprintf(const char *fmt,...)
Definition avstring.c:115
static int BS_FUNC left(const BSCTX *bc)
Return the number of the bits left in a buffer.
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition bprint.c:121
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition bprint.c:68
AVBPrint public header.
#define AV_BPRINT_SIZE_AUTOMATIC
static int FUNC metadata(CodedBitstreamContext *ctx, RWContext *rw, APVRawMetadata *current)
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define avg(a, b, c, d)
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
Public dictionary API.
error code definitions
double value
Definition eval.c:102
const char * key
static void do_exit(VideoState *is)
Definition ffplay.c:1985
static int loop
Definition ffplay.c:407
A generic FIFO API.
#define fail
Definition test.h:479
#define AV_OPT_FLAG_EXPORT
The option is intended for exporting values to the caller.
Definition opt.h:362
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_INT64
Underlying C type is int64_t.
Definition opt.h:262
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_DICT
Underlying C type is AVDictionary*.
Definition opt.h:289
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition opt.h:326
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition opt.h:275
static int av_bprint_is_complete(const AVBPrint *buf)
Test if the print buffer is complete (not truncated).
Definition bprint.h:218
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition bprint.c:234
void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
Append char c n times to a print buffer.
Definition bprint.c:129
#define AV_DICT_MULTIKEY
Allow to store several equal keys in the dictionary.
Definition dict.h:84
void av_dict_free(AVDictionary **pm)
Free all the memory allocated for an AVDictionary struct and all keys and values.
Definition dict.c:233
const AVDictionaryEntry * av_dict_iterate(const AVDictionary *m, const AVDictionaryEntry *prev)
Iterate over a dictionary.
Definition dict.c:42
int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags)
Copy entries from one AVDictionary struct into another.
Definition dict.c:247
#define AV_DICT_DONT_STRDUP_VAL
Take ownership of a value that's been allocated with av_malloc() or another memory allocation functio...
Definition dict.h:79
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
Definition dict.c:86
#define AV_DICT_DONT_STRDUP_KEY
Take ownership of a key that's been allocated with av_malloc() or another memory allocation function.
Definition dict.h:77
#define AVERROR_EXIT
Immediate exit was requested; the called function should not be restarted.
Definition error.h:58
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR_EOF
End of file.
Definition error.h:57
#define av_err2str(errnum)
Convenience macro, the return value should be used only directly in function arguments but never stan...
Definition error.h:122
#define AVERROR(e)
Definition error.h:45
AVFifo * av_fifo_alloc2(size_t nb_elems, size_t elem_size, unsigned int flags)
Allocate and initialize an AVFifo with a given element size.
Definition fifo.c:47
void av_fifo_freep2(AVFifo **f)
Free an AVFifo and reset pointer to NULL.
Definition fifo.c:286
void av_fifo_reset2(AVFifo *f)
Definition fifo.c:280
size_t av_fifo_can_write(const AVFifo *f)
Definition fifo.c:94
size_t av_fifo_can_read(const AVFifo *f)
Definition fifo.c:87
int av_fifo_write(AVFifo *f, const void *buf, size_t nb_elems)
Write data into a FIFO.
Definition fifo.c:188
int av_fifo_read(AVFifo *f, void *buf, size_t nb_elems)
Read data from a FIFO.
Definition fifo.c:240
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:241
int av_log_get_level(void)
Get the current log level.
Definition log.c:471
char * av_strndup(const char *s, size_t len)
Duplicate a substring of a string.
Definition mem.c:390
char * av_stristr(const char *s1, const char *s2)
Locate the first case-independent occurrence in the string haystack of the string needle.
Definition avstring.c:58
char * av_strtok(char *s, const char *delim, char **saveptr)
Split the string into several tokens which can be accessed by successive calls to av_strtok().
Definition avstring.c:179
int av_strcasecmp(const char *a, const char *b)
Locale-independent case-insensitive compare.
Definition avstring.c:208
static av_const int av_isdigit(int c)
Locale-independent conversion of ASCII isdigit.
Definition avstring.h:202
int av_strstart(const char *str, const char *pfx, const char **ptr)
Return non-zero if pfx is a prefix of str.
Definition avstring.c:36
static av_const int av_isspace(int c)
Locale-independent conversion of ASCII isspace.
Definition avstring.h:218
int av_match_list(const char *name, const char *list, char separator)
Check if a name is in a list.
Definition avstring.c:443
int av_stristart(const char *str, const char *pfx, const char **ptr)
Return non-zero if pfx is a prefix of str independent of case.
Definition avstring.c:47
int av_strncasecmp(const char *a, const char *b, size_t n)
Locale-independent case-insensitive compare.
Definition avstring.c:218
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
static uint64_t request_size(URLContext *h)
Definition http.c:1640
#define DEFAULT_USER_AGENT
Definition http.c:188
static void ff_http_parse_icy_packet(void *logctx, AVDictionary **metadata, char *data)
Split an in-band ICY metadata packet into its "Key='Value';" pairs.
Definition http.h:62
static int ff_http_averror(int status_code, int default_averror)
Definition http.h:120
uint32_t type
Definition jpegmpfenc.c:80
const char * arg
Definition jacosubdec.c:65
static const int factor[16]
Definition vf_pp7.c:98
static int is_recoverable(const FifoContext *fifo, int err_no)
Definition fifo.c:284
static av_always_inline FFFormatContext * ffformatcontext(AVFormatContext *s)
Definition internal.h:130
Libavformat version macros.
#define AV_MUTEX_INITIALIZER
Definition thread.h:185
static int ff_thread_setname(const char *name)
Definition thread.h:216
#define AVMutex
Definition thread.h:184
#define CURL_DEFAULT_BUFFER_SIZE
Definition libcurl.c:51
static int setup_protocols(CurlContext *c)
Definition libcurl.c:1158
static int curl_setopt_checked(CurlContext *c, const char *name, CURLcode code)
Definition libcurl.c:216
const URLProtocol ff_libcurl_protocol
Definition libcurl.c:1730
#define CURL_RETRY_MAX_US
Definition libcurl.c:67
static void execute_command(CurlLoop *loop, CurlCmd *cmd)
Definition libcurl.c:812
#define ICY_MAX_BLOCK
Definition libcurl.c:61
static void share_unlock_callback(CURL *handle, curl_lock_data data, void *userdata)
Definition libcurl.c:954
static int curl_loop_attach(CurlContext *c, AVFormatContext *avfc)
Definition libcurl.c:1053
static int curlmcode_to_curlcode(CURLMcode code)
Definition libcurl.c:236
static void print_statistics(CurlLoop *loop)
Definition libcurl.c:1009
static int wait_for_probe(CurlContext *c)
Definition libcurl.c:1333
static int curlmcode_to_averror(CURLMcode code)
Definition libcurl.c:226
static int slist_append(struct curl_slist **list, const char *str)
Definition libcurl.c:1113
static void share_lock_callback(CURL *handle, curl_lock_data data, curl_lock_access access, void *userdata)
Definition libcurl.c:947
static void update_statistics(CurlContext *c)
Definition libcurl.c:699
#define CURL_WAIT_US
Definition libcurl.c:55
static int is_recoverable(CURLcode code)
Definition libcurl.c:248
static void start_request(CurlContext *c)
Definition libcurl.c:648
static int verify_content_range(CurlContext *c, int64_t start, int64_t end, int64_t total)
Definition libcurl.c:454
static int drain_icy_block(CurlContext *c)
Definition libcurl.c:1500
#define CURL_RETRY_BASE_US
Definition libcurl.c:66
static const AVClass libcurl_context_class
Definition libcurl.c:1723
static int xferinfo_callback(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
Definition libcurl.c:635
#define CURL_SETOPT(opt, val)
Definition libcurl.c:1206
static int debug_callback(CURL *easy, curl_infotype type, char *data, size_t size, void *userdata)
Definition libcurl.c:1086
static int libcurl_open(URLContext *h, const char *url, int flags, AVDictionary **options)
Definition libcurl.c:1406
static int64_t retry_delay(CurlContext *c)
Definition libcurl.c:1357
static void curl_loop_destroy(CurlLoop *loop)
Definition libcurl.c:1029
static void curl_cond_wait(CurlContext *c)
Definition libcurl.c:1323
#define ICY_MAX_HEADERS
Definition libcurl.c:58
static void update_status_locked(CurlContext *c, int status, CURLcode code)
Definition libcurl.c:270
static int parse_offset(const char *str, int64_t *out, const char **ptr)
Definition libcurl.c:328
static int libcurl_close(URLContext *h)
Definition libcurl.c:1644
static void store_icy_header(AVDictionary **dict, const char *ptr, size_t len)
Definition libcurl.c:407
void ff_curl_loop_free(struct CurlLoop **loop)
Release a libcurl event loop and set *loop to NULL.
Definition libcurl.c:1072
static AVMutex curl_loop_lock
Definition libcurl.c:192
static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
Definition libcurl.c:279
static void parse_content_range(CurlContext *c, const char *v)
Definition libcurl.c:345
static void on_done(CurlContext *c, CURLcode code)
Definition libcurl.c:724
static void commit_icy_headers(CurlContext *c)
Definition libcurl.c:436
static int retry_request_locked(URLContext *h, int nonblock)
Definition libcurl.c:1365
static int test_short_seek(CurlContext *c)
Definition libcurl.c:799
static int libcurl_get_short_seek(URLContext *h)
Definition libcurl.c:1673
#define OFFSET(x)
Definition libcurl.c:1681
static int build_headers(CurlContext *c)
Definition libcurl.c:1123
static CurlLoop * curl_loop_create(AVFormatContext *avfc)
Definition libcurl.c:961
static size_t header_callback(char *ptr, size_t size, size_t nitems, void *userdata)
Definition libcurl.c:490
static void * curl_worker(void *arg)
Definition libcurl.c:858
static int libcurl_read(URLContext *h, unsigned char *buf, int size)
Definition libcurl.c:1535
static int64_t libcurl_seek(URLContext *h, int64_t pos, int whence)
Definition libcurl.c:1593
static int update_icy_metadata(CurlContext *c)
Definition libcurl.c:1487
static int curl_dispatch(CurlLoop *loop, enum cmd_kind kind, CurlContext *c, int64_t pos, int sync)
Definition libcurl.c:917
static int setup_curl(CurlContext *c)
Definition libcurl.c:1214
static int curlcode_to_averror(CURLcode code)
Definition libcurl.c:194
static int64_t parse_metaint(const char *p, size_t len)
Definition libcurl.c:380
cmd_kind
Definition libcurl.c:71
@ CMD_ADD
Definition libcurl.c:72
@ CMD_UNPAUSE
Definition libcurl.c:74
@ CMD_SEEK
Definition libcurl.c:75
@ CMD_REMOVE
Definition libcurl.c:73
Utility Preprocessor macros.
#define FFMIN(a, b)
Definition macros.h:49
#define FFMAX(a, b)
Definition macros.h:47
enum AVColorSpace space
enum AVColorRange range
Memory handling functions.
const char data[16]
Definition mxf.c:149
#define av_strdup(s)
Definition ops_static.c:55
AVOptions.
static av_always_inline int pthread_cond_broadcast(pthread_cond_t *cond)
Definition os2threads.h:168
static av_always_inline int pthread_mutex_lock(pthread_mutex_t *mutex)
Definition os2threads.h:119
static av_always_inline int pthread_cond_destroy(pthread_cond_t *cond)
Definition os2threads.h:150
static av_always_inline int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
Definition os2threads.h:104
static av_always_inline int pthread_join(pthread_t thread, void **value_ptr)
Definition os2threads.h:94
static av_always_inline int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
Definition os2threads.h:139
static av_always_inline int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
Definition os2threads.h:80
_fmutex pthread_mutex_t
Definition os2threads.h:53
static av_always_inline int pthread_mutex_unlock(pthread_mutex_t *mutex)
Definition os2threads.h:132
static av_always_inline int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
Definition os2threads.h:176
static av_always_inline int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
Definition os2threads.h:198
static av_always_inline int pthread_mutex_destroy(pthread_mutex_t *mutex)
Definition os2threads.h:112
const char * name
Definition qsvenc.c:142
const uint8_t * code
Definition spdifenc.c:433
unsigned int pos
Definition spdifenc.c:431
Describe the class of an AVClass context structure.
Definition log.h:76
char * key
Definition dict.h:91
char * value
Definition dict.h:92
Definition fifo.c:35
Format I/O context.
Definition avformat.h:1337
AVOption.
Definition opt.h:428
enum cmd_kind kind
Definition libcurl.c:79
int done
Definition libcurl.c:83
int sync
Definition libcurl.c:82
int64_t pos
Definition libcurl.c:81
CurlContext * ctx
Definition libcurl.c:80
struct CurlCmd * next
Definition libcurl.c:84
int64_t request_end
Definition libcurl.c:160
int retry_count
Definition libcurl.c:149
int64_t request_size
Definition libcurl.c:137
char * location
Definition libcurl.c:127
int64_t hdr_content_end
Definition libcurl.c:168
pthread_mutex_t mutex
Definition libcurl.c:181
int is_initial
Definition libcurl.c:161
int icy_status
Definition libcurl.c:142
int http_version
Definition libcurl.c:135
char * cookies
Definition libcurl.c:123
int64_t request_start
Definition libcurl.c:158
char * icy_metadata_headers
Definition libcurl.c:143
int tls_verify
Definition libcurl.c:130
struct curl_slist * header_list
Definition libcurl.c:115
int64_t icy_data_read
Definition libcurl.c:151
char * referer
Definition libcurl.c:120
char * cert_file
Definition libcurl.c:125
int64_t hdr_icy_metaint
Definition libcurl.c:172
pthread_cond_t cond
Definition libcurl.c:182
int64_t initial_request_size
Definition libcurl.c:138
int64_t retry_time
Definition libcurl.c:150
int64_t content_size
Definition libcurl.c:178
int max_retries
Definition libcurl.c:140
int multiple_requests
Definition libcurl.c:134
int64_t short_seek_size
Definition libcurl.c:139
AVFifo * fifo
Definition libcurl.c:183
char * key_file
Definition libcurl.c:126
int64_t end_off
Definition libcurl.c:129
int icy_block_len
Definition libcurl.c:152
int seek_queued
Definition libcurl.c:162
URLContext * h
Definition libcurl.c:110
uint8_t icy_block[ICY_MAX_BLOCK+1]
Definition libcurl.c:154
char * ca_file
Definition libcurl.c:124
int stream_ok
Definition libcurl.c:176
int connect_timeout
Definition libcurl.c:132
AVDictionary * hdr_icy
Definition libcurl.c:171
int64_t icy_metaint
Definition libcurl.c:188
int icy_block_filled
Definition libcurl.c:153
int64_t buffer_size
Definition libcurl.c:136
char * user_agent
Definition libcurl.c:119
int private_loop
Definition libcurl.c:113
int seekable_opt
Definition libcurl.c:131
CurlLoop * loop
Definition libcurl.c:112
int64_t off
Definition libcurl.c:128
int hdr_accept_ranges
Definition libcurl.c:165
char * icy_metadata_packet
Definition libcurl.c:144
AVDictionary * metadata
Definition libcurl.c:145
int64_t logical_pos
Definition libcurl.c:148
int hdr_compressed
Definition libcurl.c:166
int seekable
Definition libcurl.c:177
int64_t request_received
Definition libcurl.c:159
int64_t hdr_content_total
Definition libcurl.c:169
char * http_proxy
Definition libcurl.c:122
int64_t hdr_content_start
Definition libcurl.c:167
int aborted
Definition libcurl.c:185
CURL * easy
Definition libcurl.c:114
struct curl_slist * alias_list
Definition libcurl.c:116
int64_t hdr_content_length
Definition libcurl.c:170
int max_redirects
Definition libcurl.c:133
CURLcode curl_status
Definition libcurl.c:187
char * headers
Definition libcurl.c:121
AVFormatContext * avfc
Definition libcurl.c:88
pthread_cond_t cond
Definition libcurl.c:95
CURLM * multi
Definition libcurl.c:91
int64_t total_time_us
Definition libcurl.c:101
CurlCmd * cmd_head
Definition libcurl.c:96
pthread_mutex_t mutex
Definition libcurl.c:94
int num_errors
Definition libcurl.c:105
CurlCmd * cmd_tail
Definition libcurl.c:96
int num_requests
Definition libcurl.c:104
int num_connections
Definition libcurl.c:102
int num_redirects
Definition libcurl.c:103
pthread_t thread
Definition libcurl.c:90
int exit
Definition libcurl.c:97
CURLSH * share
Definition libcurl.c:92
int64_t total_bytes
Definition libcurl.c:100
struct CurlLoop * curl_loop
Shared libcurl event loop, created on demand on the first use.
Definition internal.h:127
#define av_free(p)
#define av_mallocz(s)
#define av_freep(p)
#define av_log(a,...)
static uint8_t tmp[40]
Definition aes_ctr.c:52
int av_usleep(unsigned usec)
Sleep for a period of time.
Definition time.c:93
int64_t av_gettime_relative(void)
Get the current time in microseconds since some unspecified starting point.
Definition time.c:57
int64_t av_gettime(void)
Get the current time in microseconds.
Definition time.c:40
int size
char prefix[8]
unbuffered private I/O API
#define URL_PROTOCOL_FLAG_NETWORK
Definition url.h:33
static void copy(const float *p1, float *p2, const int length)
int len
static double c[64]