FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/async.c
Date: 2026-10-19 08:11:58
Exec Total Coverage
Lines: 0 244 0.0%
Functions: 0 17 0.0%
Branches: 0 84 0.0%

Line Branch Exec Source
1 /*
2 * Input async protocol.
3 * Copyright (c) 2015 Zhang Rui <bbcallen@gmail.com>
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 * Based on libavformat/cache.c by Michael Niedermayer
22 */
23
24 /**
25 * @TODO
26 * support timeout
27 * support work with concatdec, hls
28 */
29
30 #include <inttypes.h>
31 #include <stdatomic.h>
32 #include <string.h>
33
34 #include "libavutil/avassert.h"
35 #include "libavutil/avstring.h"
36 #include "libavutil/error.h"
37 #include "libavutil/fifo.h"
38 #include "libavutil/log.h"
39 #include "libavutil/opt.h"
40 #include "libavutil/thread.h"
41 #include "url.h"
42
43 #if HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46
47 #define BUFFER_CAPACITY (4 * 1024 * 1024)
48 #define READ_BACK_CAPACITY (4 * 1024 * 1024)
49 #define SHORT_SEEK_THRESHOLD (256 * 1024)
50
51 typedef struct RingBuffer
52 {
53 AVFifo *fifo;
54 int read_back_capacity;
55
56 int read_pos;
57 } RingBuffer;
58
59 typedef struct AsyncContext {
60 AVClass *class;
61 URLContext *inner;
62
63 int seek_request;
64 int64_t seek_pos;
65 int seek_whence;
66 int seek_completed;
67 int64_t seek_ret;
68
69 int inner_io_error;
70 int io_error;
71 int io_eof_reached;
72
73 int64_t logical_pos;
74 int64_t logical_size;
75 RingBuffer ring;
76
77 pthread_cond_t cond_wakeup_main;
78 pthread_cond_t cond_wakeup_background;
79 pthread_mutex_t mutex;
80 pthread_t async_buffer_thread;
81
82 atomic_int abort_request;
83 AVIOInterruptCB interrupt_callback;
84 } AsyncContext;
85
86 ✗ static int ring_init(RingBuffer *ring, unsigned int capacity, int read_back_capacity)
87 {
88 ✗ memset(ring, 0, sizeof(RingBuffer));
89 ✗ ring->fifo = av_fifo_alloc2(capacity + read_back_capacity, 1, 0);
90 ✗ if (!ring->fifo)
91 ✗ return AVERROR(ENOMEM);
92
93 ✗ ring->read_back_capacity = read_back_capacity;
94 ✗ return 0;
95 }
96
97 ✗ static void ring_destroy(RingBuffer *ring)
98 {
99 ✗ av_fifo_freep2(&ring->fifo);
100 ✗ }
101
102 ✗ static void ring_reset(RingBuffer *ring)
103 {
104 ✗ av_fifo_reset2(ring->fifo);
105 ✗ ring->read_pos = 0;
106 ✗ }
107
108 ✗ static int ring_size(RingBuffer *ring)
109 {
110 ✗ return av_fifo_can_read(ring->fifo) - ring->read_pos;
111 }
112
113 ✗ static int ring_space(RingBuffer *ring)
114 {
115 ✗ return av_fifo_can_write(ring->fifo);
116 }
117
118 ✗ static int ring_read(RingBuffer *ring, void *dest, int buf_size)
119 {
120 ✗ int ret = 0;
121
122 av_assert2(buf_size <= ring_size(ring));
123 ✗ if (dest)
124 ✗ ret = av_fifo_peek(ring->fifo, dest, buf_size, ring->read_pos);
125 ✗ ring->read_pos += buf_size;
126
127 ✗ if (ring->read_pos > ring->read_back_capacity) {
128 ✗ av_fifo_drain2(ring->fifo, ring->read_pos - ring->read_back_capacity);
129 ✗ ring->read_pos = ring->read_back_capacity;
130 }
131
132 ✗ return ret;
133 }
134
135 ✗ static int wrapped_url_read(void *src, void *dst, size_t *size)
136 {
137 ✗ URLContext *h = src;
138 ✗ AsyncContext *c = h->priv_data;
139 int ret;
140
141 ✗ ret = ffurl_read(c->inner, dst, *size);
142 ✗ *size = ret > 0 ? ret : 0;
143 ✗ c->inner_io_error = ret < 0 ? ret : 0;
144
145 ✗ return c->inner_io_error;
146 }
147
148 ✗ static int ring_write(RingBuffer *ring, URLContext *h, size_t size)
149 {
150 int ret;
151
152 av_assert2(size <= ring_space(ring));
153 ✗ ret = av_fifo_write_from_cb(ring->fifo, wrapped_url_read, h, &size);
154 ✗ if (ret < 0)
155 ✗ return ret;
156
157 ✗ return size;
158 }
159
160 ✗ static int ring_size_of_read_back(RingBuffer *ring)
161 {
162 ✗ return ring->read_pos;
163 }
164
165 ✗ static int ring_drain(RingBuffer *ring, int offset)
166 {
167 av_assert2(offset >= -ring_size_of_read_back(ring));
168 av_assert2(offset <= ring_size(ring));
169 ✗ ring->read_pos += offset;
170 ✗ return 0;
171 }
172
173 ✗ static int async_check_interrupt(void *arg)
174 {
175 ✗ URLContext *h = arg;
176 ✗ AsyncContext *c = h->priv_data;
177
178 ✗ if (atomic_load(&c->abort_request))
179 ✗ return 1;
180
181 ✗ if (ff_check_interrupt(&c->interrupt_callback))
182 ✗ atomic_store(&c->abort_request, 1);
183
184 ✗ return atomic_load(&c->abort_request);
185 }
186
187 ✗ static void *async_buffer_task(void *arg)
188 {
189 ✗ URLContext *h = arg;
190 ✗ AsyncContext *c = h->priv_data;
191 ✗ RingBuffer *ring = &c->ring;
192 ✗ int ret = 0;
193 int64_t seek_ret;
194
195 ✗ ff_thread_setname("async");
196
197 ✗ while (1) {
198 int fifo_space, to_copy;
199
200 ✗ pthread_mutex_lock(&c->mutex);
201 ✗ if (async_check_interrupt(h)) {
202 ✗ c->io_eof_reached = 1;
203 ✗ c->io_error = AVERROR_EXIT;
204 ✗ pthread_cond_signal(&c->cond_wakeup_main);
205 ✗ pthread_mutex_unlock(&c->mutex);
206 ✗ break;
207 }
208
209 ✗ if (c->seek_request) {
210 ✗ seek_ret = ffurl_seek(c->inner, c->seek_pos, c->seek_whence);
211 ✗ if (seek_ret >= 0) {
212 ✗ c->io_eof_reached = 0;
213 ✗ c->io_error = 0;
214 ✗ ring_reset(ring);
215 }
216
217 ✗ c->seek_completed = 1;
218 ✗ c->seek_ret = seek_ret;
219 ✗ c->seek_request = 0;
220
221
222 ✗ pthread_cond_signal(&c->cond_wakeup_main);
223 ✗ pthread_mutex_unlock(&c->mutex);
224 ✗ continue;
225 }
226
227 ✗ fifo_space = ring_space(ring);
228 ✗ if (c->io_eof_reached || fifo_space <= 0) {
229 ✗ pthread_cond_signal(&c->cond_wakeup_main);
230 ✗ pthread_cond_wait(&c->cond_wakeup_background, &c->mutex);
231 ✗ pthread_mutex_unlock(&c->mutex);
232 ✗ continue;
233 }
234 ✗ pthread_mutex_unlock(&c->mutex);
235
236 ✗ to_copy = FFMIN(4096, fifo_space);
237 ✗ ret = ring_write(ring, h, to_copy);
238
239 ✗ pthread_mutex_lock(&c->mutex);
240 ✗ if (ret <= 0) {
241 ✗ c->io_eof_reached = 1;
242 ✗ if (c->inner_io_error < 0)
243 ✗ c->io_error = c->inner_io_error;
244 }
245
246 ✗ pthread_cond_signal(&c->cond_wakeup_main);
247 ✗ pthread_mutex_unlock(&c->mutex);
248 }
249
250 ✗ return NULL;
251 }
252
253 ✗ static int async_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
254 {
255 ✗ AsyncContext *c = h->priv_data;
256 int ret;
257 ✗ AVIOInterruptCB interrupt_callback = {.callback = async_check_interrupt, .opaque = h};
258
259 ✗ av_strstart(arg, "async:", &arg);
260 ✗ atomic_init(&c->abort_request, 0);
261
262 ✗ ret = ring_init(&c->ring, BUFFER_CAPACITY, READ_BACK_CAPACITY);
263 ✗ if (ret < 0)
264 ✗ goto fifo_fail;
265
266 /* wrap interrupt callback */
267 ✗ c->interrupt_callback = h->interrupt_callback;
268 ✗ ret = ffurl_open_whitelist(&c->inner, arg, flags, &interrupt_callback, options, h->protocol_whitelist, h->protocol_blacklist, h);
269 ✗ if (ret != 0) {
270 ✗ av_log(h, AV_LOG_ERROR, "ffurl_open failed : %s, %s\n", av_err2str(ret), arg);
271 ✗ goto url_fail;
272 }
273
274 ✗ c->logical_size = ffurl_size(c->inner);
275 ✗ h->is_streamed = c->inner->is_streamed;
276
277 ✗ ret = pthread_mutex_init(&c->mutex, NULL);
278 ✗ if (ret != 0) {
279 ✗ ret = AVERROR(ret);
280 ✗ av_log(h, AV_LOG_ERROR, "pthread_mutex_init failed : %s\n", av_err2str(ret));
281 ✗ goto mutex_fail;
282 }
283
284 ✗ ret = pthread_cond_init(&c->cond_wakeup_main, NULL);
285 ✗ if (ret != 0) {
286 ✗ ret = AVERROR(ret);
287 ✗ av_log(h, AV_LOG_ERROR, "pthread_cond_init failed : %s\n", av_err2str(ret));
288 ✗ goto cond_wakeup_main_fail;
289 }
290
291 ✗ ret = pthread_cond_init(&c->cond_wakeup_background, NULL);
292 ✗ if (ret != 0) {
293 ✗ ret = AVERROR(ret);
294 ✗ av_log(h, AV_LOG_ERROR, "pthread_cond_init failed : %s\n", av_err2str(ret));
295 ✗ goto cond_wakeup_background_fail;
296 }
297
298 ✗ ret = pthread_create(&c->async_buffer_thread, NULL, async_buffer_task, h);
299 ✗ if (ret) {
300 ✗ ret = AVERROR(ret);
301 ✗ av_log(h, AV_LOG_ERROR, "pthread_create failed : %s\n", av_err2str(ret));
302 ✗ goto thread_fail;
303 }
304
305 ✗ return 0;
306
307 ✗ thread_fail:
308 ✗ pthread_cond_destroy(&c->cond_wakeup_background);
309 ✗ cond_wakeup_background_fail:
310 ✗ pthread_cond_destroy(&c->cond_wakeup_main);
311 ✗ cond_wakeup_main_fail:
312 ✗ pthread_mutex_destroy(&c->mutex);
313 ✗ mutex_fail:
314 ✗ ffurl_closep(&c->inner);
315 ✗ url_fail:
316 ✗ ring_destroy(&c->ring);
317 ✗ fifo_fail:
318 ✗ return ret;
319 }
320
321 ✗ static int async_close(URLContext *h)
322 {
323 ✗ AsyncContext *c = h->priv_data;
324 int ret;
325
326 ✗ pthread_mutex_lock(&c->mutex);
327 ✗ atomic_store(&c->abort_request, 1);
328 ✗ pthread_cond_signal(&c->cond_wakeup_background);
329 ✗ pthread_mutex_unlock(&c->mutex);
330
331 ✗ ret = pthread_join(c->async_buffer_thread, NULL);
332 ✗ if (ret != 0)
333 ✗ av_log(h, AV_LOG_ERROR, "pthread_join(): %s\n", av_err2str(ret));
334
335 ✗ pthread_cond_destroy(&c->cond_wakeup_background);
336 ✗ pthread_cond_destroy(&c->cond_wakeup_main);
337 ✗ pthread_mutex_destroy(&c->mutex);
338 ✗ ffurl_closep(&c->inner);
339 ✗ ring_destroy(&c->ring);
340
341 ✗ return 0;
342 }
343
344 ✗ static int async_read_internal(URLContext *h, void *dest, int size)
345 {
346 ✗ AsyncContext *c = h->priv_data;
347 ✗ RingBuffer *ring = &c->ring;
348 ✗ int read_complete = !dest;
349 ✗ int to_read = size;
350 ✗ int ret = 0;
351
352 ✗ pthread_mutex_lock(&c->mutex);
353
354 ✗ while (to_read > 0) {
355 int fifo_size, to_copy;
356 ✗ if (async_check_interrupt(h)) {
357 ✗ ret = AVERROR_EXIT;
358 ✗ break;
359 }
360 ✗ fifo_size = ring_size(ring);
361 ✗ to_copy = FFMIN(to_read, fifo_size);
362 ✗ if (to_copy > 0) {
363 ✗ ring_read(ring, dest, to_copy);
364 ✗ if (dest)
365 ✗ dest = (uint8_t *)dest + to_copy;
366 ✗ c->logical_pos += to_copy;
367 ✗ to_read -= to_copy;
368 ✗ ret = size - to_read;
369
370 ✗ if (to_read <= 0 || !read_complete)
371 break;
372 ✗ } else if (c->io_eof_reached) {
373 ✗ if (ret <= 0) {
374 ✗ if (c->io_error)
375 ✗ ret = c->io_error;
376 else
377 ✗ ret = AVERROR_EOF;
378 }
379 ✗ break;
380 }
381 ✗ pthread_cond_signal(&c->cond_wakeup_background);
382 ✗ pthread_cond_wait(&c->cond_wakeup_main, &c->mutex);
383 }
384
385 ✗ pthread_cond_signal(&c->cond_wakeup_background);
386 ✗ pthread_mutex_unlock(&c->mutex);
387
388 ✗ return ret;
389 }
390
391 ✗ static int async_read(URLContext *h, unsigned char *buf, int size)
392 {
393 ✗ return async_read_internal(h, buf, size);
394 }
395
396 ✗ static int64_t async_seek(URLContext *h, int64_t pos, int whence)
397 {
398 ✗ AsyncContext *c = h->priv_data;
399 ✗ RingBuffer *ring = &c->ring;
400 int64_t ret;
401 int64_t new_logical_pos;
402 int fifo_size;
403 int fifo_size_of_read_back;
404
405 ✗ if (whence == AVSEEK_SIZE) {
406 ✗ av_log(h, AV_LOG_TRACE, "async_seek: AVSEEK_SIZE: %"PRId64"\n", (int64_t)c->logical_size);
407 ✗ return c->logical_size;
408 ✗ } else if (whence == SEEK_CUR) {
409 ✗ av_log(h, AV_LOG_TRACE, "async_seek: %"PRId64"\n", pos);
410 ✗ new_logical_pos = pos + c->logical_pos;
411 ✗ } else if (whence == SEEK_SET){
412 ✗ av_log(h, AV_LOG_TRACE, "async_seek: %"PRId64"\n", pos);
413 ✗ new_logical_pos = pos;
414 } else {
415 ✗ return AVERROR(EINVAL);
416 }
417 ✗ if (new_logical_pos < 0)
418 ✗ return AVERROR(EINVAL);
419
420 ✗ fifo_size = ring_size(ring);
421 ✗ fifo_size_of_read_back = ring_size_of_read_back(ring);
422 ✗ if (new_logical_pos == c->logical_pos) {
423 /* current position */
424 ✗ return c->logical_pos;
425 ✗ } else if ((new_logical_pos >= (c->logical_pos - fifo_size_of_read_back)) &&
426 ✗ (new_logical_pos < (c->logical_pos + fifo_size + SHORT_SEEK_THRESHOLD))) {
427 ✗ int pos_delta = (int)(new_logical_pos - c->logical_pos);
428 /* fast seek */
429 ✗ av_log(h, AV_LOG_TRACE, "async_seek: fask_seek %"PRId64" from %d dist:%d/%d\n",
430 ✗ new_logical_pos, (int)c->logical_pos,
431 ✗ (int)(new_logical_pos - c->logical_pos), fifo_size);
432
433 ✗ if (pos_delta > 0) {
434 // fast seek forwards
435 ✗ async_read_internal(h, NULL, pos_delta);
436 } else {
437 // fast seek backwards
438 ✗ ring_drain(ring, pos_delta);
439 ✗ c->logical_pos = new_logical_pos;
440 }
441
442 ✗ return c->logical_pos;
443 ✗ } else if (c->logical_size <= 0) {
444 /* can not seek */
445 ✗ return AVERROR(EINVAL);
446 ✗ } else if (new_logical_pos > c->logical_size) {
447 /* beyond end */
448 ✗ return AVERROR(EINVAL);
449 }
450
451 ✗ pthread_mutex_lock(&c->mutex);
452
453 ✗ c->seek_request = 1;
454 ✗ c->seek_pos = new_logical_pos;
455 ✗ c->seek_whence = SEEK_SET;
456 ✗ c->seek_completed = 0;
457 ✗ c->seek_ret = 0;
458
459 while (1) {
460 ✗ if (async_check_interrupt(h)) {
461 ✗ ret = AVERROR_EXIT;
462 ✗ break;
463 }
464 ✗ if (c->seek_completed) {
465 ✗ if (c->seek_ret >= 0)
466 ✗ c->logical_pos = c->seek_ret;
467 ✗ ret = c->seek_ret;
468 ✗ break;
469 }
470 ✗ pthread_cond_signal(&c->cond_wakeup_background);
471 ✗ pthread_cond_wait(&c->cond_wakeup_main, &c->mutex);
472 }
473
474 ✗ pthread_mutex_unlock(&c->mutex);
475
476 ✗ return ret;
477 }
478
479 #define OFFSET(x) offsetof(AsyncContext, x)
480 #define D AV_OPT_FLAG_DECODING_PARAM
481
482 static const AVOption options[] = {
483 {NULL},
484 };
485
486 #undef D
487 #undef OFFSET
488
489 static const AVClass async_context_class = {
490 .class_name = "Async",
491 .item_name = av_default_item_name,
492 .option = options,
493 .version = LIBAVUTIL_VERSION_INT,
494 };
495
496 const URLProtocol ff_async_protocol = {
497 .name = "async",
498 .url_open2 = async_open,
499 .url_read = async_read,
500 .url_seek = async_seek,
501 .url_close = async_close,
502 .priv_data_size = sizeof(AsyncContext),
503 .priv_data_class = &async_context_class,
504 };
505
506 #if 0
507
508 #define TEST_SEEK_POS (1536)
509 #define TEST_STREAM_SIZE (2048)
510
511 typedef struct TestContext {
512 AVClass *class;
513 int64_t logical_pos;
514 int64_t logical_size;
515
516 /* options */
517 int opt_read_error;
518 } TestContext;
519
520 static int async_test_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
521 {
522 TestContext *c = h->priv_data;
523 c->logical_pos = 0;
524 c->logical_size = TEST_STREAM_SIZE;
525 return 0;
526 }
527
528 static int async_test_close(URLContext *h)
529 {
530 return 0;
531 }
532
533 static int async_test_read(URLContext *h, unsigned char *buf, int size)
534 {
535 TestContext *c = h->priv_data;
536 int i;
537 int read_len = 0;
538
539 if (c->opt_read_error)
540 return c->opt_read_error;
541
542 if (c->logical_pos >= c->logical_size)
543 return AVERROR_EOF;
544
545 for (i = 0; i < size; ++i) {
546 buf[i] = c->logical_pos & 0xFF;
547
548 c->logical_pos++;
549 read_len++;
550
551 if (c->logical_pos >= c->logical_size)
552 break;
553 }
554
555 return read_len;
556 }
557
558 static int64_t async_test_seek(URLContext *h, int64_t pos, int whence)
559 {
560 TestContext *c = h->priv_data;
561 int64_t new_logical_pos;
562
563 if (whence == AVSEEK_SIZE) {
564 return c->logical_size;
565 } else if (whence == SEEK_CUR) {
566 new_logical_pos = pos + c->logical_pos;
567 } else if (whence == SEEK_SET){
568 new_logical_pos = pos;
569 } else {
570 return AVERROR(EINVAL);
571 }
572 if (new_logical_pos < 0)
573 return AVERROR(EINVAL);
574
575 c->logical_pos = new_logical_pos;
576 return new_logical_pos;
577 }
578
579 #define OFFSET(x) offsetof(TestContext, x)
580 #define D AV_OPT_FLAG_DECODING_PARAM
581
582 static const AVOption async_test_options[] = {
583 { "async-test-read-error", "cause read fail",
584 OFFSET(opt_read_error), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, .flags = D },
585 {NULL},
586 };
587
588 #undef D
589 #undef OFFSET
590
591 static const AVClass async_test_context_class = {
592 .class_name = "Async-Test",
593 .item_name = av_default_item_name,
594 .option = async_test_options,
595 .version = LIBAVUTIL_VERSION_INT,
596 };
597
598 const URLProtocol ff_async_test_protocol = {
599 .name = "async-test",
600 .url_open2 = async_test_open,
601 .url_read = async_test_read,
602 .url_seek = async_test_seek,
603 .url_close = async_test_close,
604 .priv_data_size = sizeof(TestContext),
605 .priv_data_class = &async_test_context_class,
606 };
607
608 int main(void)
609 {
610 URLContext *h = NULL;
611 int i;
612 int ret;
613 int64_t size;
614 int64_t pos;
615 int64_t read_len;
616 unsigned char buf[4096];
617 AVDictionary *opts = NULL;
618
619 ffurl_register_protocol(&ff_async_protocol);
620 ffurl_register_protocol(&ff_async_test_protocol);
621
622 /*
623 * test normal read
624 */
625 ret = ffurl_open_whitelist(&h, "async:async-test:", AVIO_FLAG_READ,
626 NULL, NULL, NULL, NULL, NULL);
627 printf("open: %d\n", ret);
628
629 size = ffurl_size(h);
630 printf("size: %"PRId64"\n", size);
631
632 pos = ffurl_seek(h, 0, SEEK_CUR);
633 read_len = 0;
634 while (1) {
635 ret = ffurl_read(h, buf, sizeof(buf));
636 if (ret == AVERROR_EOF) {
637 printf("read-error: AVERROR_EOF at %"PRId64"\n", ffurl_seek(h, 0, SEEK_CUR));
638 break;
639 }
640 else if (ret == 0)
641 break;
642 else if (ret < 0) {
643 printf("read-error: %d at %"PRId64"\n", ret, ffurl_seek(h, 0, SEEK_CUR));
644 goto fail;
645 } else {
646 for (i = 0; i < ret; ++i) {
647 if (buf[i] != (pos & 0xFF)) {
648 printf("read-mismatch: actual %d, expecting %d, at %"PRId64"\n",
649 (int)buf[i], (int)(pos & 0xFF), pos);
650 break;
651 }
652 pos++;
653 }
654 }
655
656 read_len += ret;
657 }
658 printf("read: %"PRId64"\n", read_len);
659
660 /*
661 * test normal seek
662 */
663 ret = ffurl_read(h, buf, 1);
664 printf("read: %d\n", ret);
665
666 pos = ffurl_seek(h, TEST_SEEK_POS, SEEK_SET);
667 printf("seek: %"PRId64"\n", pos);
668
669 read_len = 0;
670 while (1) {
671 ret = ffurl_read(h, buf, sizeof(buf));
672 if (ret == AVERROR_EOF)
673 break;
674 else if (ret == 0)
675 break;
676 else if (ret < 0) {
677 printf("read-error: %d at %"PRId64"\n", ret, ffurl_seek(h, 0, SEEK_CUR));
678 goto fail;
679 } else {
680 for (i = 0; i < ret; ++i) {
681 if (buf[i] != (pos & 0xFF)) {
682 printf("read-mismatch: actual %d, expecting %d, at %"PRId64"\n",
683 (int)buf[i], (int)(pos & 0xFF), pos);
684 break;
685 }
686 pos++;
687 }
688 }
689
690 read_len += ret;
691 }
692 printf("read: %"PRId64"\n", read_len);
693
694 ret = ffurl_read(h, buf, 1);
695 printf("read: %d\n", ret);
696
697 /*
698 * test read error
699 */
700 ffurl_close(h);
701 av_dict_set_int(&opts, "async-test-read-error", -10000, 0);
702 ret = ffurl_open_whitelist(&h, "async:async-test:", AVIO_FLAG_READ,
703 NULL, &opts, NULL, NULL, NULL);
704 printf("open: %d\n", ret);
705
706 ret = ffurl_read(h, buf, 1);
707 printf("read: %d\n", ret);
708
709 fail:
710 av_dict_free(&opts);
711 ffurl_close(h);
712 return 0;
713 }
714
715 #endif
716