[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