diff options
author | van <[email protected]> | 2008-04-02 17:55:48 +0000 |
---|---|---|
committer | van <[email protected]> | 2008-04-02 17:55:48 +0000 |
commit | 7d87a5afd0a6764e9ef041053ba56234480aef81 (patch) | |
tree | 250e7f1e90a43b1fd2173f133281d81a2d34f7e2 | |
parent | f960fc7344f13893175b062bffc0877634aacceb (diff) |
scan and pcm audio fixes.
- lpcm audio fixed to handle 24 bit & interpret header correctly.
- get aspect ratio from libmpeg2 rather than doing it ourselves.
- announce when aspect ratio changes during preview scan.
- if aspect ratio isn't either 4:3 or 16:9 complain & map to either 4:3 or 16:9 (whichever is closest).
- start stream previews from file position 0 rather than 1/11 in case there's only on mpeg sequence header in the file.
- don't give up on a file just because we can't get a preview due to a missing sequence header - only give up if we can't get any previews.
- get audio bitstream characteristics during preview in a uniform way (we were treating PCM & MPEG audio specially which resulted in not getting their sample rate which caused a divide by zero in sync).
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@1370 b64f7644-9d1e-0410-96f1-a4d463321fa5
-rw-r--r-- | libhb/declpcm.c | 233 | ||||
-rw-r--r-- | libhb/decmpeg2.c | 33 | ||||
-rw-r--r-- | libhb/internal.h | 1 | ||||
-rw-r--r-- | libhb/scan.c | 536 | ||||
-rwxr-xr-x | libhb/stream.c | 143 |
5 files changed, 531 insertions, 415 deletions
diff --git a/libhb/declpcm.c b/libhb/declpcm.c index 155331315..884680b94 100644 --- a/libhb/declpcm.c +++ b/libhb/declpcm.c @@ -6,6 +6,27 @@ #include "hb.h" +struct hb_work_private_s +{ + hb_job_t *job; + uint32_t size; /* frame size in bytes */ + uint32_t count; /* frame size in samples */ + uint32_t pos; /* buffer offset for next input data */ + + int64_t next_pts; /* pts for next output frame */ + int64_t sequence; + + /* the following is frame info for the frame we're currently accumulating */ + uint64_t duration; /* frame duratin (in 90KHz ticks) */ + uint32_t offset; /* where in buf frame starts */ + uint32_t samplerate; /* sample rate in bits/sec */ + uint8_t nchannels; + uint8_t sample_size; /* bits per sample */ + + uint8_t frame[HB_DVD_READ_BUFFER_SIZE*2]; +}; + +static hb_buffer_t * Decode( hb_work_object_t * w ); int declpcmInit( hb_work_object_t *, hb_job_t * ); int declpcmWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); void declpcmClose( hb_work_object_t * ); @@ -19,72 +40,196 @@ hb_work_object_t hb_declpcm = declpcmClose }; +static const int hdr2samplerate[] = { 48000, 96000, 44100, 32000 }; +static const int hdr2samplesize[] = { 16, 20, 24, 16 }; + +static void lpcmInfo( hb_work_object_t *w, hb_buffer_t *in ) +{ + hb_work_private_t * pv = w->private_data; + + /* + * LPCM packets have a 7 byte header (the substream id is stripped off + * before we get here so it's numbered -1 below):: + * byte -1 Substream id + * byte 0 Number of frames that begin in this packet + * (last frame may finish in next packet) + * byte 1,2 offset to first frame that begins in this packet (not including hdr) + * byte 3: + * bits 0-4 continuity counter (increments modulo 20) + * bit 5 reserved + * bit 6 audio mute on/off + * bit 7 audio emphasis on/off + * byte 4: + * bits 0-2 #channels - 1 (e.g., stereo = 1) + * bit 3 reserved + * bits 4-5 sample rate (0=48K,1=96K,2=44.1K,3=32K) + * bits 6-7 bits per sample (0=16 bit, 1=20 bit, 2=24 bit) + * byte 5 Dynamic range control (0x80 = off) + * + * The audio is viewed as "frames" of 150 90KHz ticks each (80 samples @ 48KHz). + * The frames are laid down continuously without regard to MPEG packet + * boundaries. E.g., for 48KHz stereo, the first packet will contain 6 + * frames plus the start of the 7th, the second packet will contain the + * end of the 7th, 8-13 & the start of 14, etc. The frame structure is + * important because the PTS on the packet gives the time of the first + * frame that starts in the packet *NOT* the time of the first sample + * in the packet. Also samples get split across packet boundaries + * so we can't assume that we can consume all the data in one packet + * on every call to the work routine. + */ + pv->offset = ( ( in->data[1] << 8 ) | in->data[2] ) + 2; + if ( pv->offset >= HB_DVD_READ_BUFFER_SIZE ) + { + hb_log( "declpcm: illegal frame offset %d", pv->offset ); + pv->offset = 2; /*XXX*/ + } + pv->samplerate = hdr2samplerate[ ( in->data[4] >> 4 ) & 0x3 ]; + pv->nchannels = ( in->data[4] & 7 ) + 1; + pv->sample_size = hdr2samplesize[in->data[4] >> 6]; + + /* + * PCM frames have a constant duration (150 90KHz ticks). + * We need to convert that to the amount of data expected. It's the + * duration divided by the sample rate (to get #samples) times the number + * of channels times the bits per sample divided by 8 to get bytes. + * (we have to compute in bits because 20 bit samples are not an integral + * number of bytes). We do all the multiplies first then the divides to + * avoid truncation errors. + */ + pv->duration = in->data[0] * 150; + pv->count = ( pv->duration * pv->nchannels * pv->samplerate ) / 90000; + pv->size = ( pv->count * pv->sample_size ) / 8; + + pv->next_pts = in->start; +} + int declpcmInit( hb_work_object_t * w, hb_job_t * job ) { + hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); + w->private_data = pv; + pv->job = job; return 0; } +/* + * Convert DVD encapsulated LPCM to floating point PCM audio buffers. + * The amount of audio in a PCM frame is always <= the amount that will fit + * in a DVD block (2048 bytes) but the standard doesn't require that the audio + * frames line up with the DVD frames. Since audio frame boundaries are unrelated + * to DVD PES boundaries, this routine has to reconstruct then extract the audio + * frames. Because of the arbitrary alignment, it can output zero, one or two buf's. + */ int declpcmWork( hb_work_object_t * w, hb_buffer_t ** buf_in, hb_buffer_t ** buf_out ) { - hb_buffer_t * in = *buf_in, * out; - int samplerate = 0; - int count; - uint8_t * samples_u8; - float * samples_fl32; - int i; - uint64_t duration; - - *buf_out = NULL; + hb_work_private_t * pv = w->private_data; + hb_buffer_t *in; + hb_buffer_t *buf = NULL; - if( in->data[5] != 0x80 ) + /* need an input buffer to do anything */ + if( ! buf_in || ! ( in = *buf_in ) ) { - hb_log( "no LPCM frame sync (%02x)", in->data[5] ); + *buf_out = buf; return HB_WORK_OK; } - switch( ( in->data[4] >> 4 ) & 0x3 ) + pv->sequence = in->sequence; + + /* if we have a frame to finish, add enough data from this buf to finish it */ + if ( pv->size ) { - case 0: - samplerate = 48000; - break; - case 1: - samplerate = 96000;//32000; /* FIXME vlc says it is 96000 */ - break; - case 2: - samplerate = 44100; - break; - case 3: - samplerate = 32000; - break; + memcpy( pv->frame + pv->pos, in->data + 6, pv->size - pv->pos ); + buf = Decode( w ); } + *buf_out = buf; - count = ( in->size - 6 ) / 2; - out = hb_buffer_init( count * sizeof( float ) ); - duration = count * 90000 / samplerate / 2; - out->start = in->start; - out->stop = out->start + duration; - - samples_u8 = in->data + 6; - samples_fl32 = (float *) out->data; - - /* Big endian int16 -> float conversion */ - for( i = 0; i < count; i++ ) + /* save the (rest of) data from this buf in our frame buffer */ + lpcmInfo( w, in ); + int off = pv->offset; + int amt = in->size - off; + pv->pos = amt; + memcpy( pv->frame, in->data + off, amt ); + if ( amt >= pv->size ) { -#ifdef WORDS_BIGENDIAN - samples_fl32[0] = *( (int16_t *) samples_u8 ); -#else - samples_fl32[0] = (int16_t) ( ( samples_u8[0] << 8 ) | samples_u8[1] ); -#endif - samples_u8 += 2; - samples_fl32 += 1; + if ( buf ) + { + buf->next = Decode( w ); + } + else + { + *buf_out = Decode( w ); + } + pv->size = 0; } + return HB_WORK_OK; +} - *buf_out = out; +static hb_buffer_t *Decode( hb_work_object_t *w ) +{ + hb_work_private_t *pv = w->private_data; + hb_buffer_t *out = hb_buffer_init( pv->count * sizeof( float ) ); + + out->start = pv->next_pts; + pv->next_pts += pv->duration; + out->stop = pv->next_pts; - return HB_WORK_OK; + uint8_t *frm = pv->frame; + float *odat = (float *)out->data; + int count = pv->count; + + switch( pv->sample_size ) + { + case 16: // 2 byte, big endian, signed (the right shift sign extends) + while ( --count >= 0 ) + { + *odat++ = ( (int)( frm[0] << 24 ) >> 16 ) | frm[1]; + frm += 2; + } + break; + case 20: + // 20 bit big endian signed (5 bytes for 2 samples = 2.5 bytes/sample + // so we do two samples per iteration). + count /= 2; + while ( --count >= 0 ) + { + *odat++ = (float)( ( (int)( frm[0] << 24 ) >> 12 ) | + ( frm[1] << 4 ) | ( frm[2] >> 4 ) ) / 16.; + *odat++ = (float)( ( (int)( frm[2] << 28 ) >> 16 ) | + ( frm[3] << 8 ) | frm[4] ) / 16.; + frm += 5; + } + break; + case 24: + // This format is bizarre. It's 24 bit samples but some confused + // individual apparently thought they would be easier to interpret + // as 16 bits if they were scrambled in the following way: + // Things are stored in 4 sample (12 byte) chunks. Each chunk has + // 4 samples containing the two top bytes of the actual samples in + // 16 bit big-endian order followed by the four least significant bytes + // of each sample. + count /= 4; // the loop has to work in 4 sample chunks + while ( --count >= 0 ) + { + *odat++ = (float)( ( (int)( frm[0] << 24 ) >> 8 ) | + ( frm[1] << 8 ) | frm[8] ) / 256.; + *odat++ = (float)( ( (int)( frm[2] << 24 ) >> 8 ) | + ( frm[3] << 8 ) | frm[9] ) / 256.; + *odat++ = (float)( ( (int)( frm[4] << 24 ) >> 8 ) | + ( frm[5] << 8 ) | frm[10] ) / 256.; + *odat++ = (float)( ( (int)( frm[6] << 24 ) >> 8 ) | + ( frm[7] << 8 ) | frm[11] ) / 256.; + frm += 12; + } + break; + } + return out; } void declpcmClose( hb_work_object_t * w ) { + if ( w->private_data ) + { + free( w->private_data ); + w->private_data = 0; + } } diff --git a/libhb/decmpeg2.c b/libhb/decmpeg2.c index 576b04591..4f0c27ddd 100644 --- a/libhb/decmpeg2.c +++ b/libhb/decmpeg2.c @@ -100,28 +100,21 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es, m->width = m->info->sequence->width; m->height = m->info->sequence->height; m->rate = m->info->sequence->frame_period; - } - if ( m->aspect_ratio <= 0 ) - { - // We can parse out the aspect ratio from the Sequence Start Header data in buf_es->data - - // Make sure we have the correct data in the buffer - if ((buf_es->data[0] == 0x00) && (buf_es->data[1] == 0x00) && (buf_es->data[2] == 0x01) && (buf_es->data[3] == 0xb3)) - { - unsigned char ar_fr = buf_es->data[7]; // Top 4 bits == aspect ratio flag - bottom 4 bits == rate flags - switch ((ar_fr & 0xf0) >> 4) + if ( m->aspect_ratio <= 0 && m->height && + m->info->sequence->pixel_height ) { - case 2: - m->aspect_ratio = HB_ASPECT_BASE * 4 / 3; // 4:3 - break; - case 3: - m->aspect_ratio = HB_ASPECT_BASE * 16 / 9; // 16:9 - break; - default: - hb_log("hb_libmpeg2_decode - STATE_SEQUENCE unexpected aspect ratio/frame rate 0x%x\n", ar_fr); - break; + /* mpeg2_parse doesn't store the aspect ratio. Instead + * it keeps the pixel width & height that would cause + * the storage width & height to come out in the correct + * aspect ratio. Convert these back to aspect ratio. + * We do the calc in floating point to get the rounding right. + * We round in the second decimal digit because we scale + * the (integer) aspect by 9 to preserve the 1st digit. + */ + double ar_numer = m->width * m->info->sequence->pixel_width; + double ar_denom = m->height * m->info->sequence->pixel_height; + m->aspect_ratio = ( ar_numer / ar_denom + .05 ) * HB_ASPECT_BASE; } - } } } else if( state == STATE_GOP && m->look_for_break == 2) diff --git a/libhb/internal.h b/libhb/internal.h index 8095967ac..52683464f 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -141,7 +141,6 @@ void hb_stream_close( hb_stream_t ** ); hb_title_t * hb_stream_title_scan( hb_stream_t *); int hb_stream_read( hb_stream_t *, hb_buffer_t *); int hb_stream_seek( hb_stream_t *, float ); -void hb_stream_update_audio( hb_stream_t *, hb_audio_t *); /*********************************************************************** * Work objects diff --git a/libhb/scan.c b/libhb/scan.c index 6c97920da..814492fc9 100644 --- a/libhb/scan.c +++ b/libhb/scan.c @@ -23,8 +23,8 @@ typedef struct static void ScanFunc( void * ); static int DecodePreviews( hb_scan_t *, hb_title_t * title ); -static void LookForAC3AndDCA( hb_title_t * title, hb_buffer_t * b ); -static int AllAC3AndDCAOK( hb_title_t * title ); +static void LookForAudio( hb_title_t * title, hb_buffer_t * b ); +static int AllAudioOK( hb_title_t * title ); hb_thread_t * hb_scan_init( hb_handle_t * handle, const char * path, int title_index, hb_list_t * list_title ) @@ -132,41 +132,14 @@ static void ScanFunc( void * _data ) continue; } - if (data->stream) - { - // Stream based processing uses PID's to handle the different audio options for a given title - for( j = 0; j < hb_list_count( title->list_audio ); j++ ) - { - audio = hb_list_item( title->list_audio, j ); - hb_stream_update_audio(data->stream, audio); - } - } - else if (data->dvd) - { - /* Make sure we found AC3 rates and bitrates */ - for( j = 0; j < hb_list_count( title->list_audio ); ) - { - audio = hb_list_item( title->list_audio, j ); - if( audio->config.in.codec == HB_ACODEC_AC3 && - !audio->config.in.bitrate ) - { - hb_list_rem( title->list_audio, audio ); - free( audio ); - continue; - } - j++; - } - } - - /* Make sure we found AC3 / DCA rates and bitrates */ + /* Make sure we found audio rates and bitrates */ for( j = 0; j < hb_list_count( title->list_audio ); ) { audio = hb_list_item( title->list_audio, j ); - if( ( audio->config.in.codec == HB_ACODEC_AC3 || audio->config.in.codec == HB_ACODEC_DCA ) && - !audio->config.in.bitrate ) + if( !audio->config.in.bitrate ) { - hb_log( "scan: removing audio with codec of 0x%x because of no bitrate", - audio->config.in.codec ); + hb_log( "scan: removing audio 0x%x because no bitrate found", + audio->id ); hb_list_rem( title->list_audio, audio ); free( audio ); continue; @@ -174,7 +147,7 @@ static void ScanFunc( void * _data ) j++; } - /* Do we still have audio */ + /* If we don't have any audio streams left, remove the title */ if( !hb_list_count( title->list_audio ) ) { hb_list_rem( data->list_title, title ); @@ -182,18 +155,6 @@ static void ScanFunc( void * _data ) continue; } - /* set a default input channel layout of stereo for LPCM or MPEG2 audio */ - /* AC3 and DCA will already have had their layout set via DecodePreviews above, */ - /* which calls LookForAC3AndDCA */ - for( j = 0; j < hb_list_count( title->list_audio ); j++ ) - { - audio = hb_list_item( title->list_audio, j ); - if( audio->config.in.codec == HB_ACODEC_LPCM || audio->config.in.codec == HB_ACODEC_MPGA ) - { - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; - } - } - i++; } @@ -227,7 +188,6 @@ static void ScanFunc( void * _data ) } job->width = title->width - job->crop[2] - job->crop[3]; -// job->height = title->height - job->crop[0] - job->crop[1]; hb_fix_aspect( job, HB_KEEP_WIDTH ); if( job->height > title->height - job->crop[0] - job->crop[1] ) { @@ -235,8 +195,8 @@ static void ScanFunc( void * _data ) hb_fix_aspect( job, HB_KEEP_HEIGHT ); } - hb_log( "scan: title (%d) job->width:%d, job->height:%d", - i,job->width, job->height ); + hb_log( "scan: title (%d) job->width:%d, job->height:%d", + i, job->width, job->height ); job->keep_ratio = 1; @@ -282,8 +242,7 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) hb_libmpeg2_t * mpeg2; int progressive_count = 0; int interlaced_preview_count = 0; - - int ar16_count = 0, ar4_count = 0; + int last_ar = 0, ar16_count = 0, ar4_count = 0; buf_ps = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE ); list_es = hb_list_init(); @@ -300,8 +259,6 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) FILE * file_preview; char filename[1024]; - //hb_log("Seeking to: %f", (float) ( i + 1 ) / 11.0 ); - if (data->dvd) { if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / 11.0 ) ) @@ -311,7 +268,10 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) } else if (data->stream) { - if (!hb_stream_seek(data->stream, (float) ( i + 1 ) / 11.0 ) ) + /* we start reading streams at zero rather than 1/11 because + * short streams may have only one sequence header in the entire + * file and we need it to decode any previews. */ + if (!hb_stream_seek(data->stream, (float) i / 11.0 ) ) { goto error; } @@ -350,26 +310,50 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) int ar = hb_libmpeg2_clear_aspect_ratio( mpeg2 ); if ( ar != 0 ) { - ( ar == (HB_ASPECT_BASE * 4 / 3) ) ? - ++ar4_count : ++ar16_count ; + if ( ar != last_ar && last_ar != 0 ) + { + hb_log( "aspect ratio changed from %d to %d", + last_ar, ar ); + } + switch ( ar ) + { + case HB_ASPECT_BASE * 4 / 3: + ++ar4_count; + break; + case HB_ASPECT_BASE * 16 / 9: + ++ar16_count; + break; + default: + hb_log( "unknown aspect ratio %d", ar ); + /* if the aspect is closer to 4:3 use that + * otherwise use 16:9 */ + if ( ar < HB_ASPECT_BASE * 14 / 9 ) + { + ++ar4_count; + } + else + { + ++ar16_count; + } + break; + } } + last_ar = ar; } - else if( !i ) + else if( ! AllAudioOK( title ) ) { - LookForAC3AndDCA( title, buf_es ); + LookForAudio( title, buf_es ); } hb_buffer_close( &buf_es ); - if( hb_list_count( list_raw ) && - ( i || AllAC3AndDCAOK( title ) ) ) + if( hb_list_count( list_raw ) && AllAudioOK( title ) ) { /* We got a picture */ break; } } - if( hb_list_count( list_raw ) && - ( i || AllAC3AndDCAOK( title ) ) ) + if( hb_list_count( list_raw ) && AllAudioOK( title ) ) { break; } @@ -378,7 +362,7 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) if( !hb_list_count( list_raw ) ) { hb_log( "scan: could not get a decoded picture" ); - goto error; + continue; } /* Get size and rate infos */ @@ -564,206 +548,292 @@ cleanup: return npreviews; } -static void LookForAC3AndDCA( hb_title_t * title, hb_buffer_t * b ) +static void update_audio_description( const char *codec, hb_audio_t *audio, + int is_dolby ) { - int i; - int flags; - int rate; - int bitrate; - int frame_length; - dca_state_t * state; + hb_log( "scan: %s, rate=%dHz, bitrate=%d", codec, audio->config.in.samplerate, + audio->config.in.bitrate ); - /* Figure out if this is a AC3 or DCA buffer for a known track */ - hb_audio_t * audio = NULL; - for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + /* XXX */ + if ( is_dolby ) { - audio = hb_list_item( title->list_audio, i ); - /* check if we have an AC3 or DCA which we recognise */ - if( ( audio->config.in.codec == HB_ACODEC_AC3 || audio->config.in.codec == HB_ACODEC_DCA ) && - audio->id == b->id ) + strcat( audio->config.lang.description, " (Dolby Surround)" ); + return; + } + + char *desc = audio->config.lang.description + + strlen( audio->config.lang.description ); + sprintf( desc, " (%d.%d ch)", + HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(audio->config.in.channel_layout) + + HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(audio->config.in.channel_layout), + HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(audio->config.in.channel_layout)); +} + +static int hb_setup_a52_audio( hb_audio_t *audio, hb_buffer_t *b ) +{ + int i, rate, bitrate, flags; + + /* 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 ) + { + if( a52_syncinfo( &b->data[i], &flags, &rate, &bitrate ) != 0 ) { break; } - else - { - audio = NULL; - } } - if( !audio ) + if ( i >= b->size - 7 ) { - return; + /* didn't find AC3 sync */ + return 0; } - if( audio->config.in.bitrate ) + audio->config.in.samplerate = rate; + audio->config.in.bitrate = bitrate; + + switch( flags & A52_CHANNEL_MASK ) { - /* Already done for this track */ - return; + /* mono sources */ + case A52_MONO: + case A52_CHANNEL1: + case A52_CHANNEL2: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_MONO; + break; + /* stereo input */ + case A52_CHANNEL: + case A52_STEREO: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; + break; + /* dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input */ + case A52_DOLBY: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_DOLBY; + break; + /* 3F/2R input */ + case A52_3F2R: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F2R; + break; + /* 3F/1R input */ + case A52_3F1R: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F1R; + break; + /* other inputs */ + case A52_3F: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F; + break; + case A52_2F1R: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F1R; + break; + case A52_2F2R: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F2R; + break; + /* unknown */ + default: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; } - for( i = 0; i < b->size - 7; i++ ) + if (flags & A52_LFE) { + audio->config.in.channel_layout |= HB_INPUT_CH_LAYOUT_HAS_LFE; + } - if ( audio->config.in.codec == HB_ACODEC_AC3 ) + /* store the AC3 flags for future reference + * This enables us to find out if we had a stereo or Dolby source later on + * Store the ac3 flags in the public ac3flags property too, so we can access + * it from the GUI + */ + audio->config.flags.ac3 = audio->priv.config.a52.ac3flags = flags; + update_audio_description( "AC3", audio, (flags & A52_CHANNEL_MASK) == A52_DOLBY ); + return 1; +} + +static int hb_setup_dca_audio( hb_audio_t *audio, hb_buffer_t *b ) +{ + int i, flags, rate, bitrate, frame_length; + dca_state_t * state = dca_init( 0 ); + + /* since DCA frames don't line up with MPEG ES frames scan the + * entire frame for an DCA sync pattern. */ + for ( i = 0; i < b->size - 7; ++i ) + { + if( dca_syncinfo( state, &b->data[i], &flags, &rate, &bitrate, + &frame_length ) ) { + break; + } + } + if ( i >= b->size - 7 ) + { + /* didn't find DCA sync */ + return 0; + } - /* check for a52 */ - if( a52_syncinfo( &b->data[i], &flags, &rate, &bitrate ) ) - { - hb_log( "scan: AC3, rate=%dHz, bitrate=%d", rate, bitrate ); - audio->config.in.samplerate = rate; - audio->config.in.bitrate = bitrate; - switch( flags & A52_CHANNEL_MASK ) - { - /* mono sources */ - case A52_MONO: - case A52_CHANNEL1: - case A52_CHANNEL2: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_MONO; - break; - /* stereo input */ - case A52_CHANNEL: - case A52_STEREO: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; - break; - /* dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input */ - case A52_DOLBY: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_DOLBY; - break; - /* 3F/2R input */ - case A52_3F2R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F2R; - break; - /* 3F/1R input */ - case A52_3F1R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F1R; - break; - /* other inputs */ - case A52_3F: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F; - break; - case A52_2F1R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F1R; - break; - case A52_2F2R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F2R; - break; - /* unknown */ - default: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; - } + audio->config.in.samplerate = rate; + audio->config.in.bitrate = bitrate; + switch( flags & DCA_CHANNEL_MASK ) + { + /* mono sources */ + case DCA_MONO: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_MONO; + break; + /* stereo input */ + case DCA_CHANNEL: + case DCA_STEREO: + case DCA_STEREO_SUMDIFF: + case DCA_STEREO_TOTAL: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; + break; + /* 3F/2R input */ + case DCA_3F2R: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F2R; + break; + /* 3F/1R input */ + case DCA_3F1R: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F1R; + break; + /* other inputs */ + case DCA_3F: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F; + break; + case DCA_2F1R: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F1R; + break; + case DCA_2F2R: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F2R; + break; + case DCA_4F2R: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_4F2R; + break; + /* unknown */ + default: + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; + } - /* add in our own LFE flag if the source has LFE */ - if (flags & A52_LFE) - { - audio->config.in.channel_layout = audio->config.in.channel_layout | HB_INPUT_CH_LAYOUT_HAS_LFE; - } + if (flags & DCA_LFE) + { + audio->config.in.channel_layout |= HB_INPUT_CH_LAYOUT_HAS_LFE; + } - /* store the AC3 flags for future reference - * This enables us to find out if we had a stereo or Dolby source later on - * Store the ac3 flags in the public ac3flags property too, so we can access it from the GUI - */ - audio->config.flags.ac3 = audio->priv.config.a52.ac3flags = flags; - - /* XXX */ - if ( (flags & A52_CHANNEL_MASK) == A52_DOLBY ) { - sprintf( audio->config.lang.description + strlen( audio->config.lang.description ), - " (Dolby Surround)" ); - } else { - sprintf( audio->config.lang.description + strlen( audio->config.lang.description ), - " (%d.%d ch)", - HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(audio->config.in.channel_layout) + - HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(audio->config.in.channel_layout), - HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(audio->config.in.channel_layout)); - } + /* store the DCA flags for future reference + * This enables us to find out if we had a stereo or Dolby source later on + * store the dca flags in the public dcaflags property too, so we can access + * it from the GUI + */ + audio->config.flags.dca = audio->priv.config.dca.dcaflags = flags; + update_audio_description( "DCA", audio, (flags & DCA_CHANNEL_MASK) == DCA_DOLBY ); + return 1; +} - break; +static int hb_setup_pcm_audio( hb_audio_t *audio, hb_buffer_t *b ) +{ + // LPCM doesn't have a sync pattern like AC3 or DCA but every + // LPCM elementary stream packet starts with a 7 byte header + // giving the characteristics of the stream. + // See libhb/declpcm.c for a description of the LPCM header. + + static const int hdr2samplerate[] = { 48000, 96000, 44100, 32000 }; + static const int hdr2samplesize[] = { 16, 20, 24, 16 }; + static const int hdr2layout[] = { + HB_INPUT_CH_LAYOUT_MONO, HB_INPUT_CH_LAYOUT_STEREO, + HB_INPUT_CH_LAYOUT_2F1R, HB_INPUT_CH_LAYOUT_2F2R, + HB_INPUT_CH_LAYOUT_3F2R, HB_INPUT_CH_LAYOUT_4F2R, + HB_INPUT_CH_LAYOUT_STEREO, HB_INPUT_CH_LAYOUT_STEREO, + }; + + int nchannels = ( b->data[4] & 7 ) + 1; + int sample_size = hdr2samplesize[b->data[4] >> 6]; + + int rate = hdr2samplerate[ ( b->data[4] >> 4 ) & 0x3 ]; + int bitrate = rate * sample_size * nchannels; + + audio->config.in.samplerate = rate; + audio->config.in.bitrate = bitrate; + audio->config.in.channel_layout = hdr2layout[nchannels - 1]; + update_audio_description( "LPCM", audio, 0 ); + return 1; +} - } +static int hb_setup_mpg_audio( hb_audio_t *audio, hb_buffer_t *b ) +{ + /* XXX + * This is a placeholder to get the audio sample rate set. + * It should be replaced by something that extracts the correct info from + * the mpeg audio bitstream. + */ + audio->config.in.samplerate = 48000; + audio->config.in.bitrate = 384000; + audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; + update_audio_description( "MPGA", audio, 0 ); + return 1; +} + +/* + * This routine is called for every frame from a non-video elementary stream. + * These are a mix of audio & subtitle streams, some of which we want & some + * we're ignoring. This routine checks the frame against all our audio streams + * to see if it's one we want and haven't identified yet. If yes, it passes the + * frame to a codec-specific id routine which is responsible for filling in + * the sample rate, bit rate, channels & other audio parameters. + * + * Since a sample rate is essential for further audio processing, any audio + * stream which isn't successfully id'd by is deleted at the end of the scan. + * This is necessary to avoid ambiguities where things that might be audio + * aren't (e.g., some European DVD Teletext streams use the same IDs as US ATSC + * AC-3 audio). + */ +static void LookForAudio( hb_title_t * title, hb_buffer_t * b ) +{ + int i; + hb_audio_t * audio = NULL; + for( i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = hb_list_item( title->list_audio, i ); + /* check if this elementary stream is one we want */ + if ( audio->id == b->id ) + { + break; } - else if ( audio->config.in.codec == HB_ACODEC_DCA ) + else { + audio = NULL; + } + } + if( !audio || audio->config.in.bitrate != 0 ) + { + /* not found or already done */ + return; + } - hb_log( "scan: checking for DCA syncinfo" ); + switch ( audio->config.in.codec ) + { + case HB_ACODEC_AC3: + hb_setup_a52_audio( audio, b ); + break; - /* check for dca */ - state = dca_init( 0 ); - if( dca_syncinfo( state, &b->data[i], &flags, &rate, &bitrate, &frame_length ) ) - { - hb_log( "scan: DCA, rate=%dHz, bitrate=%d", rate, bitrate ); - audio->config.in.samplerate = rate; - audio->config.in.bitrate = bitrate; - switch( flags & DCA_CHANNEL_MASK ) - { - /* mono sources */ - case DCA_MONO: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_MONO; - break; - /* stereo input */ - case DCA_CHANNEL: - case DCA_STEREO: - case DCA_STEREO_SUMDIFF: - case DCA_STEREO_TOTAL: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; - break; - /* 3F/2R input */ - case DCA_3F2R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F2R; - break; - /* 3F/1R input */ - case DCA_3F1R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F1R; - break; - /* other inputs */ - case DCA_3F: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F; - break; - case DCA_2F1R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F1R; - break; - case DCA_2F2R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F2R; - break; - case DCA_4F2R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_4F2R; - break; - /* unknown */ - default: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; - } + case HB_ACODEC_DCA: + hb_setup_dca_audio( audio, b ); + break; - /* add in our own LFE flag if the source has LFE */ - if (flags & DCA_LFE) - { - audio->config.in.channel_layout = audio->config.in.channel_layout | HB_INPUT_CH_LAYOUT_HAS_LFE; - } + case HB_ACODEC_LPCM: + hb_setup_pcm_audio( audio, b ); + break; - /* store the DCA flags for future reference - * This enables us to find out if we had a stereo or Dolby source later on - * store the dca flags in the public dcaflags property too, so we can access it from the GUI - */ - audio->config.flags.dca = audio->priv.config.dca.dcaflags = flags; - - /* XXX */ - if ( (flags & DCA_CHANNEL_MASK) == DCA_DOLBY ) { - sprintf( audio->config.lang.description + strlen( audio->config.lang.description ), - " (Dolby Surround)" ); - } else { - sprintf( audio->config.lang.description + strlen( audio->config.lang.description ), - " (%d.%d ch)", - HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(audio->config.in.channel_layout) + - HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(audio->config.in.channel_layout), - HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(audio->config.in.channel_layout)); - } + case HB_ACODEC_MPGA: + hb_setup_mpg_audio( audio, b ); + break; - break; - } - } + default: + hb_log( "Internal error in scan: unhandled audio type %d for 0x%x", + audio->config.in.codec, audio->id ); + break; } - } -static int AllAC3AndDCAOK( hb_title_t * title ) +/* + * This routine checks to see if we've ID'd all the audio streams associated + * with a title. It returns 0 if there are more to ID & 1 if all are done. + */ +static int AllAudioOK( hb_title_t * title ) { int i; hb_audio_t * audio; @@ -771,12 +841,10 @@ static int AllAC3AndDCAOK( hb_title_t * title ) for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { audio = hb_list_item( title->list_audio, i ); - if( ( audio->config.in.codec == HB_ACODEC_AC3 || audio->config.in.codec == HB_ACODEC_DCA ) && - !audio->config.in.bitrate ) + if( audio->config.in.bitrate == 0 ) { return 0; } } - return 1; } diff --git a/libhb/stream.c b/libhb/stream.c index fe2c962c0..4b58bda41 100755 --- a/libhb/stream.c +++ b/libhb/stream.c @@ -756,6 +756,26 @@ int hb_stream_seek( hb_stream_t * src_stream, float f ) return 1; } +static void set_audio_description( hb_audio_t *audio, iso639_lang_t *lang ) +{ + /* XXX + * This is a duplicate of code in dvd.c - it should get factored out + * into a common routine. We probably should only be putting the lang + * code or a lang pointer into the audio config & let the common description + * formatting routine in scan.c do all the stuff below. + */ + snprintf( audio->config.lang.description, + sizeof( audio->config.lang.description ), "%s (%s)", + strlen(lang->native_name) ? lang->native_name : lang->eng_name, + audio->config.in.codec == HB_ACODEC_AC3 ? "AC3" : + audio->config.in.codec == HB_ACODEC_DCA ? "DTS" : + audio->config.in.codec == HB_ACODEC_MPGA ? "MPEG" : "LPCM" ); + snprintf( audio->config.lang.simple, sizeof( audio->config.lang.simple ), "%s", + strlen(lang->native_name) ? lang->native_name : lang->eng_name ); + snprintf( audio->config.lang.iso639_2, sizeof( audio->config.lang.iso639_2 ), + "%s", lang->iso639_2); +} + static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream, int aud_pid_index) { @@ -794,7 +814,12 @@ static hb_audio_t *hb_ts_stream_set_audio_id_and_codec(hb_stream_t *stream, } } fseeko(stream->file_handle, cur_pos, SEEK_SET); - if (! audio->config.in.codec) + if ( audio->config.in.codec ) + { + set_audio_description( audio, + lang_for_code( stream->a52_info[aud_pid_index].lang_code ) ); + } + else { hb_log("transport stream pid 0x%x (type 0x%x) isn't audio", stream->ts_audio_pids[aud_pid_index], @@ -832,6 +857,7 @@ static void add_audio_to_title(hb_title_t *title, int id) return; } + set_audio_description( audio, lang_for_code( 0 ) ); hb_list_add( title->list_audio, audio ); } @@ -884,121 +910,6 @@ static void hb_ps_stream_find_audio_ids(hb_stream_t *stream, hb_title_t *title) } /*********************************************************************** - * hb_stream_update_audio - *********************************************************************** - * - **********************************************************************/ -void hb_stream_update_audio(hb_stream_t *stream, hb_audio_t *audio) -{ - iso639_lang_t *lang; - - if (stream->stream_type == hb_stream_type_transport) - { - // Find the audio stream info for this PID. The stream index is - // the subchannel id which is in the bottom four bits for MPEG audio - // and the bottom four bits of the upper byte for everything else. - int i = ( audio->id >= 0xd0 ? audio->id >> 8 : audio->id ) & 0xf; - if (i >= stream->ts_number_audio_pids) - { - hb_log("hb_stream_update_audio: no PID for audio stream 0x%x", - audio->id); - return; - } - if (audio->id < 0xd0) - { - /* XXX fake mpeg audio sample rate & bps */ - stream->a52_info[i].flags = A52_STEREO; - stream->a52_info[i].rate = 48000 /*Hz*/; - stream->a52_info[i].bitrate = 384000 /*Bps*/; - } - - lang = lang_for_code(stream->a52_info[i].lang_code); - if (!audio->config.in.samplerate) - audio->config.in.samplerate = stream->a52_info[i].rate; - if (!audio->config.in.bitrate) - audio->config.in.bitrate = stream->a52_info[i].bitrate; - if (!audio->priv.config.a52.ac3flags) - audio->priv.config.a52.ac3flags = audio->config.flags.ac3 = stream->a52_info[i].flags; - - } - else - { - // XXX should try to get language code from the AC3 bitstream - lang = lang_for_code(0x0000); - } - - if (!audio->config.in.channel_layout) - { - switch( audio->config.flags.ac3 & A52_CHANNEL_MASK ) - { - /* mono sources */ - case A52_MONO: - case A52_CHANNEL1: - case A52_CHANNEL2: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_MONO; - break; - /* stereo input */ - case A52_CHANNEL: - case A52_STEREO: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; - break; - /* dolby (DPL1 aka Dolby Surround = 4.0 matrix-encoded) input */ - case A52_DOLBY: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_DOLBY; - break; - /* 3F/2R input */ - case A52_3F2R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F2R; - break; - /* 3F/1R input */ - case A52_3F1R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F1R; - break; - /* other inputs */ - case A52_3F: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_3F; - break; - case A52_2F1R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F1R; - break; - case A52_2F2R: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_2F2R; - break; - /* unknown */ - default: - audio->config.in.channel_layout = HB_INPUT_CH_LAYOUT_STEREO; - } - - /* add in our own LFE flag if the source has LFE */ - if (audio->config.flags.ac3 & A52_LFE) - { - audio->config.in.channel_layout = audio->config.in.channel_layout | HB_INPUT_CH_LAYOUT_HAS_LFE; - } - } - - snprintf( audio->config.lang.description, sizeof( audio->config.lang.description ), "%s (%s)", strlen(lang->native_name) ? lang->native_name : lang->eng_name, - audio->config.in.codec == HB_ACODEC_AC3 ? "AC3" : ( audio->config.in.codec == HB_ACODEC_MPGA ? "MPEG" : ( audio->config.in.codec == HB_ACODEC_DCA ? "DTS" : "LPCM" ) ) ); - snprintf( audio->config.lang.simple, sizeof( audio->config.lang.simple ), "%s", strlen(lang->native_name) ? lang->native_name : lang->eng_name ); - snprintf( audio->config.lang.iso639_2, sizeof( audio->config.lang.iso639_2 ), "%s", lang->iso639_2); - - if ( (audio->config.flags.ac3 & A52_CHANNEL_MASK) == A52_DOLBY ) { - sprintf( audio->config.lang.description + strlen( audio->config.lang.description ), - " (Dolby Surround)" ); - } else { - sprintf( audio->config.lang.description + strlen( audio->config.lang.description ), - " (%d.%d ch)", - HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(audio->config.in.channel_layout) + - HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(audio->config.in.channel_layout), - HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(audio->config.in.channel_layout)); - } - - hb_log( "stream: audio %x: lang %s, rate %d, bitrate %d, " - "flags = 0x%x", audio->id, audio->config.lang.description, audio->config.in.samplerate, - audio->config.in.bitrate, audio->config.flags.ac3 ); - -} - -/*********************************************************************** * hb_ts_stream_init *********************************************************************** * |