/* $Id: HandBrake.c,v 1.15 2003/11/07 21:52:57 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 "HandBrakeInternal.h" #include #include "Ac3Dec.h" #include "AviMux.h" #include "DVDRead.h" #include "FfmpegEnc.h" #include "Fifo.h" #include "Mp3Enc.h" #include "Mpeg2Dec.h" #include "Scale.h" #include "Scan.h" #include "Thread.h" #include "Work.h" #include "XvidEnc.h" /* Local prototypes */ static void HandBrakeThread( void * ); static void _StopRip( HBHandle * ); static void FixPictureSettings( HBTitle * ); static int GetCPUCount(); struct HBHandle { HBThread * thread; int die; int pid; int cpuCount; int stopScan; int stopRip; int ripDone; int error; HBScan * scan; HBLock * lock; HBStatus status; int modeChanged; HBTitle * curTitle; HBAudio * curAudio; HBAudio * curOptAudio; int frames; uint64_t beginDate; int framesSinceFpsUpdate; uint64_t lastFpsUpdate; uint64_t pauseDate; HBLock * pauseLock; }; HBHandle * HBInit( int debug, int cpuCount ) { HBHandle * h; if( !( h = malloc( sizeof( HBHandle ) ) ) ) { HBLog( "HBInit: malloc() failed, gonna crash" ); return NULL; } /* See HBLog() in Utils.cpp */ if( debug ) { putenv( "HB_DEBUG=1" ); } /* Init libavcodec */ avcodec_init(); register_avcodec( &mpeg4_encoder ); /* Check CPU count */ if( !cpuCount ) { h->cpuCount = GetCPUCount(); HBLog( "HBInit: %d CPU%s detected", h->cpuCount, ( h->cpuCount > 1 ) ? "s" : "" ); } else { if( cpuCount < 1 ) { HBLog( "HBInit: invalid CPU count (%d), using 1", cpuCount ); h->cpuCount = 1; } else if( cpuCount > 8 ) { HBLog( "HBInit: invalid CPU count (%d), using 8", cpuCount ); h->cpuCount = 8; } else { HBLog( "HBInit: user specified %d CPU%s", cpuCount, ( cpuCount > 1 ) ? "s" : "" ); h->cpuCount = cpuCount; } } /* Initializations */ h->stopScan = 0; h->stopRip = 0; h->ripDone = 0; h->error = 0; h->scan = NULL; h->lock = HBLockInit(); h->modeChanged = 1; h->status.mode = HB_MODE_NEED_DEVICE; h->curTitle = NULL; h->curAudio = NULL; h->curOptAudio = NULL; h->pauseLock = HBLockInit(); h->die = 0; h->thread = HBThreadInit( "libhb", HandBrakeThread, h, HB_NORMAL_PRIORITY ); return h; } int HBGetStatus( HBHandle * h, HBStatus * status ) { HBLockLock( h->lock ); memcpy( status, &h->status, sizeof( HBStatus ) ); if( !h->modeChanged ) { HBLockUnlock( h->lock ); return 0; } h->modeChanged = 0; HBLockUnlock( h->lock ); return 1; } void HBScanDevice( HBHandle * h, char * device, int title ) { if( !( h->status.mode & ( HB_MODE_NEED_DEVICE | HB_MODE_INVALID_DEVICE ) ) ) { HBLog( "HBScanDevice: current mode is %d, aborting", h->status.mode ); return; } HBLockLock( h->lock ); h->modeChanged = 1; h->status.mode = HB_MODE_SCANNING; h->status.scannedTitle = 0; HBLockUnlock( h->lock ); h->scan = HBScanInit( h, device, title ); } void HBStartRip( HBHandle * h, HBTitle * t, HBAudio * a1, HBAudio * a2 ) { int i; if( !( h->status.mode & ( HB_MODE_READY_TO_RIP | HB_MODE_DONE | HB_MODE_CANCELED | HB_MODE_ERROR ) ) ) { HBLog( "HBStartRip: current mode is %d, aborting", h->status.mode ); return; } HBLockLock( h->lock ); h->modeChanged = 1; h->status.mode = HB_MODE_ENCODING; h->status.position = 0.0; h->status.pass = 1; h->status.passCount = t->twoPass ? 2 : 1; h->frames = 0; h->framesSinceFpsUpdate = 0; HBLockUnlock( h->lock ); FixPictureSettings( t ); /* Create fifos */ t->mpeg2Fifo = HBFifoInit( 512 ); t->rawFifo = HBFifoInit( 1 ); t->scaledFifo = HBFifoInit( 1 ); t->mpeg4Fifo = HBFifoInit( 1 ); a1->ac3Fifo = HBFifoInit( 512 ); a1->rawFifo = HBFifoInit( 1 ); a1->mp3Fifo = HBFifoInit( 1 ); if( a2 ) { a2->ac3Fifo = HBFifoInit( 512 ); a2->rawFifo = HBFifoInit( 1 ); a2->mp3Fifo = HBFifoInit( 1 ); } /* Create work objects */ t->mpeg2Dec = HBMpeg2DecInit( h, t ); t->scale = HBScaleInit( h, t ); if( t->codec == HB_CODEC_FFMPEG ) t->ffmpegEnc = HBFfmpegEncInit( h, t ); else if( t->codec == HB_CODEC_XVID ) t->xvidEnc = HBXvidEncInit( h, t ); a1->ac3Dec = HBAc3DecInit( h, a1 ); a1->mp3Enc = HBMp3EncInit( h, a1 ); if( a2 ) { a2->ac3Dec = HBAc3DecInit( h, a2 ); a2->mp3Enc = HBMp3EncInit( h, a2 ); } /* Create threads */ t->dvdRead = HBDVDReadInit( h, t, a1, a2 ); t->aviMux = HBAviMuxInit( h, t, a1, a2 ); for( i = 0; i < h->cpuCount; i++ ) { t->workThreads[i] = HBWorkThreadInit( h, t, a1, a2, i ? 0 : 1 ); } h->curTitle = t; h->curAudio = a1; h->curOptAudio = a2; } void HBPauseRip( HBHandle * h ) { if( h->status.mode != HB_MODE_ENCODING ) { HBLog( "HBPauseRip: current mode is %d, aborting", h->status.mode ); return; } h->pauseDate = HBGetDate(); HBLockLock( h->pauseLock ); HBLockLock( h->lock ); h->status.mode = HB_MODE_PAUSED; h->modeChanged = 1; HBLockUnlock( h->lock ); } void HBResumeRip( HBHandle * h ) { if( h->status.mode != HB_MODE_PAUSED ) { HBLog( "HBResumeRip: current mode is %d, aborting", h->status.mode ); return; } h->beginDate += HBGetDate() - h->pauseDate; h->lastFpsUpdate += HBGetDate() - h->pauseDate; HBLockUnlock( h->pauseLock ); HBLockLock( h->lock ); h->modeChanged = 1; h->status.mode = HB_MODE_ENCODING; HBLockUnlock( h->lock ); } void HBStopRip( HBHandle * h ) { if( !( h->status.mode & ( HB_MODE_ENCODING | HB_MODE_PAUSED ) ) ) { HBLog( "HBStopRip: current mode is %d, aborting", h->status.mode ); return; } if( h->status.mode & HB_MODE_PAUSED ) { HBLockUnlock( h->pauseLock ); } HBLockLock( h->lock ); h->modeChanged = 1; h->status.mode = HB_MODE_STOPPING; HBLockUnlock( h->lock ); h->stopRip = 1; } uint8_t * HBGetPreview( HBHandle * h, HBTitle * t, int picture ) { AVPicture pic1, pic2, pic3, pic4; uint8_t * buf1, * buf2, * buf3, * buf4; char fileName[1024]; FILE * file; ImgReSampleContext * resampleContext; int8_t * preview, * pen; int i; FixPictureSettings( t ); buf1 = malloc( 3 * t->inWidth * t->inHeight / 2 ); buf2 = malloc( 3 * t->inWidth * t->inHeight / 2 ); buf3 = malloc( 3 * t->outWidth * t->outHeight / 2 ); buf4 = malloc( 4 * t->outWidth * t->outHeight ); if( !buf1 || !buf2 || !buf3 || !buf4 ) { HBLog( "HBGetPreview: malloc() failed, gonna crash" ); return NULL; } /* Original YUV picture */ avpicture_fill( &pic1, buf1, PIX_FMT_YUV420P, t->inWidth, t->inHeight ); /* Deinterlaced YUV picture */ avpicture_fill( &pic2, buf2, PIX_FMT_YUV420P, t->inWidth, t->inHeight ); /* Scaled YUV picture */ avpicture_fill( &pic3, buf3, PIX_FMT_YUV420P, t->outWidth, t->outHeight ); /* Scaled RGB picture ) */ avpicture_fill( &pic4, buf4, PIX_FMT_RGBA32, t->outWidth, t->outHeight ); /* Get the original image from the temp file */ memset( fileName, 0, 1024 ); sprintf( fileName, "/tmp/HB.%d.%d.%d", h->pid, t->index, picture ); file = fopen( fileName, "r" ); if( file ) { fread( buf1, 3 * t->inWidth * t->inHeight / 2, 1, file ); fclose( file ); } else { HBLog( "HBGetPreview: could not open %s", fileName ); memset( buf1, 0, 3 * t->inWidth * t->inHeight / 2 ); } /* Deinterlace if needed, and scale */ resampleContext = img_resample_full_init( t->outWidth, t->outHeight, t->inWidth, t->inHeight, t->topCrop, t->bottomCrop, t->leftCrop, t->rightCrop ); if( t->deinterlace ) { avpicture_deinterlace( &pic2, &pic1, PIX_FMT_YUV420P, t->inWidth, t->inHeight ); img_resample( resampleContext, &pic3, &pic2 ); } else { img_resample( resampleContext, &pic3, &pic1 ); } /* Convert to RGB */ img_convert( &pic4, PIX_FMT_RGBA32, &pic3, PIX_FMT_YUV420P, t->outWidth, t->outHeight ); /* Create the final preview */ preview = malloc( 4 * ( t->outWidthMax + 2 ) * ( t->outHeightMax + 2 ) ); if( !preview ) { HBLog( "HBGetPreview: malloc() failed, gonna crash" ); return NULL; } /* Blank it */ memset( preview, 0x80, 4 * ( t->outWidthMax + 2 ) * ( t->outHeightMax + 2 ) ); /* Draw the picture (centered) and draw the cropping zone */ pen = preview + ( t->outHeightMax - t->outHeight ) * ( t->outWidthMax + 2 ) * 2 + ( t->outWidthMax - t->outWidth ) * 2; memset( pen, 0xFF, 4 * ( t->outWidth + 2 ) ); pen += 4 * ( t->outWidthMax + 2 ); for( i = 0; i < t->outHeight; i++ ) { uint8_t * nextLine = pen + 4 * ( t->outWidthMax + 2 ); memset( pen, 0xFF, 4 ); pen += 4; memcpy( pen, buf4 + 4 * t->outWidth * i, 4 * t->outWidth ); pen += 4 * t->outWidth; memset( pen, 0xFF, 4 ); pen = nextLine; } memset( pen, 0xFF, 4 * ( t->outWidth + 2 ) ); /* Free memory */ free( buf1 ); free( buf2 ); free( buf3 ); free( buf4 ); return preview; return NULL; } void HBClose( HBHandle ** _h ) { char command[1024]; HBHandle * h = *_h; h->die = 1; HBThreadClose( &h->thread ); if( h->status.mode == HB_MODE_SCANNING ) { HBScanClose( &h->scan ); } else if( h->status.mode == HB_MODE_PAUSED ) { HBLockUnlock( h->pauseLock ); _StopRip( h ); } else if( h->status.mode == HB_MODE_ENCODING ) { _StopRip( h ); } memset( command, 0, 1024 ); sprintf( command, "rm -f /tmp/HB.%d.*", h->pid ); system( command ); if( h->status.titleList ) { HBTitle * title; while( ( title = HBListItemAt( h->status.titleList, 0 ) ) ) { HBListRemove( h->status.titleList, title ); HBTitleClose( &title ); } HBListClose( &h->status.titleList ); } HBLockClose( &h->lock ); HBLockClose( &h->pauseLock ); free( h ); *_h = NULL; } /* Following functions are called by libhb's internal threads */ void HBCheckPaused( HBHandle * h ) { HBLockLock( h->pauseLock ); HBLockUnlock( h->pauseLock ); } void HBScanning( HBHandle * h, int title ) { HBLockLock( h->lock ); h->status.scannedTitle = title; HBLockUnlock( h->lock ); } void HBScanDone( HBHandle * h, HBList * titleList ) { h->status.titleList = titleList; h->stopScan = 1; } int HBGetPid( HBHandle * h ) { return h->pid; } void HBDone( HBHandle * h ) { h->ripDone = 1; } void HBPosition( HBHandle * h, float position ) { if( !h->frames ) { h->beginDate = HBGetDate(); h->lastFpsUpdate = h->beginDate; } h->frames++; h->framesSinceFpsUpdate++; HBLockLock( h->lock ); if( position - h->status.position > 0.0001 || h->frames == 2 ) { HBLog( "Progress: %.2f %%", 100.0 * position ); h->status.position = position; if( h->curTitle->twoPass ) h->status.pass = ( position < 0.5 ) ? 1 : 2; else h->status.pass = 1; } if( HBGetDate() - h->lastFpsUpdate > 1000000 ) { h->status.frameRate = 1000000.0 * h->framesSinceFpsUpdate / ( HBGetDate() - h->lastFpsUpdate ); h->status.avFrameRate = 1000000.0 * h->frames / ( HBGetDate() - h->beginDate ); h->status.remainingTime = ( 1.0 - h->status.position ) * ( HBGetDate() - h->beginDate ) / h->status.position / 1000000; HBLog( "Speed: %.2f fps (average: %.2f fps, " "remaining: %02d:%02d:%02d)", h->status.frameRate, h->status.avFrameRate, h->status.remainingTime / 3600, ( h->status.remainingTime / 60 ) % 60, h->status.remainingTime % 60 ); h->lastFpsUpdate = HBGetDate(); h->framesSinceFpsUpdate = 0; } HBLockUnlock( h->lock ); } void HBErrorOccured( HBHandle * h, HBError error ) { if( !( h->status.mode & ( HB_MODE_ENCODING | HB_MODE_PAUSED ) ) ) { return; } h->status.error = error; h->error = 1; } /* Local functions */ static void HandBrakeThread( void * _h ) { HBHandle * h = (HBHandle*) _h; h->pid = getpid(); while( !h->die ) { if( h->stopScan ) { HBScanClose( &h->scan ); HBLockLock( h->lock ); h->modeChanged = 1; h->status.mode = HBListCountItems( h->status.titleList ) ? HB_MODE_READY_TO_RIP : HB_MODE_INVALID_DEVICE; HBLockUnlock( h->lock ); h->stopScan = 0; } if( h->stopRip ) { _StopRip( h ); HBLockLock( h->lock ); h->modeChanged = 1; h->status.mode = HB_MODE_CANCELED; HBLockUnlock( h->lock ); h->stopRip = 0; } if( h->ripDone ) { /* Wait a bit */ HBSnooze( 500000 ); _StopRip( h ); HBLockLock( h->lock ); h->modeChanged = 1; h->status.mode = HB_MODE_DONE; HBLockUnlock( h->lock ); h->ripDone = 0; } if( h->error ) { _StopRip( h ); HBLockLock( h->lock ); h->modeChanged = 1; h->status.mode = HB_MODE_ERROR; HBLockUnlock( h->lock ); h->error = 0; } HBSnooze( 10000 ); } } static void _StopRip( HBHandle * h ) { int i; /* Stop threads */ HBDVDReadClose( &h->curTitle->dvdRead ); HBAviMuxClose( &h->curTitle->aviMux ); for( i = 0; i < h->cpuCount; i++ ) { HBWorkThreadClose( &h->curTitle->workThreads[h->cpuCount-i-1] ); } /* Clean up */ HBMpeg2DecClose( &h->curTitle->mpeg2Dec ); HBScaleClose( &h->curTitle->scale ); if( h->curTitle->codec == HB_CODEC_FFMPEG ) HBFfmpegEncClose( &h->curTitle->ffmpegEnc ); else if( h->curTitle->codec == HB_CODEC_XVID ) HBXvidEncClose( &h->curTitle->xvidEnc ); HBAc3DecClose( &h->curAudio->ac3Dec ); HBMp3EncClose( &h->curAudio->mp3Enc ); if( h->curOptAudio ) { HBAc3DecClose( &h->curOptAudio->ac3Dec ); HBMp3EncClose( &h->curOptAudio->mp3Enc ); } /* Destroy fifos */ HBFifoClose( &h->curTitle->mpeg2Fifo ); HBFifoClose( &h->curTitle->rawFifo ); HBFifoClose( &h->curTitle->scaledFifo ); HBFifoClose( &h->curTitle->mpeg4Fifo ); HBFifoClose( &h->curAudio->ac3Fifo ); HBFifoClose( &h->curAudio->rawFifo ); HBFifoClose( &h->curAudio->mp3Fifo ); if( h->curOptAudio ) { HBFifoClose( &h->curOptAudio->ac3Fifo ); HBFifoClose( &h->curOptAudio->rawFifo ); HBFifoClose( &h->curOptAudio->mp3Fifo ); } } static void FixPictureSettings( HBTitle * t ) { /* Sanity checks */ t->topCrop = EVEN( t->topCrop ); t->bottomCrop = EVEN( t->bottomCrop ); t->leftCrop = EVEN( t->leftCrop ); t->rightCrop = EVEN( t->rightCrop ); t->outWidth = MIN( t->outWidth, t->outWidthMax ); t->outWidth = MAX( 16, t->outWidth ); t->outHeight = MULTIPLE_16( (uint64_t) t->outWidth * t->inWidth * ( t->inHeight - t->topCrop - t->bottomCrop ) * VOUT_ASPECT_FACTOR / ( (uint64_t) t->inHeight * ( t->inWidth - t->leftCrop - t->rightCrop ) * t->aspect ) ); t->outHeight = MAX( 16, t->outHeight ); if( t->outHeight > t->outHeightMax ) { t->outHeight = t->outHeightMax; t->outWidth = MULTIPLE_16( (uint64_t) t->outHeight * t->inHeight * ( t->inWidth - t->leftCrop - t->rightCrop ) * t->aspect / ( (uint64_t) t->inWidth * ( t->inHeight - t->topCrop - t->bottomCrop ) * VOUT_ASPECT_FACTOR ) ); t->outWidth = MIN( t->outWidth, t->outWidthMax ); t->outWidth = MAX( 16, t->outWidth ); } } static int GetCPUCount() { int CPUCount = 1; #if defined( SYS_BEOS ) system_info info; get_system_info( &info ); CPUCount = info.cpu_count; #elif defined( SYS_MACOSX ) FILE * info; char buffer[256]; if( ( info = popen( "/usr/sbin/sysctl hw.ncpu", "r" ) ) ) { if( fgets( buffer, 256, info ) ) { int count; if( sscanf( buffer, "hw.ncpu: %d", &count ) == 1 ) { CPUCount = count; } else { HBLog( "GetCPUCount: sscanf() failed" ); } } else { HBLog( "GetCPUCount: fgets() failed" ); } fclose( info ); } else { HBLog( "GetCPUCount: popen() failed" ); } #elif defined( SYS_LINUX ) FILE * info; char buffer[256]; if( ( info = popen( "grep -c '^processor' /proc/cpuinfo", "r" ) ) ) { if( fgets( buffer, 256, info ) ) { int count; if( sscanf( buffer, "%d", &count ) == 1 ) { CPUCount = count; } else { HBLog( "GetCPUCount: sscanf() failed" ); } } else { HBLog( "GetCPUCount: fgets() failed" ); } fclose( info ); } else { HBLog( "GetCPUCount: fopen() failed" ); } #elif defined( SYS_CYGWIN ) /* TODO */ CPUCount = 1; #endif CPUCount = MAX( 1, CPUCount ); CPUCount = MIN( CPUCount, 8 ); return CPUCount; }