[FFmpeg-devel] [PATCH 1/7] lavc/flac_parser: use a custom FIFO implementation

Anton Khirnov anton at khirnov.net
Fri Dec 31 12:53:01 EET 2021


FLAC parser currently uses AVFifoBuffer in a highly non-trivial manner,
modifying its "internals" (the whole struct is currently public, but no
other code touches its contents directly). E.g. it does not use any
av_fifo functions for reading the FIFO contents, but implements its own.

Reimplement the needed parts of the AVFifoBuffer API in the FLAC parser,
making it completely self-contained. This will allow us to make
AVFifoBuffer private.
---
 libavcodec/flac_parser.c | 191 +++++++++++++++++++++++++++++++--------
 1 file changed, 153 insertions(+), 38 deletions(-)

diff --git a/libavcodec/flac_parser.c b/libavcodec/flac_parser.c
index 3b27b152fc..d6af5ce836 100644
--- a/libavcodec/flac_parser.c
+++ b/libavcodec/flac_parser.c
@@ -34,7 +34,6 @@
 
 #include "libavutil/attributes.h"
 #include "libavutil/crc.h"
-#include "libavutil/fifo.h"
 #include "bytestream.h"
 #include "parser.h"
 #include "flac.h"
@@ -57,6 +56,15 @@
 #define MAX_FRAME_HEADER_SIZE 16
 #define MAX_FRAME_VERIFY_SIZE (MAX_FRAME_HEADER_SIZE + 1)
 
+typedef struct FifoBuffer {
+    uint8_t *buffer;
+    uint8_t *end;
+    uint8_t *rptr;
+    uint8_t *wptr;
+    size_t rndx;
+    size_t wndx;
+} FifoBuffer;
+
 typedef struct FLACHeaderMarker {
     int offset;       /**< byte offset from start of FLACParseContext->buffer */
     int link_penalty[FLAC_MAX_SEQUENTIAL_HEADERS]; /**< array of local scores
@@ -84,7 +92,7 @@ typedef struct FLACParseContext {
     int nb_headers_buffered;       /**< number of headers that are buffered   */
     int best_header_valid;         /**< flag set when the parser returns junk;
                                         if set return best_header next time   */
-    AVFifoBuffer *fifo_buf;        /**< buffer to store all data until headers
+    FifoBuffer fifo_buf;           /**< buffer to store all data until headers
                                         can be verified                       */
     int end_padded;                /**< specifies if fifo_buf's end is padded */
     uint8_t *wrap_buf;             /**< general fifo read buffer when wrapped */
@@ -127,6 +135,17 @@ static int frame_header_is_valid(AVCodecContext *avctx, const uint8_t *buf,
     return 1;
 }
 
+static size_t flac_fifo_size(FifoBuffer *f)
+{
+    av_assert0(f->wndx >= f->rndx);
+    return f->wndx - f->rndx;
+}
+
+static size_t flac_fifo_space(FifoBuffer *f)
+{
+    return f->end - f->buffer - flac_fifo_size(f);
+}
+
 /**
  * Non-destructive fast fifo pointer fetching
  * Returns a pointer from the specified offset.
@@ -143,7 +162,7 @@ static int frame_header_is_valid(AVCodecContext *avctx, const uint8_t *buf,
 static uint8_t *flac_fifo_read_wrap(FLACParseContext *fpc, int offset, int len,
                                     uint8_t **wrap_buf, int *allocated_size)
 {
-    AVFifoBuffer *f   = fpc->fifo_buf;
+    FifoBuffer   *f   = &fpc->fifo_buf;
     uint8_t *start    = f->rptr + offset;
     uint8_t *tmp_buf;
 
@@ -180,9 +199,8 @@ static uint8_t *flac_fifo_read_wrap(FLACParseContext *fpc, int offset, int len,
  * A second call to flac_fifo_read (with new offset and len) should be called
  * to get the post-wrap buf if the returned len is less than the requested.
  **/
-static uint8_t *flac_fifo_read(FLACParseContext *fpc, int offset, int *len)
+static uint8_t *flac_fifo_read(FifoBuffer *f, int offset, int *len)
 {
-    AVFifoBuffer *f   = fpc->fifo_buf;
     uint8_t *start    = f->rptr + offset;
 
     if (start >= f->end)
@@ -191,6 +209,106 @@ static uint8_t *flac_fifo_read(FLACParseContext *fpc, int offset, int *len)
     return start;
 }
 
+static int flac_fifo_grow(FifoBuffer *f, size_t inc)
+{
+    size_t size_old = f->end - f->buffer;
+    size_t offset_r = f->rptr - f->buffer;
+    size_t offset_w = f->wptr - f->buffer;
+    size_t size_new;
+
+    uint8_t *tmp;
+
+    if (size_old > SIZE_MAX - inc)
+        return AVERROR(EINVAL);
+    size_new = size_old + inc;
+
+    tmp = av_realloc(f->buffer, size_new);
+    if (!tmp)
+        return AVERROR(ENOMEM);
+
+    // move the data from the beginning of the ring buffer
+    // to the newly allocated space
+    // the second condition distinguishes full vs empty fifo
+    if (offset_w <= offset_r && flac_fifo_size(f)) {
+        const size_t copy = FFMIN(inc, offset_w);
+        memcpy(tmp + size_old, tmp, copy);
+        if (copy < offset_w) {
+            memmove(tmp, tmp + copy, offset_w - copy);
+            offset_w -= copy;
+        } else
+            offset_w = size_old + copy;
+    }
+
+    f->buffer = tmp;
+    f->end    = f->buffer + size_new;
+    f->rptr   = f->buffer + offset_r;
+    f->wptr   = f->buffer + offset_w;
+
+    return 0;
+}
+
+static int flac_fifo_write(FifoBuffer *f, const uint8_t *src, size_t size)
+{
+    uint32_t wndx = f->wndx;
+    uint8_t *wptr;
+
+    if (flac_fifo_space(f) < size) {
+        int ret = flac_fifo_grow(f, FFMAX(flac_fifo_size(f), size));
+        if (ret < 0)
+            return ret;
+    }
+
+    wptr = f->wptr;
+    do {
+        size_t len = FFMIN(f->end - wptr, size);
+        memcpy(wptr, src, len);
+        src  += len;
+        wptr += len;
+        if (wptr >= f->end)
+            wptr = f->buffer;
+        wndx    += len;
+        size    -= len;
+    } while (size > 0);
+
+    f->wndx = wndx;
+    f->wptr = wptr;
+
+    return 0;
+}
+
+static void flac_fifo_drain(FifoBuffer *f, size_t size)
+{
+    av_assert0(flac_fifo_size(f) >= size);
+    f->rptr += size;
+    if (f->rptr >= f->end)
+        f->rptr -= f->end - f->buffer;
+    f->rndx += size;
+}
+
+static int flac_fifo_alloc(FifoBuffer *f, size_t size)
+{
+    memset(f, 0, sizeof(*f));
+
+    f->buffer = av_realloc(NULL, size);
+    if (!f->buffer)
+        return AVERROR(ENOMEM);
+
+    f->wptr = f->buffer;
+    f->rptr = f->buffer;
+    f->end  = f->buffer + size;
+
+    f->rndx = 0;
+    f->wndx = 0;
+
+    return 0;
+}
+
+static void flac_fifo_free(FifoBuffer *f)
+{
+    av_freep(&f->buffer);
+    memset(f, 0, sizeof(*f));
+}
+
 static int find_headers_search_validate(FLACParseContext *fpc, int offset)
 {
     FLACFrameInfo fi;
@@ -263,9 +381,9 @@ static int find_new_headers(FLACParseContext *fpc, int search_start)
     fpc->nb_headers_found = 0;
 
     /* Search for a new header of at most 16 bytes. */
-    search_end = av_fifo_size(fpc->fifo_buf) - (MAX_FRAME_HEADER_SIZE - 1);
+    search_end = flac_fifo_size(&fpc->fifo_buf) - (MAX_FRAME_HEADER_SIZE - 1);
     read_len   = search_end - search_start + 1;
-    buf        = flac_fifo_read(fpc, search_start, &read_len);
+    buf        = flac_fifo_read(&fpc->fifo_buf, search_start, &read_len);
     size       = find_headers_search(fpc, buf, read_len, search_start);
     search_start += read_len - 1;
 
@@ -277,7 +395,7 @@ static int find_new_headers(FLACParseContext *fpc, int search_start)
         /* search_start + 1 is the post-wrap offset in the fifo. */
         read_len = search_end - (search_start + 1) + 1;
 
-        buf      = flac_fifo_read(fpc, search_start + 1, &read_len);
+        buf      = flac_fifo_read(&fpc->fifo_buf, search_start + 1, &read_len);
         wrap[1]  = buf[0];
 
         if ((AV_RB16(wrap) & 0xFFFE) == 0xFFF8) {
@@ -406,12 +524,12 @@ static int check_header_mismatch(FLACParseContext  *fpc,
             }
 
             read_len = end->offset - start->offset;
-            buf      = flac_fifo_read(fpc, start->offset, &read_len);
+            buf      = flac_fifo_read(&fpc->fifo_buf, start->offset, &read_len);
             crc      = av_crc(av_crc_get_table(AV_CRC_16_ANSI), 0, buf, read_len);
             read_len = (end->offset - start->offset) - read_len;
 
             if (read_len) {
-                buf = flac_fifo_read(fpc, end->offset - read_len, &read_len);
+                buf = flac_fifo_read(&fpc->fifo_buf, end->offset - read_len, &read_len);
                 crc = av_crc(av_crc_get_table(AV_CRC_16_ANSI), crc, buf, read_len);
             }
         }
@@ -500,7 +618,7 @@ static int get_best_header(FLACParseContext *fpc, const uint8_t **poutbuf,
     FLACHeaderMarker *header = fpc->best_header;
     FLACHeaderMarker *child  = header->best_child;
     if (!child) {
-        *poutbuf_size = av_fifo_size(fpc->fifo_buf) - header->offset;
+        *poutbuf_size = flac_fifo_size(&fpc->fifo_buf) - header->offset;
     } else {
         *poutbuf_size = child->offset - header->offset;
 
@@ -519,7 +637,6 @@ static int get_best_header(FLACParseContext *fpc, const uint8_t **poutbuf,
                                         &fpc->wrap_buf,
                                         &fpc->wrap_buf_allocated_size);
 
-
     if (fpc->pc->flags & PARSER_FLAG_USE_CODEC_TS) {
         if (header->fi.is_var_size)
           fpc->pc->pts = header->fi.frame_or_sample_num;
@@ -534,7 +651,7 @@ static int get_best_header(FLACParseContext *fpc, const uint8_t **poutbuf,
     /* Return the negative overread index so the client can compute pos.
        This should be the amount overread to the beginning of the child */
     if (child)
-        return child->offset - av_fifo_size(fpc->fifo_buf);
+        return child->offset - flac_fifo_size(&fpc->fifo_buf);
     return 0;
 }
 
@@ -586,7 +703,7 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
             fpc->nb_headers_buffered--;
         }
         /* Release returned data from ring buffer. */
-        av_fifo_drain(fpc->fifo_buf, best_child->offset);
+        flac_fifo_drain(&fpc->fifo_buf, best_child->offset);
 
         /* Fix the offset for the headers remaining to match the new buffer. */
         for (curr = best_child->next; curr; curr = curr->next)
@@ -620,7 +737,7 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
     while ((buf_size && read_end < buf + buf_size &&
             fpc->nb_headers_buffered < FLAC_MIN_HEADERS)
            || (!buf_size && !fpc->end_padded)) {
-        int start_offset;
+        int start_offset, ret;
 
         /* Pad the end once if EOF, to check the final region for headers. */
         if (!buf_size) {
@@ -634,8 +751,8 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
                                               nb_desired * FLAC_AVG_FRAME_SIZE);
         }
 
-        if (!av_fifo_space(fpc->fifo_buf) &&
-            av_fifo_size(fpc->fifo_buf) / FLAC_AVG_FRAME_SIZE >
+        if (!flac_fifo_space(&fpc->fifo_buf) &&
+            flac_fifo_size(&fpc->fifo_buf) / FLAC_AVG_FRAME_SIZE >
             fpc->nb_headers_buffered * 20) {
             /* There is less than one valid flac header buffered for 20 headers
              * buffered. Therefore the fifo is most likely filled with invalid
@@ -644,24 +761,20 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
         }
 
         /* Fill the buffer. */
-        if (   av_fifo_space(fpc->fifo_buf) < read_end - read_start
-            && av_fifo_realloc2(fpc->fifo_buf, (read_end - read_start) + 2*av_fifo_size(fpc->fifo_buf)) < 0) {
-            av_log(avctx, AV_LOG_ERROR,
-                   "couldn't reallocate buffer of size %"PTRDIFF_SPECIFIER"\n",
-                   (read_end - read_start) + av_fifo_size(fpc->fifo_buf));
-            goto handle_error;
-        }
-
         if (buf_size) {
-            av_fifo_generic_write(fpc->fifo_buf, (void*) read_start,
-                                  read_end - read_start, NULL);
+            ret = flac_fifo_write(&fpc->fifo_buf, read_start,
+                                  read_end - read_start);
         } else {
             int8_t pad[MAX_FRAME_HEADER_SIZE] = { 0 };
-            av_fifo_generic_write(fpc->fifo_buf, pad, sizeof(pad), NULL);
+            ret = flac_fifo_write(&fpc->fifo_buf, pad, sizeof(pad));
+        }
+        if (ret < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Error buffering data\n");
+            goto handle_error;
         }
 
         /* Tag headers and update sequences. */
-        start_offset = av_fifo_size(fpc->fifo_buf) -
+        start_offset = flac_fifo_size(&fpc->fifo_buf) -
                        ((read_end - read_start) + (MAX_FRAME_HEADER_SIZE - 1));
         start_offset = FFMAX(0, start_offset);
         nb_headers   = find_new_headers(fpc, start_offset);
@@ -689,13 +802,13 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
 
         /* restore the state pre-padding */
         if (fpc->end_padded) {
-            int warp = fpc->fifo_buf->wptr - fpc->fifo_buf->buffer < MAX_FRAME_HEADER_SIZE;
+            int warp = fpc->fifo_buf.wptr - fpc->fifo_buf.buffer < MAX_FRAME_HEADER_SIZE;
             /* HACK: drain the tail of the fifo */
-            fpc->fifo_buf->wptr -= MAX_FRAME_HEADER_SIZE;
-            fpc->fifo_buf->wndx -= MAX_FRAME_HEADER_SIZE;
+            fpc->fifo_buf.wptr -= MAX_FRAME_HEADER_SIZE;
+            fpc->fifo_buf.wndx -= MAX_FRAME_HEADER_SIZE;
             if (warp) {
-                fpc->fifo_buf->wptr += fpc->fifo_buf->end -
-                    fpc->fifo_buf->buffer;
+                fpc->fifo_buf.wptr += fpc->fifo_buf.end -
+                    fpc->fifo_buf.buffer;
             }
             read_start = read_end = NULL;
         }
@@ -727,7 +840,7 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
                                                 &fpc->wrap_buf,
                                                 &fpc->wrap_buf_allocated_size);
             return buf_size ? (read_end - buf) : (fpc->best_header->offset -
-                                                  av_fifo_size(fpc->fifo_buf));
+                                                  flac_fifo_size(&fpc->fifo_buf));
         }
         if (!buf_size)
             return get_best_header(fpc, poutbuf, poutbuf_size);
@@ -742,11 +855,13 @@ handle_error:
 static av_cold int flac_parse_init(AVCodecParserContext *c)
 {
     FLACParseContext *fpc = c->priv_data;
+    int ret;
+
     fpc->pc = c;
     /* There will generally be FLAC_MIN_HEADERS buffered in the fifo before
        it drains.  This is allocated early to avoid slow reallocation. */
-    fpc->fifo_buf = av_fifo_alloc_array(FLAC_MIN_HEADERS + 3, FLAC_AVG_FRAME_SIZE);
-    if (!fpc->fifo_buf) {
+    ret = flac_fifo_alloc(&fpc->fifo_buf, (FLAC_MIN_HEADERS + 3) * FLAC_AVG_FRAME_SIZE);
+    if (ret < 0) {
         av_log(fpc->avctx, AV_LOG_ERROR,
                 "couldn't allocate fifo_buf\n");
         return AVERROR(ENOMEM);
@@ -765,7 +880,7 @@ static void flac_parse_close(AVCodecParserContext *c)
         curr = temp;
     }
     fpc->headers = NULL;
-    av_fifo_freep(&fpc->fifo_buf);
+    flac_fifo_free(&fpc->fifo_buf);
     av_freep(&fpc->wrap_buf);
 }
 
-- 
2.33.0



More information about the ffmpeg-devel mailing list