diff options
-rw-r--r-- | libhb/deca52.c | 260 | ||||
-rw-r--r-- | libhb/internal.h | 6 |
2 files changed, 169 insertions, 97 deletions
diff --git a/libhb/deca52.c b/libhb/deca52.c index 55590242e..f983a6528 100644 --- a/libhb/deca52.c +++ b/libhb/deca52.c @@ -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,64 +184,89 @@ 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 ) ) { - /* It is. W00t. */ + // 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 ) + { + // 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 ) { - pts = pv->next_expected_pts; + ++pv->crc_errors; + return NULL; } + ++pv->frames; + if ( ipts != pv->last_buf_pts ) + { + pv->last_buf_pts = ipts; + } + else + { + // 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; } @@ -251,15 +280,10 @@ static hb_buffer_t * Decode( hb_work_object_t * w ) /* 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 +304,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; diff --git a/libhb/internal.h b/libhb/internal.h index 29a88dca6..19a3bd2b3 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -233,7 +233,11 @@ union hb_esconfig_u struct { /* ac3flags stores the flags from the AC3 source, as found in scan.c */ - int ac3flags; + int ac3flags; + // next two items are used by the bsinfo routine to accumulate small + // frames until we have enough to validate the crc. + int len; // space currently used in 'buf' + uint8_t buf[HB_CONFIG_MAX_SIZE-sizeof(int)]; } a52; struct |