lavc/flac_parser: use a custom FIFO implementation

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.
This commit is contained in:
Anton Khirnov 2021-12-29 17:02:46 +01:00
parent 1b24a1ea14
commit 55ccbfd790
1 changed files with 158 additions and 40 deletions

View File

@ -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,14 @@
#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;
int empty;
} 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 +91,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 +134,18 @@ static int frame_header_is_valid(AVCodecContext *avctx, const uint8_t *buf,
return 1;
}
static size_t flac_fifo_size(const FifoBuffer *f)
{
if (f->wptr <= f->rptr && !f->empty)
return (f->wptr - f->buffer) + (f->end - f->rptr);
return f->wptr - f->rptr;
}
static size_t flac_fifo_space(const 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,108 @@ 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
if (offset_w <= offset_r && !f->empty) {
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)
{
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;
}
if (size)
f->empty = 0;
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;
size -= len;
} while (size > 0);
f->wptr = wptr;
return 0;
}
static void flac_fifo_drain(FifoBuffer *f, size_t size)
{
size_t size_cur = flac_fifo_size(f);
av_assert0(size_cur >= size);
if (size_cur == size)
f->empty = 1;
f->rptr += size;
if (f->rptr >= f->end)
f->rptr -= f->end - f->buffer;
}
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->empty = 1;
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 +383,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 +397,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 +526,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 +620,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 +639,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 +653,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 +705,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 +739,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 +753,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 +763,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));
if (buf_size) {
ret = flac_fifo_write(&fpc->fifo_buf, read_start,
read_end - read_start);
} else {
int8_t pad[MAX_FRAME_HEADER_SIZE] = { 0 };
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;
}
if (buf_size) {
av_fifo_generic_write(fpc->fifo_buf, (void*) read_start,
read_end - read_start, NULL);
} else {
int8_t pad[MAX_FRAME_HEADER_SIZE] = { 0 };
av_fifo_generic_write(fpc->fifo_buf, pad, sizeof(pad), NULL);
}
/* 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,14 +804,15 @@ 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 empty = flac_fifo_size(&fpc->fifo_buf) == 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;
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;
}
fpc->fifo_buf.empty = empty;
read_start = read_end = NULL;
}
}
@ -727,7 +843,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 +858,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 +883,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);
}