[vlc-commits] packetizer: flac: rework (fix #17932)
Francois Cartegnie
git at videolan.org
Thu Feb 9 14:30:13 CET 2017
vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Fri Feb 3 12:20:31 2017 +0100| [6ab4365088b430bccab057f5701c51ca4853350d] | committer: Francois Cartegnie
packetizer: flac: rework (fix #17932)
Fixes the framesize number of bytestream parsing
and exponential framesize memcpy sending cpu rocket
high, 4M+ memcpy vs 800 for same 10s sample on variable
frame length.
Minimizes the number of required allocs.
Previous CRC errors also dissapeared.
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=6ab4365088b430bccab057f5701c51ca4853350d
---
modules/packetizer/flac.c | 260 +++++++++++++++++++++++-----------------------
1 file changed, 132 insertions(+), 128 deletions(-)
diff --git a/modules/packetizer/flac.c b/modules/packetizer/flac.c
index bbb4651..f1d274d 100644
--- a/modules/packetizer/flac.c
+++ b/modules/packetizer/flac.c
@@ -1,8 +1,7 @@
/*****************************************************************************
* flac.c: flac packetizer module.
*****************************************************************************
- * Copyright (C) 1999-2001 VLC authors and VideoLAN
- * $Id$
+ * Copyright (C) 1999-2017 VLC authors and VideoLAN
*
* Authors: Gildas Bazin <gbazin at videolan.org>
* Sigmund Augdal Helberg <dnumgis at videolan.org>
@@ -65,6 +64,7 @@ struct decoder_sys_t
int i_state;
block_bytestream_t bytestream;
+ size_t i_offset;
/*
* FLAC properties
@@ -90,6 +90,7 @@ struct decoder_sys_t
unsigned int i_frame_length;
size_t i_frame_size;
+ size_t i_last_frame_size;
uint16_t crc;
unsigned int i_rate, i_channels, i_bits_per_sample;
size_t i_buf;
@@ -298,7 +299,7 @@ static uint16_t flac_crc16(uint16_t crc, uint8_t byte)
{
return (crc << 8) ^ flac_crc16_table[(crc >> 8) ^ byte];
}
-
+#if 0
/* Gives the previous CRC value, before hashing last_byte through it */
static uint16_t flac_crc16_undo(uint16_t crc, const uint8_t last_byte)
{
@@ -344,7 +345,7 @@ static uint16_t flac_crc16_undo(uint16_t crc, const uint8_t last_byte)
uint8_t idx = flac_crc16_rev_table[crc & 0xff];
return ((idx ^ last_byte) << 8) | ((crc ^ flac_crc16_table[idx]) >> 8);
}
-
+#endif
/*****************************************************************************
* SyncInfo: parse FLAC sync info
*****************************************************************************/
@@ -530,9 +531,31 @@ static void Flush(decoder_t *p_dec)
decoder_sys_t *p_sys = p_dec->p_sys;
p_sys->i_state = STATE_NOSYNC;
+ p_sys->i_offset = 0;
block_BytestreamEmpty(&p_sys->bytestream);
}
+static const uint8_t * FLACStartcodeHelper(const uint8_t *p, const uint8_t *end)
+{
+ while( p && p < end )
+ {
+ if( (p = memchr(p, 0xFF, end - p)) )
+ {
+ if( end - p > 1 && (p[1] & 0xFE) == 0xF8 )
+ return p;
+ else
+ p++;
+ }
+ }
+ return NULL;
+}
+
+static bool FLACStartcodeMatcher(uint8_t i, size_t i_pos, const uint8_t *p_startcode)
+{
+ VLC_UNUSED(p_startcode);
+ return (i_pos == 0) ? i == 0xFF : (i & 0xFE) == 0xF8;
+}
+
/* */
static block_t *Packetize(decoder_t *p_dec, block_t **pp_block)
{
@@ -583,17 +606,20 @@ static block_t *Packetize(decoder_t *p_dec, block_t **pp_block)
while (1) switch (p_sys->i_state) {
case STATE_NOSYNC:
- while (!block_PeekBytes(&p_sys->bytestream, p_header, 2)) {
- if (p_header[0] == 0xFF && (p_header[1] & 0xFE) == 0xF8) {
- p_sys->i_state = STATE_SYNC;
- break;
- }
- block_SkipByte(&p_sys->bytestream);
+ if(block_FindStartcodeFromOffset(&p_sys->bytestream, &p_sys->i_offset,
+ NULL, 2,
+ FLACStartcodeHelper,
+ FLACStartcodeMatcher) == VLC_SUCCESS)
+ {
+ p_sys->i_state = STATE_SYNC;
}
- if (p_sys->i_state != STATE_SYNC) {
- block_BytestreamFlush(&p_sys->bytestream);
+
+ block_SkipBytes(&p_sys->bytestream, p_sys->i_offset);
+ block_BytestreamFlush(&p_sys->bytestream);
+ p_sys->i_offset = 0;
+
+ if( p_sys->i_state != STATE_SYNC )
return NULL; /* Need more data */
- }
case STATE_SYNC:
/* Sync state is unverified until we have read frame header and checked CRC
@@ -616,6 +642,7 @@ static block_t *Packetize(decoder_t *p_dec, block_t **pp_block)
if (!i_ret) {
msg_Dbg(p_dec, "emulated sync word");
block_SkipByte(&p_sys->bytestream);
+ p_sys->i_offset = 0;
p_sys->i_state = STATE_NOSYNC;
break;
}
@@ -623,8 +650,9 @@ static block_t *Packetize(decoder_t *p_dec, block_t **pp_block)
p_dec->fmt_out.audio.i_rate = p_sys->i_rate;
}
p_sys->i_state = STATE_NEXT_SYNC;
- p_sys->i_frame_size = ( p_sys->b_stream_info ) ? p_sys->stream_info.min_framesize :
- MIN_FLAC_FRAME_SIZE;
+ p_sys->i_offset = 1;
+ p_sys->i_frame_size = 0;
+ p_sys->crc = 0;
/* We have to read until next frame sync code to compute current frame size
* from that boundary.
@@ -633,145 +661,117 @@ static block_t *Packetize(decoder_t *p_dec, block_t **pp_block)
*/
case STATE_NEXT_SYNC:
{
- /* Calculate the initial CRC for the minimal frame size,
- * We'll update it as we look for the next start code. */
- if (p_sys->i_buf < p_sys->i_frame_size)
+ if(block_FindStartcodeFromOffset(&p_sys->bytestream, &p_sys->i_offset,
+ NULL, 2,
+ FLACStartcodeHelper,
+ FLACStartcodeMatcher) == VLC_SUCCESS)
{
- uint8_t *p_buf = realloc(p_sys->p_buf, p_sys->i_frame_size);
- if (!p_buf)
- return NULL;
- p_sys->p_buf = p_buf;
- p_sys->i_buf = p_sys->i_frame_size;
+ p_sys->i_state = STATE_GET_DATA;
+ break;
}
-
- if (block_PeekOffsetBytes(&p_sys->bytestream, 0, p_sys->p_buf, p_sys->i_frame_size)) {
- return NULL;
+ else if( pp_block == NULL )
+ {
+ p_sys->i_offset = block_BytestreamRemaining( &p_sys->bytestream );
+ p_sys->i_state = STATE_GET_DATA;
}
+ return NULL;
+ }
- uint16_t crc = 0;
- for (unsigned i = 0; i < p_sys->i_frame_size; i++)
- crc = flac_crc16(crc, p_sys->p_buf[i]);
- p_sys->crc = crc;
-
- /* Check if next expected frame contains the sync word */
- while (!block_PeekOffsetBytes(&p_sys->bytestream, p_sys->i_frame_size,
- p_header, MAX_FLAC_HEADER_SIZE)) {
- if (p_header[0] == 0xFF && (p_header[1] & 0xFE) == 0xF8) {
- /* Check if frame is valid and get frame info */
- int i_ret = SyncInfo(p_dec, p_header,
- &p_sys->i_channels,
- &p_sys->i_rate,
- &p_sys->i_bits_per_sample,
- NULL, NULL, NULL );
-
- if (i_ret) {
- uint8_t crc_bytes[2];
- block_PeekOffsetBytes(&p_sys->bytestream,
- p_sys->i_frame_size - 2, crc_bytes, 2);
- /* Get the frame CRC */
- uint16_t stream_crc = (crc_bytes[0] << 8) | crc_bytes[1];
- /* Calculate the frame CRC: remove the last 2 bytes */
- uint16_t crc = flac_crc16_undo(p_sys->crc, crc_bytes[1]);
- crc = flac_crc16_undo(crc, crc_bytes[0]);
- if (stream_crc != crc)
- {
- if(i_ret > 0 || (p_sys->b_stream_info &&
- p_sys->stream_info.max_blocksize == p_sys->i_frame_size) )
- {
- msg_Warn(p_dec, "Bad CRC for frame size %zu: 0x%x != 0x%x",
- p_sys->i_frame_size, crc, stream_crc);
- block_SkipByte(&p_sys->bytestream);
- p_sys->i_state = STATE_NOSYNC;
- }
- } else {
- p_sys->i_state = STATE_SEND_DATA;
- p_sys->crc = 0;
- break;
- }
- }
- }
- p_sys->crc = flac_crc16(p_sys->crc, p_header[0]); /* update CRC */
- p_sys->i_frame_size++;
+ case STATE_GET_DATA:
+ if( p_sys->i_offset < MIN_FLAC_FRAME_SIZE ||
+ ( p_sys->b_stream_info &&
+ p_sys->stream_info.min_framesize > p_sys->i_offset ) )
+ {
+ p_sys->i_offset += 1;
+ p_sys->i_state = STATE_NEXT_SYNC;
+ break;
}
-
- if (p_sys->i_state != STATE_SEND_DATA) {
- if (p_sys->b_stream_info && p_sys->stream_info.max_framesize > 0 &&
- p_sys->i_frame_size > p_sys->stream_info.max_framesize) {
- block_SkipByte(&p_sys->bytestream);
- msg_Warn(p_dec, "Frame is too big (%zu > %d), couldn't find start code",
- p_sys->i_frame_size, p_sys->stream_info.max_framesize);
- p_sys->i_state = STATE_NOSYNC;
- p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
- return NULL;
- }
-
- if ( !in )
+ else if( p_sys->b_stream_info &&
+ p_sys->stream_info.max_framesize < p_sys->i_offset )
+ {
+ /* Something went wrong, truncate stream head and restart */
+ block_SkipBytes( &p_sys->bytestream, MAX_FLAC_HEADER_SIZE + 2 );
+ block_BytestreamFlush( &p_sys->bytestream );
+ p_sys->i_frame_size = 0;
+ p_sys->crc = 0;
+ p_sys->i_offset = 0;
+ p_sys->i_state = STATE_NOSYNC;
+ p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY;
+ break;
+ }
+ else
+ {
+ /* Allocate enough for storage */
+ if( p_sys->i_offset > p_sys->i_buf )
{
- /* There's no following frame, so we need to read current
- * data until the frame footer matches (crc16) == stream crc.
- * In the worst case, if crc might be a false positive and data
- * will be truncated. */
- uint8_t crc_bytes[2];
- if ( !block_PeekOffsetBytes(&p_sys->bytestream,
- p_sys->i_frame_size - 2, crc_bytes, 2) )
+ size_t i_min_alloc = __MAX(p_sys->i_last_frame_size, p_sys->i_offset);
+ uint8_t *p_realloc = realloc( p_sys->p_buf, i_min_alloc );
+ if( p_realloc )
{
- while ( true )
- {
- /* Read the frame CRC */
- uint16_t stream_crc = (crc_bytes[0] << 8) | crc_bytes[1];
- /* Calculate the frame CRC: remove the last 2 bytes */
- uint16_t crc = flac_crc16_undo(p_sys->crc, crc_bytes[1]);
- crc = flac_crc16_undo(crc, crc_bytes[0]);
- if (stream_crc == crc)
- {
- p_sys->i_state = STATE_SEND_DATA;
- break;
- }
- p_sys->i_frame_size++;
- if ( block_PeekOffsetBytes(&p_sys->bytestream,
- p_sys->i_frame_size - 2, crc_bytes, 2) )
- break;
- /* Update current crc */
- p_sys->crc = flac_crc16(p_sys->crc, crc_bytes[1]);
- }
+ p_sys->i_buf = i_min_alloc;
+ p_sys->p_buf = p_realloc;
}
- if ( p_sys->i_state != STATE_SEND_DATA )
+ if ( !p_sys->p_buf )
return NULL;
}
- else
+
+ /* Copy from previous sync point (frame_size) up to to current (offset) */
+ block_PeekOffsetBytes( &p_sys->bytestream, p_sys->i_frame_size,
+ &p_sys->p_buf[p_sys->i_frame_size],
+ p_sys->i_offset - p_sys->i_frame_size );
+
+ /* update crc to include this data chunk */
+ for( size_t i = p_sys->i_frame_size; i < p_sys->i_offset - 2; i++ )
+ p_sys->crc = flac_crc16( p_sys->crc, p_sys->p_buf[i] );
+
+ p_sys->i_frame_size = p_sys->i_offset;
+
+ uint16_t stream_crc = GetWBE(&p_sys->p_buf[p_sys->i_offset - 2]);
+ if( stream_crc != p_sys->crc )
{
- /* Need more data */
- return NULL;
+ /* False positive syncpoint as the CRC does not match */
+ /* Add the 2 last bytes which were not the CRC sum, and go for next sync point */
+ p_sys->crc = flac_crc16( p_sys->crc, p_sys->p_buf[p_sys->i_offset - 2] );
+ p_sys->crc = flac_crc16( p_sys->crc, p_sys->p_buf[p_sys->i_offset - 1] );
+ p_sys->i_offset += 1;
+ p_sys->i_state = STATE_NEXT_SYNC;
+ break; /* continue */
}
+
+ p_sys->i_state = STATE_SEND_DATA;
+
+ /* clean */
+ block_SkipBytes( &p_sys->bytestream, p_sys->i_offset );
+ block_BytestreamFlush( &p_sys->bytestream );
+ p_sys->i_last_frame_size = p_sys->i_frame_size;
+ p_sys->i_offset = 0;
+ p_sys->crc = 0;
}
- }
+ break;
+
case STATE_SEND_DATA:
- out = block_Alloc(p_sys->i_frame_size);
+ out = block_heap_Alloc( p_sys->p_buf, p_sys->i_frame_size );
+ out->i_dts = out->i_pts = p_sys->i_pts;
+ out->i_length = p_sys->i_duration;
+ out->i_flags = p_sys->i_next_block_flags;
- /* Copy the whole frame into the buffer. When we reach this point
- * we already know we have enough data available. */
- block_GetBytes(&p_sys->bytestream, out->p_buffer,
- p_sys->i_frame_size);
+ p_sys->i_next_block_flags = 0;
+ p_sys->i_buf = 0;
+ p_sys->p_buf = NULL;
+ p_sys->i_frame_size = 0;
p_dec->fmt_out.audio.i_channels = p_sys->i_channels;
p_dec->fmt_out.audio.i_physical_channels =
- p_dec->fmt_out.audio.i_original_channels =
- pi_channels_maps[p_sys->stream_info.channels];
+ p_dec->fmt_out.audio.i_original_channels = pi_channels_maps[p_sys->stream_info.channels];
/* So p_block doesn't get re-added several times */
if ( in )
*pp_block = block_BytestreamPop(&p_sys->bytestream);
- else
- block_BytestreamFlush(&p_sys->bytestream);
+ p_sys->i_offset = 0;
p_sys->i_state = STATE_NOSYNC;
- out->i_dts = out->i_pts = p_sys->i_pts;
- out->i_length = p_sys->i_duration;
- out->i_flags = p_sys->i_next_block_flags;
- p_sys->i_next_block_flags = 0;
-
return out;
}
@@ -786,13 +786,17 @@ static int Open(vlc_object_t *p_this)
if (p_dec->fmt_in.i_codec != VLC_CODEC_FLAC)
return VLC_EGENERIC;
+
/* */
p_dec->p_sys = p_sys = malloc(sizeof(*p_sys));
if (!p_sys)
return VLC_ENOMEM;
p_sys->i_state = STATE_NOSYNC;
+ p_sys->i_offset = 0;
p_sys->b_stream_info = false;
+ p_sys->i_last_frame_size = MIN_FLAC_FRAME_SIZE;
+ p_sys->i_frame_size = 0;
p_sys->i_firstpts = VLC_TS_INVALID;
p_sys->i_firstframepts = VLC_TS_INVALID;
p_sys->i_pts = VLC_TS_INVALID;
More information about the vlc-commits
mailing list