/* $Id: Scan.c,v 1.4 2003/11/06 13:03:19 titer Exp $ This file is part of the HandBrake source code. Homepage: . It may be used under the terms of the GNU General Public License. */ #include "Fifo.h" #include "Languages.h" #include "Scan.h" #include "Thread.h" #include #include #include #include #include #include /* Local prototypes */ static void ScanThread( void * ); static HBTitle * ScanTitle( HBScan *, dvdplay_ptr vmg, int index ); static int DecodeFrame( HBScan * s, dvdplay_ptr vmg, HBTitle * title, int which ); static char * LanguageForCode( int code ); struct HBScan { HBHandle * handle; char * device; int title; int die; HBThread * thread; }; HBScan * HBScanInit( HBHandle * handle, char * device, int title ) { HBScan * s; if( !( s = malloc( sizeof( HBScan ) ) ) ) { HBLog( "HBScanInit: malloc() failed, gonna crash" ); return NULL; } s->handle = handle; s->device = strdup( device ); s->title = title; s->die = 0; s->thread = HBThreadInit( "scan", ScanThread, s, HB_NORMAL_PRIORITY ); return s; } void HBScanClose( HBScan ** _s ) { HBScan * s = *_s; s->die = 1; HBThreadClose( &s->thread ); free( s->device ); free( s ); *_s = NULL; } static void ScanThread( void * _s ) { HBScan * s = (HBScan*) _s; dvdplay_ptr vmg; HBList * titleList = HBListInit(); HBTitle * title; int i; HBLog( "HBScan: opening device %s", s->device ); HBScanning( s->handle, 0 ); vmg = dvdplay_open( s->device, NULL, NULL ); if( !vmg ) { HBLog( "HBScan: dvdplay_open() failed (%s)", s->device ); HBScanDone( s->handle, titleList ); return; } /* Detect titles */ for( i = ( s->title ? s->title - 1 : 0 ); i < ( s->title ? s->title : dvdplay_title_nr( vmg ) ); i++ ) { if( s->die ) { break; } if( !( title = ScanTitle( s, vmg, i + 1 ) ) ) { continue; } HBListAdd( titleList, title ); } HBLog( "HBScan: closing device %s", s->device ); dvdplay_close( vmg ); HBScanDone( s->handle, titleList ); } static HBTitle * ScanTitle( HBScan * s, dvdplay_ptr vmg, int index ) { HBTitle * title; int audio_nr, foo; audio_attr_t * attr; HBAudio * audio; int i; uint8_t dummy[DVD_VIDEO_LB_LEN]; HBLog( "HBScan: scanning title %d", index ); HBScanning( s->handle, index ); title = HBTitleInit( s->device, index ); dvdplay_start( vmg, index ); /* Length */ title->length = dvdplay_title_time( vmg ); HBLog( "HBScan: title length is %d seconds", title->length ); /* Discard titles under 10 seconds */ if( title->length < 10 ) { HBLog( "HBScan: ignoring title %d (too short)", index ); HBTitleClose( &title ); return NULL; } /* Detect languages */ dvdplay_audio_info( vmg, &audio_nr, &foo ); for( i = 0; i < audio_nr; i++ ) { int id; int j; if( s->die ) { break; } id = dvdplay_audio_id( vmg, i ); if( id < 1 ) { continue; } if( ( id & 0xFF ) != 0xBD ) { HBLog( "HBScan: non-AC3 audio track detected, ignoring" ); continue; } /* Check if we don't already found an track with the same id */ audio = NULL; for( j = 0; j < HBListCountItems( title->audioList ); j++ ) { audio = (HBAudio*) HBListItemAt( title->audioList, j ); if( id == audio->id ) { break; } else { audio = NULL; } } if( audio ) { HBLog( "HBScan: discarding duplicate track %x", id ); continue; } attr = dvdplay_audio_attr( vmg, j ); audio = HBAudioInit( id, LanguageForCode( attr->lang_code ) ); HBLog( "HBScan: new language (%x, %s)", id, audio->language ); HBListAdd( title->audioList, audio ); } /* Discard titles with no audio tracks */ if( !HBListCountItems( title->audioList ) ) { HBLog( "HBScan: ignoring title %d (no audio track)", index ); HBTitleClose( &title ); return NULL; } /* Kludge : libdvdplay wants we to read a first block before seeking */ dvdplay_read( vmg, dummy, 1 ); for( i = 0; i < 10; i++ ) { if( s->die ) { break; } if( !DecodeFrame( s, vmg, title, i ) ) { HBLog( "HBScan: ignoring title %d (could not decode)", index ); HBTitleClose( &title ); return NULL; } } if( title->inHeight * title->aspect > title->inWidth * VOUT_ASPECT_FACTOR ) { title->outWidthMax = title->inWidth; title->outHeightMax = MULTIPLE_16( (uint64_t)title->inWidth * VOUT_ASPECT_FACTOR / title->aspect ); } else { title->outWidthMax = MULTIPLE_16( (uint64_t)title->inHeight * title->aspect / VOUT_ASPECT_FACTOR ); title->outHeightMax = title->inHeight; } /* Default picture size */ title->outWidth = title->outWidthMax; title->outHeight = title->outHeightMax; return title; } static int DecodeFrame( HBScan * s, dvdplay_ptr vmg, HBTitle * title, int which ) { int titleFirst = dvdplay_title_first( vmg ); int titleEnd = dvdplay_title_end( vmg ); int pictureStart = ( which + 1 ) * ( titleEnd - titleFirst ) / 11; int pictureEnd = titleFirst + ( which + 2 ) * ( titleEnd - titleFirst ) / 11; mpeg2dec_t * handle; const mpeg2_info_t * info; mpeg2_state_t state; char fileName[1024]; FILE * file; HBList * esBufferList = HBListInit(); HBBuffer * psBuffer = NULL; HBBuffer * esBuffer = NULL; /* Seek to the right place */ dvdplay_seek( vmg, pictureStart ); /* Init libmpeg2 */ handle = mpeg2_init(); info = mpeg2_info( handle ); /* Init the destination file */ memset( fileName, 0, 1024 ); sprintf( fileName, "/tmp/HB.%d.%d.%d", HBGetPid( s->handle ), title->index, which ); file = fopen( fileName, "w" ); #define CLEANUP \ while( ( esBuffer = (HBBuffer*) HBListItemAt( esBufferList, 0 ) ) ) \ { \ HBListRemove( esBufferList, esBuffer ); \ HBBufferClose( &esBuffer ); \ } \ HBListClose( &esBufferList ); \ if( psBuffer ) HBBufferClose( &psBuffer ); \ if( esBuffer ) HBBufferClose( &esBuffer ); \ mpeg2_close( handle ); \ fclose( file ) for( ;; ) { state = mpeg2_parse( handle ); if( state == STATE_BUFFER ) { /* Free the previous buffer */ if( esBuffer ) { HBBufferClose( &esBuffer ); } /* Get a new one */ while( !esBuffer ) { while( !HBListCountItems( esBufferList ) ) { psBuffer = HBBufferInit( DVD_VIDEO_LB_LEN ); if( dvdplay_read( vmg, psBuffer->data, 1 ) != 1 || !HBPStoES( &psBuffer, esBufferList ) ) { HBLog( "HBScan: failed to get a valid PS " "packet" ); CLEANUP; return 0; } if( dvdplay_position( vmg ) >= pictureEnd ) { HBLog( "HBScan: gone too far, aborting" ); CLEANUP; return 0; } } esBuffer = (HBBuffer*) HBListItemAt( esBufferList, 0 ); HBListRemove( esBufferList, esBuffer ); if( esBuffer->streamId != 0xE0 ) { HBBufferClose( &esBuffer ); } } /* Feed libmpeg2 */ mpeg2_buffer( handle, esBuffer->data, esBuffer->data + esBuffer->size ); } else if( state == STATE_SEQUENCE ) { /* Get size & framerate info */ title->inWidth = info->sequence->width; title->inHeight = info->sequence->height; title->aspect = (uint64_t)info->sequence->display_width * info->sequence->pixel_width * VOUT_ASPECT_FACTOR / ( info->sequence->display_height * info->sequence->pixel_height ); title->rate = 27000000; title->rateBase = info->sequence->frame_period; } else if( ( state == STATE_SLICE || state == STATE_END ) && ( info->display_fbuf ) && ( info->display_picture->flags & PIC_MASK_CODING_TYPE ) == PIC_FLAG_CODING_TYPE_I ) { /* Write the raw picture to a file */ fwrite( info->display_fbuf->buf[0], title->inWidth * title->inHeight, 1, file ); fwrite( info->display_fbuf->buf[1], title->inWidth * title->inHeight / 4, 1, file ); fwrite( info->display_fbuf->buf[2], title->inWidth * title->inHeight / 4, 1, file ); break; } else if( state == STATE_INVALID ) { /* Reset libmpeg2 */ mpeg2_close( handle ); handle = mpeg2_init(); } } CLEANUP; return 1; #undef CLEANUP } static char * LanguageForCode( int code ) { char codeString[2]; iso639_lang_t * lang; codeString[0] = ( code >> 8 ) & 0xFF; codeString[1] = code & 0xFF; for( lang = languages; lang->engName; lang++ ) { if( !strncmp( lang->iso639_1, codeString, 2 ) ) { if( *lang->nativeName ) { return lang->nativeName; } return lang->engName; } } return "Unknown"; }