/*********************************************************************** * hb_batch_title_scan **********************************************************************/ hb_title_t * hb_batch_title_scan( hb_batch_t * d, int t ) { hb_title_t * title; char * filename; hb_stream_t * stream; if ( t < 0 ) return NULL; filename = hb_list_item( d->list_file, t - 1 ); if ( filename == NULL ) return NULL; hb_log( "batch: scanning %s", filename ); title = hb_title_init( filename, 0 ); stream = hb_stream_open( filename, title, 1 ); if ( stream == NULL ) { hb_title_close( &title ); return NULL; } title = hb_stream_title_scan( stream, title ); hb_stream_close( &stream ); if ( title != NULL ) { title->index = t; } return title; }
static void reader_close( hb_work_object_t * w ) { hb_work_private_t * r = w->private_data; if ( r == NULL ) { return; } if (r->bd) { hb_bd_stop( r->bd ); hb_bd_close( &r->bd ); } else if (r->dvd) { hb_dvd_stop( r->dvd ); hb_dvd_close( &r->dvd ); } else if (r->stream) { hb_stream_close(&r->stream); } if ( r->stream_timing ) { free( r->stream_timing ); } free(r->fifos); free(r->splice_list); free(r); }
static void hb_reader_close( hb_work_object_t * w ) { hb_work_private_t * r = w->private_data; if (r->bd) { hb_bd_stop( r->bd ); hb_bd_close( &r->bd ); } else if (r->dvd) { hb_dvd_stop( r->dvd ); hb_dvd_close( &r->dvd ); } else if (r->stream) { hb_stream_close(&r->stream); } if ( r->stream_timing ) { free( r->stream_timing ); } free( r ); }
static void reader_close( hb_work_object_t * w ) { hb_work_private_t * r = w->private_data; if ( r == NULL ) { return; } if (r->bd) { hb_bd_stop( r->bd ); hb_bd_close( &r->bd ); } else if (r->dvd) { hb_dvd_stop( r->dvd ); hb_dvd_close( &r->dvd ); } else if (r->stream) { hb_stream_close(&r->stream); } int ii; for (ii = 0; ii < r->splice_list_size; ii++) { hb_buffer_list_close(&r->splice_list[ii].list); } free(r->fifos); free(r->splice_list); free(r); }
/*********************************************************************** * hb_bd_close *********************************************************************** * Closes and frees everything **********************************************************************/ void hb_bd_close( hb_bd_t ** _d ) { hb_bd_t * d = *_d; int ii; if ( d->title_info ) { for ( ii = 0; ii < d->title_count; ii++ ) bd_free_title_info( d->title_info[ii] ); free( d->title_info ); } if( d->stream ) hb_stream_close( &d->stream ); if( d->bd ) bd_close( d->bd ); if( d->path ) free( d->path ); free( d ); *_d = NULL; }
/*********************************************************************** * ReaderFunc *********************************************************************** * **********************************************************************/ static void ReaderFunc( void * _r ) { hb_reader_t * r = _r; hb_fifo_t ** fifos; hb_buffer_t * buf; hb_list_t * list; int n; int chapter = -1; int chapter_end = r->job->chapter_end; if ( r->title->type == HB_BD_TYPE ) { if ( !( r->bd = hb_bd_init( r->title->path ) ) ) return; } else if ( r->title->type == HB_DVD_TYPE ) { if ( !( r->dvd = hb_dvd_init( r->title->path ) ) ) return; } else if ( r->title->type == HB_STREAM_TYPE || r->title->type == HB_FF_STREAM_TYPE ) { if ( !( r->stream = hb_stream_open( r->title->path, r->title ) ) ) return; } else { // Unknown type, should never happen return; } if (r->bd) { if( !hb_bd_start( r->bd, r->title ) ) { hb_bd_close( &r->bd ); return; } if ( r->job->start_at_preview ) { // XXX code from DecodePreviews - should go into its own routine hb_bd_seek( r->bd, (float)r->job->start_at_preview / ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) ); } else if ( r->job->pts_to_start ) { // Note, bd seeks always put us to an i-frame. no need // to start decoding early using r->pts_to_start hb_bd_seek_pts( r->bd, r->job->pts_to_start ); r->job->pts_to_start = 0; r->start_found = 1; } else { hb_bd_seek_chapter( r->bd, r->job->chapter_start ); } if (r->job->angle > 1) { hb_bd_set_angle( r->bd, r->job->angle - 1 ); } } else if (r->dvd) { /* * XXX this code is a temporary hack that should go away if/when * chapter merging goes away in libhb/dvd.c * map the start and end chapter numbers to on-media chapter * numbers since chapter merging could cause the handbrake numbers * to diverge from the media numbers and, if our chapter_end is after * a media chapter that got merged, we'll stop ripping too early. */ int start = r->job->chapter_start; hb_chapter_t *chap = hb_list_item( r->title->list_chapter, chapter_end - 1 ); chapter_end = chap->index; if (start > 1) { chap = hb_list_item( r->title->list_chapter, start - 1 ); start = chap->index; } /* end chapter mapping XXX */ if( !hb_dvd_start( r->dvd, r->title, start ) ) { hb_dvd_close( &r->dvd ); return; } if (r->job->angle) { hb_dvd_set_angle( r->dvd, r->job->angle ); } if ( r->job->start_at_preview ) { // XXX code from DecodePreviews - should go into its own routine hb_dvd_seek( r->dvd, (float)r->job->start_at_preview / ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) ); } } else if ( r->stream && r->job->start_at_preview ) { // XXX code from DecodePreviews - should go into its own routine hb_stream_seek( r->stream, (float)( r->job->start_at_preview - 1 ) / ( r->job->seek_points ? ( r->job->seek_points + 1.0 ) : 11.0 ) ); } else if ( r->stream && r->job->pts_to_start ) { int64_t pts_to_start = r->job->pts_to_start; // Find out what the first timestamp of the stream is // and then seek to the appropriate offset from it if ( ( buf = hb_stream_read( r->stream ) ) ) { if ( buf->start > 0 ) { pts_to_start += buf->start; r->pts_to_start += buf->start; r->job->pts_to_start += buf->start; } } if ( hb_stream_seek_ts( r->stream, pts_to_start ) >= 0 ) { // Seek takes us to the nearest I-frame before the timestamp // that we want. So we will retrieve the start time of the // first packet we get, subtract that from pts_to_start, and // inspect the reset of the frames in sync. r->start_found = 2; r->job->pts_to_start = pts_to_start; } } else if( r->stream ) { /* * Standard stream, seek to the starting chapter, if set, and track the * end chapter so that we end at the right time. */ int start = r->job->chapter_start; hb_chapter_t *chap = hb_list_item( r->title->list_chapter, chapter_end - 1 ); chapter_end = chap->index; if (start > 1) { chap = hb_list_item( r->title->list_chapter, start - 1 ); start = chap->index; } /* * Seek to the start chapter. */ hb_stream_seek_chapter( r->stream, start ); } list = hb_list_init(); while( !*r->die && !r->job->done ) { if (r->bd) chapter = hb_bd_chapter( r->bd ); else if (r->dvd) chapter = hb_dvd_chapter( r->dvd ); else if (r->stream) chapter = hb_stream_chapter( r->stream ); if( chapter < 0 ) { hb_log( "reader: end of the title reached" ); break; } if( chapter > chapter_end ) { hb_log( "reader: end of chapter %d (media %d) reached at media chapter %d", r->job->chapter_end, chapter_end, chapter ); break; } if (r->bd) { if( (buf = hb_bd_read( r->bd )) == NULL ) { break; } } else if (r->dvd) { if( (buf = hb_dvd_read( r->dvd )) == NULL ) { break; } } else if (r->stream) { if ( (buf = hb_stream_read( r->stream )) == NULL ) { break; } if ( r->start_found == 2 ) { // We will inspect the timestamps of each frame in sync // to skip from this seek point to the timestamp we // want to start at. if ( buf->start > 0 && buf->start < r->job->pts_to_start ) { r->job->pts_to_start -= buf->start; } else if ( buf->start >= r->job->pts_to_start ) { r->job->pts_to_start = 0; r->start_found = 1; } } } if( r->job->indepth_scan ) { /* * Need to update the progress during a subtitle scan */ hb_state_t state; #define p state.param.working state.state = HB_STATE_WORKING; p.progress = (double)chapter / (double)r->job->chapter_end; if( p.progress > 1.0 ) { p.progress = 1.0; } p.rate_avg = 0.0; p.hours = -1; p.minutes = -1; p.seconds = -1; hb_set_state( r->job->h, &state ); } (hb_demux[r->title->demuxer])( buf, list, &r->demux ); while( ( buf = hb_list_item( list, 0 ) ) ) { hb_list_rem( list, buf ); fifos = GetFifoForId( r->job, buf->id ); if ( fifos && ! r->saw_video && !r->job->indepth_scan ) { // The first data packet with a PTS from an audio or video stream // that we're decoding defines 'time zero'. Discard packets until // we get one. if ( buf->start != -1 && buf->renderOffset != -1 && ( buf->id == r->title->video_id || is_audio( r, buf->id ) ) ) { // force a new scr offset computation r->scr_changes = r->demux.scr_changes - 1; // create a stream state if we don't have one so the // offset will get computed correctly. id_to_st( r, buf, 1 ); r->saw_video = 1; hb_log( "reader: first SCR %"PRId64" id 0x%x DTS %"PRId64, r->demux.last_scr, buf->id, buf->renderOffset ); } else { fifos = NULL; } } if( fifos ) { if ( buf->renderOffset != -1 ) { if ( r->scr_changes != r->demux.scr_changes ) { // This is the first audio or video packet after an SCR // change. Compute a new scr offset that would make this // packet follow the last of this stream with the // correct average spacing. stream_timing_t *st = id_to_st( r, buf, 0 ); // if this is the video stream and we don't have // audio yet or this is an audio stream // generate a new scr if ( st->is_audio || ( st == r->stream_timing && !r->saw_audio ) ) { new_scr_offset( r, buf ); } else { // defer the scr change until we get some // audio since audio has a timestamp per // frame but video & subtitles don't. Clear // the timestamps so the decoder will generate // them from the frame durations. buf->start = -1; buf->renderOffset = -1; } } } if ( buf->start != -1 ) { int64_t start = buf->start - r->scr_offset; if ( !r->start_found ) UpdateState( r, start ); if ( !r->start_found && start >= r->pts_to_start ) { // pts_to_start point found r->start_found = 1; } // This log is handy when you need to debug timing problems //hb_log("id %x scr_offset %ld start %ld --> %ld", // buf->id, r->scr_offset, buf->start, // buf->start - r->scr_offset); buf->start -= r->scr_offset; } if ( buf->renderOffset != -1 ) { if ( r->scr_changes == r->demux.scr_changes ) { // This packet is referenced to the same SCR as the last. // Adjust timestamp to remove the System Clock Reference // offset then update the average inter-packet time // for this stream. buf->renderOffset -= r->scr_offset; update_ipt( r, buf ); } } if ( !r->start_found ) { hb_buffer_close( &buf ); continue; } buf->sequence = r->sequence++; /* if there are mutiple output fifos, send a copy of the * buffer down all but the first (we have to not ship the * original buffer or we'll race with the thread that's * consuming the buffer & inject garbage into the data stream). */ for( n = 1; fifos[n] != NULL; n++) { hb_buffer_t *buf_copy = hb_buffer_init( buf->size ); hb_buffer_copy_settings( buf_copy, buf ); memcpy( buf_copy->data, buf->data, buf->size ); push_buf( r, fifos[n], buf_copy ); } push_buf( r, fifos[0], buf ); } else { hb_buffer_close( &buf ); } } } // send empty buffers downstream to video & audio decoders to signal we're done. if( !*r->die && !r->job->done ) { push_buf( r, r->job->fifo_mpeg2, hb_buffer_init(0) ); hb_audio_t *audio; for( n = 0; (audio = hb_list_item( r->job->title->list_audio, n)); ++n ) { if ( audio->priv.fifo_in ) push_buf( r, audio->priv.fifo_in, hb_buffer_init(0) ); } hb_subtitle_t *subtitle; for( n = 0; (subtitle = hb_list_item( r->job->title->list_subtitle, n)); ++n ) { if ( subtitle->fifo_in && subtitle->source == VOBSUB) push_buf( r, subtitle->fifo_in, hb_buffer_init(0) ); } } hb_list_empty( &list ); if (r->bd) { hb_bd_stop( r->bd ); hb_bd_close( &r->bd ); } else if (r->dvd) { hb_dvd_stop( r->dvd ); hb_dvd_close( &r->dvd ); } else if (r->stream) { hb_stream_close(&r->stream); } if ( r->stream_timing ) { free( r->stream_timing ); } hb_log( "reader: done. %d scr changes", r->demux.scr_changes ); if ( r->demux.dts_drops ) { hb_log( "reader: %d drops because DTS out of range", r->demux.dts_drops ); } free( r ); _r = NULL; }
/*********************************************************************** * hb_bd_stop *********************************************************************** * **********************************************************************/ void hb_bd_stop( hb_bd_t * d ) { if( d->stream ) hb_stream_close( &d->stream ); }
static void ScanFunc( void * _data ) { hb_scan_t * data = (hb_scan_t *) _data; hb_title_t * title; int i; int feature = 0; data->bd = NULL; data->dvd = NULL; data->stream = NULL; /* Try to open the path as a DVD. If it fails, try as a file */ if( ( data->bd = hb_bd_init( data->h, data->path ) ) ) { hb_log( "scan: BD has %d title(s)", hb_bd_title_count( data->bd ) ); if( data->title_index ) { /* Scan this title only */ hb_list_add( data->title_set->list_title, hb_bd_title_scan( data->bd, data->title_index, 0 ) ); } else { /* Scan all titles */ for( i = 0; i < hb_bd_title_count( data->bd ); i++ ) { UpdateState1(data, i + 1); hb_list_add( data->title_set->list_title, hb_bd_title_scan( data->bd, i + 1, data->min_title_duration ) ); } feature = hb_bd_main_feature( data->bd, data->title_set->list_title ); } } else if( ( data->dvd = hb_dvd_init( data->h, data->path ) ) ) { hb_log( "scan: DVD has %d title(s)", hb_dvd_title_count( data->dvd ) ); if( data->title_index ) { /* Scan this title only */ hb_list_add( data->title_set->list_title, hb_dvd_title_scan( data->dvd, data->title_index, 0 ) ); } else { /* Scan all titles */ for( i = 0; i < hb_dvd_title_count( data->dvd ); i++ ) { UpdateState1(data, i + 1); hb_list_add( data->title_set->list_title, hb_dvd_title_scan( data->dvd, i + 1, data->min_title_duration ) ); } feature = hb_dvd_main_feature( data->dvd, data->title_set->list_title ); } } else if ( ( data->batch = hb_batch_init( data->h, data->path ) ) ) { if( data->title_index ) { /* Scan this title only */ title = hb_batch_title_scan(data->batch, data->title_index, 0); if ( title ) { hb_list_add( data->title_set->list_title, title ); } } else { /* Scan all titles */ for( i = 0; i < hb_batch_title_count( data->batch ); i++ ) { hb_title_t * title; UpdateState1(data, i + 1); title = hb_batch_title_scan(data->batch, i + 1, data->min_title_duration); if ( title != NULL ) { hb_list_add( data->title_set->list_title, title ); } } } } else { // Title index 0 is not a valid title number and means scan all titles. // So set title index to 1 in this scenario. // // Otherwise, set title index in new title to the index that was // requested. This preserves the original index created in batch // mode. if (data->title_index == 0) data->title_index = 1; hb_title_t * title = hb_title_init( data->path, data->title_index ); data->stream = hb_stream_open(data->h, data->path, title, 1); if (data->stream != NULL) { title = hb_stream_title_scan( data->stream, title ); if ( title ) hb_list_add( data->title_set->list_title, title ); } else { hb_title_close( &title ); hb_log( "scan: unrecognized file type" ); goto finish; } } for( i = 0; i < hb_list_count( data->title_set->list_title ); ) { int j, npreviews; hb_audio_t * audio; if ( *data->die ) { goto finish; } title = hb_list_item( data->title_set->list_title, i ); UpdateState2(data, i + 1); /* Decode previews */ /* this will also detect more AC3 / DTS information */ npreviews = DecodePreviews( data, title, 1 ); if (npreviews < 2) { // Try harder to get some valid frames // Allow libav to return "corrupt" frames hb_log("scan: Too few previews (%d), trying harder", npreviews); title->flags |= HBTF_NO_IDR; npreviews = DecodePreviews( data, title, 0 ); } if (npreviews == 0) { /* TODO: free things */ hb_list_rem( data->title_set->list_title, title ); for( j = 0; j < hb_list_count( title->list_audio ); j++) { audio = hb_list_item( title->list_audio, j ); if ( audio->priv.scan_cache ) { hb_fifo_flush( audio->priv.scan_cache ); hb_fifo_close( &audio->priv.scan_cache ); } } hb_title_close( &title ); continue; } title->preview_count = npreviews; /* 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->priv.scan_cache ) { hb_fifo_flush( audio->priv.scan_cache ); hb_fifo_close( &audio->priv.scan_cache ); } 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; } j++; } // VOBSUB and PGS width and height needs to be set to the // title width and height for any stream type that does // not provide this information (DVDs, BDs, VOBs, and M2TSs). // Title width and height don't get set until we decode // previews, so we can't set subtitle width/height till // we get here. for (j = 0; j < hb_list_count(title->list_subtitle); j++) { hb_subtitle_t *subtitle = hb_list_item(title->list_subtitle, j); if ((subtitle->source == VOBSUB || subtitle->source == PGSSUB) && (subtitle->width <= 0 || subtitle->height <= 0)) { subtitle->width = title->geometry.width; subtitle->height = title->geometry.height; } } i++; } data->title_set->feature = feature; /* Mark title scan complete and init jobs */ for( i = 0; i < hb_list_count( data->title_set->list_title ); i++ ) { title = hb_list_item( data->title_set->list_title, i ); title->flags |= HBTF_SCAN_COMPLETE; } if (hb_list_count(data->title_set->list_title) > 0) { strncpy(data->title_set->path, data->path, 1024); data->title_set->path[1023] = 0; } else { data->title_set->path[0] = 0; } finish: if( data->bd ) { hb_bd_close( &data->bd ); } if( data->dvd ) { hb_dvd_close( &data->dvd ); } if (data->stream) { hb_stream_close(&data->stream); } if( data->batch ) { hb_batch_close( &data->batch ); } free( data->path ); free( data ); _data = NULL; hb_buffer_pool_free(); }
/*********************************************************************** * DecodePreviews *********************************************************************** * Decode 10 pictures for the given title. * It assumes that data->reader and data->vts have successfully been * DVDOpen()ed and ifoOpen()ed. **********************************************************************/ static int DecodePreviews( hb_scan_t * data, hb_title_t * title, int flush ) { int i, npreviews = 0, abort = 0; hb_buffer_t * buf, * buf_es; hb_buffer_list_t list_es; int progressive_count = 0; int pulldown_count = 0; int doubled_frame_count = 0; int interlaced_preview_count = 0; int vid_samples = 0; int frame_wait = 0; int cc_wait = 10; int frames; hb_stream_t * stream = NULL; info_list_t * info_list; int abort_audio = 0; info_list = calloc(data->preview_count+1, sizeof(*info_list)); crop_record_t *crops = crop_record_init( data->preview_count ); hb_buffer_list_clear(&list_es); if( data->batch ) { hb_log( "scan: decoding previews for title %d (%s)", title->index, title->path ); } else { hb_log( "scan: decoding previews for title %d", title->index ); } if (data->bd) { hb_bd_start( data->bd, title ); hb_log( "scan: title angle(s) %d", title->angle_count ); } else if (data->dvd) { hb_dvd_start( data->dvd, title, 1 ); title->angle_count = hb_dvd_angle_count( data->dvd ); hb_log( "scan: title angle(s) %d", title->angle_count ); } else if (data->batch) { stream = hb_stream_open(data->h, title->path, title, 0); } else if (data->stream) { stream = hb_stream_open(data->h, data->path, title, 0); } if (title->video_codec == WORK_NONE) { hb_error("No video decoder set!"); free(info_list); crop_record_free(crops); hb_stream_close(&stream); return 0; } hb_work_object_t *vid_decoder = hb_get_work(data->h, title->video_codec); vid_decoder->codec_param = title->video_codec_param; vid_decoder->title = title; if (vid_decoder->init(vid_decoder, NULL)) { hb_error("Decoder init failed!"); free(info_list); crop_record_free(crops); free( vid_decoder ); hb_stream_close(&stream); return 0; } for( i = 0; i < data->preview_count; i++ ) { int j; UpdateState3(data, i + 1); if ( *data->die ) { free( info_list ); crop_record_free( crops ); vid_decoder->close( vid_decoder ); free( vid_decoder ); hb_stream_close(&stream); return 0; } if (data->bd) { if( !hb_bd_seek( data->bd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) ) { continue; } } if (data->dvd) { if( !hb_dvd_seek( data->dvd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) ) { continue; } } else if (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. * * Also, seeking to position 0 loses the palette of avi files * so skip initial seek */ if (i != 0) { if (!hb_stream_seek(stream, (float)i / (data->preview_count + 1.0))) { continue; } } else { hb_stream_set_need_keyframe(stream, 1); } } hb_deep_log( 2, "scan: preview %d", i + 1 ); if (flush && vid_decoder->flush) vid_decoder->flush( vid_decoder ); if (title->flags & HBTF_NO_IDR) { if (!flush) { // If we are doing the first previews decode attempt, // set this threshold high so that we get the best // quality frames possible. frame_wait = 100; } else { // If we failed to get enough valid frames in the first // previews decode attempt, lower the threshold to improve // our chances of getting something to work with. frame_wait = 10; } } else { // For certain mpeg-2 streams, libav is delivering a // dummy first frame that is all black. So always skip // one frame frame_wait = 1; } frames = 0; hb_buffer_t * vid_buf = NULL, * last_vid_buf = NULL; int packets = 0; vid_decoder->frame_count = 0; while (vid_decoder->frame_count < PREVIEW_READ_THRESH || (!AllAudioOK(title) && packets < 10000)) { if ((buf = read_buf(data, stream)) == NULL) { // If we reach EOF and no audio, don't continue looking for // audio abort_audio = 1; if (vid_buf != NULL || last_vid_buf != NULL) { break; } hb_log("Warning: Could not read data for preview %d, skipped", i + 1 ); // If we reach EOF and no video, don't continue looking for // video abort = 1; goto skip_preview; } packets++; if (buf->size <= 0) { // Ignore "null" frames hb_buffer_close(&buf); continue; } (hb_demux[title->demuxer])(buf, &list_es, 0 ); while ((buf_es = hb_buffer_list_rem_head(&list_es)) != NULL) { if( buf_es->s.id == title->video_id && vid_buf == NULL ) { vid_decoder->work( vid_decoder, &buf_es, &vid_buf ); // There are 2 conditions we decode additional // video frames for during scan. // 1. We did not detect IDR frames, so the initial video // frames may be corrupt. We docode extra frames to // increase the probability of a complete preview frame // 2. Some frames do not contain CC data, even though // CCs are present in the stream. So we need to decode // additional frames to find the CCs. if (vid_buf != NULL && (frame_wait || cc_wait)) { hb_work_info_t vid_info; if (vid_decoder->info(vid_decoder, &vid_info)) { if (is_close_to(vid_info.rate.den, 900900, 100) && (vid_buf->s.flags & PIC_FLAG_REPEAT_FIRST_FIELD)) { /* Potentially soft telecine material */ pulldown_count++; } if (vid_buf->s.flags & PIC_FLAG_REPEAT_FRAME) { // AVCHD-Lite specifies that all streams are // 50 or 60 fps. To produce 25 or 30 fps, camera // makers are repeating all frames. doubled_frame_count++; } if (is_close_to(vid_info.rate.den, 1126125, 100 )) { // 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++; } vid_samples++; } if (frames > 0 && vid_buf->s.frametype == HB_FRAME_I) frame_wait = 0; if (frame_wait || cc_wait) { hb_buffer_close(&last_vid_buf); last_vid_buf = vid_buf; vid_buf = NULL; if (frame_wait) frame_wait--; if (cc_wait) cc_wait--; } frames++; } } else if (!AllAudioOK(title) && !abort_audio) { LookForAudio( data, title, buf_es ); buf_es = NULL; } if ( buf_es ) hb_buffer_close( &buf_es ); } if (vid_buf && (abort_audio || AllAudioOK(title))) break; } hb_buffer_list_close(&list_es); if (vid_buf == NULL) { vid_buf = last_vid_buf; last_vid_buf = NULL; } hb_buffer_close(&last_vid_buf); if (vid_buf == NULL) { hb_log( "scan: could not get a decoded picture" ); continue; } /* Get size and rate infos */ hb_work_info_t vid_info; if( !vid_decoder->info( vid_decoder, &vid_info ) ) { /* * Could not fill vid_info, don't continue and try to use vid_info * in this case. */ hb_log( "scan: could not get a video information" ); hb_buffer_close( &vid_buf ); continue; } if (vid_info.geometry.width != vid_buf->f.width || vid_info.geometry.height != vid_buf->f.height) { hb_log( "scan: video geometry information does not match buffer" ); hb_buffer_close( &vid_buf ); continue; } remember_info( info_list, &vid_info ); /* Check preview for interlacing artifacts */ if( hb_detect_comb( vid_buf, 10, 30, 9, 10, 30, 9 ) ) { hb_deep_log( 2, "Interlacing detected in preview frame %i", i+1); interlaced_preview_count++; } if( data->store_previews ) { hb_save_preview( data->h, title->index, i, vid_buf ); } /* Detect black borders */ int top, bottom, left, right; int h4 = vid_info.geometry.height / 4, w4 = vid_info.geometry.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 = vid_info.geometry.height / 100; for ( top = border; top < h4; ++top ) { if ( ! row_all_dark( vid_buf, top ) ) break; } if ( top <= border ) { // 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( vid_buf, top ) ) break; } if ( top >= border ) { top = 0; } } for ( bottom = border; bottom < h4; ++bottom ) { if ( ! row_all_dark( vid_buf, vid_info.geometry.height - 1 - bottom ) ) break; } if ( bottom <= border ) { for ( bottom = 0; bottom < border; ++bottom ) { if ( ! row_all_dark( vid_buf, vid_info.geometry.height - 1 - bottom ) ) break; } if ( bottom >= border ) { bottom = 0; } } for ( left = 0; left < w4; ++left ) { if ( ! column_all_dark( vid_buf, top, bottom, left ) ) break; } for ( right = 0; right < w4; ++right ) { if ( ! column_all_dark( vid_buf, top, bottom, vid_info.geometry.width - 1 - right ) ) break; } // 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 ) { record_crop( crops, top, bottom, left, right ); } ++npreviews; skip_preview: /* Make sure we found audio rates and bitrates */ for( j = 0; j < hb_list_count( title->list_audio ); j++ ) { hb_audio_t * audio = hb_list_item( title->list_audio, j ); if ( audio->priv.scan_cache ) { hb_fifo_flush( audio->priv.scan_cache ); } } if (vid_buf) { hb_buffer_close( &vid_buf ); } if (abort) { break; } } UpdateState3(data, i); vid_decoder->close( vid_decoder ); free( vid_decoder ); if (stream != NULL) { hb_stream_close(&stream); } 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->has_resolution_change = has_resolution_change( info_list ); if ( title->video_codec_name == NULL ) { title->video_codec_name = strdup( vid_info.name ); } title->geometry.width = vid_info.geometry.width; title->geometry.height = vid_info.geometry.height; if (vid_info.rate.num && vid_info.rate.den) { // if the frame rate is very close to one of our "common" // framerates, assume it actually is said frame rate; // e.g. some 24000/1001 sources may have a rate.den of 1126124 // instead of 1126125 const hb_rate_t *video_framerate = NULL; while ((video_framerate = hb_video_framerate_get_next(video_framerate)) != NULL) { if (is_close_to(vid_info.rate.den, video_framerate->rate, 100)) { vid_info.rate.den = video_framerate->rate; break; } } title->vrate = vid_info.rate; if( vid_info.rate.den == 900900 ) { if (vid_samples >= 4 && pulldown_count >= vid_samples / 4) { title->vrate.den = 1126125; hb_deep_log( 2, "Pulldown detected, setting fps to 23.976" ); } if (vid_samples >= 2 && progressive_count >= vid_samples / 2) { // We've already deduced that the frame rate is 23.976, // so set it back again. title->vrate.den = 1126125; hb_deep_log( 2, "Title's mostly NTSC Film, setting fps to 23.976" ); } } if (vid_samples >= 2 && doubled_frame_count >= 3 * vid_samples / 4) { // We've detected that a significant number of the frames // have been doubled in duration by repeat flags. title->vrate.den = 2 * vid_info.rate.den; hb_deep_log(2, "Repeat frames detected, setting fps to %.3f", (float)title->vrate.num / title->vrate.den ); } } title->video_bitrate = vid_info.bitrate; if( vid_info.geometry.par.num && vid_info.geometry.par.den ) { title->geometry.par = vid_info.geometry.par; } title->color_prim = vid_info.color_prim; title->color_transfer = vid_info.color_transfer; title->color_matrix = vid_info.color_matrix; title->video_decode_support = vid_info.video_decode_support; // compute the aspect ratio based on the storage dimensions and PAR. hb_reduce(&title->dar.num, &title->dar.den, title->geometry.par.num * title->geometry.width, title->geometry.height * title->geometry.par.den); // 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 (data->dvd && (title->dar.num != title->container_dar.num || title->dar.den != title->container_dar.den)) { hb_log("scan: content PAR gives wrong aspect %d:%d; " "using container aspect %d:%d", title->dar.num, title->dar.den, title->container_dar.num, title->container_dar.den); title->dar = title->container_dar; hb_reduce(&title->geometry.par.num, &title->geometry.par.den, title->geometry.height * title->dar.num, title->geometry.width * title->dar.den); } // 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] ); } hb_log( "scan: %d previews, %dx%d, %.3f fps, autocrop = %d/%d/%d/%d, " "aspect %s, PAR %d:%d", npreviews, title->geometry.width, title->geometry.height, (float)title->vrate.num / title->vrate.den, title->crop[0], title->crop[1], title->crop[2], title->crop[3], aspect_to_string(&title->dar), title->geometry.par.num, title->geometry.par.den); if (title->video_decode_support != HB_DECODE_SUPPORT_SW) { hb_log("scan: supported video decoders:%s%s", !(title->video_decode_support & HB_DECODE_SUPPORT_SW) ? "" : " avcodec", !(title->video_decode_support & HB_DECODE_SUPPORT_QSV) ? "" : " qsv"); } 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; } }
static void ScanFunc( void * _data ) { hb_scan_t * data = (hb_scan_t *) _data; hb_title_t * title; int i; int feature = 0; data->bd = NULL; data->dvd = NULL; data->stream = NULL; /* Try to open the path as a DVD. If it fails, try as a file */ if( ( data->bd = hb_bd_init( data->path ) ) ) { hb_log( "scan: BD has %d title(s)", hb_bd_title_count( data->bd ) ); if( data->title_index ) { /* Scan this title only */ hb_list_add( data->list_title, hb_bd_title_scan( data->bd, data->title_index, 0 ) ); } else { /* Scan all titles */ for( i = 0; i < hb_bd_title_count( data->bd ); i++ ) { hb_list_add( data->list_title, hb_bd_title_scan( data->bd, i + 1, data->min_title_duration ) ); } feature = hb_bd_main_feature( data->bd, data->list_title ); } } else if( ( data->dvd = hb_dvd_init( data->path ) ) ) { hb_log( "scan: DVD has %d title(s)", hb_dvd_title_count( data->dvd ) ); if( data->title_index ) { /* Scan this title only */ hb_list_add( data->list_title, hb_dvd_title_scan( data->dvd, data->title_index, 0 ) ); } else { /* Scan all titles */ for( i = 0; i < hb_dvd_title_count( data->dvd ); i++ ) { hb_list_add( data->list_title, hb_dvd_title_scan( data->dvd, i + 1, data->min_title_duration ) ); } feature = hb_dvd_main_feature( data->dvd, data->list_title ); } } else if ( ( data->batch = hb_batch_init( data->path ) ) ) { if( data->title_index ) { /* Scan this title only */ title = hb_batch_title_scan( data->batch, data->title_index ); if ( title ) { hb_list_add( data->list_title, title ); } } else { /* Scan all titles */ for( i = 0; i < hb_batch_title_count( data->batch ); i++ ) { hb_title_t * title; title = hb_batch_title_scan( data->batch, i + 1 ); if ( title != NULL ) { hb_list_add( data->list_title, title ); } } } } else { hb_title_t * title = hb_title_init( data->path, 0 ); if ( (data->stream = hb_stream_open( data->path, title, 1 ) ) != NULL ) { title = hb_stream_title_scan( data->stream, title ); if ( title ) hb_list_add( data->list_title, title ); } else { hb_title_close( &title ); hb_log( "scan: unrecognized file type" ); return; } } for( i = 0; i < hb_list_count( data->list_title ); ) { int j; hb_state_t state; hb_audio_t * audio; if ( *data->die ) { goto finish; } title = hb_list_item( data->list_title, i ); #define p state.param.scanning /* Update the UI */ state.state = HB_STATE_SCANNING; p.title_cur = title->index; p.title_count = data->dvd ? hb_dvd_title_count( data->dvd ) : data->bd ? hb_bd_title_count( data->bd ) : data->batch ? hb_batch_title_count( data->batch ) : 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 */ hb_list_rem( data->list_title, title ); for( j = 0; j < hb_list_count( title->list_audio ); j++) { audio = hb_list_item( title->list_audio, j ); if ( audio->priv.scan_cache ) { hb_fifo_flush( audio->priv.scan_cache ); hb_fifo_close( &audio->priv.scan_cache ); } } hb_title_close( &title ); continue; } /* 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->priv.scan_cache ) { hb_fifo_flush( audio->priv.scan_cache ); hb_fifo_close( &audio->priv.scan_cache ); } 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; } j++; } if ( data->dvd || data->bd ) { // The subtitle width and height needs to be set to the // title widht and height for DVDs. title width and // height don't get set until we decode previews, so // we can't set subtitle width/height till we get here. for( j = 0; j < hb_list_count( title->list_subtitle ); j++ ) { hb_subtitle_t *subtitle = hb_list_item( title->list_subtitle, j ); if ( subtitle->source == VOBSUB || subtitle->source == PGSSUB ) { subtitle->width = title->width; subtitle->height = title->height; } } } i++; } /* Init jobs templates */ for( i = 0; i < hb_list_count( data->list_title ); i++ ) { hb_job_t * job; title = hb_list_item( data->list_title, i ); job = calloc( sizeof( hb_job_t ), 1 ); title->job = job; job->title = title; job->feature = feature; /* Set defaults settings */ job->chapter_start = 1; job->chapter_end = hb_list_count( title->list_chapter ); /* 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] ) { 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_MPEG4; job->vquality = -1.0; job->vbitrate = 1000; job->pass = 0; job->vrate = title->rate; job->vrate_base = title->rate_base; job->list_audio = hb_list_init(); job->list_subtitle = hb_list_init(); job->list_filter = hb_list_init(); job->mux = HB_MUX_MP4; } finish: if( data->bd ) { hb_bd_close( &data->bd ); } if( data->dvd ) { hb_dvd_close( &data->dvd ); } if (data->stream) { hb_stream_close(&data->stream); } if( data->batch ) { hb_batch_close( &data->batch ); } free( data->path ); free( data ); _data = NULL; }
/*********************************************************************** * DecodePreviews *********************************************************************** * Decode 10 pictures for the given title. * It assumes that data->reader and data->vts have successfully been * DVDOpen()ed and ifoOpen()ed. **********************************************************************/ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) { int i, npreviews = 0; hb_buffer_t * buf, * buf_es; hb_list_t * list_es; int progressive_count = 0; int pulldown_count = 0; int doubled_frame_count = 0; int interlaced_preview_count = 0; info_list_t * info_list = calloc( data->preview_count+1, sizeof(*info_list) ); crop_record_t *crops = crop_record_init( data->preview_count ); list_es = hb_list_init(); if( data->batch ) { hb_log( "scan: decoding previews for title %d (%s)", title->index, title->path ); } else { hb_log( "scan: decoding previews for title %d", title->index ); } if (data->bd) { hb_bd_start( data->bd, title ); hb_log( "scan: title angle(s) %d", title->angle_count ); } else if (data->dvd) { hb_dvd_start( data->dvd, title, 1 ); title->angle_count = hb_dvd_angle_count( data->dvd ); hb_log( "scan: title angle(s) %d", title->angle_count ); } else if (data->batch) { data->stream = hb_stream_open( title->path, title, 1 ); } int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2; #if defined(USE_FF_MPEG2) if (vcodec == WORK_DECMPEG2) { vcodec = WORK_DECAVCODECV; title->video_codec_param = CODEC_ID_MPEG2VIDEO; } #endif hb_work_object_t *vid_decoder = hb_get_work( vcodec ); vid_decoder->codec_param = title->video_codec_param; vid_decoder->title = title; vid_decoder->init( vid_decoder, NULL ); for( i = 0; i < data->preview_count; i++ ) { int j; if ( *data->die ) { free( info_list ); crop_record_free( crops ); return 0; } if (data->bd) { if( !hb_bd_seek( data->bd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) ) { continue; } } if (data->dvd) { 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_deep_log( 2, "scan: preview %d", i + 1 ); if ( vid_decoder->flush ) vid_decoder->flush( vid_decoder ); hb_buffer_t * vid_buf = NULL; for( j = 0; j < 10240 ; j++ ) { if (data->bd) { if( (buf = hb_bd_read( data->bd )) == NULL ) { if ( vid_buf ) { break; } hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 ); goto skip_preview; } } else if (data->dvd) { if( (buf = hb_dvd_read( data->dvd )) == NULL ) { if ( vid_buf ) { break; } hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 ); goto skip_preview; } } else if (data->stream) { if ( (buf = hb_stream_read( data->stream )) == NULL ) { if ( vid_buf ) { break; } hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 ); goto skip_preview; } } else { // Silence compiler warning buf = NULL; hb_error( "Error: This can't happen!" ); goto skip_preview; } (hb_demux[title->demuxer])(buf, list_es, 0 ); while( ( buf_es = hb_list_item( list_es, 0 ) ) ) { hb_list_rem( list_es, buf_es ); if( buf_es->s.id == title->video_id && vid_buf == NULL ) { vid_decoder->work( vid_decoder, &buf_es, &vid_buf ); } else if( ! AllAudioOK( title ) ) { LookForAudio( title, buf_es ); buf_es = NULL; } if ( buf_es ) hb_buffer_close( &buf_es ); } if( vid_buf && AllAudioOK( title ) ) break; } if( ! vid_buf ) { hb_log( "scan: could not get a decoded picture" ); continue; } /* Get size and rate infos */ hb_work_info_t vid_info; if( !vid_decoder->info( vid_decoder, &vid_info ) ) { /* * Could not fill vid_info, don't continue and try to use vid_info * in this case. */ if (vid_buf) { hb_buffer_close( &vid_buf ); } hb_log( "scan: could not get a video information" ); continue; } remember_info( info_list, &vid_info ); if( is_close_to( vid_info.rate_base, 900900, 100 ) && ( vid_buf->s.flags & PIC_FLAG_REPEAT_FIRST_FIELD ) ) { /* Potentially soft telecine material */ pulldown_count++; } if( vid_buf->s.flags & PIC_FLAG_REPEAT_FRAME ) { // AVCHD-Lite specifies that all streams are // 50 or 60 fps. To produce 25 or 30 fps, camera // makers are repeating all frames. doubled_frame_count++; } if( is_close_to( vid_info.rate_base, 1126125, 100 ) ) { // 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++; } while( ( buf_es = hb_list_item( list_es, 0 ) ) ) { hb_list_rem( list_es, buf_es ); hb_buffer_close( &buf_es ); } /* Check preview for interlacing artifacts */ if( hb_detect_comb( vid_buf, 10, 30, 9, 10, 30, 9 ) ) { hb_deep_log( 2, "Interlacing detected in preview frame %i", i+1); interlaced_preview_count++; } if( data->store_previews ) { hb_save_preview( data->h, title->index, i, vid_buf ); } /* Detect black borders */ int top, bottom, left, right; int h4 = vid_info.height / 4, w4 = vid_info.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 = vid_info.height / 100; for ( top = border; top < h4; ++top ) { if ( ! row_all_dark( vid_buf, top ) ) break; } if ( top <= border ) { // 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( vid_buf, top ) ) break; } if ( top >= border ) { top = 0; } } for ( bottom = border; bottom < h4; ++bottom ) { if ( ! row_all_dark( vid_buf, vid_info.height - 1 - bottom ) ) break; } if ( bottom <= border ) { for ( bottom = 0; bottom < border; ++bottom ) { if ( ! row_all_dark( vid_buf, vid_info.height - 1 - bottom ) ) break; } if ( bottom >= border ) { bottom = 0; } } for ( left = 0; left < w4; ++left ) { if ( ! column_all_dark( vid_buf, top, bottom, left ) ) break; } for ( right = 0; right < w4; ++right ) { if ( ! column_all_dark( vid_buf, top, bottom, vid_info.width - 1 - right ) ) break; } // 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 ) { record_crop( crops, top, bottom, left, right ); } ++npreviews; skip_preview: /* Make sure we found audio rates and bitrates */ for( j = 0; j < hb_list_count( title->list_audio ); j++ ) { hb_audio_t * audio = hb_list_item( title->list_audio, j ); if ( audio->priv.scan_cache ) { hb_fifo_flush( audio->priv.scan_cache ); } } if (vid_buf) { hb_buffer_close( &vid_buf ); } } vid_decoder->close( vid_decoder ); free( vid_decoder ); if ( data->batch && data->stream ) { hb_stream_close( &data->stream ); } 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->has_resolution_change = has_resolution_change( info_list ); if ( title->video_codec_name == NULL ) { title->video_codec_name = strdup( vid_info.name ); } title->width = vid_info.width; title->height = vid_info.height; if ( vid_info.rate && vid_info.rate_base ) { // if the frame rate is very close to one of our "common" framerates, // assume it actually is said frame rate; e.g. some 24000/1001 sources // may have a rate_base of 1126124 (instead of 1126125) for( i = 0; i < hb_video_rates_count; i++ ) { if( is_close_to( vid_info.rate_base, hb_video_rates[i].rate, 100 ) ) { vid_info.rate_base = hb_video_rates[i].rate; break; } } title->rate = vid_info.rate; title->rate_base = vid_info.rate_base; if( vid_info.rate_base == 900900 ) { if( pulldown_count >= npreviews / 4 ) { title->rate_base = 1126125; hb_deep_log( 2, "Pulldown detected, setting fps to 23.976" ); } if( progressive_count >= npreviews / 2 ) { // We've already deduced that the frame rate is 23.976, // so set it back again. title->rate_base = 1126125; hb_deep_log( 2, "Title's mostly NTSC Film, setting fps to 23.976" ); } } if( doubled_frame_count >= 3 * npreviews / 4 ) { // We've detected that a significant number of the frames // have been doubled in duration by repeat flags. title->rate_base = 2 * vid_info.rate_base; hb_deep_log( 2, "Repeat frames detected, setting fps to %.3f", (float)title->rate / title->rate_base ); } } title->video_bitrate = vid_info.bitrate; if( vid_info.pixel_aspect_width && vid_info.pixel_aspect_height ) { title->pixel_aspect_width = vid_info.pixel_aspect_width; title->pixel_aspect_height = vid_info.pixel_aspect_height; } title->color_prim = vid_info.color_prim; title->color_transfer = vid_info.color_transfer; title->color_matrix = vid_info.color_matrix; // 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; 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 ); } // 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] ); } 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 ); 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; } }
/*********************************************************************** * DecodePreviews *********************************************************************** * Decode 10 pictures for the given title. * It assumes that data->reader and data->vts have successfully been * DVDOpen()ed and ifoOpen()ed. **********************************************************************/ static int DecodePreviews( hb_scan_t * data, hb_title_t * title ) { 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(); hb_log( "scan: decoding previews for title %d", title->index ); if (data->bd) { hb_bd_start( data->bd, title ); hb_log( "scan: title angle(s) %d", title->angle_count ); } else if (data->dvd) { hb_dvd_start( data->dvd, title, 1 ); title->angle_count = hb_dvd_angle_count( data->dvd ); hb_log( "scan: title angle(s) %d", title->angle_count ); } else if (data->batch) { data->stream = hb_stream_open( title->path, title ); } for( i = 0; i < data->preview_count; i++ ) { int j; FILE * file_preview; char filename[1024]; if ( *data->die ) { return 0; } if (data->bd) { if( !hb_bd_seek( data->bd, (float) ( i + 1 ) / ( data->preview_count + 1.0 ) ) ) { continue; } } if (data->dvd) { 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_deep_log( 2, "scan: preview %d", i + 1 ); int vcodec = title->video_codec? title->video_codec : WORK_DECMPEG2; #if defined(USE_FF_MPEG2) if (vcodec == WORK_DECMPEG2) { vcodec = WORK_DECAVCODECV; title->video_codec_param = CODEC_ID_MPEG2VIDEO; } #endif hb_work_object_t *vid_decoder = hb_get_work( vcodec ); vid_decoder->codec_param = title->video_codec_param; vid_decoder->title = title; 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; } // If it's a BD, we can relax this a bit. Since seeks will // at least get us to a recovery point. if (data->bd) vidskip = 4; } for( j = 0; j < 10240 ; j++ ) { if (data->bd) { if( !hb_bd_read( data->bd, buf_ps ) ) { if ( vid_buf ) { break; } hb_log( "Warning: Could not read data for preview %d, skipped", i + 1 ); goto skip_preview; } } 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) { 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[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 == title->video_id && vid_buf == NULL ) { 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 while (vid_buf && --vidskip >= 0) { hb_buffer_t * next = vid_buf->next; vid_buf->next = NULL; hb_buffer_close( &vid_buf ); vid_buf = next; } } } else if( ! AllAudioOK( title ) ) { LookForAudio( title, buf_es ); buf_es = NULL; } if ( buf_es ) hb_buffer_close( &buf_es ); } if( vid_buf && AllAudioOK( title ) ) break; } if( ! vid_buf ) { hb_log( "scan: could not get a decoded picture" ); continue; } /* Get size and rate infos */ hb_work_info_t vid_info; if( !vid_decoder->info( vid_decoder, &vid_info ) ) { /* * Could not fill vid_info, don't continue and try to use vid_info * in this case. */ if (vid_buf) { hb_buffer_close( &vid_buf ); } vid_decoder->close( vid_decoder ); free( vid_decoder ); continue; } vid_decoder->close( vid_decoder ); free( vid_decoder ); remember_info( info_list, &vid_info ); 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 ) ) ) { hb_list_rem( list_es, buf_es ); hb_buffer_close( &buf_es ); } /* Check preview for interlacing artifacts */ if( hb_detect_comb( vid_buf, title->width, title->height, 10, 30, 9, 10, 30, 9 ) ) { hb_deep_log( 2, "Interlacing detected in preview frame %i", i+1); interlaced_preview_count++; } if( data->store_previews ) { hb_get_tempory_filename( data->h, filename, "%d_%d_%d", hb_get_instance_id(data->h), title->index, i ); file_preview = fopen( filename, "wb" ); 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 ); } } /* Detect black borders */ #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 ) { if ( ! row_all_dark( title, Y, top ) ) break; } if ( top <= border ) { // 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; } 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; } // 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 ) { record_crop( crops, top, bottom, left, right ); } ++npreviews; skip_preview: /* Make sure we found audio rates and bitrates */ for( j = 0; j < hb_list_count( title->list_audio ); j++ ) { hb_audio_t * audio = hb_list_item( title->list_audio, j ); if ( audio->priv.scan_cache ) { hb_fifo_flush( audio->priv.scan_cache ); } } if (vid_buf) { hb_buffer_close( &vid_buf ); } } if ( data->batch && data->stream ) { hb_stream_close( &data->stream ); } 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 ); } } // 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 ); 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 ); 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; } }
static void ScanFunc( void * _data ) { hb_scan_t * data = (hb_scan_t *) _data; hb_title_t * title; int i; int feature = 0; data->bd = NULL; data->dvd = NULL; data->stream = NULL; /* Try to open the path as a DVD. If it fails, try as a file */ if( ( data->bd = hb_bd_init( data->path ) ) ) { hb_log( "scan: BD has %d title(s)", hb_bd_title_count( data->bd ) ); if( data->title_index ) { /* Scan this title only */ hb_list_add( data->title_set->list_title, hb_bd_title_scan( data->bd, data->title_index, 0 ) ); } else { /* Scan all titles */ for( i = 0; i < hb_bd_title_count( data->bd ); i++ ) { UpdateState1(data, i + 1); hb_list_add( data->title_set->list_title, hb_bd_title_scan( data->bd, i + 1, data->min_title_duration ) ); } feature = hb_bd_main_feature( data->bd, data->title_set->list_title ); } } else if( ( data->dvd = hb_dvd_init( data->path ) ) ) { hb_log( "scan: DVD has %d title(s)", hb_dvd_title_count( data->dvd ) ); if( data->title_index ) { /* Scan this title only */ hb_list_add( data->title_set->list_title, hb_dvd_title_scan( data->dvd, data->title_index, 0 ) ); } else { /* Scan all titles */ for( i = 0; i < hb_dvd_title_count( data->dvd ); i++ ) { UpdateState1(data, i + 1); hb_list_add( data->title_set->list_title, hb_dvd_title_scan( data->dvd, i + 1, data->min_title_duration ) ); } feature = hb_dvd_main_feature( data->dvd, data->title_set->list_title ); } } else if ( ( data->batch = hb_batch_init( data->path ) ) ) { if( data->title_index ) { /* Scan this title only */ title = hb_batch_title_scan( data->batch, data->title_index ); if ( title ) { hb_list_add( data->title_set->list_title, title ); } } else { /* Scan all titles */ for( i = 0; i < hb_batch_title_count( data->batch ); i++ ) { hb_title_t * title; UpdateState1(data, i + 1); title = hb_batch_title_scan( data->batch, i + 1 ); if ( title != NULL ) { hb_list_add( data->title_set->list_title, title ); } } } } else { data->title_index = 1; hb_title_t * title = hb_title_init( data->path, data->title_index ); if ( (data->stream = hb_stream_open( data->path, title, 1 ) ) != NULL ) { title = hb_stream_title_scan( data->stream, title ); if ( title ) hb_list_add( data->title_set->list_title, title ); } else { hb_title_close( &title ); hb_log( "scan: unrecognized file type" ); return; } } for( i = 0; i < hb_list_count( data->title_set->list_title ); ) { int j; hb_audio_t * audio; if ( *data->die ) { goto finish; } title = hb_list_item( data->title_set->list_title, i ); UpdateState2(data, i + 1); /* Decode previews */ /* this will also detect more AC3 / DTS information */ if( !DecodePreviews( data, title ) ) { /* TODO: free things */ hb_list_rem( data->title_set->list_title, title ); for( j = 0; j < hb_list_count( title->list_audio ); j++) { audio = hb_list_item( title->list_audio, j ); if ( audio->priv.scan_cache ) { hb_fifo_flush( audio->priv.scan_cache ); hb_fifo_close( &audio->priv.scan_cache ); } } hb_title_close( &title ); continue; } /* 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->priv.scan_cache ) { hb_fifo_flush( audio->priv.scan_cache ); hb_fifo_close( &audio->priv.scan_cache ); } 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; } j++; } if ( data->dvd || data->bd ) { // The subtitle width and height needs to be set to the // title widht and height for DVDs. title width and // height don't get set until we decode previews, so // we can't set subtitle width/height till we get here. for( j = 0; j < hb_list_count( title->list_subtitle ); j++ ) { hb_subtitle_t *subtitle = hb_list_item( title->list_subtitle, j ); if ( subtitle->source == VOBSUB || subtitle->source == PGSSUB ) { subtitle->width = title->width; subtitle->height = title->height; } } } i++; } data->title_set->feature = feature; /* Mark title scan complete and init jobs */ for( i = 0; i < hb_list_count( data->title_set->list_title ); i++ ) { title = hb_list_item( data->title_set->list_title, i ); title->flags |= HBTF_SCAN_COMPLETE; #if defined(HB_TITLE_JOBS) title->job = hb_job_init( title ); #endif } finish: if( data->bd ) { hb_bd_close( &data->bd ); } if( data->dvd ) { hb_dvd_close( &data->dvd ); } if (data->stream) { hb_stream_close(&data->stream); } if( data->batch ) { hb_batch_close( &data->batch ); } free( data->path ); free( data ); _data = NULL; hb_buffer_pool_free(); }