summaryrefslogtreecommitdiff
path: root/libavcodec/flac_parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'libavcodec/flac_parser.c')
-rw-r--r--libavcodec/flac_parser.c145
1 files changed, 105 insertions, 40 deletions
diff --git a/libavcodec/flac_parser.c b/libavcodec/flac_parser.c
index 8150ec4d11..2721286464 100644
--- a/libavcodec/flac_parser.c
+++ b/libavcodec/flac_parser.c
@@ -2,20 +2,20 @@
* FLAC parser
* Copyright (c) 2010 Michael Chinen
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg 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.
*
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg 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 Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -40,7 +40,7 @@
#include "flac.h"
/** maximum number of adjacent headers that compare CRCs against each other */
-#define FLAC_MAX_SEQUENTIAL_HEADERS 3
+#define FLAC_MAX_SEQUENTIAL_HEADERS 4
/** minimum number of headers buffered and checked before returning frames */
#define FLAC_MIN_HEADERS 10
/** estimate for average size of a FLAC frame */
@@ -87,14 +87,16 @@ typedef struct FLACParseContext {
int end_padded; /**< specifies if fifo_buf's end is padded */
uint8_t *wrap_buf; /**< general fifo read buffer when wrapped */
int wrap_buf_allocated_size; /**< actual allocated size of the buffer */
+ FLACFrameInfo last_fi; /**< last decoded frame header info */
+ int last_fi_valid; /**< set if last_fi is valid */
} FLACParseContext;
static int frame_header_is_valid(AVCodecContext *avctx, const uint8_t *buf,
FLACFrameInfo *fi)
{
- BitstreamContext bc;
- bitstream_init8(&bc, buf, MAX_FRAME_HEADER_SIZE);
- return !ff_flac_decode_frame_header(avctx, &bc, fi, 127);
+ GetBitContext gb;
+ init_get_bits(&gb, buf, MAX_FRAME_HEADER_SIZE * 8);
+ return !ff_flac_decode_frame_header(avctx, &gb, fi, 127);
}
/**
@@ -180,7 +182,7 @@ static int find_headers_search_validate(FLACParseContext *fpc, int offset)
size++;
}
- *end_handle = av_mallocz(sizeof(FLACHeaderMarker));
+ *end_handle = av_mallocz(sizeof(**end_handle));
if (!*end_handle) {
av_log(fpc->avctx, AV_LOG_ERROR,
"couldn't allocate FLACHeaderMarker\n");
@@ -190,6 +192,13 @@ static int find_headers_search_validate(FLACParseContext *fpc, int offset)
(*end_handle)->offset = offset;
(*end_handle)->link_penalty = av_malloc(sizeof(int) *
FLAC_MAX_SEQUENTIAL_HEADERS);
+ if (!(*end_handle)->link_penalty) {
+ av_freep(end_handle);
+ av_log(fpc->avctx, AV_LOG_ERROR,
+ "couldn't allocate link_penalty\n");
+ return AVERROR(ENOMEM);
+ }
+
for (i = 0; i < FLAC_MAX_SEQUENTIAL_HEADERS; i++)
(*end_handle)->link_penalty[i] = FLAC_HEADER_NOT_PENALIZED_YET;
@@ -267,13 +276,12 @@ static int find_new_headers(FLACParseContext *fpc, int search_start)
return size;
}
-static int check_header_mismatch(FLACParseContext *fpc,
- FLACHeaderMarker *header,
- FLACHeaderMarker *child,
- int log_level_offset)
+static int check_header_fi_mismatch(FLACParseContext *fpc,
+ FLACFrameInfo *header_fi,
+ FLACFrameInfo *child_fi,
+ int log_level_offset)
{
- FLACFrameInfo *header_fi = &header->fi, *child_fi = &child->fi;
- int deduction = 0, deduction_expected = 0, i;
+ int deduction = 0;
if (child_fi->samplerate != header_fi->samplerate) {
deduction += FLAC_HEADER_CHANGED_PENALTY;
av_log(fpc->avctx, AV_LOG_WARNING + log_level_offset,
@@ -288,13 +296,25 @@ static int check_header_mismatch(FLACParseContext *fpc,
/* Changing blocking strategy not allowed per the spec */
deduction += FLAC_HEADER_BASE_SCORE;
av_log(fpc->avctx, AV_LOG_WARNING + log_level_offset,
- "blocking strategy change detected in adjacent frames\n");
+ "blocking strategy change detected in adjacent frames\n");
}
if (child_fi->channels != header_fi->channels) {
deduction += FLAC_HEADER_CHANGED_PENALTY;
av_log(fpc->avctx, AV_LOG_WARNING + log_level_offset,
- "number of channels change detected in adjacent frames\n");
+ "number of channels change detected in adjacent frames\n");
}
+ return deduction;
+}
+
+static int check_header_mismatch(FLACParseContext *fpc,
+ FLACHeaderMarker *header,
+ FLACHeaderMarker *child,
+ int log_level_offset)
+{
+ FLACFrameInfo *header_fi = &header->fi, *child_fi = &child->fi;
+ int deduction, deduction_expected = 0, i;
+ deduction = check_header_fi_mismatch(fpc, header_fi, child_fi,
+ log_level_offset);
/* Check sample and frame numbers. */
if ((child_fi->frame_or_sample_num - header_fi->frame_or_sample_num
!= header_fi->blocksize) &&
@@ -399,11 +419,18 @@ static int score_header(FLACParseContext *fpc, FLACHeaderMarker *header)
FLACHeaderMarker *child;
int dist = 0;
int child_score;
-
+ int base_score = FLAC_HEADER_BASE_SCORE;
if (header->max_score != FLAC_HEADER_NOT_SCORED_YET)
return header->max_score;
- header->max_score = FLAC_HEADER_BASE_SCORE;
+ /* Modify the base score with changes from the last output header */
+ if (fpc->last_fi_valid) {
+ /* Silence the log since this will be repeated if selected */
+ base_score -= check_header_fi_mismatch(fpc, &fpc->last_fi, &header->fi,
+ AV_LOG_DEBUG);
+ }
+
+ header->max_score = base_score;
/* Check and compute the children's scores. */
child = header->next;
@@ -419,7 +446,7 @@ static int score_header(FLACParseContext *fpc, FLACHeaderMarker *header)
if (FLAC_HEADER_BASE_SCORE + child_score > header->max_score) {
/* Keep the child because the frame scoring is dynamic. */
header->best_child = child;
- header->max_score = FLAC_HEADER_BASE_SCORE + child_score;
+ header->max_score = base_score + child_score;
}
child = child->next;
}
@@ -430,7 +457,7 @@ static int score_header(FLACParseContext *fpc, FLACHeaderMarker *header)
static void score_sequences(FLACParseContext *fpc)
{
FLACHeaderMarker *curr;
- int best_score = FLAC_HEADER_NOT_SCORED_YET;
+ int best_score = 0;//FLAC_HEADER_NOT_SCORED_YET;
/* First pass to clear all old scores. */
for (curr = fpc->headers; curr; curr = curr->next)
curr->max_score = FLAC_HEADER_NOT_SCORED_YET;
@@ -469,7 +496,18 @@ 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;
+ else if (header->best_child)
+ fpc->pc->pts = header->fi.frame_or_sample_num * header->fi.blocksize;
+ }
+
fpc->best_header_valid = 0;
+ fpc->last_fi_valid = 1;
+ fpc->last_fi = header->fi;
+
/* 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)
@@ -489,8 +527,16 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) {
FLACFrameInfo fi;
- if (frame_header_is_valid(avctx, buf, &fi))
+ if (frame_header_is_valid(avctx, buf, &fi)) {
s->duration = fi.blocksize;
+ if (!avctx->sample_rate)
+ avctx->sample_rate = fi.samplerate;
+ if (fpc->pc->flags & PARSER_FLAG_USE_CODEC_TS){
+ fpc->pc->pts = fi.frame_or_sample_num;
+ if (!fi.is_var_size)
+ fpc->pc->pts *= fi.blocksize;
+ }
+ }
*poutbuf = buf;
*poutbuf_size = buf_size;
return buf_size;
@@ -540,20 +586,26 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
temp = curr->next;
av_freep(&curr->link_penalty);
av_free(curr);
+ fpc->nb_headers_buffered--;
}
fpc->headers = fpc->best_header->next;
av_freep(&fpc->best_header->link_penalty);
av_freep(&fpc->best_header);
+ fpc->nb_headers_buffered--;
}
- /* Find and score new headers. */
- while ((buf && read_end < buf + buf_size &&
+ /* Find and score new headers. */
+ /* buf_size is to zero when padding, so check for this since we do */
+ /* not want to try to read more input once we have found the end. */
+ /* Note that as (non-modified) parameters, buf can be non-NULL, */
+ /* while buf_size is 0. */
+ while ((buf && buf_size && read_end < buf + buf_size &&
fpc->nb_headers_buffered < FLAC_MIN_HEADERS)
- || (!buf && !fpc->end_padded)) {
+ || ((!buf || !buf_size) && !fpc->end_padded)) {
int start_offset;
/* Pad the end once if EOF, to check the final region for headers. */
- if (!buf) {
+ if (!buf || !buf_size) {
fpc->end_padded = 1;
buf_size = MAX_FRAME_HEADER_SIZE;
read_end = read_start + MAX_FRAME_HEADER_SIZE;
@@ -567,23 +619,23 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
if (!av_fifo_space(fpc->fifo_buf) &&
av_fifo_size(fpc->fifo_buf) / FLAC_AVG_FRAME_SIZE >
- fpc->nb_headers_buffered * 10) {
- /* There is less than one valid flac header buffered for 10 headers
+ 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
* data and the input is not a flac file. */
goto handle_error;
}
/* Fill the buffer. */
- if (av_fifo_realloc2(fpc->fifo_buf,
- (read_end - read_start) + av_fifo_size(fpc->fifo_buf)) < 0) {
+ 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 %td\n",
+ "couldn't reallocate buffer of size %"PTRDIFF_SPECIFIER"\n",
(read_end - read_start) + av_fifo_size(fpc->fifo_buf));
goto handle_error;
}
- if (buf) {
+ if (buf && buf_size) {
av_fifo_generic_write(fpc->fifo_buf, (void*) read_start,
read_end - read_start, NULL);
} else {
@@ -620,10 +672,11 @@ 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;
/* HACK: drain the tail of the fifo */
fpc->fifo_buf->wptr -= MAX_FRAME_HEADER_SIZE;
fpc->fifo_buf->wndx -= MAX_FRAME_HEADER_SIZE;
- if (fpc->fifo_buf->wptr < 0) {
+ if (warp) {
fpc->fifo_buf->wptr += fpc->fifo_buf->end -
fpc->fifo_buf->buffer;
}
@@ -632,10 +685,17 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
}
}
- curr = fpc->headers;
- for (curr = fpc->headers; curr; curr = curr->next)
- if (!fpc->best_header || curr->max_score > fpc->best_header->max_score)
+ for (curr = fpc->headers; curr; curr = curr->next) {
+ if (!fpc->best_header || curr->max_score > fpc->best_header->max_score) {
fpc->best_header = curr;
+ }
+ }
+
+ if (fpc->best_header && fpc->best_header->max_score <= 0) {
+ // Only accept a bad header if there is no other option to continue
+ if (!buf_size || !buf || read_end != buf || fpc->nb_headers_buffered < FLAC_MIN_HEADERS)
+ fpc->best_header = NULL;
+ }
if (fpc->best_header) {
fpc->best_header_valid = 1;
@@ -660,7 +720,7 @@ static int flac_parse(AVCodecParserContext *s, AVCodecContext *avctx,
handle_error:
*poutbuf = NULL;
*poutbuf_size = 0;
- return read_end - buf;
+ return buf_size ? read_end - buf : 0;
}
static av_cold int flac_parse_init(AVCodecParserContext *c)
@@ -669,7 +729,12 @@ static av_cold int flac_parse_init(AVCodecParserContext *c)
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(FLAC_AVG_FRAME_SIZE * (FLAC_MIN_HEADERS + 3));
+ fpc->fifo_buf = av_fifo_alloc_array(FLAC_MIN_HEADERS + 3, FLAC_AVG_FRAME_SIZE);
+ if (!fpc->fifo_buf) {
+ av_log(fpc->avctx, AV_LOG_ERROR,
+ "couldn't allocate fifo_buf\n");
+ return AVERROR(ENOMEM);
+ }
return 0;
}
@@ -684,8 +749,8 @@ static void flac_parse_close(AVCodecParserContext *c)
av_free(curr);
curr = temp;
}
- av_fifo_free(fpc->fifo_buf);
- av_free(fpc->wrap_buf);
+ av_fifo_freep(&fpc->fifo_buf);
+ av_freep(&fpc->wrap_buf);
}
AVCodecParser ff_flac_parser = {