OSDN Git Service

fix: cannot preview with QT
[handbrake-jp/handbrake-jp.git] / libhb / deca52.c
index 5559024..db3b56f 100644 (file)
@@ -7,6 +7,7 @@
 #include "hb.h"
 
 #include "a52dec/a52.h"
+#include "libavutil/crc.h"
 
 struct hb_work_private_s
 {
@@ -19,23 +20,18 @@ struct hb_work_private_s
     int           flags_out;
     int           rate;
     int           bitrate;
+    int           out_discrete_channels;
+    int           error;
+    int           frames;                   // number of good frames decoded
+    int           crc_errors;               // number of frames with crc errors
+    int           bytes_dropped;            // total bytes dropped while resyncing
     float         level;
     float         dynamic_range_compression;
-
-    int           error;
-    int           sync;
-    int           size;
-
-    int64_t       next_expected_pts;
-
-    int64_t       sequence;
-
+    double        next_expected_pts;
+    int64_t       last_buf_pts;
+    hb_list_t    *list;
+    const AVCRC  *crc_table;
     uint8_t       frame[3840];
-
-    hb_list_t   * list;
-
-       int           out_discrete_channels;
-
 };
 
 static int  deca52Init( hb_work_object_t *, hb_job_t * );
@@ -97,6 +93,7 @@ static int deca52Init( hb_work_object_t * w, hb_job_t * job )
 
     pv->job   = job;
 
+    pv->crc_table = av_crc_get_table( AV_CRC_16_ANSI );
     pv->list      = hb_list_init();
     pv->state     = a52_init( 0 );
 
@@ -112,9 +109,6 @@ static int deca52Init( hb_work_object_t * w, hb_job_t * job )
     pv->level     = 32768.0;
     pv->dynamic_range_compression = audio->config.out.dynamic_range_compression;
 
-    pv->next_expected_pts = 0;
-    pv->sequence = 0;
-
     return 0;
 }
 
@@ -126,6 +120,12 @@ static int deca52Init( hb_work_object_t * w, hb_job_t * job )
 static void deca52Close( hb_work_object_t * w )
 {
     hb_work_private_t * pv = w->private_data;
+
+    if ( pv->crc_errors )
+    {
+        hb_log( "deca52: %d frames decoded, %d crc errors, %d bytes dropped",
+                pv->frames, pv->crc_errors, pv->bytes_dropped );
+    }
     a52_free( pv->state );
     hb_list_empty( &pv->list );
     free( pv );
@@ -152,7 +152,12 @@ static int deca52Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
         return HB_WORK_DONE;
     }
 
-    pv->sequence = (*buf_in)->sequence;
+    if ( (*buf_in)->start < -1 && pv->next_expected_pts == 0 )
+    {
+        // discard buffers that start before video time 0
+        *buf_out = NULL;
+        return HB_WORK_OK;
+    }
 
     hb_list_add( pv->list, *buf_in );
     *buf_in = NULL;
@@ -161,7 +166,6 @@ static int deca52Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
     *buf_out = buf = Decode( w );
     while( buf )
     {
-        buf->sequence = pv->sequence;
         buf->next = Decode( w );
         buf       = buf->next;
     }
@@ -180,86 +184,113 @@ static hb_buffer_t * Decode( hb_work_object_t * w )
     hb_buffer_t * buf;
     hb_audio_t  * audio = w->audio;
     int           i, j, k;
-    uint64_t      pts, pos;
+    int           size = 0;
 
-    /* Get a frame header if don't have one yet */
-    if( !pv->sync )
+    // check that we're at the start of a valid frame and align to the
+    // start of a valid frame if we're not.
+    // we have to check the header & crc so we need at least
+    // 7 (the header size) + 128 (the minimum frame size) bytes
+    while( hb_list_bytes( pv->list ) >= 7+128 )
     {
-        while( hb_list_bytes( pv->list ) >= 7 )
+        /* check if this is a valid header */
+        hb_list_seebytes( pv->list, pv->frame, 7 );
+        size = a52_syncinfo( pv->frame, &pv->flags_in, &pv->rate, &pv->bitrate );
+        if ( size > 0 )
         {
-            /* We have 7 bytes, check if this is a correct header */
-            hb_list_seebytes( pv->list, pv->frame, 7 );
-            pv->size = a52_syncinfo( pv->frame, &pv->flags_in, &pv->rate,
-                                    &pv->bitrate );
-            if( pv->size )
+            // header looks valid - check the crc1
+            if( size > hb_list_bytes( pv->list ) )
+            {
+                // don't have all the frame's data yet
+                return NULL;
+            }
+            int crc1size = (size >> 1) + (size >> 3);
+            hb_list_seebytes( pv->list, pv->frame, crc1size );
+            if ( av_crc( pv->crc_table, 0, pv->frame + 2, crc1size - 2 ) == 0 )
             {
-                /* It is. W00t. */
+                // crc1 is ok - say we have valid frame sync
                 if( pv->error )
                 {
-                    hb_log( "a52_syncinfo ok" );
+                    hb_log( "output track %d: ac3 in sync after skipping %d bytes",
+                            audio->config.out.track, pv->error );
+                    pv->bytes_dropped += pv->error;
+                    pv->error = 0;
                 }
-                pv->error = 0;
-                pv->sync  = 1;
                 break;
             }
-
-            /* It is not */
-            if( !pv->error )
-            {
-                hb_log( "a52_syncinfo failed" );
-                pv->error = 1;
-            }
-
-            /* Try one byte later */
-            hb_list_getbytes( pv->list, pv->frame, 1, NULL, NULL );
         }
+        // no sync - discard one byte then try again
+        hb_list_getbytes( pv->list, pv->frame, 1, NULL, NULL );
+        ++pv->error;
     }
 
-    if( !pv->sync ||
-        hb_list_bytes( pv->list ) < pv->size )
+    // we exit the above loop either in error state (we didn't find sync
+    // or don't have enough data yet to validate sync) or in sync. If we're
+    // not in sync we need more data so just return.
+    if( pv->error || size <= 0 || hb_list_bytes( pv->list ) < size )
     {
         /* Need more data */
         return NULL;
     }
 
-    /* Get the whole frame */
-    hb_list_getbytes( pv->list, pv->frame, pv->size, &pts, &pos );
-    if (pts == -1)
+    // Get the whole frame and check its CRC. If the CRC is wrong
+    // discard the frame - we'll resync on the next call.
+
+    uint64_t ipts;
+    hb_list_getbytes( pv->list, pv->frame, size, &ipts, NULL );
+    if ( av_crc( pv->crc_table, 0, pv->frame + 2, size - 2 ) != 0 )
+    {
+        ++pv->crc_errors;
+        return NULL;
+    }
+    ++pv->frames;
+    if ( ipts != pv->last_buf_pts )
+    {
+        pv->last_buf_pts = ipts;
+    }
+    else
     {
-        pts = pv->next_expected_pts;
+        // spec says that the PTS is the start time of the first frame
+        // that starts in the PES frame so we only use the PTS once then
+        // get the following frames' PTS from the frame length.
+        ipts = -1;
     }
 
+    double pts = ( ipts != -1 ) ? ipts : pv->next_expected_pts;
+    double frame_dur = (6. * 256. * 90000.) / pv->rate;
+
     /* AC3 passthrough: don't decode the AC3 frame */
     if( audio->config.out.codec == HB_ACODEC_AC3 )
     {
-        buf = hb_buffer_init( pv->size );
-        memcpy( buf->data, pv->frame, pv->size );
-        buf->start = pts + ( pos / pv->size ) * 6 * 256 * 90000 / pv->rate;
-        buf->stop  = buf->start + 6 * 256 * 90000 / pv->rate;
-        pv->next_expected_pts = buf->stop;
-        pv->sync = 0;
+        buf = hb_buffer_init( size );
+        memcpy( buf->data, pv->frame, size );
+        buf->start = pts;
+        pts += frame_dur;
+        buf->stop  = pts;
+        pv->next_expected_pts = pts;
         return buf;
     }
 
     /* Feed liba52 */
     a52_frame( pv->state, pv->frame, &pv->flags_out, &pv->level, 0 );
 
-    if ( pv->dynamic_range_compression > 1.0 )
+    /* If a user specifies strong dynamic range compression (>1), adjust it.
+       If a user specifies default dynamic range compression (1), leave it alone.
+       If a user specifies no dynamic range compression (0), call a null function. */
+    if( pv->dynamic_range_compression > 1.0 )
     {
-        a52_dynrng( pv->state, dynrng_call, &pv->dynamic_range_compression);
+        a52_dynrng( pv->state, dynrng_call, &pv->dynamic_range_compression );
+    }
+    else if( !pv->dynamic_range_compression )
+    {
+        a52_dynrng( pv->state, NULL, NULL );
     }
 
     /* 6 blocks per frame, 256 samples per block, channelsused channels */
     buf        = hb_buffer_init( 6 * 256 * pv->out_discrete_channels * sizeof( float ) );
-    buf->start = pts + ( pos / pv->size ) * 6 * 256 * 90000 / pv->rate;
-    buf->stop  = buf->start + 6 * 256 * 90000 / pv->rate;
-
-    /*
-       * To track AC3 PTS add this back in again.
-        *hb_log("AC3: pts is %lld, buf->start %lld buf->stop %lld", pts, buf->start, buf->stop);
-        */
-
-    pv->next_expected_pts = buf->stop;
+    buf->start = pts;
+    pts += frame_dur;
+    buf->stop  = pts;
+    pv->next_expected_pts = pts;
 
     for( i = 0; i < 6; i++ )
     {
@@ -280,56 +311,100 @@ static hb_buffer_t * Decode( hb_work_object_t * w )
         }
 
     }
-
-    pv->sync = 0;
     return buf;
 }
 
-static int deca52BSInfo( hb_work_object_t *w, const hb_buffer_t *b,
-                         hb_work_info_t *info )
+static int find_sync( const uint8_t *buf, int len )
 {
     int i;
-    int rate = 0, bitrate = 0, flags = 0;
-    int old_rate = 0, old_bitrate = 0;
-    uint8_t raw;
 
+    // since AC3 frames don't line up with MPEG ES frames scan the
+    // frame for an AC3 sync pattern.
+    for ( i = 0; i < len - 16; ++i )
+    {
+        int rate, bitrate, flags;
+        int size = a52_syncinfo( (uint8_t *)buf + i, &flags, &rate, &bitrate );
+        if( size > 0 )
+        {
+            // we have a plausible sync header - see if crc1 checks
+            int crc1size = (size >> 1) + (size >> 3); 
+            if ( i + crc1size > len )
+            {
+                // don't have enough data to check crc1
+                break;
+            }
+            if ( av_crc( av_crc_get_table( AV_CRC_16_ANSI ), 0,
+                         buf + i + 2, crc1size - 2 ) == 0 )
+            {
+                // crc checks - we've got sync
+                return i;
+            }
+        }
+    }
+    return -1;
+}
+
+static int deca52BSInfo( hb_work_object_t *w, const hb_buffer_t *b,
+                         hb_work_info_t *info )
+{
     memset( info, 0, sizeof(*info) );
 
-    /* since AC3 frames don't line up with MPEG ES frames scan the
-     * entire frame for an AC3 sync pattern.  */
-    for ( i = 0; i < b->size - 7; ++i )
+    // We don't know if the way that AC3 frames are fragmented into whatever
+    // packetization the container uses will give us enough bytes per fragment
+    // to check the CRC (we need at least 5/8 of the the frame). So we
+    // copy the fragment we got into an accumulation buffer in the audio object
+    // then look for sync over all the frags we've accumulated so far.
+    uint8_t *buf = w->audio->priv.config.a52.buf;
+    int len = w->audio->priv.config.a52.len, blen = b->size;
+    if ( len + blen > sizeof(w->audio->priv.config.a52.buf) )
     {
-        if( a52_syncinfo( &b->data[i], &flags, &rate, &bitrate ) != 0 )
+        // we don't have enough empty space in the accumulation buffer to
+        // hold the new frag - make room for it by discarding the oldest data.
+        if ( blen >= sizeof(w->audio->priv.config.a52.buf) )
         {
-            /*
-             * Got sync apparently, save these values and check that they
-             * also match when we get sync again.
-             */
-            if( old_rate ) 
-            {
-                if( rate == old_rate && bitrate == old_bitrate )
-                {
-                    break;
-                } 
-            } 
-            
-            old_rate = rate;
-            old_bitrate = bitrate;
-            raw = b->data[i+5];
+            // the frag is bigger than our accumulation buffer - copy all
+            // that will fit (the excess doesn't matter since the buffer
+            // is many times the size of a max length ac3 frame).
+            blen = sizeof(w->audio->priv.config.a52.buf);
+            len = 0;
+        }
+        else
+        {
+            // discard enough bytes from the front of the buffer to make
+            // room for the new stuff
+            int newlen = sizeof(w->audio->priv.config.a52.buf) - blen;
+            memcpy( buf, buf + len - newlen, newlen );
+            len = newlen;
         }
     }
-    if ( rate == 0 || bitrate == 0 )
+    // add the new frag to the buffer
+    memcpy( buf+len, b->data, blen );
+    len += blen;
+
+    int i;
+    if ( ( i = find_sync( buf, len ) ) < 0 )
     {
-        /* didn't find AC3 sync */
+        // didn't find sync - wait for more data
+        w->audio->priv.config.a52.len = len;
         return 0;
     }
 
-    /*
-     * bsid | bsmod | acmod | cmixlev | surmixlev | dsurmod | lfeon | dialnorm | compre
-     *    5       3       3         2           2         2       1          5        1
-     * [    byte1  ][         byte2                    ][   byte3                     ]
-     */
+    // got sync - extract and canoncalize the bitstream parameters
+    int rate = 0, bitrate = 0, flags = 0;
+    uint8_t raw = buf[i + 5];
+    a52_syncinfo( buf + i, &flags, &rate, &bitrate );
+
+    if ( rate == 0 || bitrate == 0 )
+    {
+        // invalid AC-3 parameters - toss what we have so we'll start over
+        // with the next buf otherwise we'll keep syncing on this junk.
+        w->audio->priv.config.a52.len = 0;
+        return 0;
+    }
 
+    // bsid | bsmod | acmod | cmixlv | surmixlv | dsurmod | lfeon | dialnorm | compre
+    //   5       3      3        2         2         2        1          5        1
+    //      byte1   |          byte2                 |    byte3  
 
     info->name = "AC-3";
     info->rate = rate;