summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvan <[email protected]>2008-04-02 17:55:48 +0000
committervan <[email protected]>2008-04-02 17:55:48 +0000
commit7d87a5afd0a6764e9ef041053ba56234480aef81 (patch)
tree250e7f1e90a43b1fd2173f133281d81a2d34f7e2
parentf960fc7344f13893175b062bffc0877634aacceb (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.c233
-rw-r--r--libhb/decmpeg2.c33
-rw-r--r--libhb/internal.h1
-rw-r--r--libhb/scan.c536
-rwxr-xr-xlibhb/stream.c143
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
***********************************************************************
*