]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/aviobuf.c
avformat/avio: Add Metacube support
[ffmpeg] / libavformat / aviobuf.c
index 5a33f82950c32b2a1dee172ce4c61ed97469c51e..dd7b58ff21fd8df35bbc7ae8437333bacb9e7b6f 100644 (file)
 #include "libavutil/log.h"
 #include "libavutil/opt.h"
 #include "libavutil/avassert.h"
+#include "libavutil/thread.h"
 #include "avformat.h"
 #include "avio.h"
 #include "avio_internal.h"
 #include "internal.h"
+#include "metacube2.h"
 #include "url.h"
 #include <stdarg.h>
 
  * data instead of calling the protocol seek function, for seekable
  * protocols.
  */
-#define SHORT_SEEK_THRESHOLD 4096
-
-typedef struct AVIOInternal {
-    URLContext *h;
-} AVIOInternal;
+#define SHORT_SEEK_THRESHOLD 32768
 
 static void *ff_avio_child_next(void *obj, void *prev)
 {
     AVIOContext *s = obj;
-    AVIOInternal *internal = s->opaque;
-    return prev ? NULL : internal->h;
+    return prev ? NULL : s->opaque;
 }
 
-static const AVClass *ff_avio_child_class_next(const AVClass *prev)
+static const AVClass *child_class_iterate(void **iter)
 {
-    return prev ? NULL : &ffurl_context_class;
+    const AVClass *c = *iter ? NULL : &ffurl_context_class;
+    *iter = (void*)(uintptr_t)c;
+    return c;
 }
 
 #define OFFSET(x) offsetof(AVIOContext,x)
 #define E AV_OPT_FLAG_ENCODING_PARAM
 #define D AV_OPT_FLAG_DECODING_PARAM
 static const AVOption ff_avio_options[] = {
-    {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL },  CHAR_MIN, CHAR_MAX, D },
+    {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL },  0, 0, D },
     { NULL },
 };
 
@@ -72,7 +71,7 @@ const AVClass ff_avio_class = {
     .version    = LIBAVUTIL_VERSION_INT,
     .option     = ff_avio_options,
     .child_next = ff_avio_child_next,
-    .child_class_next = ff_avio_child_class_next,
+    .child_class_iterate = child_class_iterate,
 };
 
 static void fill_buffer(AVIOContext *s);
@@ -108,6 +107,7 @@ int ffio_init_context(AVIOContext *s,
     s->seekable        = seek ? AVIO_SEEKABLE_NORMAL : 0;
     s->min_packet_size = 0;
     s->max_packet_size = 0;
+    s->metacube        = 0;
     s->update_checksum = NULL;
     s->short_seek_threshold = SHORT_SEEK_THRESHOLD;
 
@@ -177,10 +177,66 @@ static void writeout(AVIOContext *s, const uint8_t *data, int len)
     s->pos += len;
 }
 
+static AVOnce metacube2_crc_once_control = AV_ONCE_INIT;
+static AVCRC metacube2_crc_table[257];
+
+static void metacube2_crc_init_table_once(void)
+{
+    av_assert0(av_crc_init(metacube2_crc_table, 0, 16, 0x8fdb, sizeof(metacube2_crc_table)) >= 0);
+}
+
+static uint16_t metacube2_compute_crc(const struct metacube2_block_header *hdr)
+{
+    static const int data_len = sizeof(hdr->size) + sizeof(hdr->flags);
+    const uint8_t *data = (uint8_t *)&hdr->size;
+    uint16_t crc;
+
+    ff_thread_once(&metacube2_crc_once_control, metacube2_crc_init_table_once);
+
+    // Metacube2 specifies a CRC start of 0x1234, but its pycrc-derived CRC
+    // includes a finalization step that is done somewhat differently in av_crc().
+    // 0x1234 alone sent through that finalization becomes 0x394a, and then we
+    // need a byte-swap of the CRC value (both on input and output) to account for
+    // differing conventions.
+    crc = av_crc(metacube2_crc_table, 0x4a39, data, data_len);
+    return av_bswap16(crc);
+}
+
+static void finalize_metacube_block_header(AVIOContext *s)
+{
+    struct metacube2_block_header hdr;
+    int len = s->buf_ptr_max - s->buffer;
+    int flags = 0;
+
+    if (s->current_type == AVIO_DATA_MARKER_SYNC_POINT)
+        s->seen_sync_point = 1;
+    else if (s->current_type == AVIO_DATA_MARKER_HEADER)
+        // NOTE: If there are multiple blocks marked METACUBE_FLAGS_HEADER,
+        // only the last one will count. This may become a problem if the
+        // mux flushes halfway through the stream header; if so, we would
+        // need to keep track of and concatenate the different parts.
+        flags |= METACUBE_FLAGS_HEADER;
+    else if (s->seen_sync_point)
+        flags |= METACUBE_FLAGS_NOT_SUITABLE_FOR_STREAM_START;
+
+    memcpy(hdr.sync, METACUBE2_SYNC, sizeof(hdr.sync));
+    AV_WB32(&hdr.size, len - sizeof(hdr));
+    AV_WB16(&hdr.flags, flags);
+    AV_WB16(&hdr.csum, metacube2_compute_crc(&hdr));
+    memcpy(s->buffer, &hdr, sizeof(hdr));
+}
+
 static void flush_buffer(AVIOContext *s)
 {
+    int buffer_empty;
     s->buf_ptr_max = FFMAX(s->buf_ptr, s->buf_ptr_max);
-    if (s->write_flag && s->buf_ptr_max > s->buffer) {
+    if (s->metacube)
+       buffer_empty = s->buf_ptr_max <= s->buffer + sizeof(struct metacube2_block_header);
+    else
+       buffer_empty = s->buf_ptr_max <= s->buffer;
+    if (s->write_flag && !buffer_empty) {
+        if (s->metacube)
+            finalize_metacube_block_header(s);
         writeout(s, s->buffer, s->buf_ptr_max - s->buffer);
         if (s->update_checksum) {
             s->checksum     = s->update_checksum(s->checksum, s->checksum_ptr,
@@ -189,6 +245,10 @@ static void flush_buffer(AVIOContext *s)
         }
     }
     s->buf_ptr = s->buf_ptr_max = s->buffer;
+
+    // Add space for Metacube header.
+    if (s->write_flag && s->metacube)
+        s->buf_ptr += sizeof(struct metacube2_block_header);
     if (!s->write_flag)
         s->buf_end = s->buffer;
 }
@@ -217,7 +277,7 @@ void ffio_fill(AVIOContext *s, int b, int count)
 
 void avio_write(AVIOContext *s, const unsigned char *buf, int size)
 {
-    if (s->direct && !s->update_checksum) {
+    if (s->direct && !s->update_checksum && !s->metacube) {
         avio_flush(s);
         writeout(s, buf, size);
         return;
@@ -255,6 +315,9 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
     if(!s)
         return AVERROR(EINVAL);
 
+    if ((whence & AVSEEK_SIZE))
+        return s->seek ? s->seek(s->opaque, offset, AVSEEK_SIZE) : AVERROR(ENOSYS);
+
     buffer_size = s->buf_end - s->buffer;
     // pos is the absolute position that the beginning of s->buffer corresponds to in the file
     pos = s->pos - (s->write_flag ? 0 : buffer_size);
@@ -264,22 +327,24 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
 
     if (whence == SEEK_CUR) {
         offset1 = pos + (s->buf_ptr - s->buffer);
-        if (offset == 0)
-            return offset1;
+        if (offset == 0) {
+            if (s->metacube && s->write_flag)
+                return offset1 - sizeof(struct metacube2_block_header);
+            else
+                return offset1;
+        }
         if (offset > INT64_MAX - offset1)
             return AVERROR(EINVAL);
         offset += offset1;
+    } else if (s->metacube && s->write_flag) {
+        offset += sizeof(struct metacube2_block_header);
     }
     if (offset < 0)
         return AVERROR(EINVAL);
 
-    if (s->short_seek_get) {
-        short_seek = s->short_seek_get(s->opaque);
-        /* fallback to default short seek */
-        if (short_seek <= 0)
-            short_seek = s->short_seek_threshold;
-    } else
-        short_seek = s->short_seek_threshold;
+    short_seek = s->short_seek_threshold;
+    if (s->short_seek_get)
+        short_seek = FFMAX(s->short_seek_get(s->opaque), short_seek);
 
     offset1 = offset - pos; // "offset1" is the relative offset from the beginning of s->buffer
     s->buf_ptr_max = FFMAX(s->buf_ptr_max, s->buf_ptr);
@@ -325,7 +390,10 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
         s->pos = offset;
     }
     s->eof_reached = 0;
-    return offset;
+    if (s->metacube && s->write_flag)
+        return offset - sizeof(struct metacube2_block_header);
+    else
+        return offset;
 }
 
 int64_t avio_skip(AVIOContext *s, int64_t offset)
@@ -434,26 +502,6 @@ PUT_STR16(be, 1)
 
 #undef PUT_STR16
 
-int ff_get_v_length(uint64_t val)
-{
-    int i = 1;
-
-    while (val >>= 7)
-        i++;
-
-    return i;
-}
-
-void ff_put_v(AVIOContext *bc, uint64_t val)
-{
-    int i = ff_get_v_length(val);
-
-    while (--i > 0)
-        avio_w8(bc, 128 | (uint8_t)(val >> (7*i)));
-
-    avio_w8(bc, val & 127);
-}
-
 void avio_wl64(AVIOContext *s, uint64_t val)
 {
     avio_wl32(s, (uint32_t)(val & 0xffffffff));
@@ -497,7 +545,7 @@ void avio_write_marker(AVIOContext *s, int64_t time, enum AVIODataMarkerType typ
             avio_flush(s);
         return;
     }
-    if (!s->write_data_type)
+    if (!s->write_data_type && !s->metacube)
         return;
     // If ignoring boundary points, just treat it as unknown
     if (type == AVIO_DATA_MARKER_BOUNDARY_POINT && s->ignore_boundary_point)
@@ -533,14 +581,7 @@ static int read_packet_wrapper(AVIOContext *s, uint8_t *buf, int size)
     if (!s->read_packet)
         return AVERROR(EINVAL);
     ret = s->read_packet(s->opaque, buf, size);
-#if FF_API_OLD_AVIO_EOF_0
-    if (!ret && !s->max_packet_size) {
-        av_log(NULL, AV_LOG_WARNING, "Invalid return value 0 for stream protocol\n");
-        ret = AVERROR_EOF;
-    }
-#else
     av_assert2(ret || s->max_packet_size);
-#endif
     return ret;
 }
 
@@ -550,7 +591,7 @@ static void fill_buffer(AVIOContext *s)
 {
     int max_buffer_size = s->max_packet_size ?
                           s->max_packet_size : IO_BUFFER_SIZE;
-    uint8_t *dst        = s->buf_end - s->buffer + max_buffer_size < s->buffer_size ?
+    uint8_t *dst        = s->buf_end - s->buffer + max_buffer_size <= s->buffer_size ?
                           s->buf_end : s->buffer;
     int len             = s->buffer_size - (dst - s->buffer);
 
@@ -570,7 +611,7 @@ static void fill_buffer(AVIOContext *s)
     }
 
     /* make buffer smaller in case it ended up large after probing */
-    if (s->read_packet && s->orig_buffer_size && s->buffer_size > s->orig_buffer_size) {
+    if (s->read_packet && s->orig_buffer_size && s->buffer_size > s->orig_buffer_size && len >= s->orig_buffer_size) {
         if (dst == s->buffer && s->buf_ptr != dst) {
             int ret = ffio_set_buf_size(s, s->orig_buffer_size);
             if (ret < 0)
@@ -578,7 +619,6 @@ static void fill_buffer(AVIOContext *s)
 
             s->checksum_ptr = dst = s->buffer;
         }
-        av_assert0(len >= s->orig_buffer_size);
         len = s->orig_buffer_size;
     }
 
@@ -697,9 +737,11 @@ int avio_read(AVIOContext *s, unsigned char *buf, int size)
 int ffio_read_size(AVIOContext *s, unsigned char *buf, int size)
 {
     int ret = avio_read(s, buf, size);
-    if (ret != size)
-        return AVERROR_INVALIDDATA;
-    return ret;
+    if (ret == size)
+        return ret;
+    if (ret < 0 && ret != AVERROR_EOF)
+        return ret;
+    return AVERROR_INVALIDDATA;
 }
 
 int ffio_read_indirect(AVIOContext *s, unsigned char *buf, int size, const unsigned char **data)
@@ -719,7 +761,7 @@ int avio_read_partial(AVIOContext *s, unsigned char *buf, int size)
     int len;
 
     if (size < 0)
-        return -1;
+        return AVERROR(EINVAL);
 
     if (s->read_packet && s->write_flag) {
         len = read_packet_wrapper(s, buf, size);
@@ -730,13 +772,6 @@ int avio_read_partial(AVIOContext *s, unsigned char *buf, int size)
 
     len = s->buf_end - s->buf_ptr;
     if (len == 0) {
-        /* Reset the buf_end pointer to the start of the buffer, to make sure
-         * the fill_buffer call tries to read as much data as fits into the
-         * full buffer, instead of just what space is left after buf_end.
-         * This avoids returning partial packets at the end of the buffer,
-         * for packet based inputs.
-         */
-        s->buf_end = s->buf_ptr = s->buffer;
         fill_buffer(s);
         len = s->buf_end - s->buf_ptr;
     }
@@ -938,49 +973,8 @@ uint64_t ffio_read_varlen(AVIOContext *bc){
     return val;
 }
 
-static int io_read_packet(void *opaque, uint8_t *buf, int buf_size)
-{
-    AVIOInternal *internal = opaque;
-    return ffurl_read(internal->h, buf, buf_size);
-}
-
-static int io_write_packet(void *opaque, uint8_t *buf, int buf_size)
-{
-    AVIOInternal *internal = opaque;
-    return ffurl_write(internal->h, buf, buf_size);
-}
-
-static int64_t io_seek(void *opaque, int64_t offset, int whence)
-{
-    AVIOInternal *internal = opaque;
-    return ffurl_seek(internal->h, offset, whence);
-}
-
-static int io_short_seek(void *opaque)
-{
-    AVIOInternal *internal = opaque;
-    return ffurl_get_short_seek(internal->h);
-}
-
-static int io_read_pause(void *opaque, int pause)
-{
-    AVIOInternal *internal = opaque;
-    if (!internal->h->prot->url_read_pause)
-        return AVERROR(ENOSYS);
-    return internal->h->prot->url_read_pause(internal->h, pause);
-}
-
-static int64_t io_read_seek(void *opaque, int stream_index, int64_t timestamp, int flags)
-{
-    AVIOInternal *internal = opaque;
-    if (!internal->h->prot->url_read_seek)
-        return AVERROR(ENOSYS);
-    return internal->h->prot->url_read_seek(internal->h, stream_index, timestamp, flags);
-}
-
 int ffio_fdopen(AVIOContext **s, URLContext *h)
 {
-    AVIOInternal *internal = NULL;
     uint8_t *buffer = NULL;
     int buffer_size, max_packet_size;
 
@@ -990,18 +984,19 @@ int ffio_fdopen(AVIOContext **s, URLContext *h)
     } else {
         buffer_size = IO_BUFFER_SIZE;
     }
+    if (!(h->flags & AVIO_FLAG_WRITE) && h->is_streamed) {
+        if (buffer_size > INT_MAX/2)
+            return AVERROR(EINVAL);
+        buffer_size *= 2;
+    }
     buffer = av_malloc(buffer_size);
     if (!buffer)
         return AVERROR(ENOMEM);
 
-    internal = av_mallocz(sizeof(*internal));
-    if (!internal)
-        goto fail;
-
-    internal->h = h;
-
-    *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE,
-                            internal, io_read_packet, io_write_packet, io_seek);
+    *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h,
+                            (int (*)(void *, uint8_t *, int))  ffurl_read,
+                            (int (*)(void *, uint8_t *, int))  ffurl_write,
+                            (int64_t (*)(void *, int64_t, int))ffurl_seek);
     if (!*s)
         goto fail;
 
@@ -1021,76 +1016,138 @@ int ffio_fdopen(AVIOContext **s, URLContext *h)
     (*s)->max_packet_size = max_packet_size;
     (*s)->min_packet_size = h->min_packet_size;
     if(h->prot) {
-        (*s)->read_pause = io_read_pause;
-        (*s)->read_seek  = io_read_seek;
+        (*s)->read_pause = (int (*)(void *, int))h->prot->url_read_pause;
+        (*s)->read_seek  =
+            (int64_t (*)(void *, int, int64_t, int))h->prot->url_read_seek;
 
         if (h->prot->url_read_seek)
             (*s)->seekable |= AVIO_SEEKABLE_TIME;
     }
-    (*s)->short_seek_get = io_short_seek;
+    (*s)->short_seek_get = (int (*)(void *))ffurl_get_short_seek;
     (*s)->av_class = &ff_avio_class;
+    (*s)->metacube = h->flags & AVIO_FLAG_METACUBE;
+    (*s)->seen_sync_point = 0;
     return 0;
 fail:
-    av_freep(&internal);
     av_freep(&buffer);
     return AVERROR(ENOMEM);
 }
 
 URLContext* ffio_geturlcontext(AVIOContext *s)
 {
-    AVIOInternal *internal;
     if (!s)
         return NULL;
 
-    internal = s->opaque;
-    if (internal && s->read_packet == io_read_packet)
-        return internal->h;
+    if (s->opaque && s->read_packet == (int (*)(void *, uint8_t *, int))ffurl_read)
+        return s->opaque;
     else
         return NULL;
 }
 
+static void update_checksum(AVIOContext *s)
+{
+    if (s->update_checksum && s->buf_ptr > s->checksum_ptr) {
+        s->checksum = s->update_checksum(s->checksum, s->checksum_ptr,
+                                         s->buf_ptr - s->checksum_ptr);
+    }
+}
+
 int ffio_ensure_seekback(AVIOContext *s, int64_t buf_size)
 {
     uint8_t *buffer;
     int max_buffer_size = s->max_packet_size ?
                           s->max_packet_size : IO_BUFFER_SIZE;
-    int filled = s->buf_end - s->buffer;
-    ptrdiff_t checksum_ptr_offset = s->checksum_ptr ? s->checksum_ptr - s->buffer : -1;
+    ptrdiff_t filled = s->buf_end - s->buf_ptr;
+
+    if (buf_size <= s->buf_end - s->buf_ptr)
+        return 0;
 
-    buf_size += s->buf_ptr - s->buffer + max_buffer_size;
+    buf_size += max_buffer_size - 1;
 
-    if (buf_size < filled || s->seekable || !s->read_packet)
+    if (buf_size + s->buf_ptr - s->buffer <= s->buffer_size || s->seekable || !s->read_packet)
         return 0;
     av_assert0(!s->write_flag);
 
+    if (buf_size <= s->buffer_size) {
+        update_checksum(s);
+        memmove(s->buffer, s->buf_ptr, filled);
+    } else {
+        buffer = av_malloc(buf_size);
+        if (!buffer)
+            return AVERROR(ENOMEM);
+        update_checksum(s);
+        memcpy(buffer, s->buf_ptr, filled);
+        av_free(s->buffer);
+        s->buffer = buffer;
+        s->buffer_size = buf_size;
+    }
+    s->buf_ptr = s->buffer;
+    s->buf_end = s->buffer + filled;
+    s->checksum_ptr = s->buffer;
+    return 0;
+}
+
+int ffio_set_buf_size(AVIOContext *s, int buf_size)
+{
+    uint8_t *buffer;
+
+    if (s->metacube)
+        buf_size += sizeof(struct metacube2_block_header);
+
     buffer = av_malloc(buf_size);
     if (!buffer)
         return AVERROR(ENOMEM);
 
-    memcpy(buffer, s->buffer, filled);
     av_free(s->buffer);
-    s->buf_ptr = buffer + (s->buf_ptr - s->buffer);
-    s->buf_end = buffer + (s->buf_end - s->buffer);
     s->buffer = buffer;
+    s->orig_buffer_size =
     s->buffer_size = buf_size;
-    if (checksum_ptr_offset >= 0)
-        s->checksum_ptr = s->buffer + checksum_ptr_offset;
+    s->buf_ptr = s->buf_ptr_max = buffer;
+
+    // Add space for Metacube header.
+    if (s->metacube)
+        s->buf_ptr += sizeof(struct metacube2_block_header);
+
+    url_resetbuf(s, s->write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ);
     return 0;
 }
 
-int ffio_set_buf_size(AVIOContext *s, int buf_size)
+int ffio_realloc_buf(AVIOContext *s, int buf_size)
 {
     uint8_t *buffer;
+    int data_size;
+
+    if (s->metacube && s->write_flag)
+        buf_size += sizeof(struct metacube2_block_header);
+
+    if (!s->buffer_size)
+        return ffio_set_buf_size(s, buf_size);
+
+    if (buf_size <= s->buffer_size)
+        return 0;
+
     buffer = av_malloc(buf_size);
     if (!buffer)
         return AVERROR(ENOMEM);
 
+    data_size = s->write_flag ? (s->buf_ptr - s->buffer) : (s->buf_end - s->buf_ptr);
+    if (data_size > 0)
+        memcpy(buffer, s->write_flag ? s->buffer : s->buf_ptr, data_size);
     av_free(s->buffer);
     s->buffer = buffer;
-    s->orig_buffer_size =
+    s->orig_buffer_size = buf_size;
     s->buffer_size = buf_size;
-    s->buf_ptr = s->buf_ptr_max = buffer;
-    url_resetbuf(s, s->write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ);
+    s->buf_ptr = s->write_flag ? (s->buffer + data_size) : s->buffer;
+
+    // Add space for Metacube header.
+    if (s->metacube && s->write_flag && data_size == 0)
+        s->buf_ptr += sizeof(struct metacube2_block_header);
+
+    if (s->write_flag)
+        s->buf_ptr_max = s->buffer + data_size;
+
+    s->buf_end = s->write_flag ? (s->buffer + s->buffer_size) : (s->buf_ptr + data_size);
+
     return 0;
 }
 
@@ -1164,6 +1221,8 @@ int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,
     URLContext *h;
     int err;
 
+    *s = NULL;
+
     err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
     if (err < 0)
         return err;
@@ -1181,25 +1240,17 @@ int avio_open2(AVIOContext **s, const char *filename, int flags,
     return ffio_open_whitelist(s, filename, flags, int_cb, options, NULL, NULL);
 }
 
-int ffio_open2_wrapper(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags,
-                       const AVIOInterruptCB *int_cb, AVDictionary **options)
-{
-    return ffio_open_whitelist(pb, url, flags, int_cb, options, s->protocol_whitelist, s->protocol_blacklist);
-}
-
 int avio_close(AVIOContext *s)
 {
-    AVIOInternal *internal;
     URLContext *h;
 
     if (!s)
         return 0;
 
     avio_flush(s);
-    internal = s->opaque;
-    h        = internal->h;
+    h         = s->opaque;
+    s->opaque = NULL;
 
-    av_freep(&s->opaque);
     av_freep(&s->buffer);
     if (s->write_flag)
         av_log(s, AV_LOG_VERBOSE, "Statistics: %d seeks, %d writeouts\n", s->seek_count, s->writeout_count);
@@ -1222,14 +1273,26 @@ int avio_closep(AVIOContext **s)
 int avio_printf(AVIOContext *s, const char *fmt, ...)
 {
     va_list ap;
-    char buf[4096]; /* update doc entry in avio.h if changed */
-    int ret;
+    AVBPrint bp;
 
+    av_bprint_init(&bp, 0, INT_MAX);
     va_start(ap, fmt);
-    ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+    av_vbprintf(&bp, fmt, ap);
     va_end(ap);
-    avio_write(s, buf, strlen(buf));
-    return ret;
+    if (!av_bprint_is_complete(&bp)) {
+        av_bprint_finalize(&bp, NULL);
+        s->error = AVERROR(ENOMEM);
+        return AVERROR(ENOMEM);
+    }
+    avio_write(s, bp.str, bp.len);
+    av_bprint_finalize(&bp, NULL);
+    return bp.len;
+}
+
+void avio_print_string_array(AVIOContext *s, const char *strings[])
+{
+    for(; *strings; strings++)
+        avio_write(s, (const unsigned char *)*strings, strlen(*strings));
 }
 
 int avio_pause(AVIOContext *s, int pause)
@@ -1279,8 +1342,7 @@ int avio_read_to_bprint(AVIOContext *h, AVBPrint *pb, size_t max_size)
 int avio_accept(AVIOContext *s, AVIOContext **c)
 {
     int ret;
-    AVIOInternal *internal = s->opaque;
-    URLContext *sc = internal->h;
+    URLContext *sc = s->opaque;
     URLContext *cc = NULL;
     ret = ffurl_accept(sc, &cc);
     if (ret < 0)
@@ -1290,8 +1352,7 @@ int avio_accept(AVIOContext *s, AVIOContext **c)
 
 int avio_handshake(AVIOContext *c)
 {
-    AVIOInternal *internal = c->opaque;
-    URLContext *cc = internal->h;
+    URLContext *cc = c->opaque;
     return ffurl_handshake(cc);
 }
 
@@ -1307,22 +1368,21 @@ typedef struct DynBuffer {
 static int dyn_buf_write(void *opaque, uint8_t *buf, int buf_size)
 {
     DynBuffer *d = opaque;
-    unsigned new_size, new_allocated_size;
+    unsigned new_size;
 
     /* reallocate buffer if needed */
-    new_size = d->pos + buf_size;
-    new_allocated_size = d->allocated_size;
-    if (new_size < d->pos || new_size > INT_MAX/2)
-        return -1;
-    while (new_size > new_allocated_size) {
-        if (!new_allocated_size)
-            new_allocated_size = new_size;
-        else
+    new_size = (unsigned)d->pos + buf_size;
+    if (new_size < d->pos || new_size > INT_MAX)
+        return AVERROR(ERANGE);
+    if (new_size > d->allocated_size) {
+        unsigned new_allocated_size = d->allocated_size ? d->allocated_size
+                                                        : new_size;
+        int err;
+        while (new_size > new_allocated_size)
             new_allocated_size += new_allocated_size / 2 + 1;
-    }
 
-    if (new_allocated_size > d->allocated_size) {
-        int err;
+        new_allocated_size = FFMIN(new_allocated_size, INT_MAX);
+
         if ((err = av_reallocp(&d->buffer, new_allocated_size)) < 0) {
             d->allocated_size = 0;
             d->size = 0;
@@ -1360,8 +1420,10 @@ static int64_t dyn_buf_seek(void *opaque, int64_t offset, int whence)
         offset += d->pos;
     else if (whence == SEEK_END)
         offset += d->size;
-    if (offset < 0 || offset > 0x7fffffffLL)
-        return -1;
+    if (offset < 0)
+        return AVERROR(EINVAL);
+    if (offset > INT_MAX)
+        return AVERROR(ERANGE);
     d->pos = offset;
     return 0;
 }
@@ -1372,7 +1434,7 @@ static int url_open_dyn_buf_internal(AVIOContext **s, int max_packet_size)
     unsigned io_buffer_size = max_packet_size ? max_packet_size : 1024;
 
     if (sizeof(DynBuffer) + io_buffer_size < io_buffer_size)
-        return -1;
+        return AVERROR(ERANGE);
     d = av_mallocz(sizeof(DynBuffer) + io_buffer_size);
     if (!d)
         return AVERROR(ENOMEM);
@@ -1396,7 +1458,7 @@ int avio_open_dyn_buf(AVIOContext **s)
 int ffio_open_dyn_packet_buf(AVIOContext **s, int max_packet_size)
 {
     if (max_packet_size <= 0)
-        return -1;
+        return AVERROR(EINVAL);
     return url_open_dyn_buf_internal(s, max_packet_size);
 }
 
@@ -1408,15 +1470,31 @@ int avio_get_dyn_buf(AVIOContext *s, uint8_t **pbuffer)
         *pbuffer = NULL;
         return 0;
     }
+    d = s->opaque;
+
+    if (!s->error && !d->size) {
+        *pbuffer = d->io_buffer;
+        return FFMAX(s->buf_ptr, s->buf_ptr_max) - s->buffer;
+    }
 
     avio_flush(s);
 
-    d = s->opaque;
     *pbuffer = d->buffer;
 
     return d->size;
 }
 
+void ffio_reset_dyn_buf(AVIOContext *s)
+{
+    DynBuffer *d = s->opaque;
+    int max_packet_size = s->max_packet_size;
+
+    ffio_init_context(s, d->io_buffer, d->io_buffer_size, 1, d, NULL,
+                      s->write_packet, s->seek);
+    s->max_packet_size = max_packet_size;
+    d->pos = d->size = 0;
+}
+
 int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer)
 {
     DynBuffer *d;
@@ -1449,12 +1527,15 @@ int avio_close_dyn_buf(AVIOContext *s, uint8_t **pbuffer)
 
 void ffio_free_dyn_buf(AVIOContext **s)
 {
-    uint8_t *tmp;
+    DynBuffer *d;
+
     if (!*s)
         return;
-    avio_close_dyn_buf(*s, &tmp);
-    av_free(tmp);
-    *s = NULL;
+
+    d = (*s)->opaque;
+    av_free(d->buffer);
+    av_free(d);
+    avio_context_free(s);
 }
 
 static int null_buf_write(void *opaque, uint8_t *buf, int buf_size)