OSDN Git Service

WinGui:
[handbrake-jp/handbrake-jp-git.git] / macosx / Controller.m
index a806356..5a85cd2 100644 (file)
@@ -4,6 +4,7 @@
    Homepage: <http://handbrake.fr/>.
    It may be used under the terms of the GNU General Public License. */
 
+#include <dlfcn.h>
 #import "Controller.h"
 #import "HBOutputPanelController.h"
 #import "HBPreferencesController.h"
@@ -514,7 +515,7 @@ static NSString *        ChooseSourceIdentifier             = @"Choose Source It
         fQueueStatus,fPresetsAdd,fPresetsDelete,fSrcAngleLabel,fSrcAnglePopUp,
                fCreateChapterMarkers,fVidTurboPassCheck,fDstMp4LargeFileCheck,fSubForcedCheck,fPresetsOutlineView,
         fAudDrcLabel,fDstMp4HttpOptFileCheck,fDstMp4iPodFileCheck,fVidQualityRFField,fVidQualityRFLabel,
-        fEncodeStartStopPopUp,fSrcTimeStartEncodingField,fSrcTimeEndEncodingField,fSrcFrameStartEncodingField,fSrcFrameEndEncodingField};
+        fEncodeStartStopPopUp,fSrcTimeStartEncodingField,fSrcTimeEndEncodingField,fSrcFrameStartEncodingField,fSrcFrameEndEncodingField, fLoadChaptersButton, fSaveChaptersButton};
     
     for( unsigned i = 0;
         i < sizeof( controls ) / sizeof( NSControl * ); i++ )
@@ -1614,31 +1615,20 @@ static NSString *        ChooseSourceIdentifier             = @"Choose Source It
     
     if( [detector isVideoDVD] )
     {
-        int hb_arch;
-#if defined( __LP64__ )
-        /* we are 64 bit */
-        hb_arch = 64;
-#else
-        /* we are 32 bit */
-        hb_arch = 32;
-#endif 
-        
-        
         // The chosen path was actually on a DVD, so use the raw block
         // device path instead.
         path = [detector devicePath];
         [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
         
         /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
-        NSString *vlcPath = @"/Applications/VLC.app/Contents/MacOS/lib/libdvdcss.2.dylib";
-        NSFileManager * fileManager = [NSFileManager defaultManager];
-           if ([fileManager fileExistsAtPath:vlcPath] == 0) 
-           {
-            /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */
+        void *dvdcss = dlopen("libdvdcss.2.dylib", RTLD_LAZY);
+        if (dvdcss == NULL) 
+        {
+            /*compatible vlc not found, so we set the bool to cancel scanning to 1 */
             cancelScanDecrypt = 1;
             [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
             int status;
-            status = NSRunAlertPanel(@"HandBrake could not find VLC or your VLC is out of date.",@"Please download and install VLC media player in your /Applications folder if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway");
+            status = NSRunAlertPanel(@"HandBrake could not find VLC or your VLC is incompatible (Note: 32 bit vlc is not compatible with 64 bit HandBrake and vice-versa).",@"Please download and install VLC media player if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway");
             [NSApp requestUserAttention:NSCriticalRequest];
             
             if (status == NSAlertDefaultReturn)
@@ -1664,111 +1654,8 @@ static NSString *        ChooseSourceIdentifier             = @"Choose Source It
             /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
             [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
             vlcFound = 1;
+            dlclose(dvdcss);
         }
-        /* test for architecture of the vlc app */
-        NSArray *vlc_architecturesArray = [[NSBundle bundleWithPath:@"/Applications/VLC.app"] executableArchitectures];
-        BOOL vlcIntel32bit = NO;
-        BOOL vlcIntel64bit = NO;
-        BOOL vlcPPC32bit = NO;
-        BOOL vlcPPC64bit = NO;
-        /* check the available architectures for vlc and note accordingly */
-        NSEnumerator *enumerator = [vlc_architecturesArray objectEnumerator];
-        id tempObject;
-        while (tempObject = [enumerator nextObject])
-        {
-            
-            if ([tempObject intValue] == NSBundleExecutableArchitectureI386)
-            {
-                vlcIntel32bit = YES;   
-            }
-            if ([tempObject intValue] == NSBundleExecutableArchitectureX86_64)
-            {
-                vlcIntel64bit = YES;   
-            }
-            if ([tempObject intValue] == NSBundleExecutableArchitecturePPC)
-            {
-                vlcPPC32bit = YES;   
-            }
-            if ([tempObject intValue] == NSBundleExecutableArchitecturePPC64)
-            {
-                vlcPPC64bit = YES;   
-            }
-            
-        }
-        /* Write vlc architecture findings to activity window */
-        if (vlcIntel32bit)
-        {
-            [self writeToActivityLog: " 32-Bit VLC app found for decrypting physical dvd"];
-        }
-        if (vlcIntel64bit)
-        {
-            [self writeToActivityLog: " 64-Bit VLC app found for decrypting physical dvd"];
-        }
-        
-        
-        
-        if (vlcFound && hb_arch == 64 && !vlcIntel64bit && cancelScanDecrypt != 1)
-        {
-            
-            /* we are 64 bit */
-            
-            /* Appropriate VLC not found, so cancel */
-            cancelScanDecrypt = 1;
-            [self writeToActivityLog: "This version of HandBrake is 64 bit, 64 bit version of vlc not found, scan cancelled"];
-            /*On Screen Notification*/
-            int status;
-            NSBeep();
-            status = NSRunAlertPanel(@"This version of HandBrake is 64 bit, VLC found but not 64 bit!",@"", @"Cancel Scan", @"Attempt Scan Anyway", @"Get 64 bit VLC", nil);
-            [NSApp requestUserAttention:NSCriticalRequest];
-            
-            if (status == NSAlertDefaultReturn)
-            {
-                /* User chose to cancel the scan */
-                [self writeToActivityLog: "cannot open physical dvd VLC found but not 64 bit, scan cancelled"];
-                cancelScanDecrypt = 1;
-            }
-            else if (status == NSAlertAlternateReturn)
-            {
-                [self writeToActivityLog: "user overrode 64-bit warning trying to open physical dvd without proper decryption"];
-                cancelScanDecrypt = 0;
-            }
-            else
-            {
-                /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
-                [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/vlc/download-macosx.html"]];
-            }
-            
-        }    
-        else if (vlcFound && hb_arch == 32 && !vlcIntel32bit && cancelScanDecrypt != 1)
-        {
-            /* we are 32 bit */
-            /* Appropriate VLC not found, so cancel */
-            cancelScanDecrypt = 1;
-            [self writeToActivityLog: "This version of HandBrake is 32 bit, 32 bit version of vlc not found, scan cancelled"];
-            /*On Screen Notification*/
-            int status;
-            NSBeep();
-            status = NSRunAlertPanel(@"This version of HandBrake is 32 bit, VLC found but not 32 bit!",@"", @"Cancel Scan", @"Attempt Scan Anyway", @"Get 32 bit VLC", nil);
-            [NSApp requestUserAttention:NSCriticalRequest];
-            
-            if (status == NSAlertDefaultReturn)
-            {
-                /* User chose to cancel the scan */
-                [self writeToActivityLog: "cannot open physical dvd VLC found but not 32 bit, scan cancelled"];
-                cancelScanDecrypt = 1;
-            }
-            else if (status == NSAlertAlternateReturn)
-            {
-                [self writeToActivityLog: "user overrode 32-bit warning trying to open physical dvd without proper decryption"];
-                cancelScanDecrypt = 0;
-            }
-            else
-            {
-                /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
-                [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/vlc/download-macosx.html"]];
-            }
-            
-        } 
     }
     
     if (cancelScanDecrypt == 0)
@@ -1803,9 +1690,7 @@ static NSString *        ChooseSourceIdentifier             = @"Choose Source It
 {
     hb_list_t  * list;
        hb_title_t * title;
-       int indxpri=0;    // Used to search the longuest title (default in combobox)
-       int longuestpri=0; // Used to search the longuest title (default in combobox)
-    
+       int feature_title=0; // Used to store the main feature title
 
         list = hb_get_titles( fHandle );
         
@@ -1879,11 +1764,10 @@ static NSString *        ChooseSourceIdentifier             = @"Choose Source It
                                                      @"%@/Desktop/%@.mp4", NSHomeDirectory(),[browsedSourceDisplayName stringByDeletingPathExtension]]];
                 }
                 
-                
-                if (longuestpri < title->hours*60*60 + title->minutes *60 + title->seconds)
+                /* See if this is the main feature according to libhb */
+                if (title->index == title->job->feature)
                 {
-                    longuestpri=title->hours*60*60 + title->minutes *60 + title->seconds;
-                    indxpri=i;
+                    feature_title = i;
                 }
                 
                 [fSrcTitlePopUp addItemWithTitle: [NSString
@@ -1899,8 +1783,8 @@ static NSString *        ChooseSourceIdentifier             = @"Choose Source It
             }
             else
             {
-                /* if not then select the longest title (dvd) */
-                [fSrcTitlePopUp selectItemAtIndex: indxpri];
+                /* if not then select the main feature title */
+                [fSrcTitlePopUp selectItemAtIndex: feature_title];
             }
             [self titlePopUpChanged:nil];
             
@@ -2047,7 +1931,7 @@ static NSString *        ChooseSourceIdentifier             = @"Choose Source It
      * by one to keep in sync with the queue array
      */
     currentQueueEncodeIndex--;
-    [self writeToActivityLog: "removeQueueFileItem: Removing a cancelled/finished encode, decrement currentQueueEncodeIndex to %d", currentQueueEncodeIndex];
+
     }
     [QueueFileArray removeObjectAtIndex:queueItemToRemove];
     [self saveQueueFileItem];
@@ -2244,7 +2128,7 @@ fWorkingCount = 0;
     
     
     /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */
-        int title_duration_seconds = (title->hours * 3600) + (title->minutes * 60) + (title->seconds);
+    int title_duration_seconds = (title->hours * 3600) + (title->minutes * 60) + (title->seconds);
     [queueFileJob setObject:[NSNumber numberWithInt:title_duration_seconds] forKey:@"SourceTotalSeconds"];
     
     [queueFileJob setObject:[fDstFile2Field stringValue] forKey:@"DestinationPath"];
@@ -2443,7 +2327,6 @@ fWorkingCount = 0;
     /*Audio*/
     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
     {
-        //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio1Encoder"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1CodecPopUp selectedItem] tag]] forKey:@"JobAudio1Encoder"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1MixPopUp selectedItem] tag]] forKey:@"JobAudio1Mixdown"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1RatePopUp selectedItem] tag]] forKey:@"JobAudio1Samplerate"];
@@ -2451,7 +2334,6 @@ fWorkingCount = 0;
      }
     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
     {
-        //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio2Encoder"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2CodecPopUp selectedItem] tag]] forKey:@"JobAudio2Encoder"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2MixPopUp selectedItem] tag]] forKey:@"JobAudio2Mixdown"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2RatePopUp selectedItem] tag]] forKey:@"JobAudio2Samplerate"];
@@ -2459,7 +2341,6 @@ fWorkingCount = 0;
     }
     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
     {
-        //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio3Encoder"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3CodecPopUp selectedItem] tag]] forKey:@"JobAudio3Encoder"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3MixPopUp selectedItem] tag]] forKey:@"JobAudio3Mixdown"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3RatePopUp selectedItem] tag]] forKey:@"JobAudio3Samplerate"];
@@ -2467,14 +2348,12 @@ fWorkingCount = 0;
     }
     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
     {
-        //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio4Encoder"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4CodecPopUp selectedItem] tag]] forKey:@"JobAudio4Encoder"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4MixPopUp selectedItem] tag]] forKey:@"JobAudio4Mixdown"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4RatePopUp selectedItem] tag]] forKey:@"JobAudio4Samplerate"];
         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4BitratePopUp selectedItem] tag]] forKey:@"JobAudio4Bitrate"];
     }
 
     /* we need to auto relase the queueFileJob and return it */
     [queueFileJob autorelease];
     return queueFileJob;
@@ -2525,21 +2404,17 @@ fWorkingCount = 0;
        
     /* We save all of the Queue data here */
     [self saveQueueFileItem];
-       /* We Reload the New Table data for presets */
-    //[fPresetsOutlineView reloadData];
 
     /* Since we have now marked a queue item as done
      * we can go ahead and increment currentQueueEncodeIndex 
      * so that if there is anything left in the queue we can
      * go ahead and move to the next item if we want to */
     currentQueueEncodeIndex++ ;
-    [self writeToActivityLog: "incrementQueueItemDone currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex];
     int queueItems = [QueueFileArray count];
     /* If we still have more items in our queue, lets go to the next one */
     if (currentQueueEncodeIndex < queueItems)
     {
-    [self writeToActivityLog: "incrementQueueItemDone currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex];
-    [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
+        [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
     }
     else
     {
@@ -2553,13 +2428,11 @@ fWorkingCount = 0;
    /* Tell HB to output a new activity log file for this encode */
     [outputPanel startEncodeLog:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"]];
     
-    
-     /* use a bool to determine whether or not we can decrypt using vlc */
+    /* use a bool to determine whether or not we can decrypt using vlc */
     BOOL cancelScanDecrypt = 0;
     /* set the bool so that showNewScan knows to apply the appropriate queue
-    * settings as this is a queue rescan
-    */
-    //applyQueueToScan = YES;
+     * settings as this is a queue rescan
+     */
     NSString *path = scanPath;
     HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
     
@@ -2571,10 +2444,9 @@ fWorkingCount = 0;
         [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
 
         /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
-        NSString *vlcPath = @"/Applications/VLC.app";
-        NSFileManager * fileManager = [NSFileManager defaultManager];
-           if ([fileManager fileExistsAtPath:vlcPath] == 0) 
-           {
+        void *dvdcss = dlopen("libdvdcss.2.dylib", RTLD_LAZY);
+        if (dvdcss == NULL) 
+        {
             /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */
             cancelScanDecrypt = 1;
             [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
@@ -2603,6 +2475,7 @@ fWorkingCount = 0;
         else
         {
             /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
+            dlclose(dvdcss);
             [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
         }
     }
@@ -2622,8 +2495,7 @@ fWorkingCount = 0;
             [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
         }
         
-        [self writeToActivityLog: "performNewQueueScan currentQueueEncodeIndex is: %d", currentQueueEncodeIndex];
-        /* We use our advance pref to determine how many previews to scan */
+         /* We use our advance pref to determine how many previews to scan */
         int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
         hb_scan( fQueueEncodeLibhb, [path UTF8String], scanTitleNum, hb_num_previews, 0 );
     }
@@ -2645,7 +2517,6 @@ fWorkingCount = 0;
     [self writeToActivityLog: "Preset: %s", [[queueToApply objectForKey:@"PresetName"] UTF8String]];
     [self writeToActivityLog: "processNewQueueEncode number of passes expected is: %d", ([[queueToApply objectForKey:@"VideoTwoPass"] intValue] + 1)];
     job->file = [[queueToApply objectForKey:@"DestinationPath"] UTF8String];
-    //[self writeToActivityLog: "processNewQueueEncode sending to prepareJob"];
     [self prepareJob];
     
     /*
@@ -2720,7 +2591,6 @@ fWorkingCount = 0;
         free(subtitle);
     }
     
-    
     /* We should be all setup so let 'er rip */   
     [self doRip];
 }
@@ -2817,7 +2687,7 @@ fWorkingCount = 0;
     }
     
     [self videoMatrixChanged:nil];
-    [self writeToActivityLog: "applyQueueSettingsToMainWindow: video matrix changed"];    
+        
     /* Video framerate */
     /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
      detected framerate in the fVidRatePopUp so we use index 0*/
@@ -2921,7 +2791,6 @@ fWorkingCount = 0;
         [self audioTrackPopUpChanged: fAudLang4PopUp];
     }
     
-    [self writeToActivityLog: "applyQueueSettingsToMainWindow: audio set up"];
     /*Subtitles*/
     /* Crashy crashy right now, working on it */
     [fSubtitlesDelegate setNewSubtitles:[queueToApply objectForKey:@"SubtitleList"]];
@@ -2981,9 +2850,6 @@ fWorkingCount = 0;
     job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"]  intValue];
     job->modulus = [[queueToApply objectForKey:@"PictureModulus"]  intValue];
     
-    [self writeToActivityLog: "applyQueueSettingsToMainWindow: picture sizing set up"];
-    
-    
     /* Filters */
     
     /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key.
@@ -3081,10 +2947,9 @@ fWorkingCount = 0;
     [fPictureController SetTitle:fTitle];
     [self calculatePictureSizing:nil];
     
-    [self writeToActivityLog: "applyQueueSettingsToMainWindow: picture filters set up"];
     /* somehow we need to figure out a way to tie the queue item to a preset if it used one */
     //[queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"];
-    //    [queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"];
+    //[queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"];
     if ([queueToApply objectForKey:@"PresetIndexNum"]) // This item used a preset so insert that info
        {
                /* Deselect the currently selected Preset if there is one*/
@@ -3243,23 +3108,18 @@ bool one_burned = FALSE;
                 [self writeToActivityLog: "Foreign Language Search: %d", 1];
                 
                 job->indepth_scan = 1;
-                if (burned == 1 || job->mux != HB_MUX_MP4)
+                
+                if (burned != 1)
                 {
-                    if (burned != 1 && job->mux == HB_MUX_MKV)
-                    {
-                        job->select_subtitle_config.dest = PASSTHRUSUB;
-                    }
-                    else
-                    {
-                        job->select_subtitle_config.dest = RENDERSUB;
-                    }
-                    
-                    job->select_subtitle_config.force = force;
-                    job->select_subtitle_config.default_track = def;
-                    
+                    job->select_subtitle_config.dest = PASSTHRUSUB;
+                }
+                else
+                {
+                    job->select_subtitle_config.dest = RENDERSUB;
                 }
                 
-                
+                job->select_subtitle_config.force = force;
+                job->select_subtitle_config.default_track = def;
             }
             else
             {
@@ -3307,17 +3167,10 @@ bool one_burned = FALSE;
                 {
                     hb_subtitle_config_t sub_config = subt->config;
                     
-                    if (!burned && job->mux == HB_MUX_MKV && 
-                        subt->format == PICTURESUB)
+                    if ( !burned && subt->format == PICTURESUB )
                     {
                         sub_config.dest = PASSTHRUSUB;
                     }
-                    else if (!burned && job->mux == HB_MUX_MP4 && 
-                             subt->format == PICTURESUB)
-                    {
-                        // Skip any non-burned vobsubs when output is mp4
-                        continue;
-                    }
                     else if ( burned && subt->format == PICTURESUB )
                     {
                         // Only allow one subtitle to be burned into the video
@@ -3444,6 +3297,7 @@ bool one_burned = FALSE;
     */
     
        /* Detelecine */
+    hb_filter_detelecine.settings = NULL;
     if ([fPictureController detelecine] == 1)
     {
         /* use a custom detelecine string */
@@ -3462,6 +3316,7 @@ bool one_burned = FALSE;
     {
         /* Decomb */
         /* we add the custom string if present */
+        hb_filter_decomb.settings = NULL;
         if ([fPictureController decomb] == 1)
         {
             /* use a custom decomb string */
@@ -3572,51 +3427,28 @@ bool one_burned = FALSE;
         /* we are pts based start / stop */
         [self writeToActivityLog: "Start / Stop set to seconds ..."];
         
-        /* Point A to Point B. Since we cannot get frame accurate start times, attempt to glean a semi-accurate start time based on a percentage of the
-         * scanned title time as per live preview, while in some cases inaccurate its the best I can do with what I have barring a pre-scan index afaik.
-         */
-        /* Attempt to bastardize the live preview code to get a roughly 1 second accurate point a to point b encode ... */
+        /* Point A to Point B. Time to time in seconds.*/
         /* get the start seconds from the start seconds field */
         int start_seconds = [[queueToApply objectForKey:@"StartSeconds"] intValue];
-        //job->start_at_preview = start_seconds;
-        /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */
-        //job->seek_points = [[queueToApply objectForKey:@"SourceTotalSeconds"] intValue];
         job->pts_to_start = start_seconds * 90000LL;
         /* Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds */
         int stop_seconds = [[queueToApply objectForKey:@"StopSeconds"] intValue];
         job->pts_to_stop = stop_seconds * 90000LL;
-
-        /* A bunch of verbose activity log messages to check on what should be expected */
-        [self writeToActivityLog: "point a to b should start at: %d seconds", start_seconds];
-        [self writeToActivityLog: "point a to b should start at (hh:mm:ss): %d:%d:%d", start_seconds / 3600, ( start_seconds / 60 ) % 60,start_seconds % 60];
-        [self writeToActivityLog: "point a to b duration: %d seconds", stop_seconds];
-        [self writeToActivityLog: "point a to b duration (hh:mm:ss): %d:%d:%d", stop_seconds / 3600, ( stop_seconds / 60 ) % 60,stop_seconds % 60];
-        [self writeToActivityLog: "point a to b should end at: %d seconds", start_seconds + stop_seconds];
-        [self writeToActivityLog: "point a to b should end at (hh:mm:ss): %d:%d:%d", (start_seconds + stop_seconds) / 3600, ( (start_seconds + stop_seconds) / 60 ) % 60,(start_seconds + stop_seconds) % 60];
+        
     }
     else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 2)
     {
         /* we are frame based start / stop */
         [self writeToActivityLog: "Start / Stop set to frames ..."];
         
-        /* Point A to Point B. Since we cannot get frame accurate start times, attempt to glean a semi-accurate start time based on a percentage of the
-         * scanned title time as per live preview, while in some cases inaccurate its the best I can do with what I have barring a pre-scan index afaik.
-         */
-        /* Attempt to bastardize the live preview code to get a roughly 1 second accurate point a to point b encode ... */
-        /* get the start seconds from the start seconds field */
+        /* Point A to Point B. Frame to frame */
+        /* get the start frame from the start frame field */
         int start_frame = [[queueToApply objectForKey:@"StartFrame"] intValue];
-        //job->start_at_preview = start_seconds;
-        /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */
-        //job->seek_points = [[queueToApply objectForKey:@"SourceTotalSeconds"] intValue];
         job->frame_to_start = start_frame;
-        /* Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds */
+        /* get the frame to stop on from the end frame field */
         int stop_frame = [[queueToApply objectForKey:@"StopFrame"] intValue];
         job->frame_to_stop = stop_frame;
-
-        /* A bunch of verbose activity log messages to check on what should be expected */
-        [self writeToActivityLog: "point a to b should start at frame %d", start_frame];
-        [self writeToActivityLog: "point a to b duration: %d frames", stop_frame];
-        [self writeToActivityLog: "point a to b should end at frame %d", start_frame + stop_frame];
+        
     }
 
        
@@ -3839,22 +3671,18 @@ bool one_burned = FALSE;
                 [self writeToActivityLog: "Foreign Language Search: %d", 1];
                 
                 job->indepth_scan = 1;
-                if (burned == 1 || job->mux != HB_MUX_MP4)
+                
+                if (burned != 1)
                 {
-                    if (burned != 1 && job->mux == HB_MUX_MKV)
-                    {
-                        job->select_subtitle_config.dest = PASSTHRUSUB;
-                    }
-                    else
-                    {
-                        job->select_subtitle_config.dest = RENDERSUB;
-                    }
-                    
-                    job->select_subtitle_config.force = force;
-                    job->select_subtitle_config.default_track = def;
+                    job->select_subtitle_config.dest = PASSTHRUSUB;
+                }
+                else
+                {
+                    job->select_subtitle_config.dest = RENDERSUB;
                 }
                 
-                
+                job->select_subtitle_config.force = force;
+                job->select_subtitle_config.default_track = def;
             }
             else
             {
@@ -3903,17 +3731,10 @@ bool one_burned = FALSE;
                 {
                     hb_subtitle_config_t sub_config = subt->config;
                     
-                    if (!burned && job->mux == HB_MUX_MKV && 
-                        subt->format == PICTURESUB)
+                    if ( !burned && subt->format == PICTURESUB )
                     {
                         sub_config.dest = PASSTHRUSUB;
                     }
-                    else if (!burned && job->mux == HB_MUX_MP4 && 
-                             subt->format == PICTURESUB)
-                    {
-                        // Skip any non-burned vobsubs when output is mp4
-                        continue;
-                    }
                     else if ( burned && subt->format == PICTURESUB )
                     {
                         // Only allow one subtitle to be burned into the video
@@ -4020,6 +3841,7 @@ bool one_burned = FALSE;
      * The order of the filters is critical
      */
     /* Detelecine */
+    hb_filter_detelecine.settings = NULL;
     if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
     {
         /* use a custom detelecine string */
@@ -4036,6 +3858,7 @@ bool one_burned = FALSE;
     {
         /* Decomb */
         /* we add the custom string if present */
+        hb_filter_decomb.settings = NULL;
         if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1)
         {
             /* use a custom decomb string */
@@ -5600,6 +5423,9 @@ the user is using "Custom" settings by determining the sender*/
                 // FAAC
                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
                 [menuItem setTag: HB_ACODEC_FAAC];
+                // MP3
+                menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
+                [menuItem setTag: HB_ACODEC_LAME];
                 // AC3 Passthru
                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
                 [menuItem setTag: HB_ACODEC_AC3];
@@ -5750,16 +5576,10 @@ the user is using "Custom" settings by determining the sender*/
         {
 
             /* find out if our selected output audio codec supports mono and / or 6ch */
-            /* we also check for an input codec of AC3 or DCA,
-             as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */
             /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now,
              but this may change in the future, so they are separated for flexibility */
-            int audioCodecsSupportMono =
-                    (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
-                    (acodec != HB_ACODEC_LAME);
-            int audioCodecsSupport6Ch =
-                    (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
-                    (acodec != HB_ACODEC_LAME);
+            int audioCodecsSupportMono = (audio->in.codec && acodec != HB_ACODEC_LAME);
+            int audioCodecsSupport6Ch = (audio->in.codec && acodec != HB_ACODEC_LAME);
             
             /* check for AC-3 passthru */
             if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
@@ -5996,17 +5816,16 @@ the user is using "Custom" settings by determining the sender*/
             {
                 /* FAAC has a minimum of 192 kbps for 6-channel discrete */
                 minbitrate = 192;
-                /* If either mixdown popup includes 6-channel discrete, then allow up to 448 kbps */
-                maxbitrate = 448;
+                /* If either mixdown popup includes 6-channel discrete, then allow up to 768 kbps */
+                maxbitrate = 768;
                 break;
             }
             else
             {
                 /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */
                 minbitrate = 32;
-                /* FAAC won't honour anything more than 160 for stereo, so let's not offer it */
                 /* note: haven't dealt with mono separately here, FAAC will just use the max it can */
-                maxbitrate = 160;
+                maxbitrate = 320;
                 break;
             }
 
@@ -6746,6 +6565,7 @@ return YES;
         {
             
             /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
+            NSPopUpButton * trackLangPreviousPopUp = nil;
             NSPopUpButton * trackLangPopUp = nil;
             NSPopUpButton * mixdownPopUp = nil;
             NSPopUpButton * audiocodecPopUp = nil;
@@ -6772,6 +6592,7 @@ return YES;
                 }
                 if( i == 2 )
                 {
+                    trackLangPreviousPopUp = fAudLang1PopUp;
                     trackLangPopUp = fAudLang2PopUp;
                     mixdownPopUp = fAudTrack2MixPopUp;
                     audiocodecPopUp = fAudTrack2CodecPopUp;
@@ -6781,6 +6602,7 @@ return YES;
                 }
                 if( i == 3 )
                 {
+                    trackLangPreviousPopUp = fAudLang2PopUp;
                     trackLangPopUp = fAudLang3PopUp;
                     mixdownPopUp = fAudTrack3MixPopUp;
                     audiocodecPopUp = fAudTrack3CodecPopUp;
@@ -6790,6 +6612,7 @@ return YES;
                 }
                 if( i == 4 )
                 {
+                    trackLangPreviousPopUp = fAudLang3PopUp;
                     trackLangPopUp = fAudLang4PopUp;
                     mixdownPopUp = fAudTrack4MixPopUp;
                     audiocodecPopUp = fAudTrack4CodecPopUp;
@@ -6801,7 +6624,16 @@ return YES;
                 
                 if ([trackLangPopUp indexOfSelectedItem] == 0)
                 {
-                    [trackLangPopUp selectItemAtIndex: 1];
+                    if (i ==1)
+                    {
+                        [trackLangPopUp selectItemAtIndex: 1];
+                    }
+                    else
+                    {
+                        /* if we are greater than track 1, select
+                         * the same track as the previous track */
+                        [trackLangPopUp selectItemAtIndex: [trackLangPreviousPopUp indexOfSelectedItem]];
+                    }
                 }
                 [self audioTrackPopUpChanged: trackLangPopUp];
                 [audiocodecPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioEncoder"]];
@@ -8027,6 +7859,154 @@ return YES;
     
 }
 
+#pragma mark -
+#pragma mark Chapter Files Import / Export
+
+- (IBAction) browseForChapterFile: (id) sender
+{
+       /* Open a panel to let the user choose the file */
+       NSOpenPanel * panel = [NSOpenPanel openPanel];
+       /* We get the current file name and path from the destination field here */
+       [panel beginSheetForDirectory: [NSString stringWithFormat:@"%@/",
+                                    [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"]]
+                             file: NULL
+                            types: [NSArray arrayWithObjects:@"csv",nil]
+                   modalForWindow: fWindow modalDelegate: self
+                   didEndSelector: @selector( browseForChapterFileDone:returnCode:contextInfo: )
+                      contextInfo: NULL];
+}
+
+- (void) browseForChapterFileDone: (NSOpenPanel *) sheet
+    returnCode: (int) returnCode contextInfo: (void *) contextInfo
+{
+    NSArray *chaptersArray; /* temp array for chapters */
+       NSMutableArray *chaptersMutableArray; /* temp array for chapters */
+    NSString *chapterName;     /* temp string from file */
+    int chapters, i;
+    
+    if( returnCode == NSOKButton )  /* if they click OK */
+    {  
+        chapterName = [[NSString alloc] initWithContentsOfFile:[sheet filename] encoding:NSUTF8StringEncoding error:NULL];
+        chaptersArray = [chapterName componentsSeparatedByString:@"\n"];
+        chaptersMutableArray= [chaptersArray mutableCopy];
+               chapters = [fChapterTitlesDelegate numberOfRowsInTableView:fChapterTable];
+        if ([chaptersMutableArray count] > 0)
+        { 
+        /* if last item is empty remove it */
+            if ([[chaptersMutableArray objectAtIndex:[chaptersArray count]-1] length] == 0)
+            {
+                [chaptersMutableArray removeLastObject];
+            }
+        }
+        /* if chapters in table is not equal to array count */
+        if ((unsigned int) chapters != [chaptersMutableArray count])
+        {
+            [sheet close];
+            [[NSAlert alertWithMessageText:NSLocalizedString(@"Unable to load chapter file", @"Unable to load chapter file")
+                             defaultButton:NSLocalizedString(@"OK", @"OK")
+                           alternateButton:NULL 
+                               otherButton:NULL
+                 informativeTextWithFormat:NSLocalizedString(@"%d chapters expected, %d chapters found in %@", @"%d chapters expected, %d chapters found in %@"), 
+              chapters, [chaptersMutableArray count], [[sheet filename] lastPathComponent]] runModal];
+            return;
+        }
+               /* otherwise, go ahead and populate table with array */
+               for (i=0; i<chapters; i++)
+        {
+         
+            if([[chaptersMutableArray objectAtIndex:i] length] > 5)
+            { 
+                /* avoid a segfault */
+                /* Get the Range.location of the first comma in the line and then put everything after that into chapterTitle */
+                NSRange firstCommaRange = [[chaptersMutableArray objectAtIndex:i] rangeOfString:@","];
+                NSString *chapterTitle = [[chaptersMutableArray objectAtIndex:i] substringFromIndex:firstCommaRange.location + 1];
+                /* Since we store our chapterTitle commas as "\," for the cli, we now need to remove the escaping "\" from the title */
+                chapterTitle = [chapterTitle stringByReplacingOccurrencesOfString:@"\\," withString:@","];
+                [fChapterTitlesDelegate tableView:fChapterTable 
+                                   setObjectValue:chapterTitle
+                                   forTableColumn:fChapterTableNameColumn
+                                              row:i];
+            }
+            else 
+            {
+                [sheet close];
+                [[NSAlert alertWithMessageText:NSLocalizedString(@"Unable to load chapter file", @"Unable to load chapter file")
+                                 defaultButton:NSLocalizedString(@"OK", @"OK")
+                               alternateButton:NULL 
+                                   otherButton:NULL
+                     informativeTextWithFormat:NSLocalizedString(@"%@ was not formatted as expected.", @"%@ was not formatted as expected."), [[sheet filename] lastPathComponent]] runModal];   
+                [fChapterTable reloadData];
+                return;
+            }
+        }
+        [fChapterTable reloadData];
+    }
+}
+
+- (IBAction) browseForChapterFileSave: (id) sender
+{
+    NSSavePanel *panel = [NSSavePanel savePanel];
+    /* Open a panel to let the user save to a file */
+    [panel setAllowedFileTypes:[NSArray arrayWithObjects:@"csv",nil]];
+    [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] 
+                             file: [[[[fDstFile2Field stringValue] lastPathComponent] stringByDeletingPathExtension] 
+                                     stringByAppendingString:@"-chapters.csv"]
+                   modalForWindow: fWindow 
+                    modalDelegate: self
+                   didEndSelector: @selector( browseForChapterFileSaveDone:returnCode:contextInfo: )
+                      contextInfo: NULL];
+}
+
+- (void) browseForChapterFileSaveDone: (NSSavePanel *) sheet
+    returnCode: (int) returnCode contextInfo: (void *) contextInfo
+{
+    NSString *chapterName;      /* pointer for string for later file-writing */
+    NSString *chapterTitle;
+    NSError *saveError = [[NSError alloc] init];
+    int chapters, i;    /* ints for the number of chapters in the table and the loop */
+    
+    if( returnCode == NSOKButton )   /* if they clicked OK */
+    {  
+        chapters = [fChapterTitlesDelegate numberOfRowsInTableView:fChapterTable];
+        chapterName = [NSString string];
+        for (i=0; i<chapters; i++)
+        {
+            /* put each chapter title from the table into the array */
+            if (i<9)
+            { /* if i is from 0 to 8 (chapters 1 to 9) add two leading zeros */
+                chapterName = [chapterName stringByAppendingFormat:@"00%d,",i+1];
+            }
+            else if (i<99)
+            { /* if i is from 9 to 98 (chapters 10 to 99) add one leading zero */
+                chapterName = [chapterName stringByAppendingFormat:@"0%d,",i+1];
+            }
+            else if (i<999)
+            { /* in case i is from 99 to 998 (chapters 100 to 999) no leading zeros */
+                chapterName = [chapterName stringByAppendingFormat:@"%d,",i+1];
+            }
+            
+            chapterTitle = [fChapterTitlesDelegate tableView:fChapterTable objectValueForTableColumn:fChapterTableNameColumn row:i];
+            /* escape any commas in the chapter name with "\," */
+            chapterTitle = [chapterTitle stringByReplacingOccurrencesOfString:@"," withString:@"\\,"];
+            chapterName = [chapterName stringByAppendingString:chapterTitle];
+            if (i+1 != chapters)
+            { /* if not the last chapter */
+                chapterName = [chapterName stringByAppendingString:@ "\n"];
+            }
+
+            
+        }
+        /* try to write it to where the user wanted */
+        if (![chapterName writeToFile:[sheet filename] 
+                           atomically:NO 
+                             encoding:NSUTF8StringEncoding 
+                                error:&saveError])
+        {
+            [sheet close];
+            [[NSAlert alertWithError:saveError] runModal];
+        }
+    }
+}
 
 @end