LCOV - code coverage report
Current view: top level - src/libavformat - async.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 227 0.0 %
Date: 2017-01-24 04:42:20 Functions: 0 18 0.0 %

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

Generated by: LCOV version 1.12