X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=libhb%2Fscan.c;h=2088883abe24e5eafa6c9ea28c179c1be7d1b2e4;hb=8375c1de85d64b43d5de799491c1ce2b241c2f60;hp=097f116381cf2bf294d89e7adc3752690ce0d08a;hpb=db27e0f2df5e2a09a9a664aa35dfe9a6f5cdcff2;p=handbrake-jp%2Fhandbrake-jp-git.git diff --git a/libhb/scan.c b/libhb/scan.c index 097f1163..2088883a 100644 --- a/libhb/scan.c +++ b/libhb/scan.c @@ -1,31 +1,51 @@ /* $Id: scan.c,v 1.52 2005/11/25 15:05:25 titer Exp $ This file is part of the HandBrake source code. - Homepage: . + Homepage: . It may be used under the terms of the GNU General Public License. */ #include "hb.h" #include "a52dec/a52.h" +#include "dca.h" + +#define HB_MAX_PREVIEWS 30 // 30 previews = every 5 minutes of a 2.5 hour video typedef struct { hb_handle_t * h; - + char * path; int title_index; hb_list_t * list_title; - + hb_dvd_t * dvd; + hb_stream_t * stream; + + int preview_count; + int store_previews; } hb_scan_t; static void ScanFunc( void * ); static int DecodePreviews( hb_scan_t *, hb_title_t * title ); -static void LookForAC3( hb_title_t * title, hb_buffer_t * b ); -static int AllAC3OK( hb_title_t * title ); +static void LookForAudio( hb_title_t * title, hb_buffer_t * b ); +static int AllAudioOK( hb_title_t * title ); + +static const char *aspect_to_string( double aspect ) +{ + switch ( (int)(aspect * 9.) ) + { + case 9 * 4 / 3: return "4:3"; + case 9 * 16 / 9: return "16:9"; + } + static char arstr[32]; + sprintf( arstr, aspect >= 1.? "%.2f:1" : "1:%.2f", aspect ); + return arstr; +} hb_thread_t * hb_scan_init( hb_handle_t * handle, const char * path, - int title_index, hb_list_t * list_title ) + int title_index, hb_list_t * list_title, + int preview_count, int store_previews ) { hb_scan_t * data = calloc( sizeof( hb_scan_t ), 1 ); @@ -33,7 +53,10 @@ hb_thread_t * hb_scan_init( hb_handle_t * handle, const char * path, data->path = strdup( path ); data->title_index = title_index; data->list_title = list_title; - + + data->preview_count = preview_count; + data->store_previews = store_previews; + return hb_thread_init( "scan", ScanFunc, data, HB_NORMAL_PRIORITY ); } @@ -43,6 +66,9 @@ static void ScanFunc( void * _data ) hb_title_t * title; int i; + data->dvd = NULL; + data->stream = NULL; + /* Try to open the path as a DVD. If it fails, try as a file */ hb_log( "scan: trying to open with libdvdread" ); if( ( data->dvd = hb_dvd_init( data->path ) ) ) @@ -65,22 +91,14 @@ static void ScanFunc( void * _data ) } } } + else if ( (data->stream = hb_stream_open( data->path, 0 ) ) != NULL ) + { + hb_list_add( data->list_title, hb_stream_title_scan( data->stream ) ); + } else { - /* Open as a VOB file */ - FILE * file; - hb_log( "scan: trying to open as VOB file" ); - file = fopen( data->path, "rb" ); - if( file ) - { - /* XXX */ - fclose( file ); - } - else - { - hb_log( "scan: fopen failed" ); - return; - } + hb_log( "scan: unrecognized file type" ); + return; } for( i = 0; i < hb_list_count( data->list_title ); ) @@ -114,7 +132,7 @@ static void ScanFunc( void * _data ) hb_log( "scan: title %d is duplicate with title %d", title->index, title_tmp->index ); hb_list_rem( data->list_title, title ); - free( title ); + free( title ); /* This _will_ leak! */ continue; } @@ -122,11 +140,12 @@ static void ScanFunc( void * _data ) /* Update the UI */ state.state = HB_STATE_SCANNING; p.title_cur = title->index; - p.title_count = hb_dvd_title_count( data->dvd ); + p.title_count = data->dvd ? hb_dvd_title_count( data->dvd ) : hb_list_count(data->list_title); hb_set_state( data->h, &state ); #undef p /* Decode previews */ + /* this will also detect more AC3 / DTS information */ if( !DecodePreviews( data, title ) ) { /* TODO: free things */ @@ -134,13 +153,14 @@ static void ScanFunc( void * _data ) continue; } - /* Make sure we found AC3 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->codec == HB_ACODEC_AC3 && - !audio->bitrate ) + if( !audio->config.in.bitrate ) { + hb_log( "scan: removing audio 0x%x because no bitrate found", + audio->id ); hb_list_rem( title->list_audio, audio ); free( audio ); continue; @@ -148,14 +168,6 @@ static void ScanFunc( void * _data ) j++; } - /* Do we still have audio */ - if( !hb_list_count( title->list_audio ) ) - { - hb_list_rem( data->list_title, title ); - free( title ); - continue; - } - i++; } @@ -177,6 +189,20 @@ static void ScanFunc( void * _data ) /* Autocrop by default. Gnark gnark */ memcpy( job->crop, title->crop, 4 * sizeof( int ) ); + /* Preserve a source's pixel aspect, if it's available. */ + if( title->pixel_aspect_width && title->pixel_aspect_height ) + { + job->anamorphic.par_width = title->pixel_aspect_width; + job->anamorphic.par_height = title->pixel_aspect_height; + } + + if( title->aspect != 0 && title->aspect != 1. && + !job->anamorphic.par_width && !job->anamorphic.par_height) + { + hb_reduce( &job->anamorphic.par_width, &job->anamorphic.par_height, + (int)(title->aspect * title->height + 0.5), title->width ); + } + job->width = title->width - job->crop[2] - job->crop[3]; hb_fix_aspect( job, HB_KEEP_WIDTH ); if( job->height > title->height - job->crop[0] - job->crop[1] ) @@ -184,6 +210,10 @@ static void ScanFunc( void * _data ) job->height = title->height - job->crop[0] - job->crop[1]; hb_fix_aspect( job, HB_KEEP_HEIGHT ); } + + hb_log( "scan: title (%d) job->width:%d, job->height:%d", + i, job->width, job->height ); + job->keep_ratio = 1; job->vcodec = HB_VCODEC_FFMPEG; @@ -193,12 +223,7 @@ static void ScanFunc( void * _data ) job->vrate = title->rate; job->vrate_base = title->rate_base; - job->audios[0] = 0; - job->audios[1] = -1; - - job->acodec = HB_ACODEC_FAAC; - job->abitrate = 128; - job->arate = 44100; + job->list_audio = hb_list_init(); job->subtitle = -1; @@ -209,6 +234,153 @@ static void ScanFunc( void * _data ) { hb_dvd_close( &data->dvd ); } + if (data->stream) + { + hb_stream_close(&data->stream); + } + free( data->path ); + free( data ); + _data = NULL; +} + +// ----------------------------------------------- +// stuff related to cropping + +#define DARK 32 + +static inline int absdiff( int x, int y ) +{ + return x < y ? y - x : x - y; +} + +static inline int clampBlack( int x ) +{ + // luma 'black' is 16 and anything less should be clamped at 16 + return x < 16 ? 16 : x; +} + +static int row_all_dark( hb_title_t *title, uint8_t* luma, int row ) +{ + luma += title->width * row; + + // compute the average luma value of the row + int i, avg = 0; + for ( i = 0; i < title->width; ++i ) + { + avg += clampBlack( luma[i] ); + } + avg /= title->width; + if ( avg >= DARK ) + return 0; + + // since we're trying to detect smooth borders, only take the row if + // all pixels are within +-16 of the average (this range is fairly coarse + // but there's a lot of quantization noise for luma values near black + // so anything less will fail to crop because of the noise). + for ( i = 0; i < title->width; ++i ) + { + if ( absdiff( avg, clampBlack( luma[i] ) ) > 16 ) + return 0; + } + return 1; +} + +static int column_all_dark( hb_title_t *title, uint8_t* luma, int top, int bottom, + int col ) +{ + int stride = title->width; + int height = title->height - top - bottom; + luma += stride * top + col; + + // compute the average value of the column + int i = height, avg = 0, row = 0; + for ( ; --i >= 0; row += stride ) + { + avg += clampBlack( luma[row] ); + } + avg /= height; + if ( avg >= DARK ) + return 0; + + // since we're trying to detect smooth borders, only take the column if + // all pixels are within +-16 of the average. + i = height, row = 0; + for ( ; --i >= 0; row += stride ) + { + if ( absdiff( avg, clampBlack( luma[row] ) ) > 16 ) + return 0; + } + return 1; +} +#undef DARK + +typedef struct { + int n; + int t[HB_MAX_PREVIEWS]; + int b[HB_MAX_PREVIEWS]; + int l[HB_MAX_PREVIEWS]; + int r[HB_MAX_PREVIEWS]; +} crop_record_t; + +static void record_crop( crop_record_t *crops, int t, int b, int l, int r ) +{ + crops->t[crops->n] = t; + crops->b[crops->n] = b; + crops->l[crops->n] = l; + crops->r[crops->n] = r; + ++crops->n; +} + +static int compare_int( const void *a, const void *b ) +{ + return *(const int *)a - *(const int *)b; +} + +static void sort_crops( crop_record_t *crops ) +{ + qsort( crops->t, crops->n, sizeof(crops->t[0]), compare_int ); + qsort( crops->b, crops->n, sizeof(crops->t[0]), compare_int ); + qsort( crops->l, crops->n, sizeof(crops->t[0]), compare_int ); + qsort( crops->r, crops->n, sizeof(crops->t[0]), compare_int ); +} + +// ----------------------------------------------- +// stuff related to title width/height/aspect info + +typedef struct { + int count; /* number of times we've seen this info entry */ + hb_work_info_t info; /* copy of info entry */ +} info_list_t; + +static void remember_info( info_list_t *info_list, hb_work_info_t *info ) +{ + for ( ; info_list->count; ++info_list ) + { + if ( memcmp( &info_list->info, info, sizeof(*info) ) == 0 ) + { + // we found a match - bump its count + ++info_list->count; + return; + } + } + // no match found - add new entry to list (info_list points to + // the first free slot). NB - we assume that info_list was allocated + // so that it's big enough even if there are no dups. I.e., 10 slots + // allocated if there are 10 previews. + info_list->count = 1; + info_list->info = *info; +} + +static void most_common_info( info_list_t *info_list, hb_work_info_t *info ) +{ + int i, biggest = 0; + for ( i = 1; info_list[i].count; ++i ) + { + if ( info_list[i].count > info_list[biggest].count ) + biggest = i; + } + *info = info_list[biggest].info; + free( info_list ); } /*********************************************************************** @@ -220,87 +392,191 @@ static void ScanFunc( void * _data ) **********************************************************************/ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) { - int i, ret; - hb_buffer_t * buf_ps, * buf_es, * buf_raw; - hb_list_t * list_es, * list_raw; - hb_libmpeg2_t * mpeg2; - - buf_ps = hb_buffer_init( 2048 ); + int i, npreviews = 0; + hb_buffer_t * buf_ps, * buf_es; + hb_list_t * list_es; + int progressive_count = 0; + int interlaced_preview_count = 0; + info_list_t * info_list = calloc( data->preview_count+1, sizeof(*info_list) ); + crop_record_t *crops = calloc( 1, sizeof(*crops) ); + + buf_ps = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE ); list_es = hb_list_init(); - list_raw = hb_list_init(); hb_log( "scan: decoding previews for title %d", title->index ); - hb_dvd_start( data->dvd, title->index, 1 ); + if (data->dvd) + hb_dvd_start( data->dvd, title->index, 1 ); - for( i = 0; i < 10; i++ ) + for( i = 0; i < data->preview_count; i++ ) { - int j, k; + int j; FILE * file_preview; char filename[1024]; - if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / 11.0 ) ) + if (data->dvd) { - goto error; + if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) ) + { + continue; + } + } + else if (data->stream) + { + /* 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 / ( data->preview_count + 1.0 ) ) ) + { + continue; + } } - hb_log( "scan: preview %d", i + 1 ); + hb_deep_log( 2, "scan: preview %d", i + 1 ); - mpeg2 = hb_libmpeg2_init(); + int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2; + hb_work_object_t *vid_decoder = hb_get_work( vcodec ); + vid_decoder->codec_param = title->video_codec_param; + vid_decoder->init( vid_decoder, NULL ); + hb_buffer_t * vid_buf = NULL; + int vidskip = 0; + + if ( title->flags & HBTF_NO_IDR ) + { + // title doesn't have IDR frames so we decode but drop one second's + // worth of frames to allow the decoder to converge. + if ( ! title->rate_base ) + { + vidskip = 30; + } + else + { + vidskip = (double)title->rate / (double)title->rate_base + 0.5; + } + } for( j = 0; j < 10240 ; j++ ) { - if( !hb_dvd_read( data->dvd, buf_ps ) ) + if (data->dvd) + { + if( !hb_dvd_read( data->dvd, buf_ps ) ) + { + if ( vid_buf ) + { + break; + } + hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 ); + goto skip_preview; + } + } + else if (data->stream) { - goto error; + if ( !hb_stream_read(data->stream,buf_ps) ) + { + if ( vid_buf ) + { + break; + } + hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 ); + goto skip_preview; + } } - hb_demux_ps( buf_ps, list_es ); + (hb_demux[title->demuxer])(buf_ps, list_es, 0 ); while( ( buf_es = hb_list_item( list_es, 0 ) ) ) { hb_list_rem( list_es, buf_es ); - if( buf_es->id == 0xE0 && !hb_list_count( list_raw ) ) + if( buf_es->id == title->video_id && vid_buf == NULL ) { - hb_libmpeg2_decode( mpeg2, buf_es, list_raw ); + vid_decoder->work( vid_decoder, &buf_es, &vid_buf ); + if ( vid_buf && vidskip && --vidskip > 0 ) + { + // we're dropping frames to get the video decoder in sync + // when the video stream doesn't contain IDR frames + hb_buffer_close( &vid_buf ); + vid_buf = NULL; + } } - else if( !i ) + else if( ! AllAudioOK( title ) ) { - LookForAC3( title, buf_es ); - } - hb_buffer_close( &buf_es ); - - if( hb_list_count( list_raw ) && - ( i || AllAC3OK( title ) ) ) - { - /* We got a picture */ - break; + LookForAudio( title, buf_es ); } + if ( buf_es ) + hb_buffer_close( &buf_es ); } - if( hb_list_count( list_raw ) && - ( i || AllAC3OK( title ) ) ) - { + if( vid_buf && AllAudioOK( title ) ) break; - } } - if( !hb_list_count( list_raw ) ) + if( ! vid_buf ) { hb_log( "scan: could not get a decoded picture" ); - goto error; + continue; } - if( !i ) + /* Get size and rate infos */ + + hb_work_info_t vid_info; + if( !vid_decoder->info( vid_decoder, &vid_info ) ) { - /* Get size and rate infos */ - title->rate = 27000000; - hb_libmpeg2_info( mpeg2, &title->width, &title->height, - &title->rate_base ); - title->crop[0] = title->crop[1] = title->height / 2; - title->crop[2] = title->crop[3] = title->width / 2; + /* + * Could not fill vid_info, don't continue and try to use vid_info + * in this case. + */ + vid_decoder->close( vid_decoder ); + free( vid_decoder ); + continue; } + vid_decoder->close( vid_decoder ); + free( vid_decoder ); + + remember_info( info_list, &vid_info ); - hb_libmpeg2_close( &mpeg2 ); + title->video_codec_name = strdup( vid_info.name ); + title->width = vid_info.width; + title->height = vid_info.height; + title->rate = vid_info.rate; + title->rate_base = vid_info.rate_base; + title->video_bitrate = vid_info.bitrate; + + if( title->rate_base == 1126125 ) + { + /* Frame FPS is 23.976 (meaning it's progressive), so + start keeping track of how many are reporting at + that speed. When enough show up that way, we want + to make that the overall title FPS. + */ + progressive_count++; + + if( progressive_count < 6 ) + { + /* Not enough frames are reporting as progressive, + which means we should be conservative and use + 29.97 as the title's FPS for now. + */ + title->rate_base = 900900; + } + else + { + /* A majority of the scan frames are progressive. Make that + the title's FPS, and announce it once to the log. + */ + if( progressive_count == 6 ) + { + hb_deep_log( 2, "Title's mostly NTSC Film, setting fps to 23.976"); + } + title->rate_base = 1126125; + } + } + else if( title->rate_base == 900900 && progressive_count >= 6 ) + { + /* + * We've already deduced that the frame rate is 23.976, so set it + * back again. + */ + title->rate_base = 1126125; + } while( ( buf_es = hb_list_item( list_es, 0 ) ) ) { @@ -308,85 +584,183 @@ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) hb_buffer_close( &buf_es ); } - buf_raw = hb_list_item( list_raw, 0 ); - - hb_get_tempory_filename( data->h, filename, "%x%d", - (int) title, i ); - - file_preview = fopen( filename, "w" ); - if( file_preview ) + /* Check preview for interlacing artifacts */ + if( hb_detect_comb( vid_buf, title->width, title->height, 10, 30, 9, 10, 30, 9 ) ) { - fwrite( buf_raw->data, title->width * title->height * 3 / 2, - 1, file_preview ); - fclose( file_preview ); + hb_deep_log( 2, "Interlacing detected in preview frame %i", i+1); + interlaced_preview_count++; } - else + + if( data->store_previews ) { - hb_log( "scan: fopen failed (%s)", filename ); + hb_get_tempory_filename( data->h, filename, "%" PRIxPTR "%d", + (intptr_t)title, i ); + + file_preview = fopen( filename, "w" ); + if( file_preview ) + { + fwrite( vid_buf->data, title->width * title->height * 3 / 2, + 1, file_preview ); + fclose( file_preview ); + } + else + { + hb_log( "scan: fopen failed (%s)", filename ); + } } -#define Y buf_raw->data -#define DARK 64 - /* Detect black borders */ - - for( j = 0; j < title->width; j++ ) + +#define Y vid_buf->data + int top, bottom, left, right; + int h4 = title->height / 4, w4 = title->width / 4; + + // When widescreen content is matted to 16:9 or 4:3 there's sometimes + // a thin border on the outer edge of the matte. On TV content it can be + // "line 21" VBI data that's normally hidden in the overscan. For HD + // content it can just be a diagnostic added in post production so that + // the frame borders are visible. We try to ignore these borders so + // we can crop the matte. The border width depends on the resolution + // (12 pixels on 1080i looks visually the same as 4 pixels on 480i) + // so we allow the border to be up to 1% of the frame height. + const int border = title->height / 100; + + for ( top = border; top < h4; ++top ) { - for( k = 0; k < title->crop[0]; k++ ) - if( Y[ k * title->width + j ] > DARK ) - { - title->crop[0] = k; - break; - } - for( k = 0; k < title->crop[1]; k++ ) - if( Y[ ( title->height - k - 1 ) * - title->width + j ] > DARK ) - { - title->crop[1] = k; - break; - } + if ( ! row_all_dark( title, Y, top ) ) + break; } - for( j = 0; j < title->height; j++ ) + if ( top <= border ) { - for( k = 0; k < title->crop[2]; k++ ) - if( Y[ j * title->width + k ] > DARK ) - { - title->crop[2] = k; + // we never made it past the border region - see if the rows we + // didn't check are dark or if we shouldn't crop at all. + for ( top = 0; top < border; ++top ) + { + if ( ! row_all_dark( title, Y, top ) ) break; - } - for( k = 0; k < title->crop[3]; k++ ) - if( Y[ j * title->width + - title->width - k - 1 ] > DARK ) - { - title->crop[3] = k; + } + if ( top >= border ) + { + top = 0; + } + } + for ( bottom = border; bottom < h4; ++bottom ) + { + if ( ! row_all_dark( title, Y, title->height - 1 - bottom ) ) + break; + } + if ( bottom <= border ) + { + for ( bottom = 0; bottom < border; ++bottom ) + { + if ( ! row_all_dark( title, Y, title->height - 1 - bottom ) ) break; - } + } + if ( bottom >= border ) + { + bottom = 0; + } + } + for ( left = 0; left < w4; ++left ) + { + if ( ! column_all_dark( title, Y, top, bottom, left ) ) + break; + } + for ( right = 0; right < w4; ++right ) + { + if ( ! column_all_dark( title, Y, top, bottom, title->width - 1 - right ) ) + break; } - while( ( buf_raw = hb_list_item( list_raw, 0 ) ) ) + // only record the result if all the crops are less than a quarter of + // the frame otherwise we can get fooled by frames with a lot of black + // like titles, credits & fade-thru-black transitions. + if ( top < h4 && bottom < h4 && left < w4 && right < w4 ) { - hb_list_rem( list_raw, buf_raw ); - hb_buffer_close( &buf_raw ); + record_crop( crops, top, bottom, left, right ); } + ++npreviews; + +skip_preview: + if ( vid_buf ) + hb_buffer_close( &vid_buf ); } - title->crop[0] = EVEN( title->crop[0] ); - title->crop[1] = EVEN( title->crop[1] ); - title->crop[2] = EVEN( title->crop[2] ); - title->crop[3] = EVEN( title->crop[3] ); + if ( npreviews ) + { + // use the most common frame info for our final title dimensions + hb_work_info_t vid_info; + most_common_info( info_list, &vid_info ); + + title->width = vid_info.width; + title->height = vid_info.height; + title->pixel_aspect_width = vid_info.pixel_aspect_width; + title->pixel_aspect_height = vid_info.pixel_aspect_height; + + // compute the aspect ratio based on the storage dimensions and the + // pixel aspect ratio (if supplied) or just storage dimensions if no PAR. + title->aspect = (double)title->width / (double)title->height; + if( title->pixel_aspect_width && title->pixel_aspect_height ) + { + title->aspect *= (double)title->pixel_aspect_width / + (double)title->pixel_aspect_height; + + // For unknown reasons some French PAL DVDs put the original + // content's aspect ratio into the mpeg PAR even though it's + // the wrong PAR for the DVD. Apparently they rely on the fact + // that DVD players ignore the content PAR and just use the + // aspect ratio from the DVD metadata. So, if the aspect computed + // from the PAR is different from the container's aspect we use + // the container's aspect & recompute the PAR from it. + if( title->container_aspect && (int)(title->aspect * 9) != (int)(title->container_aspect * 9) ) + { + hb_log("scan: content PAR gives wrong aspect %.2f; " + "using container aspect %.2f", title->aspect, + title->container_aspect ); + title->aspect = title->container_aspect; + hb_reduce( &title->pixel_aspect_width, &title->pixel_aspect_height, + (int)(title->aspect * title->height + 0.5), title->width ); + } + } - hb_log( "scan: %dx%d, %.3f fps, autocrop = %d/%d/%d/%d", - title->width, title->height, (float) title->rate / - (float) title->rate_base, title->crop[0], title->crop[1], - title->crop[2], title->crop[3] ); + // don't try to crop unless we got at least 3 previews + if ( crops->n > 2 ) + { + sort_crops( crops ); + // The next line selects median cropping - at least + // 50% of the frames will have their borders removed. + // Other possible choices are loose cropping (i = 0) where + // no non-black pixels will be cropped from any frame and a + // tight cropping (i = crops->n - (crops->n >> 2)) where at + // least 75% of the frames will have their borders removed. + i = crops->n >> 1; + title->crop[0] = EVEN( crops->t[i] ); + title->crop[1] = EVEN( crops->b[i] ); + title->crop[2] = EVEN( crops->l[i] ); + title->crop[3] = EVEN( crops->r[i] ); + } + free( crops ); - ret = 1; - goto cleanup; + hb_log( "scan: %d previews, %dx%d, %.3f fps, autocrop = %d/%d/%d/%d, " + "aspect %s, PAR %d:%d", + npreviews, title->width, title->height, (float) title->rate / + (float) title->rate_base, + title->crop[0], title->crop[1], title->crop[2], title->crop[3], + aspect_to_string( title->aspect ), title->pixel_aspect_width, + title->pixel_aspect_height ); -error: - ret = 0; + if( interlaced_preview_count >= ( npreviews / 2 ) ) + { + hb_log("Title is likely interlaced or telecined (%i out of %i previews). You should do something about that.", + interlaced_preview_count, npreviews); + title->detected_interlacing = 1; + } + else + { + title->detected_interlacing = 0; + } + } -cleanup: hb_buffer_close( &buf_ps ); while( ( buf_es = hb_list_item( list_es, 0 ) ) ) { @@ -394,30 +768,36 @@ cleanup: hb_buffer_close( &buf_es ); } hb_list_close( &list_es ); - while( ( buf_raw = hb_list_item( list_raw, 0 ) ) ) - { - hb_list_rem( list_raw, buf_raw ); - hb_buffer_close( &buf_raw ); - } - hb_list_close( &list_raw ); - hb_dvd_stop( data->dvd ); - return ret; + if (data->dvd) + hb_dvd_stop( data->dvd ); + + return npreviews; } -static void LookForAC3( hb_title_t * title, hb_buffer_t * b ) +/* + * 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; - int flags; - int rate; - int bitrate; - /* Figure out if this is a AC3 buffer for a known track */ hb_audio_t * audio = NULL; for( i = 0; i < hb_list_count( title->list_audio ); i++ ) { audio = hb_list_item( title->list_audio, i ); - if( audio->codec == HB_ACODEC_AC3 && - audio->id == b->id ) + /* check if this elementary stream is one we want */ + if ( audio->id == b->id ) { break; } @@ -426,57 +806,85 @@ static void LookForAC3( hb_title_t * title, hb_buffer_t * b ) audio = NULL; } } - if( !audio ) + if( !audio || audio->config.in.bitrate != 0 ) { + /* not found or already done */ return; } - if( audio->bitrate ) + hb_work_object_t *w = hb_codec_decoder( audio->config.in.codec ); + + if ( w == NULL || w->bsinfo == NULL ) { - /* Already done for this track */ - return; + hb_log( "Internal error in scan: unhandled audio type %d for id 0x%x", + audio->config.in.codec, audio->id ); + goto drop_audio; } - for( i = 0; i < b->size - 7; i++ ) + hb_work_info_t info; + w->audio = audio; + w->codec_param = audio->config.in.codec_param; + int ret = w->bsinfo( w, b, &info ); + if ( ret < 0 ) { - if( a52_syncinfo( &b->data[i], &flags, &rate, &bitrate ) ) - { - hb_log( "scan: rate=%dHz, bitrate=%d", rate, bitrate ); - audio->rate = rate; - audio->bitrate = bitrate; - switch( flags & A52_CHANNEL_MASK ) - { - case A52_MONO: - case A52_CHANNEL1: - case A52_CHANNEL2: - audio->channels = 1; - break; - case A52_STEREO: - case A52_DOLBY: - case A52_CHANNEL: - audio->channels = 2; - break; - case A52_3F: - case A52_2F1R: - audio->channels = 3; - break; - case A52_3F1R: - case A52_2F2R: - audio->channels = 4; - break; - case A52_3F2R: - audio->channels = 5; - break; - } - /* XXX */ - sprintf( audio->lang + strlen( audio->lang ), - " (%d ch)", audio->channels ); - break; - } + hb_log( "no info on audio type %d/0x%x for id 0x%x", + audio->config.in.codec, audio->config.in.codec_param, + audio->id ); + goto drop_audio; } + if ( !info.bitrate ) + { + /* didn't find any info */ + return; + } + audio->config.in.samplerate = info.rate; + audio->config.in.bitrate = info.bitrate; + audio->config.in.channel_layout = info.channel_layout; + audio->config.in.version = info.version; + audio->config.in.mode = info.mode; + audio->config.flags.ac3 = info.flags; + + // update the audio description string based on the info we found + if ( audio->config.flags.ac3 & AUDIO_F_DOLBY ) + { + strcat( audio->config.lang.description, " (Dolby Surround)" ); + } + else + { + int layout = audio->config.in.channel_layout; + char *desc = audio->config.lang.description + + strlen( audio->config.lang.description ); + sprintf( desc, " (%d.%d ch)", + HB_INPUT_CH_LAYOUT_GET_DISCRETE_FRONT_COUNT(layout) + + HB_INPUT_CH_LAYOUT_GET_DISCRETE_REAR_COUNT(layout), + HB_INPUT_CH_LAYOUT_GET_DISCRETE_LFE_COUNT(layout) ); + } + + hb_log( "scan: audio 0x%x: %s, rate=%dHz, bitrate=%d %s", audio->id, + info.name, audio->config.in.samplerate, audio->config.in.bitrate, + audio->config.lang.description ); + + free( w ); + return; + + // We get here if there's no hope of finding info on an audio bitstream, + // either because we don't have a decoder (or a decoder with a bitstream + // info proc) or because the decoder's info proc said that the stream + // wasn't something it could handle. Delete the item from the title's + // audio list so we won't keep reading packets while trying to get its + // bitstream info. + drop_audio: + if ( w ) + free( w ); + + hb_list_rem( title->list_audio, audio ); } -static int AllAC3OK( 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; @@ -484,12 +892,10 @@ static int AllAC3OK( 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->codec == HB_ACODEC_AC3 && - !audio->bitrate ) + if( audio->config.in.bitrate == 0 ) { return 0; } } - return 1; }