diff options
-rw-r--r-- | libhb/common.h | 1 | ||||
-rw-r--r-- | libhb/hb.c | 7 | ||||
-rw-r--r-- | libhb/hb.h | 1 | ||||
-rw-r--r-- | macosx/Controller.mm | 74 | ||||
-rw-r--r-- | macosx/HBQueueController.h | 93 | ||||
-rw-r--r-- | macosx/HBQueueController.mm | 572 | ||||
-rw-r--r-- | macosx/HandBrake.xcodeproj/project.pbxproj | 4 | ||||
-rw-r--r-- | macosx/icons/EncodeCanceled.png | bin | 0 -> 558 bytes |
8 files changed, 445 insertions, 307 deletions
diff --git a/libhb/common.h b/libhb/common.h index 2a56701db..682b60c89 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -462,6 +462,7 @@ struct hb_state_s int hours; int minutes; int seconds; + int sequence_id; } working; struct diff --git a/libhb/hb.c b/libhb/hb.c index 6b108bdf0..55e2da646 100644 --- a/libhb/hb.c +++ b/libhb/hb.c @@ -964,6 +964,7 @@ void hb_start( hb_handle_t * h ) p.hours = -1; p.minutes = -1; p.seconds = -1; + p.sequence_id = 0; #undef p hb_unlock( h->state_lock ); @@ -1203,6 +1204,12 @@ void hb_set_state( hb_handle_t * h, hb_state_t * s ) h->state.param.working.job_cur = h->job_count_permanent - hb_list_count( h->jobs ); h->state.param.working.job_count = h->job_count_permanent; + + // Set which job is being worked on + if (h->current_job) + h->state.param.working.sequence_id = h->current_job->sequence_id; + else + h->state.param.working.sequence_id = 0; } hb_unlock( h->state_lock ); hb_unlock( h->pause_lock ); diff --git a/libhb/hb.h b/libhb/hb.h index be629505f..9de4af732 100644 --- a/libhb/hb.h +++ b/libhb/hb.h @@ -83,7 +83,6 @@ void hb_set_anamorphic_size( hb_job_t * ); /* Handling jobs */ int hb_count( hb_handle_t * ); hb_job_t * hb_job( hb_handle_t *, int ); -hb_job_t * hb_current_job( hb_handle_t * h ); void hb_add( hb_handle_t *, hb_job_t * ); void hb_rem( hb_handle_t *, hb_job_t * ); diff --git a/macosx/Controller.mm b/macosx/Controller.mm index c16de3fef..260bbc62f 100644 --- a/macosx/Controller.mm +++ b/macosx/Controller.mm @@ -172,15 +172,14 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It // Warn if encoding a movie hb_state_t s; hb_get_state( fHandle, &s ); - hb_job_t * job = hb_current_job( fHandle ); - if ( job && ( s.state != HB_STATE_IDLE ) ) + HBJobGroup * jobGroup = [fQueueController currentJobGroup]; + if ( jobGroup && ( s.state != HB_STATE_IDLE ) ) { - hb_job_t * job = hb_current_job( fHandle ); int result = NSRunCriticalAlertPanel( NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil), NSLocalizedString(@"%@ is currently encoding. If you quit HandBrake, your movie will be lost. Do you want to quit anyway?", nil), NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil, - job ? [NSString stringWithUTF8String:job->title->name] : @"A movie" ); + jobGroup ? [jobGroup name] : @"A movie" ); if (result == NSAlertDefaultReturn) { @@ -655,23 +654,11 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It // or someone calling hb_stop. In the latter case, hb_stop does not clear // out the remaining passes/jobs in the queue. We'll do that here. - // Delete all remaining scans of this job, ie, delete whole encodes. + // Delete all remaining jobs of this encode. hb_job_t * job; - while( ( job = hb_job( fHandle, 0 ) ) && (job->sequence_id != 0) ) + while( ( job = hb_job( fHandle, 0 ) ) && ( !IsFirstPass(job->sequence_id) ) ) hb_rem( fHandle, job ); - // Start processing back up if jobs still left in queue - if (hb_count(fHandle) > 0) - { - hb_start(fHandle); - fEncodeState = 1; - // Validate the toolbar (hack). The toolbar will usually get autovalidated - // before we had the chance to restart the queue, hence it will now be in - // the wrong state. - [toolbar validateVisibleItems]; - break; - } - [fStatusField setStringValue: _( @"Done." )]; [fRipIndicator setIndeterminate: NO]; [fRipIndicator setDoubleValue: 0.0]; @@ -1658,10 +1645,15 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It hb_title_t * title = (hb_title_t *) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); hb_job_t * job = title->job; - // Assign a sequence number, starting at zero, to each job added so they can - // be lumped together in the UI. - job->sequence_id = -1; - + // Assign a unique job group ID for all passes of the same encode. This is how the + // UI lumps together jobs to form encodes. libhb does not use this id. + static int jobGroupID = 0; + jobGroupID++; + + // A sequence number, starting at zero, is also used to identifiy to each pass. + // This is used by the UI to determine if a pass if the first pass of an encode. + int sequenceNum = -1; + [self prepareJob]; /* Destination file */ @@ -1697,7 +1689,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It /* * Add the pre-scan job */ - job->sequence_id++; // for job grouping + job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum); hb_add( fHandle, job ); job->x264opts = x264opts_tmp; @@ -1722,11 +1714,11 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It job->select_subtitle = NULL; job->pass = 1; - job->sequence_id++; // for job grouping + job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum); hb_add( fHandle, job ); job->pass = 2; - job->sequence_id++; // for job grouping + job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum); job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */ strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]); @@ -1739,7 +1731,7 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It { job->indepth_scan = 0; job->pass = 0; - job->sequence_id++; // for job grouping + job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum); hb_add( fHandle, job ); } @@ -1881,7 +1873,8 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It } //------------------------------------------------------------------------------------ -// Cancels the current job and proceeds with the next one in the queue. +// Cancels and deletes the current job and stops libhb from processing the remaining +// encodes. //------------------------------------------------------------------------------------ - (void) doCancelCurrentJob { @@ -1906,11 +1899,11 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It { if (!fHandle) return; - hb_job_t * job = hb_current_job(fHandle); - if (!job) return; + HBJobGroup * jobGroup = [fQueueController currentJobGroup]; + if (!jobGroup) return; - NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Do you want to stop encoding of %@?", nil), - [NSString stringWithUTF8String:job->title->name]]; + NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop encoding %@?", nil), + [jobGroup name]]; // Which window to attach the sheet to? NSWindow * docWindow; @@ -1922,32 +1915,19 @@ static NSString * ChooseSourceIdentifier = @"Choose Source It NSBeginCriticalAlertSheet( alertTitle, NSLocalizedString(@"Keep Encoding", nil), - NSLocalizedString(@"Delete All", nil), + nil, NSLocalizedString(@"Stop Encoding", nil), docWindow, self, nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil, - NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil), - [NSString stringWithUTF8String:job->title->name]); + NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil)); // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed - - // N.B.: didDimissCancelCurrentJob:returnCode:contextInfo: is designated as the dismiss - // selector to prevent a crash. As a dismiss selector, the alert window will - // have already be dismissed. If we don't do it this way, the dismissing of - // the alert window will cause the table view to be redrawn at a point where - // current job has been deleted by hblib but we don't know about it yet. This - // is a prime example of wy we need to NOT be relying on hb_current_job!!!! } - (void) didDimissCancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo { if (returnCode == NSAlertOtherReturn) - [self doCancelCurrentJob]; - else if (returnCode == NSAlertAlternateReturn) - { - [self doDeleteQueuedJobs]; - [self doCancelCurrentJob]; - } + [self doCancelCurrentJob]; // <- this also stops libhb } diff --git a/macosx/HBQueueController.h b/macosx/HBQueueController.h index cab1c201b..ae90acc65 100644 --- a/macosx/HBQueueController.h +++ b/macosx/HBQueueController.h @@ -9,10 +9,19 @@ #include "hb.h" @class HBController; +@class HBJob; +@class HBJobGroup; #define HB_QUEUE_DRAGGING 0 // <--- NOT COMPLETELY FUNCTIONAL YET #define HB_OUTLINE_METRIC_CONTROLS 1 // for tweaking the outline cell spacings +// hb_job_t contains a sequence_id field. The high word is a unique job group id. +// The low word contains the "sequence id" which is a value starting at 0 and +// incremented for each pass in the job group. Use the function below to create and +// interpret a sequence_id field. +int MakeJobID(int jobGroupID, int sequenceNum); +bool IsFirstPass(int jobID); + typedef enum _HBQueueJobGroupStatus { HBStatusNone = 0, @@ -52,17 +61,66 @@ BOOL fIsDragging; @end //------------------------------------------------------------------------------------ +// HBJob is the UI's equivalent to libhb's hb_job_t struct. It is used mainly for +// drawing the job's description. HBJob are referred to in the UI as 'passes'. +//------------------------------------------------------------------------------------ @interface HBJob : NSObject { - hb_job_t *hbJob; + HBJobGroup *jobGroup; + + // The following fields match up with similar fields found in hb_job_t and it's + // various substructures. +@public + // from hb_job_s + int sequence_id; // This is how we xref to the jobs inside libhb + + int chapter_start; + int chapter_end; + int chapter_markers; + int crop[4]; + int deinterlace; + int width; + int height; + int keep_ratio; + int grayscale; + int pixel_ratio; + int pixel_aspect_width; + int pixel_aspect_height; + int vcodec; + float vquality; + int vbitrate; + int vrate; + int vrate_base; + int pass; + int h264_level; + int crf; + NSString *x264opts; + + int audio_mixdowns[8]; + int acodec; + int abitrate; + int arate; + int subtitle; + + int mux; + NSString *file; + + // from hb_title_s + NSString *titleName; + int titleIndex; + int titleWidth; + int titleHeight; + + // from hb_subtitle_s + NSString *subtitleLang; } + (HBJob*) jobWithJob: (hb_job_t *) job; - (id) initWithJob: (hb_job_t *) job; -- (hb_job_t *) job; -- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle - withIcon: (BOOL)withIcon +- (HBJobGroup *) jobGroup; +- (void) setJobGroup: (HBJobGroup *)aJobGroup; +- (NSMutableAttributedString *) attributedDescriptionWithIcon: (BOOL)withIcon withTitle: (BOOL)withTitle withPassName: (BOOL)withPassName withFormatInfo: (BOOL)withFormatInfo @@ -76,6 +134,11 @@ BOOL fIsDragging; @end //------------------------------------------------------------------------------------ +// HBJobGroup is what's referred to in the UI as an 'encode'. A job group contains +// multiple HBJobs, one for each 'pass' of the encode. Whereas libhb keeps a simple +// list of jobs in it's queue, the queue controller presents them to the user as a +// series of encodes and passes (HBJObGroups and HBJobs). +//------------------------------------------------------------------------------------ @interface HBJobGroup : NSObject { @@ -85,7 +148,6 @@ BOOL fIsDragging; float fLastDescriptionHeight; float fLastDescriptionWidth; HBQueueJobGroupStatus fStatus; - NSString *fPath; } // Creating a job group @@ -94,23 +156,20 @@ BOOL fIsDragging; // Adding jobs - (void) addJob: (HBJob *)aJob; -// Removing jobs -- (void) removeAllJobs; - // Querying a job group - (unsigned int) count; - (HBJob *) jobAtIndex: (unsigned)index; -- (unsigned) indexOfJob: (HBJob *)aJob; +- (unsigned int) indexOfJob: (HBJob *)aJob; - (NSEnumerator *) jobEnumerator; - (void) setStatus: (HBQueueJobGroupStatus)status; - (HBQueueJobGroupStatus) status; -- (void) setPath: (NSString *)path; - (NSString *) path; +- (NSString *) name; // Creating a description - (void) setNeedsDescription: (BOOL)flag; -- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle; -- (float) heightOfDescriptionForWidth:(float)width withHBHandle: (hb_handle_t *)handle; +- (NSMutableAttributedString *) attributedDescription; +- (float) heightOfDescriptionForWidth:(float)width; - (float) lastDescriptionHeight; @end @@ -122,15 +181,16 @@ BOOL fIsDragging; hb_handle_t *fHandle; // reference to hblib HBController *fHBController; // reference to HBController NSMutableArray *fJobGroups; // hblib's job list organized in a hierarchy of HBJobGroup and HBJob - HBJobGroup *fCurrentJobGroup; // the HJobGroup current being processed by hblib + HBJobGroup *fCurrentJobGroup; // the HJobGroup currently being processed by hblib + HBJob *fCurrentJob; // the HJob (pass) currently being processed by hblib + int fCurrentJobID; // this is how we track when hbib has started processing a different job. This is the job's sequence_id. BOOL fCurrentJobPaneShown; // NO when fCurrentJobPane has been shifted out of view (see showCurrentJobPane) - hb_job_t *fLastKnownCurrentJob; // this is how we track when hbib has started processing a different job NSMutableIndexSet *fSavedExpandedItems; // used by save/restoreOutlineViewState to preserve which items are expanded NSMutableIndexSet *fSavedSelectedItems; // used by save/restoreOutlineViewState to preserve which items are selected #if HB_QUEUE_DRAGGING NSArray *fDraggedNodes; #endif - NSMutableArray *fCompleted; // HBJobGroups that have been completed. These also appear in fJobGroups. + NSMutableArray *fCompleted; // HBJobGroups that libhb has finihsed with, whether successfully encoded or canceled by the user. These also appear in fJobGroups. NSTimer *fAnimationTimer; // animates the icon of the current job in the queue outline view int fAnimationIndex; // used to generate name of image used to animate the current job in the queue outline view @@ -177,6 +237,9 @@ BOOL fIsDragging; - (void)hblibStateChanged: (hb_state_t &)state; - (void)hblibWillStop; +- (HBJobGroup *) currentJobGroup; +- (HBJob *) currentJob; + - (IBAction)showQueueWindow: (id)sender; - (IBAction)removeSelectedJobGroups: (id)sender; - (IBAction)revealSelectedJobGroups: (id)sender; diff --git a/macosx/HBQueueController.mm b/macosx/HBQueueController.mm index fa307e837..428956d60 100644 --- a/macosx/HBQueueController.mm +++ b/macosx/HBQueueController.mm @@ -13,6 +13,19 @@ // Pasteboard type for or drag operations #define HBQueuePboardType @"HBQueuePboardType" +//------------------------------------------------------------------------------------ +// Job ID Utilities +//------------------------------------------------------------------------------------ + +int MakeJobID(int jobGroupID, int sequenceNum) +{ + return jobGroupID<<16 | sequenceNum; +} + +bool IsFirstPass(int jobID) +{ + return LoWord(jobID) == 0; +} //------------------------------------------------------------------------------------ // NSMutableAttributedString (HBAdditions) @@ -100,7 +113,7 @@ static int hb_group_count(hb_handle_t * h) int index = 0; while( ( job = hb_job( h, index++ ) ) ) { - if (job->sequence_id == 0) + if (IsFirstPass(job->sequence_id)) count++; } return count; @@ -119,7 +132,7 @@ static hb_job_t * hb_group(hb_handle_t * h, int i) int index = 0; while( ( job = hb_job( h, index++ ) ) ) { - if (job->sequence_id == 0) + if (IsFirstPass(job->sequence_id)) { if (count == i) return job; @@ -145,7 +158,7 @@ static void hb_rem_group( hb_handle_t * h, hb_job_t * job ) { // Delete this job plus the following ones in the sequence hb_rem( h, job ); - while( ( j = hb_job( h, index ) ) && (j->sequence_id != 0) ) + while( ( j = hb_job( h, index ) ) && ( !IsFirstPass(j->sequence_id ) ) ) hb_rem( h, j ); return; } @@ -189,23 +202,80 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) { if (self = [super init]) { - // job is not owned by HBJob. It does not get dealloacted when HBJob is released. - hbJob = job; + sequence_id = job->sequence_id; + + chapter_start = job->chapter_start; + chapter_end = job->chapter_end; + chapter_markers = job->chapter_markers; + memcpy(crop, job->crop, sizeof(crop)); + deinterlace = job->deinterlace; + width = job->width; + height = job->height; + keep_ratio = job->keep_ratio; + grayscale = job->grayscale; + pixel_ratio = job->pixel_ratio; + pixel_aspect_width = job->pixel_aspect_width; + pixel_aspect_height = job->pixel_aspect_height; + vcodec = job->vcodec; + vquality = job->vquality; + vbitrate = job->vbitrate; + vrate = job->vrate; + vrate_base = job->vrate_base; + pass = job->pass; + h264_level = job->h264_level; + crf = job->crf; + if (job->x264opts) + x264opts = [[NSString stringWithUTF8String:job->x264opts] retain]; + memcpy(audio_mixdowns, job->audio_mixdowns, sizeof(audio_mixdowns)); + acodec = job->acodec; + abitrate = job->abitrate; + arate = job->arate; + subtitle = job->subtitle; + mux = job->mux; + if (job->file) + file = [[NSString stringWithUTF8String:job->file] retain]; + if (job->title->name) + titleName = [[NSString stringWithUTF8String:job->title->name] retain]; + titleIndex = job->title->index; + titleWidth = job->title->width; + titleHeight = job->title->height; + if (job->subtitle >= 0) + { + hb_subtitle_t * aSubtitle = (hb_subtitle_t *) hb_list_item(job->title->list_subtitle, 0); + if (aSubtitle) + subtitleLang = [[NSString stringWithUTF8String:aSubtitle->lang] retain]; + } + } - return self; + return self; +} + +- (void) dealloc +{ + // jobGroup is a weak reference and does not need to be deleted + [x264opts release]; + [file release]; + [titleName release]; + [subtitleLang release]; + [super dealloc]; } -- (hb_job_t*) job +- (HBJobGroup *) jobGroup { - return hbJob; + return jobGroup; +} + +- (void) setJobGroup: (HBJobGroup *)aJobGroup +{ + // This is a weak reference. We don't retain or release it. + jobGroup = aJobGroup; } //------------------------------------------------------------------------------------ // Generate string to display in UI. //------------------------------------------------------------------------------------ -- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle - withIcon: (BOOL)withIcon +- (NSMutableAttributedString *) attributedDescriptionWithIcon: (BOOL)withIcon withTitle: (BOOL)withTitle withPassName: (BOOL)withPassName withFormatInfo: (BOOL)withFormatInfo @@ -219,8 +289,6 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) { NSMutableAttributedString * finalString = [[[NSMutableAttributedString alloc] initWithString: @""] autorelease]; - hb_title_t * title = hbJob->title; - // Attributes static NSMutableParagraphStyle * ps = NULL; if (!ps) @@ -271,15 +339,15 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) // Note: use title->name instead of title->dvd since name is just the chosen // folder, instead of dvd which is the full path - [finalString appendString:[NSString stringWithUTF8String:title->name] withAttributes:titleAttribute]; + [finalString appendString:titleName withAttributes:titleAttribute]; NSString * summaryInfo; - NSString * chapterString = (hbJob->chapter_start == hbJob->chapter_end) ? - [NSString stringWithFormat:@"Chapter %d", hbJob->chapter_start] : - [NSString stringWithFormat:@"Chapters %d through %d", hbJob->chapter_start, hbJob->chapter_end]; + NSString * chapterString = (chapter_start == chapter_end) ? + [NSString stringWithFormat:@"Chapter %d", chapter_start] : + [NSString stringWithFormat:@"Chapters %d through %d", chapter_start, chapter_end]; - BOOL hasIndepthScan = (hbJob->pass == -1); + BOOL hasIndepthScan = (pass == -1); int numVideoPasses = 0; // To determine number of video passes, we need to skip past the subtitle scan. @@ -287,25 +355,24 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) { // When job is the one currently being processed, then the next in its group // is the the first job in the queue. - hb_job_t * nextjob; - if (hbJob == hb_current_job(handle)) - nextjob = hb_job(handle, 0); - else - nextjob = hb_next_job(handle, hbJob); + HBJob * nextjob = nil; + unsigned int index = [jobGroup indexOfJob:self]; + if (index != NSNotFound) + nextjob = [jobGroup jobAtIndex:index+1]; if (nextjob) // Overly cautious in case there is no next job! numVideoPasses = MIN( 2, nextjob->pass + 1 ); } else - numVideoPasses = MIN( 2, hbJob->pass + 1 ); + numVideoPasses = MIN( 2, pass + 1 ); if (hasIndepthScan && numVideoPasses == 1) - summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Deep Scan, Single Video Pass)", title->index, chapterString]; + summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Deep Scan, Single Video Pass)", titleIndex, chapterString]; else if (hasIndepthScan && numVideoPasses > 1) - summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Deep Scan, %d Video Passes)", title->index, chapterString, numVideoPasses]; + summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Deep Scan, %d Video Passes)", titleIndex, chapterString, numVideoPasses]; else if (numVideoPasses == 1) - summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Single Video Pass)", title->index, chapterString]; + summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, Single Video Pass)", titleIndex, chapterString]; else - summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, %d Video Passes)", title->index, chapterString, numVideoPasses]; + summaryInfo = [NSString stringWithFormat: @" (Title %d, %@, %d Video Passes)", titleIndex, chapterString, numVideoPasses]; [finalString appendString:[NSString stringWithFormat:@"%@\n", summaryInfo] withAttributes:detailAttribute]; @@ -322,7 +389,7 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) if (withIcon) { NSString * imageName; - switch (hbJob->pass) + switch (pass) { case -1: imageName = @"JobPassSubtitleSmall"; break; case 0: imageName = @"JobPassFirstSmall"; break; @@ -348,11 +415,11 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) } NSString * jobPassName; - if (hbJob->pass == -1) + if (pass == -1) jobPassName = NSLocalizedString (@"Deep Scan", nil); else { - int passNum = MAX( 1, hbJob->pass ); + int passNum = MAX( 1, pass ); if (passNum == 0) jobPassName = NSLocalizedString (@"1st Pass", nil); else if (passNum == 1) @@ -371,14 +438,14 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) { // 2097152 // Video Codec settings (Encoder in the gui) - if (hbJob->vcodec == HB_VCODEC_FFMPEG) + if (vcodec == HB_VCODEC_FFMPEG) jobVideoCodec = @"FFmpeg"; // HB_VCODEC_FFMPEG - else if (hbJob->vcodec == HB_VCODEC_XVID) + else if (vcodec == HB_VCODEC_XVID) jobVideoCodec = @"XviD"; // HB_VCODEC_XVID - else if (hbJob->vcodec == HB_VCODEC_X264) + else if (vcodec == HB_VCODEC_X264) { // Deterimine for sure how we are now setting iPod uuid atom - if (hbJob->h264_level) // We are encoding for iPod + if (h264_level) // We are encoding for iPod jobVideoCodec = @"x264 (H.264 iPod)"; // HB_VCODEC_X264 else jobVideoCodec = @"x264 (H.264 Main)"; // HB_VCODEC_X264 @@ -391,13 +458,13 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) NSString * jobAudioCodec = nil; if (withFormatInfo || withAudioInfo) { - if (hbJob->acodec == 256) + if (acodec == 256) jobAudioCodec = @"AAC"; // HB_ACODEC_FAAC - else if (hbJob->acodec == 512) + else if (acodec == 512) jobAudioCodec = @"MP3"; // HB_ACODEC_LAME - else if (hbJob->acodec == 1024) + else if (acodec == 1024) jobAudioCodec = @"Vorbis"; // HB_ACODEC_VORBIS - else if (hbJob->acodec == 2048) + else if (acodec == 2048) jobAudioCodec = @"AC3"; // HB_ACODEC_AC3 } if (jobAudioCodec == nil) @@ -408,18 +475,18 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) { NSString * jobFormatInfo; // Muxer settings (File Format in the gui) - if (hbJob->mux == 65536 || hbJob->mux == 131072 || hbJob->mux == 1048576) + if (mux == 65536 || mux == 131072 || mux == 1048576) jobFormatInfo = @"MP4"; // HB_MUX_MP4,HB_MUX_PSP,HB_MUX_IPOD - else if (hbJob->mux == 262144) + else if (mux == 262144) jobFormatInfo = @"AVI"; // HB_MUX_AVI - else if (hbJob->mux == 524288) + else if (mux == 524288) jobFormatInfo = @"OGM"; // HB_MUX_OGM - else if (hbJob->mux == 2097152) + else if (mux == 2097152) jobFormatInfo = @"MKV"; // HB_MUX_MKV else jobFormatInfo = @"unknown"; - if (hbJob->chapter_markers == 1) + if (chapter_markers == 1) jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video + %@ Audio, Chapter Markers\n", jobFormatInfo, jobVideoCodec, jobAudioCodec]; else jobFormatInfo = [NSString stringWithFormat:@"%@ Container, %@ Video + %@ Audio\n", jobFormatInfo, jobVideoCodec, jobAudioCodec]; @@ -431,7 +498,7 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) if (withDestination) { [finalString appendString: @"Destination: " withAttributes:detailBoldAttribute]; - [finalString appendString:[NSString stringWithFormat:@"%@\n", [NSString stringWithUTF8String:hbJob->file]] withAttributes:detailAttribute]; + [finalString appendString:[NSString stringWithFormat:@"%@\n", file] withAttributes:detailAttribute]; } @@ -440,22 +507,22 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) NSString * jobPictureInfo; // integers for picture values deinterlace, crop[4], keep_ratio, grayscale, pixel_ratio, pixel_aspect_width, pixel_aspect_height, // maxWidth, maxHeight - if (hbJob->pixel_ratio == 1) + if (pixel_ratio == 1) { - int titlewidth = title->width - hbJob->crop[2] - hbJob->crop[3]; - int displayparwidth = titlewidth * hbJob->pixel_aspect_width / hbJob->pixel_aspect_height; - int displayparheight = title->height - hbJob->crop[0] - hbJob->crop[1]; - jobPictureInfo = [NSString stringWithFormat:@"%dx%d (%dx%d Anamorphic)", displayparwidth, displayparheight, hbJob->width, displayparheight]; + int croppedWidth = titleWidth - crop[2] - crop[3]; + int displayparwidth = croppedWidth * pixel_aspect_width / pixel_aspect_height; + int displayparheight = titleHeight - crop[0] - crop[1]; + jobPictureInfo = [NSString stringWithFormat:@"%dx%d (%dx%d Anamorphic)", displayparwidth, displayparheight, width, displayparheight]; } else - jobPictureInfo = [NSString stringWithFormat:@"%dx%d", hbJob->width, hbJob->height]; - if (hbJob->keep_ratio == 1) + jobPictureInfo = [NSString stringWithFormat:@"%dx%d", width, height]; + if (keep_ratio == 1) jobPictureInfo = [jobPictureInfo stringByAppendingString:@" Keep Aspect Ratio"]; - if (hbJob->grayscale == 1) + if (grayscale == 1) jobPictureInfo = [jobPictureInfo stringByAppendingString:@", Grayscale"]; - if (hbJob->deinterlace == 1) + if (deinterlace == 1) jobPictureInfo = [jobPictureInfo stringByAppendingString:@", Deinterlace"]; if (withIcon) // implies indent the info [finalString appendString: @"\t" withAttributes:detailBoldAttribute]; @@ -468,25 +535,25 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) NSString * jobVideoQuality; NSString * jobVideoDetail; - if (hbJob->vquality <= 0 || hbJob->vquality >= 1) - jobVideoQuality = [NSString stringWithFormat:@"%d kbps", hbJob->vbitrate]; + if (vquality <= 0 || vquality >= 1) + jobVideoQuality = [NSString stringWithFormat:@"%d kbps", vbitrate]; else { NSNumber * vidQuality; - vidQuality = [NSNumber numberWithInt:hbJob->vquality * 100]; + vidQuality = [NSNumber numberWithInt:vquality * 100]; // this is screwed up kind of. Needs to be formatted properly. - if (hbJob->crf == 1) + if (crf == 1) jobVideoQuality = [NSString stringWithFormat:@"%@%% CRF", vidQuality]; else jobVideoQuality = [NSString stringWithFormat:@"%@%% CQP", vidQuality]; } - if (hbJob->vrate_base == 1126125) + if (vrate_base == 1126125) { // NTSC FILM 23.976 jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 23.976 fps", jobVideoCodec, jobVideoQuality]; } - else if (hbJob->vrate_base == 900900) + else if (vrate_base == 900900) { // NTSC 29.97 jobVideoDetail = [NSString stringWithFormat:@"%@, %@, 29.97 fps", jobVideoCodec, jobVideoQuality]; @@ -494,7 +561,7 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) else { // Everything else - jobVideoDetail = [NSString stringWithFormat:@"%@, %@, %d fps", jobVideoCodec, jobVideoQuality, hbJob->vrate / hbJob->vrate_base]; + jobVideoDetail = [NSString stringWithFormat:@"%@, %@, %d fps", jobVideoCodec, jobVideoQuality, vrate / vrate_base]; } if (withIcon) // implies indent the info [finalString appendString: @"\t" withAttributes:detailBoldAttribute]; @@ -504,12 +571,12 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) if (withx264Info) { - if (hbJob->vcodec == HB_VCODEC_X264 && hbJob->x264opts) + if (vcodec == HB_VCODEC_X264 && x264opts) { if (withIcon) // implies indent the info [finalString appendString: @"\t" withAttributes:detailBoldAttribute]; [finalString appendString: @"x264 Options: " withAttributes:detailBoldAttribute]; - [finalString appendString:[NSString stringWithFormat:@"%@\n", [NSString stringWithUTF8String:hbJob->x264opts]] withAttributes:detailAttribute]; + [finalString appendString:[NSString stringWithFormat:@"%@\n", x264opts] withAttributes:detailAttribute]; } } @@ -519,7 +586,7 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) if ([jobAudioCodec isEqualToString: @"AC3"]) jobAudioInfo = [NSString stringWithFormat:@"%@, Pass-Through", jobAudioCodec]; else - jobAudioInfo = [NSString stringWithFormat:@"%@, %d kbps, %d Hz", jobAudioCodec, hbJob->abitrate, hbJob->arate]; + jobAudioInfo = [NSString stringWithFormat:@"%@, %d kbps, %d Hz", jobAudioCodec, abitrate, arate]; // we now get the audio mixdown info for each of the two gui audio tracks // lets do it the long way here to get a handle on things. @@ -527,15 +594,15 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) int ai; // counter for each audios [] , macgui only allows for two audio tracks currently for( ai = 0; ai < 2; ai++ ) { - if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_MONO) + if (audio_mixdowns[ai] == HB_AMIXDOWN_MONO) jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Mono", ai + 1]]; - if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_STEREO) + if (audio_mixdowns[ai] == HB_AMIXDOWN_STEREO) jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Stereo", ai + 1]]; - if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBY) + if (audio_mixdowns[ai] == HB_AMIXDOWN_DOLBY) jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Surround", ai + 1]]; - if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_DOLBYPLII) + if (audio_mixdowns[ai] == HB_AMIXDOWN_DOLBYPLII) jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: Dolby Pro Logic II", ai + 1]]; - if (hbJob->audio_mixdowns[ai] == HB_AMIXDOWN_6CH) + if (audio_mixdowns[ai] == HB_AMIXDOWN_6CH) jobAudioInfo = [jobAudioInfo stringByAppendingString:[NSString stringWithFormat:@", Track %d: 6-channel discreet", ai + 1]]; } if (withIcon) // implies indent the info @@ -546,25 +613,24 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) if (withSubtitleInfo) { - // hbJob->subtitle can == -1 in two cases: + // subtitle can == -1 in two cases: // autoselect: when pass == -1 // none: when pass != -1 - if ((hbJob->subtitle == -1) && (hbJob->pass == -1)) + if ((subtitle == -1) && (pass == -1)) { if (withIcon) // implies indent the info [finalString appendString: @"\t" withAttributes:detailBoldAttribute]; [finalString appendString: @"Subtitles: " withAttributes:detailBoldAttribute]; [finalString appendString: @"Autoselect " withAttributes:detailAttribute]; } - else if (hbJob->subtitle >= 0) + else if (subtitle >= 0) { - hb_subtitle_t * subtitle = (hb_subtitle_t *) hb_list_item( title->list_subtitle, 0 ); - if (subtitle) + if (subtitleLang) { if (withIcon) // implies indent the info [finalString appendString: @"\t" withAttributes:detailBoldAttribute]; [finalString appendString: @"Subtitles: " withAttributes:detailBoldAttribute]; - [finalString appendString: [NSString stringWithCString: subtitle->lang] withAttributes:detailAttribute]; + [finalString appendString: subtitleLang withAttributes:detailAttribute]; } } } @@ -606,7 +672,6 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) - (void) dealloc { [fJobs release]; - [fPath release]; [super dealloc]; } @@ -617,17 +682,13 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) - (void) addJob: (HBJob *)aJob { + [aJob setJobGroup:self]; [fJobs addObject: aJob]; [self setNeedsDescription: YES]; fLastDescriptionHeight = 0; fLastDescriptionWidth = 0; } -- (void) removeAllJobs -{ - [fJobs removeAllObjects]; -} - - (HBJob *) jobAtIndex: (unsigned)index { return [fJobs objectAtIndex: index]; @@ -648,7 +709,7 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) fNeedsDescription = flag; } -- (void) updateDescriptionWithHBHandle: (hb_handle_t *)handle +- (void) updateDescription { fNeedsDescription = NO; @@ -662,8 +723,7 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) HBJob * job = [self jobAtIndex:0]; - [fDescription appendAttributedString: [job attributedDescriptionWithHBHandle: handle - withIcon: NO + [fDescription appendAttributedString: [job attributedDescriptionWithIcon: NO withTitle: YES withPassName: NO withFormatInfo: YES @@ -679,11 +739,10 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) NSEnumerator * e = [self jobEnumerator]; while ( (job = [e nextObject]) ) { - int pass = [job job]->pass; + int pass = job->pass; [fDescription appendAttributedString:carriageReturn]; [fDescription appendAttributedString: - [job attributedDescriptionWithHBHandle: handle - withIcon: YES + [job attributedDescriptionWithIcon: YES withTitle: NO withPassName: YES withFormatInfo: NO @@ -697,21 +756,21 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) } -- (NSMutableAttributedString *) attributedDescriptionWithHBHandle: (hb_handle_t *)handle +- (NSMutableAttributedString *) attributedDescription { if (fNeedsDescription) - [self updateDescriptionWithHBHandle: handle]; + [self updateDescription]; return fDescription; } -- (float) heightOfDescriptionForWidth:(float)width withHBHandle: (hb_handle_t *)handle +- (float) heightOfDescriptionForWidth:(float)width { // Try to return the cached value if no changes have happened since the last time if ((width == fLastDescriptionWidth) && (fLastDescriptionHeight != 0) && !fNeedsDescription) return fLastDescriptionHeight; if (fNeedsDescription) - [self updateDescriptionWithHBHandle: handle]; + [self updateDescription]; // Calculate the height NSRect bounds = [fDescription boundingRectWithSize:NSMakeSize(width, 10000) options:NSStringDrawingUsesLineFragmentOrigin]; @@ -748,16 +807,16 @@ static hb_job_t * hb_next_job( hb_handle_t * h, hb_job_t * job ) return self->fStatus; } -- (void) setPath: (NSString *)path +- (NSString *) name { - [path retain]; - [fPath release]; - fPath = path; + HBJob * firstJob = [self jobAtIndex:0]; + return firstJob ? firstJob->titleName : nil; } - (NSString *) path { - return fPath; + HBJob * firstJob = [self jobAtIndex:0]; + return firstJob ? firstJob->file : nil; } @end @@ -837,6 +896,24 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe } //------------------------------------------------------------------------------------ +// Returns the HBJobGroup that is currently being encoded; nil if no encoding is +// occurring. +//------------------------------------------------------------------------------------ +- (HBJobGroup *) currentJobGroup; +{ + return fCurrentJobGroup; +} + +//------------------------------------------------------------------------------------ +// Returns the HBJob (pass) that is currently being encoded; nil if no encoding is +// occurring. +//------------------------------------------------------------------------------------ +- (HBJob *) currentJob +{ + return fCurrentJob; +} + +//------------------------------------------------------------------------------------ // Displays and brings the queue window to the front //------------------------------------------------------------------------------------ - (IBAction) showQueueWindow: (id)sender @@ -845,6 +922,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe [fQueueWindow makeKeyAndOrderFront: self]; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"QueueWindowIsOpen"]; } + //------------------------------------------------------------------------------------ // Show or hide the current job pane (fCurrentJobPane). //------------------------------------------------------------------------------------ @@ -897,7 +975,14 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ - (void)rebuildJobGroups { - // Currently, job groups are rdered like this: + // This method is called every time we detect that hblib has changed its job list. + // It releases the previous job group list and rebuilds it by reading the current + // list of jobs from libhb. libhb does not implement job groups/encodes itself. It + // just maintains a simply list of jobs. The queue controller however, presents + // these jobs to the user organized into encodes where all jobs associated with + // the same encode are grouped into a logical job group/encode. + + // Currently, job groups are ordered like this: // Completed job groups // Current job group // Pending job groups @@ -908,7 +993,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe // Add all the completed job groups [fJobGroups addObjectsFromArray: fCompleted]; - // Add all the completed job groups + // Add the current job group if (fCurrentJobGroup) [fJobGroups addObject: fCurrentJobGroup]; @@ -921,7 +1006,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe hb_job_t * nextJob = hb_group( fHandle, 0 ); while( nextJob ) { - if (nextJob->sequence_id == 0) + if (IsFirstPass(nextJob->sequence_id)) { // Encountered a new group. Add the current one to fJobGroups and then start a new one. if ([aJobGroup count] > 0) @@ -932,7 +1017,6 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe } } [aJobGroup addJob: [HBJob jobWithJob:nextJob]]; - [aJobGroup setPath: [NSString stringWithUTF8String:nextJob->file]]; nextJob = hb_next_job (fHandle, nextJob); } if ([aJobGroup count] > 0) @@ -943,68 +1027,70 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe } //------------------------------------------------------------------------------------ -// Adds aJobGroup to the list of completed job groups, marking its status as -// HBStatusComplete. +// Adds aJobGroup to the list of completed job groups. The completed list is +// maintained by the queue, since libhb deletes all its jobs after they are complete. //------------------------------------------------------------------------------------ - (void) addCompletedJobGroup: (HBJobGroup *)aJobGroup { - // Once hblib has completed its work, the hb_job_t objects will be freed, so we - // can't keep a reference to them. - [aJobGroup removeAllJobs]; - - [aJobGroup setStatus: HBStatusComplete]; - - // Put the group in the completed list for permanent storage, and also rebuild - // the master job group list which contains completed and pending groups. + // Put the group in the completed list for permanent storage. [fCompleted addObject: aJobGroup]; } -- (void) setCurrentJobGroupFromJob: (hb_job_t *)aJob +//------------------------------------------------------------------------------------ +// Sets fCurrentJobGroup to a new job group. +//------------------------------------------------------------------------------------ +- (void) setCurrentJobGroup: (HBJobGroup *)aJobGroup { - HBJobGroup * aJobGroup = nil; - - // Try to find this new group in our existing job groups. - if (aJob) + if (aJobGroup) + [aJobGroup setStatus: HBStatusWorking]; + + [aJobGroup retain]; + [fCurrentJobGroup release]; + fCurrentJobGroup = aJobGroup; +} + +//------------------------------------------------------------------------------------ +// Locates and returns a HBJob whose sequence_id matches a specified value. +//------------------------------------------------------------------------------------ +- (HBJob *) findJobWithID: (int)aJobID +{ + HBJobGroup * aJobGroup; + NSEnumerator * groupEnum = [fJobGroups objectEnumerator]; + while ( (aJobGroup = [groupEnum nextObject]) ) { - BOOL found = NO; - NSEnumerator * groupEnum = [fJobGroups objectEnumerator]; - while ( !found && (aJobGroup = [groupEnum nextObject]) ) + HBJob * job; + NSEnumerator * jobEnum = [aJobGroup jobEnumerator]; + while ( (job = [jobEnum nextObject]) ) { - HBJob * j; - NSEnumerator * jobEnum = [aJobGroup jobEnumerator]; - while ( !found && (j = [jobEnum nextObject]) ) - { - if ([j job] == aJob) - found = YES; - } + if (job->sequence_id == aJobID) + return job; } - - // Or create the job group. - if (!aJobGroup) - { - aJobGroup = [HBJobGroup jobGroup]; - [aJobGroup addJob: [HBJob jobWithJob: aJob]]; - [aJobGroup setPath: [NSString stringWithUTF8String:aJob->file]]; - while ( (aJob = hb_next_job(fHandle, aJob)) && (aJob->sequence_id != 0) ) - [aJobGroup addJob: [HBJob jobWithJob: aJob]]; + } + return nil; +} - [aJobGroup updateDescriptionWithHBHandle: fHandle]; - } - - [aJobGroup setStatus: HBStatusWorking]; +//------------------------------------------------------------------------------------ +// Locates and returns a libhb job whose sequence_id matches a specified value. +//------------------------------------------------------------------------------------ +- (hb_job_t *) findLibhbJobWithID: (int)aJobID +{ + hb_job_t * job; + int index = 0; + while( ( job = hb_job( fHandle, index++ ) ) ) + { + if (job->sequence_id == aJobID) + return job; } - - [aJobGroup retain]; - [fCurrentJobGroup release]; - fCurrentJobGroup = aJobGroup; + return nil; } #pragma mark - #pragma mark UI Updating //------------------------------------------------------------------------------------ -// Saves the state of the items that are currently expanded. Calling restoreOutlineViewState -// will restore the state of all items to match what was saved by saveOutlineViewState. +// Saves the state of the items that are currently expanded and selected. Calling +// restoreOutlineViewState will restore the state of all items to match what was saved +// by saveOutlineViewState. Nested calls to saveOutlineViewState are not supported. //------------------------------------------------------------------------------------ - (void) saveOutlineViewState { @@ -1013,29 +1099,18 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe else [fSavedExpandedItems removeAllIndexes]; - // NB: This code is stuffing the address of each job into an index set. While it - // works 99.9% of the time, it's not the ideal solution. We need unique ids in - // each job, possibly using the existing sequence_id field. Could use the high - // word as a unique encode id and the low word the sequence number. + // This code stores the sequence_id of the first job of each job group into an + // index set. This is sufficient to identify each group uniquely. HBJobGroup * aJobGroup; NSEnumerator * e = [fJobGroups objectEnumerator]; while ( (aJobGroup = [e nextObject]) ) { if ([fOutlineView isItemExpanded: aJobGroup]) - { - if ([aJobGroup status] == HBStatusComplete) - [fSavedExpandedItems addIndex: (unsigned int)aJobGroup]; - else - [fSavedExpandedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]]; - } + [fSavedExpandedItems addIndex: [aJobGroup jobAtIndex:0]->sequence_id]; } - // Save the selection also. This is really UGLY code. Since I have to rebuild the - // entire outline hierachy every time hblib changes its job list, there's no easy - // way for me to remember the selection state other than saving off the first - // hb_job_t item in each selected group. This is done by saving the object's - // address. This could go away if I'd save a unique id in each job object. + // Save the selection also. if (!fSavedSelectedItems) fSavedSelectedItems = [[NSMutableIndexSet alloc] init]; @@ -1047,10 +1122,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe while (row != NSNotFound) { aJobGroup = [fOutlineView itemAtRow: row]; - if ([aJobGroup status] == HBStatusComplete) - [fSavedSelectedItems addIndex: (unsigned int)aJobGroup]; - else - [fSavedSelectedItems addIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]]; + [fSavedSelectedItems addIndex: [aJobGroup jobAtIndex:0]->sequence_id]; row = [selectedRows indexGreaterThanIndex: row]; } @@ -1068,40 +1140,24 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe NSEnumerator * e = [fJobGroups objectEnumerator]; while ( (aJobGroup = [e nextObject]) ) { - if ([aJobGroup status] == HBStatusComplete) - { - if ([fSavedExpandedItems containsIndex: (unsigned int)aJobGroup]) - [fOutlineView expandItem: aJobGroup]; - } - else - { - hb_job_t * j = [[aJobGroup jobAtIndex:0] job]; - if ([fSavedExpandedItems containsIndex: (unsigned int)j]) - [fOutlineView expandItem: aJobGroup]; - } + HBJob * job = [aJobGroup jobAtIndex:0]; + if (job && [fSavedExpandedItems containsIndex: job->sequence_id]) + [fOutlineView expandItem: aJobGroup]; } } if (fSavedSelectedItems) { - // Ugh. Have to cycle through each row looking for the previously selected job. - // See the explanation in saveOutlineViewState about the logic here. - NSMutableIndexSet * rowsToSelect = [[[NSMutableIndexSet alloc] init] autorelease]; - for (int i = 0; i < [fOutlineView numberOfRows]; i++) + HBJobGroup * aJobGroup; + NSEnumerator * e = [fJobGroups objectEnumerator]; + int i = 0; + while ( (aJobGroup = [e nextObject]) ) { - HBJobGroup * aJobGroup = [fOutlineView itemAtRow: i]; - // Test to see if the group or the group's first job is a match - if ([aJobGroup status] == HBStatusComplete) - { - if ([fSavedSelectedItems containsIndex: (unsigned int)aJobGroup]) - [rowsToSelect addIndex: i]; - } - else - { - if ([fSavedSelectedItems containsIndex: (unsigned int)[[aJobGroup jobAtIndex:0] job]]) - [rowsToSelect addIndex: i]; - } + HBJob * job = [aJobGroup jobAtIndex:0]; + if (job && [fSavedSelectedItems containsIndex: job->sequence_id]) + [rowsToSelect addIndex: i]; + i++; } if ([rowsToSelect count] == 0) [fOutlineView deselectAll: nil]; @@ -1158,7 +1214,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ // Generate string to display in UI. //------------------------------------------------------------------------------------ -- (NSString *) progressStatusStringForJob: (hb_job_t *)job state: (hb_state_t *)s +- (NSString *) progressStatusStringForJob: (HBJob *)job state: (hb_state_t *)s { if (s->state == HB_STATE_WORKING) { @@ -1198,7 +1254,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ // Generate string to display in UI. //------------------------------------------------------------------------------------ -- (NSString *) progressTimeRemainingStringForJob: (hb_job_t *)job state: (hb_state_t *)s +- (NSString *) progressTimeRemainingStringForJob: (HBJob *)job state: (hb_state_t *)s { if (s->state == HB_STATE_WORKING) { @@ -1270,7 +1326,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ // Refresh progress bar (fProgressTextField) from current state. //------------------------------------------------------------------------------------ -- (void) updateProgressTextForJob: (hb_job_t *)job state: (hb_state_t *)s +- (void) updateProgressTextForJob: (HBJob *)job state: (hb_state_t *)s { NSString * statusMsg = [self progressStatusStringForJob:job state:s]; NSString * timeMsg = [self progressTimeRemainingStringForJob:job state:s]; @@ -1335,17 +1391,13 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ - (void)updateCurrentJobDescription { - hb_job_t * job = fHandle ? hb_current_job(fHandle) : nil; - - if (job) + if (fCurrentJob) { - HBJob * currentJob = [HBJob jobWithJob: job]; - switch (job->pass) + switch (fCurrentJob->pass) { case -1: // Subtitle scan [fJobDescTextField setAttributedStringValue: - [currentJob attributedDescriptionWithHBHandle:fHandle - withIcon: NO + [fCurrentJob attributedDescriptionWithIcon: NO withTitle: YES withPassName: YES withFormatInfo: NO @@ -1359,8 +1411,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe case 1: // video 1st pass [fJobDescTextField setAttributedStringValue: - [currentJob attributedDescriptionWithHBHandle:fHandle - withIcon: NO + [fCurrentJob attributedDescriptionWithIcon: NO withTitle: YES withPassName: YES withFormatInfo: NO @@ -1375,8 +1426,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe case 0: // single pass case 2: // video 2nd pass + audio [fJobDescTextField setAttributedStringValue: - [currentJob attributedDescriptionWithHBHandle:fHandle - withIcon: NO + [fCurrentJob attributedDescriptionWithIcon: NO withTitle: YES withPassName: YES withFormatInfo: NO @@ -1390,8 +1440,7 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe default: // unknown [fJobDescTextField setAttributedStringValue: - [currentJob attributedDescriptionWithHBHandle:fHandle - withIcon: NO + [fCurrentJob attributedDescriptionWithIcon: NO withTitle: YES withPassName: YES withFormatInfo: NO @@ -1414,10 +1463,9 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ - (void)updateCurrentJobProgress { - hb_job_t * job = fHandle ? hb_current_job(fHandle) : nil; hb_state_t s; hb_get_state2( fHandle, &s ); - [self updateProgressTextForJob: job state: &s]; + [self updateProgressTextForJob: fCurrentJob state: &s]; [self updateProgressBarWithState:&s]; } @@ -1462,8 +1510,10 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe [self cancelCurrentJob: sender]; break; case HBStatusPending: - hb_job_t * job = [[jobGroup jobAtIndex: 0] job]; - hb_rem_group( fHandle, job ); + HBJob * job = [jobGroup jobAtIndex: 0]; + hb_job_t * libhbJob = [self findLibhbJobWithID:job->sequence_id]; + if (libhbJob) + hb_rem_group( fHandle, libhbJob ); break; case HBStatusNone: break; @@ -1548,18 +1598,52 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ // Notifies HBQueueController that hblib's current job has changed //------------------------------------------------------------------------------------ -- (void)currentJobGroupChanged: (hb_job_t *) currentJob +- (void)currentJobChanged: (HBJob *) currentJob { - if (fCurrentJobGroup && [fCurrentJobGroup status] != HBStatusCanceled) - [self addCompletedJobGroup: fCurrentJobGroup]; - [self setCurrentJobGroupFromJob: currentJob]; - [self updateCurrentJobDescription]; - [self updateCurrentJobProgress]; - [self showCurrentJobPane: fCurrentJobGroup != nil]; - if (fCurrentJobGroup) - [self startAnimatingCurrentJobGroupInQueue]; - else - [self stopAnimatingCurrentJobGroupInQueue]; + [currentJob retain]; + [fCurrentJob release]; + fCurrentJob = currentJob; + + // Check to see if this is also a change in Job Group + + HBJobGroup * theJobGroup = [currentJob jobGroup]; + if ((theJobGroup == nil) || (theJobGroup != fCurrentJobGroup)) // no more job groups or start of a new group + { + // Previous job has completed + if (fCurrentJobGroup) + { + // Update the status of the job that just finished. If the user canceled, + // the status will have already been set to canceled by hblibWillStop. So + // all other cases are assumed to be a successful encode. BTW, libhb + // doesn't currently report errors back to the GUI. + if ([fCurrentJobGroup status] != HBStatusCanceled) + [fCurrentJobGroup setStatus:HBStatusComplete]; + + [self addCompletedJobGroup: fCurrentJobGroup]; + } + + // Set the new group + [self setCurrentJobGroup: theJobGroup]; + + // Update the UI + [self updateCurrentJobDescription]; + [self updateCurrentJobProgress]; + [self showCurrentJobPane: fCurrentJobGroup != nil]; + if (fCurrentJobGroup) + [self startAnimatingCurrentJobGroupInQueue]; + else + [self stopAnimatingCurrentJobGroupInQueue]; + + [self hblibJobListChanged]; + } + + else // start a new job/pass in the same group + { + // Update the UI + [self updateCurrentJobDescription]; + [self updateCurrentJobProgress]; + } + } //------------------------------------------------------------------------------------ @@ -1590,32 +1674,26 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe //------------------------------------------------------------------------------------ - (void)hblibStateChanged: (hb_state_t &)state { - // First check to see if hblib has moved on to another job. We get no direct - // message when this happens, so we have to detect it ourself. The new job could - // be either just the next job in the current group, or the start of a new group. - if (fLastKnownCurrentJob != hb_current_job(fHandle)) - { - hb_job_t * currentJob = hb_current_job(fHandle); - if (!currentJob || currentJob->sequence_id == 0) // start of a new group - { - [self currentJobGroupChanged: currentJob]; - [self hblibJobListChanged]; - } - else - { - [self updateCurrentJobDescription]; - [self updateCurrentJobProgress]; - } - - fLastKnownCurrentJob = currentJob; - } - switch( state.state ) { case HB_STATE_WORKING: { - [self updateCurrentJobProgress]; - [self startAnimatingCurrentJobGroupInQueue]; + //NSLog(@"job = %x; job_cur = %d; job_count = %d", state.param.working.sequence_id, state.param.working.job_cur, state.param.working.job_count); + // First check to see if hblib has moved on to another job. We get no direct + // message when this happens, so we have to detect it ourself. The new job could + // be either just the next job in the current group, or the start of a new group. + if (fCurrentJobID != state.param.working.sequence_id) + { + fCurrentJobID = state.param.working.sequence_id; + HBJob * currentJob = [self findJobWithID:fCurrentJobID]; + [self currentJobChanged: currentJob]; + } + + if (fCurrentJob) + { + [self updateCurrentJobProgress]; + [self startAnimatingCurrentJobGroupInQueue]; + } break; } @@ -1637,6 +1715,9 @@ static NSString* HBQueuePauseResumeToolbarIdentifier = @"HBQueuePauseRe // HB_STATE_WORKDONE means that hblib has finished processing all the jobs // in *its* queue. This message is NOT sent as each individual job is // completed. + + [self currentJobChanged: nil]; + fCurrentJobID = 0; } } @@ -1984,7 +2065,7 @@ static float spacingWidth = 3.0; // Column width is NOT what is ultimately used width -= 47; // 26 pixels for disclosure triangle, 20 for icon, 1 for intercell spacing - float height = [item heightOfDescriptionForWidth: width withHBHandle: fHandle]; + float height = [item heightOfDescriptionForWidth: width]; return height; } else @@ -1997,11 +2078,14 @@ static float spacingWidth = 3.0; // using the image portion of the cell so we could switch back to a regular NSTextFieldCell. if ([[tableColumn identifier] isEqualToString:@"desc"]) - return [item attributedDescriptionWithHBHandle: fHandle]; + return [item attributedDescription]; else if ([[tableColumn identifier] isEqualToString:@"icon"]) { switch ([(HBJobGroup*)item status]) { + case HBStatusCanceled: + return [NSImage imageNamed:@"EncodeCanceled"]; + break; case HBStatusComplete: return [NSImage imageNamed:@"EncodeComplete"]; break; diff --git a/macosx/HandBrake.xcodeproj/project.pbxproj b/macosx/HandBrake.xcodeproj/project.pbxproj index 5696d2cef..d99718c38 100644 --- a/macosx/HandBrake.xcodeproj/project.pbxproj +++ b/macosx/HandBrake.xcodeproj/project.pbxproj @@ -165,6 +165,7 @@ E3C844F80CA6B3F90013B683 /* RevealHighlight.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C844F40CA6B3F90013B683 /* RevealHighlight.png */; }; E3C844F90CA6B3F90013B683 /* Reveal.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C844F50CA6B3F90013B683 /* Reveal.png */; }; E3C845870CA6E9080013B683 /* EncodeComplete.png in Resources */ = {isa = PBXBuildFile; fileRef = E3C845860CA6E9080013B683 /* EncodeComplete.png */; }; + E3FC10910D1611EC00470E7B /* EncodeCanceled.png in Resources */ = {isa = PBXBuildFile; fileRef = E3FC10900D1611EC00470E7B /* EncodeCanceled.png */; }; EAA526930C3B25D200944FF2 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = EAA526920C3B25D200944FF2 /* stream.c */; }; EAA526940C3B25D200944FF2 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = EAA526920C3B25D200944FF2 /* stream.c */; }; FC8519500C59A02C0073812C /* denoise.c in Sources */ = {isa = PBXBuildFile; fileRef = FC85194C0C59A02C0073812C /* denoise.c */; }; @@ -343,6 +344,7 @@ E3C844F40CA6B3F90013B683 /* RevealHighlight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = RevealHighlight.png; sourceTree = "<group>"; }; E3C844F50CA6B3F90013B683 /* Reveal.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Reveal.png; sourceTree = "<group>"; }; E3C845860CA6E9080013B683 /* EncodeComplete.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeComplete.png; sourceTree = "<group>"; }; + E3FC10900D1611EC00470E7B /* EncodeCanceled.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = EncodeCanceled.png; sourceTree = "<group>"; }; EAA526920C3B25D200944FF2 /* stream.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = stream.c; path = ../libhb/stream.c; sourceTree = SOURCE_ROOT; }; FC85194C0C59A02C0073812C /* denoise.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = denoise.c; path = ../libhb/denoise.c; sourceTree = SOURCE_ROOT; }; FC85194D0C59A02C0073812C /* deinterlace.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = deinterlace.c; path = ../libhb/deinterlace.c; sourceTree = SOURCE_ROOT; }; @@ -599,6 +601,7 @@ E3C844F20CA6B3F90013B683 /* RevealPressed.png */, E3C844F40CA6B3F90013B683 /* RevealHighlight.png */, E3C844F30CA6B3F90013B683 /* RevealHighlightPressed.png */, + E3FC10900D1611EC00470E7B /* EncodeCanceled.png */, E3C845860CA6E9080013B683 /* EncodeComplete.png */, E3997A290CAB58BC00287239 /* EncodeWorking0.png */, E3997A280CAB58BC00287239 /* EncodeWorking1.png */, @@ -823,6 +826,7 @@ E3997A2F0CAB58BC00287239 /* EncodeWorking0.png in Resources */, E3997A300CAB58BC00287239 /* EncodeWorking2.png in Resources */, E3997A310CAB58BC00287239 /* EncodeWorking5.png in Resources */, + E3FC10910D1611EC00470E7B /* EncodeCanceled.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/macosx/icons/EncodeCanceled.png b/macosx/icons/EncodeCanceled.png Binary files differnew file mode 100644 index 000000000..66c5bf7c9 --- /dev/null +++ b/macosx/icons/EncodeCanceled.png |