* FLAC parser
* Copyright (c) 2010 Michael Chinen
*
- * This file is part of FFmpeg.
+ * This file is part of Libav.
*
- * FFmpeg is free software; you can redistribute it and/or
+ * Libav is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
- * FFmpeg is distributed in the hope that it will be useful,
+ * Libav is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with FFmpeg; if not, write to the Free Software
+ * License along with Libav; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FLAC_MAX_SEQUENTIAL_HEADERS 3
/** minimum number of headers buffered and checked before returning frames */
#define FLAC_MIN_HEADERS 10
+/** estimate for average size of a FLAC frame */
+#define FLAC_AVG_FRAME_SIZE 8192
/** scoring settings for score_header */
#define FLAC_HEADER_BASE_SCORE 10
} FLACHeaderMarker;
typedef struct FLACParseContext {
+ AVCodecParserContext *pc; /**< parent context */
AVCodecContext *avctx; /**< codec context pointer for logging */
FLACHeaderMarker *headers; /**< linked-list that starts at the first
CRC-8 verified header within buffer */
FLACHeaderMarker *best_header; /**< highest scoring header within buffer */
int nb_headers_found; /**< number of headers found in the last
flac_parse() call */
+ 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
fpc->avctx->sample_rate = header->fi.samplerate;
fpc->avctx->channels = header->fi.channels;
- fpc->avctx->frame_size = header->fi.blocksize;
+ fpc->pc->duration = header->fi.blocksize;
*poutbuf = flac_fifo_read_wrap(fpc, header->offset, *poutbuf_size,
&fpc->wrap_buf,
&fpc->wrap_buf_allocated_size);
FLACParseContext *fpc = s->priv_data;
FLACHeaderMarker *curr;
int nb_headers;
+ const uint8_t *read_end = buf;
+ const uint8_t *read_start = buf;
if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) {
FLACFrameInfo fi;
if (frame_header_is_valid(avctx, buf, &fi))
- avctx->frame_size = fi.blocksize;
+ s->duration = fi.blocksize;
*poutbuf = buf;
*poutbuf_size = buf_size;
return buf_size;
temp = curr->next;
av_freep(&curr->link_penalty);
av_free(curr);
+ fpc->nb_headers_buffered--;
}
/* Release returned data from ring buffer. */
av_fifo_drain(fpc->fifo_buf, best_child->offset);
for (curr = best_child->next; curr; curr = curr->next)
curr->offset -= best_child->offset;
+ fpc->nb_headers_buffered--;
best_child->offset = 0;
fpc->headers = best_child;
+ if (fpc->nb_headers_buffered >= FLAC_MIN_HEADERS) {
+ fpc->best_header = best_child;
+ return get_best_header(fpc, poutbuf, poutbuf_size);
+ }
fpc->best_header = NULL;
} else if (fpc->best_header) {
/* No end frame no need to delete the buffer; probably eof */
}
/* Find and score new headers. */
- if (buf_size || !fpc->end_padded) {
+ while ((buf && read_end < buf + buf_size &&
+ fpc->nb_headers_buffered < FLAC_MIN_HEADERS)
+ || (!buf && !fpc->end_padded)) {
int start_offset;
/* Pad the end once if EOF, to check the final region for headers. */
- if (!buf_size) {
- fpc->end_padded = 1;
- buf_size = MAX_FRAME_HEADER_SIZE;
+ if (!buf) {
+ fpc->end_padded = 1;
+ buf_size = MAX_FRAME_HEADER_SIZE;
+ read_end = read_start + MAX_FRAME_HEADER_SIZE;
+ } else {
+ /* The maximum read size is the upper-bound of what the parser
+ needs to have the required number of frames buffered */
+ int nb_desired = FLAC_MIN_HEADERS - fpc->nb_headers_buffered + 1;
+ read_end = read_end + FFMIN(buf + buf_size - read_end,
+ nb_desired * FLAC_AVG_FRAME_SIZE);
}
/* Fill the buffer. */
if (av_fifo_realloc2(fpc->fifo_buf,
- buf_size + av_fifo_size(fpc->fifo_buf)) < 0) {
+ (read_end - read_start) + av_fifo_size(fpc->fifo_buf)) < 0) {
av_log(avctx, AV_LOG_ERROR,
- "couldn't reallocate buffer of size %d\n",
- buf_size + av_fifo_size(fpc->fifo_buf));
+ "couldn't reallocate buffer of size %td\n",
+ (read_end - read_start) + av_fifo_size(fpc->fifo_buf));
goto handle_error;
}
if (buf) {
- av_fifo_generic_write(fpc->fifo_buf, (void*) buf, buf_size, NULL);
+ av_fifo_generic_write(fpc->fifo_buf, (void*) read_start,
+ read_end - read_start, NULL);
} else {
- int8_t pad[MAX_FRAME_HEADER_SIZE];
- memset(pad, 0, sizeof(pad));
+ int8_t pad[MAX_FRAME_HEADER_SIZE] = { 0 };
av_fifo_generic_write(fpc->fifo_buf, (void*) pad, sizeof(pad), NULL);
}
/* Tag headers and update sequences. */
start_offset = av_fifo_size(fpc->fifo_buf) -
- (buf_size + (MAX_FRAME_HEADER_SIZE - 1));
+ ((read_end - read_start) + (MAX_FRAME_HEADER_SIZE - 1));
start_offset = FFMAX(0, start_offset);
nb_headers = find_new_headers(fpc, start_offset);
- /* Wait till FLAC_MIN_HEADERS to output a valid frame. */
- if (!fpc->end_padded && nb_headers < FLAC_MIN_HEADERS)
+ if (nb_headers < 0) {
+ av_log(avctx, AV_LOG_ERROR,
+ "find_new_headers couldn't allocate FLAC header\n");
goto handle_error;
+ }
+
+ fpc->nb_headers_buffered = nb_headers;
+ /* Wait till FLAC_MIN_HEADERS to output a valid frame. */
+ if (!fpc->end_padded && fpc->nb_headers_buffered < FLAC_MIN_HEADERS) {
+ if (buf && read_end < buf + buf_size) {
+ read_start = read_end;
+ continue;
+ } else {
+ goto handle_error;
+ }
+ }
/* If headers found, update the scores since we have longer chains. */
if (fpc->end_padded || fpc->nb_headers_found)
fpc->fifo_buf->buffer;
}
buf_size = 0;
+ read_start = read_end = NULL;
}
}
av_log(avctx, AV_LOG_DEBUG, "Junk frame till offset %i\n",
fpc->best_header->offset);
- /* Set frame_size to 0. It is unknown or invalid in a junk frame. */
- avctx->frame_size = 0;
+ /* Set duration to 0. It is unknown or invalid in a junk frame. */
+ s->duration = 0;
*poutbuf_size = fpc->best_header->offset;
*poutbuf = flac_fifo_read_wrap(fpc, 0, *poutbuf_size,
&fpc->wrap_buf,
&fpc->wrap_buf_allocated_size);
- return buf_size ? buf_size : (fpc->best_header->offset -
- av_fifo_size(fpc->fifo_buf));
+ return buf_size ? (read_end - buf) : (fpc->best_header->offset -
+ av_fifo_size(fpc->fifo_buf));
}
if (!buf_size)
return get_best_header(fpc, poutbuf, poutbuf_size);
handle_error:
*poutbuf = NULL;
*poutbuf_size = 0;
- return buf_size;
+ return read_end - buf;
}
static int flac_parse_init(AVCodecParserContext *c)
{
FLACParseContext *fpc = c->priv_data;
+ fpc->pc = c;
/* There will generally be FLAC_MIN_HEADERS buffered in the fifo before
- it drains. 8192 is an approximate average size of a flac frame */
- fpc->fifo_buf = av_fifo_alloc(8192 * FLAC_MIN_HEADERS);
-
+ it drains. This is allocated early to avoid slow reallocation. */
+ fpc->fifo_buf = av_fifo_alloc(FLAC_AVG_FRAME_SIZE * (FLAC_MIN_HEADERS + 3));
return 0;
}
av_free(fpc->wrap_buf);
}
-AVCodecParser flac_parser = {
- { CODEC_ID_FLAC },
- sizeof(FLACParseContext),
- flac_parse_init,
- flac_parse,
- flac_parse_close,
+AVCodecParser ff_flac_parser = {
+ .codec_ids = { CODEC_ID_FLAC },
+ .priv_data_size = sizeof(FLACParseContext),
+ .parser_init = flac_parse_init,
+ .parser_parse = flac_parse,
+ .parser_close = flac_parse_close,
};