OSDN Git Service

fix ffmpeg multiple audio decode issue
[handbrake-jp/handbrake-jp-git.git] / macosx / HBAdvancedController.m
1 /* HBAdvancedController
2
3     This file is part of the HandBrake source code.
4     Homepage: <http://handbrake.fr/>.
5     It may be used under the terms of the GNU General Public License. */
6     
7 #import "HBAdvancedController.h"
8
9 @implementation HBAdvancedController
10
11 - (id)init
12 {
13     [super init];
14     [self loadMyNibFile];
15     
16     return self;
17 }
18
19 - (void) setView: (NSBox *) box
20 {
21     fOptionsBox = box;
22     [fOptionsBox setContentView:fX264optView];
23 }
24
25 - (BOOL) loadMyNibFile
26 {
27     if(![NSBundle loadNibNamed:@"AdvancedView" owner:self])
28     {
29         NSLog(@"Warning! Could not load myNib file.\n");
30         return NO;
31     }
32     
33     return YES;
34 }
35
36 - (NSString *) optionsString
37 {
38     return [fDisplayX264Options stringValue];
39 }
40
41 - (void) setOptions: (NSString *)string
42 {
43     [fDisplayX264Options setStringValue:string];
44     [self X264AdvancedOptionsSet:nil];
45 }
46
47 - (void) setHidden: (BOOL) hide
48 {
49     if(hide)
50     {
51         [fOptionsBox setContentView:fEmptyView];
52         [fX264optViewTitleLabel setStringValue: @"Only Used With The x264 (H.264) Codec"];
53     }
54     else
55     {
56         [fOptionsBox setContentView:fX264optView];
57         [fX264optViewTitleLabel setStringValue: @""];
58     }
59     return;
60 }
61
62  - (void) enableUI: (bool) b
63 {
64     unsigned i;
65     NSControl * controls[] =
66       { fX264optViewTitleLabel,fDisplayX264Options,fDisplayX264OptionsLabel,fX264optBframesLabel,
67         fX264optBframesPopUp,fX264optRefLabel,fX264optRefPopUp,
68         fX264optNodctdcmtLabel,fX264optNodctdcmtSwitch,fX264optSubmeLabel,fX264optSubmePopUp,
69         fX264optTrellisLabel,fX264optTrellisPopUp, fX264optWeightPLabel, fX264optWeightPSwitch,
70         fX264optMotionEstLabel,fX264optMotionEstPopUp,fX264optMERangeLabel,fX264optMERangePopUp,
71         fX264optBPyramidLabel,fX264optBPyramidPopUp, fX264optAqLabel, fX264optAqSlider,
72         fX264optDirectPredLabel,fX264optDirectPredPopUp,fX264optDeblockLabel,fX264optAnalyseLabel,
73         fX264optAnalysePopUp,fX264opt8x8dctLabel,fX264opt8x8dctSwitch,fX264optCabacLabel,fX264optCabacSwitch,
74         fX264optAlphaDeblockPopUp,fX264optBetaDeblockPopUp, fX264optPsyRDSlider, fX264optPsyRDLabel, fX264optPsyTrellisSlider, fX264optPsyTrellisLabel, fX264optBAdaptPopUp, fX264optBAdaptLabel };
75
76     for( i = 0; i < sizeof( controls ) / sizeof( NSControl * ); i++ )
77     {
78         if( [[controls[i] className] isEqualToString: @"NSTextField"] )
79         {
80             NSTextField * tf = (NSTextField *) controls[i];
81             if( ![tf isBezeled] )
82             {
83                 [tf setTextColor: b ? [NSColor controlTextColor] :
84                     [NSColor disabledControlTextColor]];
85                 continue;
86             }
87         }
88         [controls[i] setEnabled: b];
89
90     }
91     
92     [fX264optView setWantsLayer:YES];
93 }
94
95 - (void)dealloc
96 {
97     [super dealloc];
98 }
99
100 /**
101  * Populates the option widgets
102  */
103 - (IBAction) X264AdvancedOptionsSet: (id) sender
104 {
105     /*Set opt widget values here*/
106     
107     NSString * toolTip = @"";
108     
109     /*B-Frames fX264optBframesPopUp*/
110     int i;
111     [fX264optBframesPopUp removeAllItems];
112     [fX264optBframesPopUp addItemWithTitle:@"Default (3)"];
113     for (i=0; i<17;i++)
114     {
115         [fX264optBframesPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
116     }
117     toolTip =
118         @"Sane values are ~2-5.  This specifies the maximum number of sequential B-frames that the encoder can use.  Large numbers generally won't help significantly unless Adaptive B-frames is set to Optimal.  Cel-animated source material and B-pyramid also significantly increase the usefulness of larger values. Baseline profile, as required for iPods and similar devices, requires B-frames to be set to 0 (off).";
119     [fX264optBframesPopUp setToolTip: toolTip];
120     [fX264optBframesLabel setToolTip: toolTip];
121     
122     /*Reference Frames fX264optRefPopUp*/
123     [fX264optRefPopUp removeAllItems];
124     [fX264optRefPopUp addItemWithTitle:@"Default (3)"];
125     for (i=1; i<17;i++)
126     {
127         [fX264optRefPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
128     }
129     toolTip =
130         @"Sane values are ~1-6.  The more you add, the better the compression, but the slower the encode.  Cel animation tends to benefit from more reference frames a lot more than film content.  Note that many hardware devices have limitations on the number of supported reference frames, so if you're encoding for a handheld or standalone player, don't touch this unless you're absolutely sure you know what you're doing!";
131     [fX264optRefPopUp setToolTip: toolTip];
132     [fX264optRefLabel setToolTip: toolTip];
133
134     /*Weight-P fX264optWeightPSwitch BOOLEAN*/
135     [fX264optWeightPSwitch setState:1];
136     toolTip = 
137         @"Performs extra analysis to decide upon weighting parameters for each frame.  This improves overall compression slightly and improves the quality of fades greatly. Baseline profile, as required for iPods and similar devices, requires weighted P-frame prediction to be disabled.  Note that some devices and players, even those that support Main Profile, may have problems with Weighted P-frame prediction: the Apple TV is completely incompatible with it, for example.";
138     [fX264optWeightPSwitch setToolTip: toolTip];
139     [fX264optWeightPLabel setToolTip: toolTip];
140     
141     /*No Dict Decimate fX264optNodctdcmtSwitch BOOLEAN*/
142     [fX264optNodctdcmtSwitch setState:0];    
143     toolTip =
144         @"x264 normally zeroes out nearly-empty data blocks to save bits to be better used for some other purpose in the video.  However, this can sometimes have slight negative effects on retention of subtle grain and dither.  Don't touch this unless you're having banding issues or other such cases where you are having trouble keeping fine noise.";
145     [fX264optNodctdcmtSwitch setToolTip: toolTip];
146     [fX264optNodctdcmtLabel setToolTip: toolTip];
147     
148     /*Sub Me fX264optSubmePopUp*/
149     [fX264optSubmePopUp removeAllItems];
150     [fX264optSubmePopUp addItemWithTitle:@"Default (7)"];
151     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"0: SAD, no subpel (super fast!)"]];
152     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"1: SAD, qpel"]];
153     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"2: SATD, qpel"]];
154     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"3: SATD, multi-qpel"]];
155     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"4: SATD, qpel on all"]];
156     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"5: SATD, multi-qpel on all"]];
157     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"6: RD in I/P-frames"]];
158     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"7: RD in all frames"]];
159     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"8: RD refine in I/P-frames"]];
160     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"9: RD refine in all frames"]];
161     [fX264optSubmePopUp addItemWithTitle:[NSString stringWithFormat:@"10: QPRD in all frames"]];
162     toolTip =
163         @"This setting controls both subpixel-precision motion estimation and mode decision methods.\n\nSubpixel motion estimation is used for refining motion estimates beyond mere pixel accuracy, improving compression.\n\nMode decision is the method used to choose how to encode each block of the frame: a very important decision.\n\nSAD is the fastest method, followed by SATD, RD, RD refinement, and the slowest, QPRD.\n\n6 or higher is strongly recommended: Psy-RD, a very powerful psy optimization that helps retain detail, requires RD.\n\n10, the most powerful and slowest option, requires trellis=2.";
164     [fX264optSubmePopUp setToolTip: toolTip];
165     [fX264optSubmeLabel setToolTip: toolTip];
166     
167     /*Trellis fX264optTrellisPopUp*/
168     [fX264optTrellisPopUp removeAllItems];
169     [fX264optTrellisPopUp addItemWithTitle:@"Default (Encode only)"];
170     [fX264optTrellisPopUp addItemWithTitle:[NSString stringWithFormat:@"Off"]];
171     [fX264optTrellisPopUp addItemWithTitle:[NSString stringWithFormat:@"Encode only"]];
172     [fX264optTrellisPopUp addItemWithTitle:[NSString stringWithFormat:@"Always"]];
173     [fX264optTrellisPopUp setWantsLayer:YES];
174     toolTip =
175         @"Trellis fine-tunes the rounding of transform coefficients to squeeze out 3-5% more compression at the cost of some speed. \"Always\" uses trellis not only during the main encoding process, but also during analysis, which improves compression even more, albeit at great speed cost. Trellis costs more speed at higher bitrates.";
176     [fX264optTrellisPopUp setToolTip: toolTip];
177     [fX264optTrellisLabel setToolTip: toolTip];
178     
179     /*Motion Estimation fX264optMotionEstPopUp*/
180     [fX264optMotionEstPopUp removeAllItems];
181     [fX264optMotionEstPopUp addItemWithTitle:@"Default (Hexagon)"];
182     [fX264optMotionEstPopUp addItemWithTitle:@"Diamond"];
183     [fX264optMotionEstPopUp addItemWithTitle:@"Hexagon"];
184     [fX264optMotionEstPopUp addItemWithTitle:@"Uneven Multi-Hexagon"];
185     [fX264optMotionEstPopUp addItemWithTitle:@"Exhaustive"];
186     [fX264optMotionEstPopUp addItemWithTitle:@"Transformed Exhaustive"];
187     toolTip =
188         @"Controls the motion estimation method. Motion estimation is how the encoder estimates how each block of pixels in a frame has moved.  A better motion search method improves compression at the cost of speed.\n\nDiamond: performs an extremely fast and simple search using a diamond pattern.\n\nHexagon: performs a somewhat more effective but slightly slower search using a hexagon pattern.\n\nUneven Multi-Hex: performs a very wide search using a variety of patterns, more accurately capturing complex motion.\n\nExhaustive: performs a \"dumb\" search of every pixel in a wide area.  Significantly slower for only a small compression gain.\n\nTransformed Exhaustive: Like exhaustive, but makes even more accurate decisions. Accordingly, somewhat slower, also for only a small improvement.";
189     [fX264optMotionEstPopUp setToolTip: toolTip];
190     [fX264optMotionEstLabel setToolTip: toolTip];
191     
192     /*Motion Estimation range fX264optMERangePopUp*/
193     [fX264optMERangePopUp removeAllItems];
194     [fX264optMERangePopUp addItemWithTitle:@"Default (16)"];
195     for (i=4; i<65;i++)
196     {
197         [fX264optMERangePopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
198     }
199     toolTip =
200         @"This is the distance x264 searches from its best guess at the motion of a block in order to try to find its actual motion.  Doesn't apply to Diamond or Hexagon search options.  The default is fine for most content, but extremely high motion video, especially at HD resolutions, may benefit from higher ranges, albeit at a high speed cost.";
201     [fX264optMERangePopUp setToolTip: toolTip];
202     [fX264optMERangeLabel setToolTip: toolTip];
203     
204     /*B-frame Pyramids fX264optBPyramidPopUp*/
205     [fX264optBPyramidPopUp removeAllItems];
206     [fX264optBPyramidPopUp addItemWithTitle:@"Default (Normal)"];
207     [fX264optBPyramidPopUp addItemWithTitle:@"Off"];
208     [fX264optBPyramidPopUp addItemWithTitle:@"Strict"];
209     [fX264optBPyramidPopUp setWantsLayer:YES];
210     toolTip =
211         @"B-pyramid improves compression by creating a pyramidal structure (hence the name) of B-frames, allowing B-frames to reference each other to improve compression.  Requires Max B-frames greater than 1; optimal adaptive B-frames is strongly recommended for full compression benefit.";
212     [fX264optBPyramidPopUp setToolTip: toolTip];
213     [fX264optBPyramidLabel setToolTip: toolTip];
214     
215     /*Direct B-Frame Prediction Mode fX264optDirectPredPopUp*/
216     [fX264optDirectPredPopUp removeAllItems];
217     [fX264optDirectPredPopUp addItemWithTitle:@"Default (Spatial)"];
218     [fX264optDirectPredPopUp addItemWithTitle:@"None"];
219     [fX264optDirectPredPopUp addItemWithTitle:@"Spatial"];
220     [fX264optDirectPredPopUp addItemWithTitle:@"Temporal"];
221     [fX264optDirectPredPopUp addItemWithTitle:@"Automatic"];
222     [fX264optDirectPredPopUp setWantsLayer:YES];
223     toolTip =
224         @"H.264 allows for two different prediction modes, spatial and temporal, in B-frames.\n\nSpatial, the default, is almost always better, but temporal is sometimes useful too.\n\nx264 can, at the cost of a small amount of speed (and accordingly for a small compression gain), adaptively select which is better for each particular frame.";
225     [fX264optDirectPredPopUp setToolTip: toolTip];
226     [fX264optDirectPredLabel setToolTip: toolTip];
227     
228     /* Adaptive B-Frames Mode fX264optBAdaptPopUp */
229     [fX264optBAdaptPopUp removeAllItems];
230     [fX264optBAdaptPopUp addItemWithTitle:@"Default (Fast)"];
231     [fX264optBAdaptPopUp addItemWithTitle:@"Off"];
232     [fX264optBAdaptPopUp addItemWithTitle:@"Fast"];
233     [fX264optBAdaptPopUp addItemWithTitle:@"Optimal"];
234     [fX264optBAdaptPopUp setWantsLayer:YES];
235     toolTip =
236         @"x264 has a variety of algorithms to decide when to use B-frames and how many to use.\n\nFast mode takes roughly the same amount of time no matter how many B-frames you specify.  However, while fast, its decisions are often suboptimal.\n\nOptimal mode gets slower as the maximum number of B-Frames increases, but makes much more accurate decisions, especially when used with B-pyramid.";
237     [fX264optBAdaptPopUp setToolTip: toolTip];
238     [fX264optBAdaptLabel setToolTip: toolTip];
239     
240     /*Alpha Deblock*/
241     [fX264optAlphaDeblockPopUp removeAllItems];
242     [fX264optAlphaDeblockPopUp addItemWithTitle:@"Default (0)"];
243     for (i=-6; i<7;i++)
244     {
245         [fX264optAlphaDeblockPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
246     }
247     toolTip =
248         @"H.264 has a built-in deblocking filter that smooths out blocking artifacts after decoding each frame.  This not only improves visual quality, but also helps compression significantly. The deblocking filter takes a lot of CPU power, so if you're looking to minimize CPU requirements for video playback, disable it.\n\nThe deblocking filter has two adjustable parameters, \"strength\" and \"threshold\". The former controls how strong (or weak) the deblocker is, while the latter controls how many (or few) edges it applies to. Lower values mean less deblocking, higher values mean more deblocking. The default is 0 (normal strength) for both parameters.";
249     [fX264optAlphaDeblockPopUp setToolTip: toolTip];
250     [fX264optDeblockLabel setToolTip: toolTip];
251
252     /*Beta Deblock*/
253     [fX264optBetaDeblockPopUp removeAllItems];
254     [fX264optBetaDeblockPopUp addItemWithTitle:@"Default (0)"];
255     for (i=-6; i<7;i++)
256     {
257         [fX264optBetaDeblockPopUp addItemWithTitle:[NSString stringWithFormat:@"%d",i]];
258     }
259     [fX264optBetaDeblockPopUp setToolTip: toolTip];
260     [fX264optDeblockLabel setToolTip: toolTip];
261
262     /* Analysis fX264optAnalysePopUp */
263     [fX264optAnalysePopUp removeAllItems];
264     [fX264optAnalysePopUp addItemWithTitle:@"Default (Most)"]; /* 0=default */
265     [fX264optAnalysePopUp addItemWithTitle:[NSString stringWithFormat:@"None"]]; /* 1=none */
266     [fX264optAnalysePopUp addItemWithTitle:[NSString stringWithFormat:@"Some"]]; /* 2=some */
267     [fX264optAnalysePopUp addItemWithTitle:[NSString stringWithFormat:@"All"]]; /* 3=all */
268     toolTip =
269         @"Mode decision picks from a variety of options to make its decision: this option chooses what options those are.  Fewer partitions to check means faster encoding, at the cost of worse decisions, since the best option might have been one that was turned off.";
270     [fX264optAnalysePopUp setToolTip: toolTip];
271     [fX264optAnalyseLabel setToolTip: toolTip];
272
273     /* 8x8 DCT fX264op8x8dctSwitch */
274     [fX264opt8x8dctSwitch setState:1];
275     [fX264opt8x8dctSwitch setWantsLayer:YES];
276     toolTip =
277         @"The 8x8 transform is the single most useful feature of x264 in terms of compression-per-speed.  It improves compression by at least 5% at a very small speed cost and may provide an unusually high visual quality benefit compared to its compression gain.  However, it requires High Profile, which many devices may not support.";
278     [fX264opt8x8dctSwitch setToolTip: toolTip];
279     [fX264opt8x8dctLabel setToolTip: toolTip];
280
281     /* CABAC fX264opCabacSwitch */
282     [fX264optCabacSwitch setState:1];
283     toolTip =
284         @"After the encoder has done its work, it has a bunch of data that needs to be compressed losslessly, similar to ZIP or RAR.  H.264 provides two options for this: CAVLC and CABAC.  CABAC decodes a lot slower but compresses significantly better (10-30%), especially at lower bitrates.  If you're looking to minimize CPU requirements for video playback, disable this option. Baseline profile, as required for iPods and similar devices, requires CABAC to be disabled.";
285     [fX264optCabacSwitch setToolTip: toolTip];
286     [fX264optCabacLabel setToolTip: toolTip];
287
288     /* Adaptive Quantization Strength fX264opAqSlider */
289     [fX264optAqSlider setMinValue:0.0];
290     [fX264optAqSlider setMaxValue:2.0];
291     [fX264optAqSlider setTickMarkPosition:NSTickMarkBelow];
292     [fX264optAqSlider setNumberOfTickMarks:21];
293     [fX264optAqSlider setAllowsTickMarkValuesOnly:YES];
294     [fX264optAqSlider setFloatValue:1.0];
295     toolTip =
296         @"Adaptive quantization controls how the encoder distributes bits across the frame.  Higher values take more bits away from edges and complex areas to improve areas with finer detail.";
297     [fX264optAqSlider setToolTip: toolTip];
298     [fX264optAqLabel setToolTip: toolTip];
299     
300     /* PsyRDO fX264optPsyRDSlider */
301     [fX264optPsyRDSlider setMinValue:0.0];
302     [fX264optPsyRDSlider setMaxValue:2.0];
303     [fX264optPsyRDSlider setTickMarkPosition:NSTickMarkBelow];
304     [fX264optPsyRDSlider setNumberOfTickMarks:21];
305     [fX264optPsyRDSlider setAllowsTickMarkValuesOnly:YES];
306     [fX264optPsyRDSlider setFloatValue:1.0];
307     toolTip =
308         @"Psychovisual rate-distortion optimization takes advantage of the characteristics of human vision to dramatically improve apparent detail and sharpness.  The effect can be made weaker or stronger by adjusting the strength.  Being an RD algorithm, it requires mode decision to be at least \"6\".";
309     [fX264optPsyRDSlider setToolTip: toolTip];
310     [fX264optPsyRDLabel setToolTip: toolTip];
311
312     /* PsyTrellis fX264optPsyRDSlider */
313     [fX264optPsyTrellisSlider setMinValue:0.0];
314     [fX264optPsyTrellisSlider setMaxValue:1.0];
315     [fX264optPsyTrellisSlider setTickMarkPosition:NSTickMarkBelow];
316     [fX264optPsyTrellisSlider setNumberOfTickMarks:21];
317     [fX264optPsyTrellisSlider setAllowsTickMarkValuesOnly:YES];
318     [fX264optPsyTrellisSlider setFloatValue:0.0];
319     toolTip =
320         @"Psychovisual trellis is an experimental algorithm to further improve sharpness and detail retention beyond what Psychovisual RD does.  Recommended values are around 0.2, though higher values may help for very grainy video or lower bitrate encodes.  Not recommended for cel animation and other sharp-edged graphics.";
321     [fX264optPsyTrellisSlider setToolTip: toolTip];
322     [fX264optPsyTrellisLabel setToolTip: toolTip];
323
324     /* Standardize the option string */
325     [self X264AdvancedOptionsStandardizeOptString:nil];
326
327     /* Set Current GUI Settings based on newly standardized string */
328     [self X264AdvancedOptionsSetCurrentSettings:sender];
329
330     /* Fade out options that don't apply */
331     [self X264AdvancedOptionsAnimate: sender];
332 }
333
334 /**
335  * Cleans the option string to use a standard format of option=value
336  */
337 - (IBAction) X264AdvancedOptionsStandardizeOptString: (id) sender
338 {
339     /* Set widgets depending on the opt string in field */
340     NSString * thisOpt; // The separated option such as "bframes=3"
341     NSString * optName = @""; // The option name such as "bframes"
342     NSString * optValue = @"";// The option value such as "3"
343     NSString * changedOptString = @"";
344     NSArray *currentOptsArray;
345     
346     /*First, we get an opt string to process */
347     NSString *currentOptString = [fDisplayX264Options stringValue];
348     
349     /* Verify there is an opt string to process by making sure an
350        option is getting its value set. If so, start to process it. */
351     NSRange currentOptRange = [currentOptString rangeOfString:@"="];
352     if (currentOptRange.location != NSNotFound)
353     {
354         /*Put individual options into an array based on the ":" separator for processing, result is "<opt>=<value>"*/
355         currentOptsArray = [currentOptString componentsSeparatedByString:@":"];
356         
357         /*iterate through the array and get <opts> and <values*/
358         int loopcounter;
359         int currentOptsArrayCount = [currentOptsArray count];
360         for (loopcounter = 0; loopcounter < currentOptsArrayCount; loopcounter++)
361         {
362             thisOpt = [currentOptsArray objectAtIndex:loopcounter];
363             
364             NSRange splitOptRange = [thisOpt rangeOfString:@"="];
365             if (splitOptRange.location != NSNotFound)
366             {
367                 optName = [thisOpt substringToIndex:splitOptRange.location];
368                 optValue = [thisOpt substringFromIndex:splitOptRange.location + 1];
369                 
370                 /* Standardize the names here depending on whats in the string */
371                 optName = [self X264AdvancedOptionsStandardizeOptNames:optName];
372                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,optValue];    
373             }
374             else // No value given so we use a default of "1"
375             {
376                 optName = thisOpt;
377
378                 /* Standardize the names here depending on whats in the string */
379                 optName = [self X264AdvancedOptionsStandardizeOptNames:optName];
380                 thisOpt = [NSString stringWithFormat:@"%@=%d",optName,1];
381             }
382             
383             /* Construct New String for opts here.*/
384             if ([thisOpt isEqualToString:@""])
385             {
386                 /* Blank option, just add it to the string. (Why?) */
387                 changedOptString = [NSString stringWithFormat:@"%@%@",changedOptString,thisOpt];
388             }
389             else
390             {
391                 if ([changedOptString isEqualToString:@""])
392                 {
393                     /* Blank string, output the current option. */
394                     changedOptString = [NSString stringWithFormat:@"%@",thisOpt];
395                 }
396                 else
397                 {
398                     /* Option exists and string exists, so append the option
399                        to the string with a semi-colon inbetween them.       */
400                     changedOptString = [NSString stringWithFormat:@"%@:%@",changedOptString,thisOpt];
401                 }
402             }
403         }
404     }
405     
406     /* Change the option string to reflect the new standardized option string */
407     [fDisplayX264Options setStringValue:changedOptString];
408 }
409
410 /**
411  * Cleans the option string to use a standard set of option names, by conflating synonyms.
412  */
413 - (NSString *) X264AdvancedOptionsStandardizeOptNames:(NSString *) cleanOptNameString
414 {
415     /* Reference Frames */
416     if ([cleanOptNameString isEqualToString:@"ref"] || [cleanOptNameString isEqualToString:@"frameref"])
417     {
418         cleanOptNameString = @"ref";
419     }
420     
421     /*No Dict Decimate*/
422     if ([cleanOptNameString isEqualToString:@"no-dct-decimate"] || [cleanOptNameString isEqualToString:@"no_dct_decimate"] || [cleanOptNameString isEqualToString:@"nodct_decimate"])
423     {
424         cleanOptNameString = @"no-dct-decimate";
425     }
426     
427     /*Subme*/
428     if ([cleanOptNameString isEqualToString:@"subme"])
429     {
430         cleanOptNameString = @"subq";
431     }
432     
433     /*ME Range*/
434     if ([cleanOptNameString isEqualToString:@"me-range"] || [cleanOptNameString isEqualToString:@"me_range"])
435         cleanOptNameString = @"merange";
436     
437     /*B Pyramid*/
438     if ([cleanOptNameString isEqualToString:@"b_pyramid"])
439     {
440         cleanOptNameString = @"b-pyramid";
441     }
442     
443     /*Direct Prediction*/
444     if ([cleanOptNameString isEqualToString:@"direct-pred"] || [cleanOptNameString isEqualToString:@"direct_pred"])
445     {
446         cleanOptNameString = @"direct";
447     }
448     
449     /*Deblocking*/
450     if ([cleanOptNameString isEqualToString:@"filter"])
451     {
452         cleanOptNameString = @"deblock";
453     }
454     
455     /*Analysis*/
456     if ([cleanOptNameString isEqualToString:@"partitions"])
457     {
458         cleanOptNameString = @"analyse";
459     }
460     
461     return cleanOptNameString;    
462 }
463
464 /**
465  * Fades options in and out depending on whether they're available..
466  */
467 - (IBAction) X264AdvancedOptionsAnimate: (id) sender
468 {
469     /* Lots of situations to cover.
470        - B-frames (when 0 turn of b-frame specific stuff, when < 2 disable b-pyramid)
471        - CABAC (when 0 turn off trellis and psy-trel)
472        - subme (if under 6, turn off psy-rd and psy-trel)
473        - trellis (if 0, turn off psy-trel)
474     */
475     
476     if( sender == fX264optBframesPopUp || sender == nil || sender == fDisplayX264Options )
477     {
478         if ( [fX264optBframesPopUp indexOfSelectedItem ] == 1 )
479         {
480             /* If the b-frame widget is at 1, the user has chosen
481                not to use b-frames at all. So disable the options
482                that can only be used when b-frames are enabled.        */
483             
484             if( [fX264optBPyramidPopUp isHidden] == false )
485             {
486                 [[fX264optBPyramidPopUp animator] setHidden:YES];
487                 [[fX264optBPyramidLabel animator] setHidden:YES];
488                 if ( [fX264optBPyramidPopUp indexOfSelectedItem] > 0 )
489                 {
490                     [fX264optBPyramidPopUp selectItemAtIndex: 0];
491                     [[fX264optBPyramidPopUp cell] performClick:self];
492                 }
493             }
494
495             if( [fX264optDirectPredPopUp isHidden] == false )
496             {
497                 [[fX264optDirectPredPopUp animator] setHidden:YES];
498                 [[fX264optDirectPredLabel animator] setHidden:YES];
499                 if ( [fX264optDirectPredPopUp indexOfSelectedItem] > 0 )
500                 {
501                     [fX264optDirectPredPopUp selectItemAtIndex: 0];
502                     [[fX264optDirectPredPopUp cell] performClick:self];
503                 }
504             }
505
506             if( [fX264optBAdaptPopUp isHidden] == false )
507             {
508                 [[fX264optBAdaptPopUp animator] setHidden:YES];
509                 [[fX264optBAdaptLabel animator] setHidden:YES];
510                 if ( [fX264optBAdaptPopUp indexOfSelectedItem] > 0 )
511                 {
512                     [fX264optBAdaptPopUp selectItemAtIndex: 0];
513                     [[fX264optBAdaptPopUp cell] performClick:self];
514                 }
515             }
516         }
517         else if ( [fX264optBframesPopUp indexOfSelectedItem ] == 2)
518         {
519             /* Only 1 b-frame? Disable b-pyramid. */
520             if( [fX264optBPyramidPopUp isHidden] == false )
521             {
522                 [[fX264optBPyramidPopUp animator] setHidden:YES];
523                 [[fX264optBPyramidLabel animator] setHidden:YES];
524                 if ( [fX264optBPyramidPopUp indexOfSelectedItem] > 0 )
525                 {
526                     [fX264optBPyramidPopUp selectItemAtIndex: 0];
527                     [[fX264optBPyramidPopUp cell] performClick:self];
528                 }
529             }
530
531             if( [fX264optDirectPredPopUp isHidden] == true )
532             {
533                 [[fX264optDirectPredPopUp animator] setHidden:NO];
534                 [[fX264optDirectPredLabel animator] setHidden:NO];
535             }
536             
537             if( [fX264optBAdaptPopUp isHidden] == true )
538             {
539                 [[fX264optBAdaptPopUp animator] setHidden:NO];
540                 [[fX264optBAdaptLabel animator] setHidden:NO];
541             }
542         }
543         else
544         {
545             if( [fX264optBPyramidPopUp isHidden] == true )
546             {
547                 [[fX264optBPyramidPopUp animator] setHidden:NO];
548                 [[fX264optBPyramidLabel animator] setHidden:NO];
549             }
550
551             if( [fX264optDirectPredPopUp isHidden] == true )
552             {
553                 [[fX264optDirectPredPopUp animator] setHidden:NO];
554                 [[fX264optDirectPredLabel animator] setHidden:NO];
555             }
556             
557             if( [fX264optBAdaptPopUp isHidden] == true )
558             {
559                 [[fX264optBAdaptPopUp animator] setHidden:NO];
560                 [[fX264optBAdaptLabel animator] setHidden:NO];
561             }
562         }
563     }
564     
565     if( sender == fX264optMotionEstPopUp || sender == nil || sender == fDisplayX264Options )
566     {
567         if ( [fX264optMotionEstPopUp indexOfSelectedItem] < 3 )
568         {
569             /* ME-range can only be above 16 if me >= umh
570               and changing it to < 16 is idiotic so hide it . */
571             if( [fX264optMERangePopUp isHidden] == false )
572             {
573                 [[fX264optMERangePopUp animator] setHidden:YES];
574                 [[fX264optMERangeLabel animator] setHidden:YES];
575                 if ( [fX264optMERangePopUp indexOfSelectedItem] > 0 )
576                 {
577                     [fX264optMERangePopUp selectItemAtIndex:0];
578                     [[fX264optMERangePopUp cell] performClick:self];
579                 }
580             }
581         }
582         else
583         {
584             if( [fX264optMERangePopUp isHidden] == true )
585             {
586                 [[fX264optMERangePopUp animator] setHidden:NO];
587                 [[fX264optMERangeLabel animator] setHidden:NO];
588             }
589         }
590     }
591     
592     if( sender == fX264optSubmePopUp || sender == nil || sender == fDisplayX264Options )
593     {
594         if( [fX264optSubmePopUp indexOfSelectedItem] != 0 && [fX264optSubmePopUp indexOfSelectedItem] < 7 )
595         {
596             /* No Psy-RDO or Psy=trel if subme < 6. */
597             if( [fX264optPsyRDSlider isHidden] == false )
598             {
599                 [[fX264optPsyRDSlider animator] setHidden:YES];
600                 [[fX264optPsyRDLabel animator] setHidden:YES];
601                 if ( [fX264optPsyRDSlider floatValue] < 1.0 )
602                 {
603                     [fX264optPsyRDSlider setFloatValue:1.0];
604                     [[fX264optPsyRDSlider cell] performClick:self];            
605                 }
606             }
607
608             if( [fX264optPsyTrellisSlider isHidden] == false)
609             {
610                 [[fX264optPsyTrellisSlider animator] setHidden:YES];
611                 [[fX264optPsyTrellisLabel animator] setHidden:YES];
612                 if ( [fX264optPsyTrellisSlider floatValue] > 0.0 )
613                 {
614                     [fX264optPsyTrellisSlider setFloatValue:0.0];
615                     [[fX264optPsyTrellisSlider cell] performClick:self];
616                 }
617             }
618         }
619         else
620         {
621             if( [fX264optPsyRDSlider isHidden] == true )
622             {
623                 [[fX264optPsyRDSlider animator] setHidden:NO];
624                 [[fX264optPsyRDLabel animator] setHidden:NO];
625             }
626
627             if( ( [fX264optTrellisPopUp indexOfSelectedItem] == 0 || [fX264optTrellisPopUp indexOfSelectedItem] >= 2 ) && [fX264optPsyTrellisSlider isHidden] == true )
628             {
629                 [[fX264optPsyTrellisSlider animator] setHidden:NO];
630                 [[fX264optPsyTrellisLabel animator] setHidden:NO];
631             }
632         }
633     }
634     
635     if( sender == fX264optTrellisPopUp || sender == nil || sender == fDisplayX264Options )
636     {
637         if( [fX264optTrellisPopUp indexOfSelectedItem] > 0 && [fX264optTrellisPopUp indexOfSelectedItem] < 2 )
638         {
639             if( [fX264optPsyTrellisSlider isHidden] == false )
640             {
641                 /* No Psy-trellis without trellis. */
642                 [[fX264optPsyTrellisSlider animator] setHidden:YES];
643                 [[fX264optPsyTrellisLabel animator] setHidden:YES];
644                 [[fX264optPsyTrellisSlider animator] setFloatValue:0.0];
645                 [[fX264optPsyTrellisSlider cell] performClick:self];
646             }
647         }
648         else
649         {
650             if( ( [fX264optSubmePopUp indexOfSelectedItem] == 0 || [fX264optSubmePopUp indexOfSelectedItem] >= 7 ) && [fX264optPsyTrellisSlider isHidden] == true )
651             {
652                 [[fX264optPsyTrellisSlider animator] setHidden:NO];
653                 [[fX264optPsyTrellisLabel animator] setHidden:NO];
654             }
655         }
656     }
657 }
658
659 /**
660  * Resets the GUI widgets to the contents of the option string.
661  */
662 - (IBAction) X264AdvancedOptionsSetCurrentSettings: (id) sender
663 {
664     /* Set widgets depending on the opt string in field */
665     NSString * thisOpt; // The separated option such as "bframes=3"
666     NSString * optName = @""; // The option name such as "bframes"
667     NSString * optValue = @"";// The option value such as "3"
668     NSArray *currentOptsArray;
669     
670     /*First, we get an opt string to process */
671     NSString *currentOptString = [fDisplayX264Options stringValue];
672     
673     /* Verify there is an opt string to process by making sure an
674        option is getting its value set. If so, start to process it. */
675     NSRange currentOptRange = [currentOptString rangeOfString:@"="];
676     if (currentOptRange.location != NSNotFound)
677     {
678         /*Put individual options into an array based on the ":" separator for processing, result is "<opt>=<value>"*/
679         currentOptsArray = [currentOptString componentsSeparatedByString:@":"];
680         
681         /*iterate through the array and get <opts> and <values*/
682         int loopcounter;
683         int currentOptsArrayCount = [currentOptsArray count];
684         for (loopcounter = 0; loopcounter < currentOptsArrayCount; loopcounter++)
685         {
686             thisOpt = [currentOptsArray objectAtIndex:loopcounter];
687             
688             /* Verify the option sets a value */
689             NSRange splitOptRange = [thisOpt rangeOfString:@"="];            
690             if (splitOptRange.location != NSNotFound)
691             {
692                 /* Split thisOpt into an optName setting an optValue. */
693                 optName = [thisOpt substringToIndex:splitOptRange.location];
694                 optValue = [thisOpt substringFromIndex:splitOptRange.location + 1];
695                 
696                 /*Run through the available widgets for x264 opts and set them, as you add widgets, 
697                     they need to be added here. This should be moved to its own method probably*/
698                 
699                 /*bframes NSPopUpButton*/
700                 if ([optName isEqualToString:@"bframes"])
701                 {
702                     [fX264optBframesPopUp selectItemAtIndex:[optValue intValue]+1];
703                 }
704                 /*ref NSPopUpButton*/
705                 if ([optName isEqualToString:@"ref"])
706                 {
707                     // Clamp values to a minimum of 1 and a maximum of 16
708                     if ( [optValue intValue] < 1 )
709                     {
710                         [fX264optRefPopUp selectItemAtIndex:1];
711                         [ self X264AdvancedOptionsChanged: fX264optRefPopUp];
712                     }
713                     else if ( [optValue intValue] > 16 )
714                     {
715                         [fX264optRefPopUp selectItemAtIndex:16];
716                         [ self X264AdvancedOptionsChanged: fX264optRefPopUp];
717                     }
718                     else
719                     {
720                         [fX264optRefPopUp selectItemAtIndex:[optValue intValue]];
721                     }
722                 }
723                 /*WeightP NSButton*/
724                 if ([optName isEqualToString:@"weightp"])
725                 {
726                     if ([optValue intValue] < 1)
727                         [fX264optWeightPSwitch setState:0];
728                     else
729                         [fX264optWeightPSwitch setState:1];
730                 }
731                 /*No Dict Decimate NSButton*/
732                 if ([optName isEqualToString:@"no-dct-decimate"])
733                 {
734                     [fX264optNodctdcmtSwitch setState:[optValue intValue]];
735                 }
736                 /*Sub Me NSPopUpButton*/
737                 if ([optName isEqualToString:@"subq"])
738                 {
739                     [fX264optSubmePopUp selectItemAtIndex:[optValue intValue]+1];
740                 }
741                 /*Trellis NSPopUpButton*/
742                 if ([optName isEqualToString:@"trellis"])
743                 {
744                     [fX264optTrellisPopUp selectItemAtIndex:[optValue intValue]+1];
745                 }
746                 /*Motion Estimation NSPopUpButton*/
747                 if ([optName isEqualToString:@"me"])
748                 {
749                     if ([optValue isEqualToString:@"dia"])
750                         [fX264optMotionEstPopUp selectItemAtIndex:1];
751                     else if ([optValue isEqualToString:@"hex"])
752                         [fX264optMotionEstPopUp selectItemAtIndex:2];
753                     else if ([optValue isEqualToString:@"umh"])
754                         [fX264optMotionEstPopUp selectItemAtIndex:3];
755                     else if ([optValue isEqualToString:@"esa"])
756                         [fX264optMotionEstPopUp selectItemAtIndex:4];
757                     else if ([optValue isEqualToString:@"tesa"])
758                         [fX264optMotionEstPopUp selectItemAtIndex:5];
759                 }
760                 /*ME Range NSPopUpButton*/
761                 if ([optName isEqualToString:@"merange"])
762                 {
763                     [fX264optMERangePopUp selectItemAtIndex:[optValue intValue]-3];
764                 }
765                 /* Adaptive B-Frames NSPopUpButton*/
766                 if ([optName isEqualToString:@"b-adapt"])
767                 {
768                     [fX264optBAdaptPopUp selectItemAtIndex:[optValue intValue]+1];
769                 }
770                 /*B Pyramid NSPButton*/
771                 if ([optName isEqualToString:@"b-pyramid"])
772                 {
773                     
774                     if( [optValue isEqualToString:@"normal"] )
775                     {
776                         [self X264AdvancedOptionsChanged: fX264optBPyramidPopUp];
777                         [fX264optBPyramidPopUp selectItemAtIndex:0];
778                     }
779                     else if( [optValue isEqualToString:@"2"] )
780                     {
781                         [fX264optBPyramidPopUp selectItemAtIndex:0];
782                         [self X264AdvancedOptionsChanged: fX264optBPyramidPopUp];
783                     }
784                     if( [optValue isEqualToString:@"strict"] )
785                     {
786                         [fX264optBPyramidPopUp selectItemAtIndex:2];
787                     }
788                     else if( [optValue isEqualToString:@"1"] )
789                     {
790                         [fX264optBPyramidPopUp selectItemAtIndex:2];
791                         [self X264AdvancedOptionsChanged: fX264optBPyramidPopUp];
792                     }
793                     if( [optValue isEqualToString:@"none"] )
794                     {
795                         [fX264optBPyramidPopUp selectItemAtIndex:1];
796                     }
797                     else if( [optValue isEqualToString:@"0"] )
798                     {
799                         [fX264optBPyramidPopUp selectItemAtIndex:1];
800                         [self X264AdvancedOptionsChanged: fX264optBPyramidPopUp];
801                     }
802                 }                
803                 /*Direct B-frame Prediction NSPopUpButton*/
804                                 if ([optName isEqualToString:@"direct"])
805                 {
806                     if ([optValue isEqualToString:@"none"])
807                         [fX264optDirectPredPopUp selectItemAtIndex:1];
808                     else if ([optValue isEqualToString:@"spatial"])
809                         [fX264optDirectPredPopUp selectItemAtIndex:2];
810                     else if ([optValue isEqualToString:@"temporal"])
811                         [fX264optDirectPredPopUp selectItemAtIndex:3];
812                     else if ([optValue isEqualToString:@"auto"])
813                         [fX264optDirectPredPopUp selectItemAtIndex:4];                        
814                 }
815                 /*Deblocking NSPopUpButtons*/
816                 if ([optName isEqualToString:@"deblock"])
817                 {
818                     NSString * alphaDeblock = @"";
819                     NSString * betaDeblock = @"";
820                     
821                     NSRange splitDeblock = [optValue rangeOfString:@","];
822                     alphaDeblock = [optValue substringToIndex:splitDeblock.location];
823                     betaDeblock = [optValue substringFromIndex:splitDeblock.location + 1];
824                     
825                     if ([alphaDeblock isEqualToString:@"0"] && [betaDeblock isEqualToString:@"0"])
826                     {
827                         /* When both filters are at 0, default */
828                         [fX264optAlphaDeblockPopUp selectItemAtIndex:0];                        
829                         [fX264optBetaDeblockPopUp selectItemAtIndex:0];                               
830                     }
831                     else
832                     {
833                         if (![alphaDeblock isEqualToString:@"0"])
834                         {
835                             /* Alpha isn't 0, so set it. The offset of 7 is
836                                because filters start at -6 instead of at 0. */
837                             [fX264optAlphaDeblockPopUp selectItemAtIndex:[alphaDeblock intValue]+7];
838                         }
839                         else
840                         {
841                             /* Set alpha filter to 0, which is 7 up
842                                because filters start at -6, not 0. */
843                             [fX264optAlphaDeblockPopUp selectItemAtIndex:7];                        
844                         }
845                         
846                         if (![betaDeblock isEqualToString:@"0"])
847                         {
848                             /* Beta isn't 0, so set it. */
849                             [fX264optBetaDeblockPopUp selectItemAtIndex:[betaDeblock intValue]+7];
850                         }
851                         else
852                         {
853                             /* Set beta filter to 0. */
854                             [fX264optBetaDeblockPopUp selectItemAtIndex:7];                        
855                         }
856                     }
857                 }
858                 /* Analysis NSPopUpButton */
859                 if ([optName isEqualToString:@"analyse"])
860                 {
861                     if ([optValue isEqualToString:@"p8x8,b8x8,i8x8,i4x4"])
862                     {
863                         /* Default ("most") */
864                         [fX264optAnalysePopUp selectItemAtIndex:0];
865                     }
866                     else if ([optValue isEqualToString:@"i4x4,i8x8"] ||
867                              [optValue isEqualToString:@"i8x8,i4x4"] )
868                     {
869                         /* Some */
870                         [fX264optAnalysePopUp selectItemAtIndex:2];
871                     }
872                     else if ([optValue isEqualToString:@"none"])
873                     {
874                         [fX264optAnalysePopUp selectItemAtIndex:1];
875                     }
876                     else if ([optValue isEqualToString:@"all"])
877                     {
878                         [fX264optAnalysePopUp selectItemAtIndex:3];
879                     }
880                     
881                 }
882                 /* 8x8 DCT NSButton */
883                 if ([optName isEqualToString:@"8x8dct"])
884                 {
885                     [fX264opt8x8dctSwitch setState:[optValue intValue]];
886                 }
887                 /* CABAC NSButton */
888                 if ([optName isEqualToString:@"cabac"])
889                 {
890                     [fX264optCabacSwitch setState:[optValue intValue]];
891                 }
892                 /* Adaptive Quantization Strength NSSlider */
893                 if ([optName isEqualToString:@"aq-strength"])
894                 {
895                     [fX264optAqSlider setFloatValue:[optValue floatValue]];
896                 }                                                              
897                 /* Psy-RD and Psy-Trellis NSSliders */
898                 if ([optName isEqualToString:@"psy-rd"])
899                 {
900                     NSString * rdOpt = @"";
901                     NSString * trellisOpt = @"";
902                     
903                     NSRange splitRD = [optValue rangeOfString:@","];
904                     rdOpt = [optValue substringToIndex:splitRD.location];
905                     trellisOpt = [optValue substringFromIndex:splitRD.location + 1];
906                     
907                     [fX264optPsyRDSlider setFloatValue:[rdOpt floatValue]];
908                     [fX264optPsyTrellisSlider setFloatValue:[trellisOpt floatValue]];
909                 }                                                              
910             }
911         }
912     }
913 }
914
915 - (NSString *) X264AdvancedOptionsOptIDToString: (id) widget
916 {
917     /*Determine which outlet is being used and set optName to process accordingly */
918     NSString * optNameToChange = @""; // The option name such as "bframes"
919     
920     if (widget == fX264optBframesPopUp)
921     {
922         optNameToChange = @"bframes";
923     }
924     if (widget == fX264optRefPopUp)
925     {
926         optNameToChange = @"ref";
927     }
928     if (widget == fX264optWeightPSwitch)
929     {
930         optNameToChange = @"weightp";
931     }
932     if (widget == fX264optNodctdcmtSwitch)
933     {
934         optNameToChange = @"no-dct-decimate";
935     }
936     if (widget == fX264optSubmePopUp)
937     {
938         optNameToChange = @"subq";
939     }
940     if (widget == fX264optTrellisPopUp)
941     {
942         optNameToChange = @"trellis";
943     }
944     if (widget == fX264optMotionEstPopUp)
945     {
946         optNameToChange = @"me";
947     }
948     if (widget == fX264optMERangePopUp)
949     {
950         optNameToChange = @"merange";
951     }
952     if (widget == fX264optBAdaptPopUp)
953     {
954         optNameToChange = @"b-adapt";
955     }
956     if (widget == fX264optBPyramidPopUp)
957     {
958         optNameToChange = @"b-pyramid";
959     }
960     if (widget == fX264optDirectPredPopUp)
961     {
962         optNameToChange = @"direct";
963     }
964     if (widget == fX264optAlphaDeblockPopUp)
965     {
966         optNameToChange = @"deblock";
967     }
968     if (widget == fX264optBetaDeblockPopUp)
969     {
970         optNameToChange = @"deblock";
971     }        
972     if (widget == fX264optAnalysePopUp)
973     {
974         optNameToChange = @"analyse";
975     }
976     if (widget == fX264opt8x8dctSwitch)
977     {
978         optNameToChange = @"8x8dct";
979     }
980     if (widget == fX264optCabacSwitch)
981     {
982         optNameToChange = @"cabac";
983     }
984     if( widget == fX264optAqSlider)
985     {
986         optNameToChange = @"aq-strength";
987     }
988     if( widget == fX264optPsyRDSlider)
989     {
990         optNameToChange = @"psy-rd";
991     }
992     if( widget == fX264optPsyTrellisSlider)
993     {
994         optNameToChange = @"psy-rd";
995     }
996     
997     return optNameToChange;
998 }
999
1000 - (NSString *) X264AdvancedOptionsWidgetToString: (NSString *) optName withID: (id) sender
1001 {
1002     NSString * thisOpt = @""; // The option=value string the method will return
1003     
1004     if ([optName isEqualToString:@"deblock"])
1005     {
1006         if ((([fX264optAlphaDeblockPopUp indexOfSelectedItem] == 0) || ([fX264optAlphaDeblockPopUp indexOfSelectedItem] == 7)) && (([fX264optBetaDeblockPopUp indexOfSelectedItem] == 0) || ([fX264optBetaDeblockPopUp indexOfSelectedItem] == 7)))
1007         {
1008             /* When both deblock widgets are 0 or default or a mix of the two,
1009                use a blank string, since deblocking defaults to 0,0.           */
1010             thisOpt = @"";                                
1011         }
1012         else
1013         {
1014             /* Otherwise the format is deblock=a,b, where a and b both have an array
1015                offset of 7 because deblocking values start at -6 instead of at zero. */
1016             thisOpt = [NSString stringWithFormat:@"%@=%d,%d",optName, ([fX264optAlphaDeblockPopUp indexOfSelectedItem] != 0) ? [fX264optAlphaDeblockPopUp indexOfSelectedItem]-7 : 0,([fX264optBetaDeblockPopUp indexOfSelectedItem] != 0) ? [fX264optBetaDeblockPopUp indexOfSelectedItem]-7 : 0];
1017         }
1018     }
1019     
1020     else if ([optName isEqualToString:@"aq-strength"])
1021     {
1022         if( [fX264optAqSlider floatValue] == 1.0 ) 
1023         {
1024             /* When Aq is 1 it's the default value and can be ignored. */
1025             thisOpt = @"";                                
1026         }
1027         else
1028         {
1029             thisOpt = [NSString stringWithFormat:@"%@=%0.1f", optName, [fX264optAqSlider floatValue] ];
1030         }
1031     }
1032
1033     else if ([optName isEqualToString:@"psy-rd"])
1034     {
1035         if( [fX264optPsyRDSlider floatValue] == 1.0 && [fX264optPsyTrellisSlider floatValue] == 0.0 ) 
1036         {
1037             /* When  PsyRD is 1 and PsyTrel is 0 they're default values and can be ignored. */
1038             thisOpt = @"";                                
1039         }
1040         else
1041         {
1042             /* Otherwise the format is psy-rd=a,b */
1043             thisOpt = [NSString stringWithFormat:@"%@=%0.1f,%0.2f", optName, [fX264optPsyRDSlider floatValue], [fX264optPsyTrellisSlider floatValue] ];
1044         }
1045     }
1046     
1047     else if /*Boolean Switches*/ ( [optName isEqualToString:@"no-dct-decimate"] )
1048     {
1049         /* Here is where we take care of the boolean options that work overtly:
1050            no-dct-decimate being on means no-dct-decimate=1, etc. Some options
1051            require the inverse, but those will be handled a couple lines down. */
1052         if ([sender state] == 0)
1053         {
1054             /* When these options are false, don't include them. They all default
1055                to being set off, so they don't need to be mentioned at all.       */
1056             thisOpt = @"";
1057         }
1058         else
1059         {
1060             /* Otherwise, include them as optioname=1 */
1061             thisOpt = [NSString stringWithFormat:@"%@=%d",optName,1];
1062         }
1063     }
1064     
1065     else if ( [optName isEqualToString:@"8x8dct"] || [optName isEqualToString:@"cabac"] || [optName isEqualToString:@"weightp"] )
1066     {
1067         /* These options default to being on. That means they
1068            only need to be included in the string when turned off. */
1069         if ([sender state] == 1)
1070         {
1071             /* It's true so don't include it. */
1072             thisOpt = @"";
1073         }
1074         else
1075         {
1076             /* Otherwise, include cabac=0, etc, in the string. */
1077             thisOpt = [NSString stringWithFormat:@"%@=%d",optName,0];
1078         }
1079     }
1080                                             
1081     else if (([sender indexOfSelectedItem] == 0) && (sender != fX264optAlphaDeblockPopUp) && (sender != fX264optBetaDeblockPopUp) ) // means that "unspecified" is chosen, lets then remove it from the string
1082     {
1083         /* When a widget is at index 0, it's default. Default means don't add to the string.
1084            The exception for deblocking is because for those, *both* need to at index 0
1085            for it to default, so it's handled separately, above this section.                */
1086         thisOpt = @"";
1087     }
1088     
1089     else if ([optName isEqualToString:@"me"])
1090     {
1091         /* Motion estimation uses string values, so this switch
1092            pairs the widget index with the right value string.  */
1093         switch ([sender indexOfSelectedItem])
1094         {   
1095             case 1:
1096                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"dia"];
1097                 break;
1098                 
1099             case 2:
1100                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"hex"];
1101                 break;
1102                 
1103             case 3:
1104                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"umh"];
1105                 break;
1106                 
1107             case 4:
1108                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"esa"];
1109                 break;
1110             
1111             case 5:
1112                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"tesa"];
1113             
1114             default:
1115                 break;
1116         }
1117     }
1118     
1119     else if ([optName isEqualToString:@"direct"])
1120     {
1121         /* Direct prediction uses string values, so this switch
1122            pairs the right string value with the right widget index. */
1123         switch ([sender indexOfSelectedItem])
1124         {   
1125             case 1:
1126                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"none"];
1127                 break;
1128                 
1129             case 2:
1130                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"spatial"];
1131                 break;
1132                 
1133             case 3:
1134                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"temporal"];
1135                 break;
1136                 
1137             case 4:
1138                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"auto"];
1139                 break;
1140                 
1141             default:
1142                 break;
1143         }
1144     }
1145     
1146     else if ([optName isEqualToString:@"analyse"])
1147     {
1148         /* Analysis uses string values as well. */
1149         switch ([sender indexOfSelectedItem])
1150         {   
1151             case 1:
1152                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"none"];
1153                 break;
1154             case 2:
1155                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"i4x4,i8x8"];
1156                 break;
1157             case 3:
1158                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"all"];
1159                 break;
1160                 
1161             default:
1162                 break;
1163         }
1164     }
1165
1166     else if ([optName isEqualToString:@"b-pyramid"])
1167     {
1168         /* B-pyramid uses string values too. */
1169         switch ([sender indexOfSelectedItem])
1170         {   
1171             case 1:
1172                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"none"];
1173                 break;
1174             case 2:
1175                 thisOpt = [NSString stringWithFormat:@"%@=%@",optName,@"strict"];
1176                 break;
1177             case 0:
1178                 thisOpt = @"";
1179                 break;
1180                 
1181             default:
1182                 break;
1183         }
1184     }
1185     
1186     else if ([optName isEqualToString:@"merange"])
1187     {
1188         /* Motion estimation range uses an odd array offset because in addition
1189            to starting with index 0 as default, index 1 starts at 4 instead of 1,
1190            because merange can't go below 4. So it has to be handled separately.  */
1191         thisOpt = [NSString stringWithFormat:@"%@=%d",optName,[sender indexOfSelectedItem]+3];
1192     }
1193     
1194     else if ([optName isEqualToString:@"b-adapt"])
1195     {
1196         /* B-adapt starts at index 0 with default then goes 0, 1, 2)*/
1197         thisOpt = [NSString stringWithFormat:@"%@=%d", optName, [sender indexOfSelectedItem]-1];
1198     }
1199     
1200     else if ([optName isEqualToString:@"ref"])
1201     {
1202         /* Refs use actual index numbers */
1203         thisOpt = [NSString stringWithFormat:@"%@=%d",optName,[sender indexOfSelectedItem]];
1204     }
1205     
1206     else // we have a valid value to change, so change it
1207     {
1208         if ( [sender indexOfSelectedItem] != 0 )
1209         /* Here's our general case, that catches things like b-frames.
1210            Basically, any options that are PopUp menus with index 0 as default and
1211            index 1 as 0, with numerical values, are all handled right here. All of
1212            the above stuff is for the exceptions to the general case.              */
1213             thisOpt = [NSString stringWithFormat:@"%@=%d",optName,[sender indexOfSelectedItem]-1];
1214     }
1215     
1216     return thisOpt;
1217 }
1218
1219 - (BOOL) X264AdvancedOptionsIsOpt: (NSString *) optNameToChange inString: (NSString *) currentOptString
1220 {
1221     /* If the option is in the string but not the beginning of it,
1222        it will be in the form of ":optName=value" so we really want
1223        to be looking for ":optNameToChange=" rather than "optNameToChange". */
1224     NSString *checkOptNameToChange = [NSString stringWithFormat:@":%@=",optNameToChange];
1225     
1226     /* Now we store the part of the string up through the option name in currentOptRange. */
1227     NSRange currentOptRange = [currentOptString rangeOfString:checkOptNameToChange];
1228
1229     /*  We need to know if the option is at the beginning of the string.
1230         If it is at the start, it won't be preceded by a colon.
1231         To figure this out, we'll use the rangeOfString method. First,
1232         store what the option name would be if if it was at the beginning,
1233         in checkOptNameToChangeBeginning. Then, find its range in the string.
1234         If the range is 0, it's the first option listed in the string.       */        
1235     NSString *checkOptNameToChangeBeginning = [NSString stringWithFormat:@"%@=",optNameToChange];
1236     NSRange currentOptRangeBeginning = [currentOptString rangeOfString:checkOptNameToChangeBeginning];
1237
1238     if (currentOptRange.location != NSNotFound || currentOptRangeBeginning.location == 0)
1239         return true;
1240     else
1241         return false;
1242
1243
1244 /**
1245  * Resets the option string to mirror the GUI widgets.
1246  */
1247 - (IBAction) X264AdvancedOptionsChanged: (id) sender
1248 {
1249     /* Look up the equivalent string option name of the calling widget. */
1250     NSString * optNameToChange = [self X264AdvancedOptionsOptIDToString: sender];
1251     
1252     NSString * thisOpt = @"";  // The separated option such as "bframes=3"
1253     NSString * optName = @"";  // The option name such as "bframes"
1254     NSString * optValue = @""; // The option value such as "3"
1255     NSArray *currentOptsArray;
1256     
1257     /* Get the current opt string being displayed. */
1258     NSString *currentOptString = [fDisplayX264Options stringValue];
1259     
1260     /* There are going to be a few possibilities.
1261        - The option might start off the string.
1262        - The option might be in the middle of the string.
1263        - The option might not be in the string at all yet.
1264        - The string itself might not yet exist.             */
1265     
1266     if( [self X264AdvancedOptionsIsOpt: optNameToChange inString: currentOptString] )
1267     {
1268         /* If the option is in the string wth a semicolon, or starts the string, it's time to edit.
1269            This means parsing the whole string into an array of options and values. From there,
1270            iterate through the options, and when you reach the one that's been changed, edit it.   */
1271         
1272         /* Create new empty opt string*/
1273         NSString *changedOptString = @"";
1274         
1275         /* Put individual options into an array based on the ":"
1276            separator for processing, result is "<opt>=<value>"   */
1277         currentOptsArray = [currentOptString componentsSeparatedByString:@":"];
1278         
1279         /* Iterate through the array and get <opts> and <values*/
1280         int loopcounter;
1281         int currentOptsArrayCount = [currentOptsArray count];
1282         for (loopcounter = 0; loopcounter < currentOptsArrayCount; loopcounter++)
1283         {
1284             thisOpt = [currentOptsArray objectAtIndex:loopcounter];
1285             NSRange splitOptRange = [thisOpt rangeOfString:@"="];
1286             
1287             if (splitOptRange.location != NSNotFound)
1288             {
1289                 /* First off, it's time to handle option strings that
1290                    already have at least one option=value pair in them. */
1291                    
1292                 optName = [thisOpt substringToIndex:splitOptRange.location];
1293                 optValue = [thisOpt substringFromIndex:splitOptRange.location + 1];
1294
1295                 /*If the optNameToChange is found, appropriately change the value or delete it if
1296                     "Unspecified" is set.*/
1297                 if ([optName isEqualToString:optNameToChange])
1298                 {
1299                     thisOpt = [self X264AdvancedOptionsWidgetToString: optName withID: sender];
1300                 }
1301             }
1302             
1303             /* Construct New String for opts here */
1304             if ([thisOpt isEqualToString:@""])
1305             {
1306                 /* Blank option, so just add it to the string. (Why?) */
1307                 changedOptString = [NSString stringWithFormat:@"%@%@",changedOptString,thisOpt];
1308             }
1309             else
1310             {
1311                 if ([changedOptString isEqualToString:@""])
1312                 {
1313                     /* No existing string, make the string this option. */
1314                     changedOptString = [NSString stringWithFormat:@"%@",thisOpt];
1315                 }
1316                 else
1317                 {
1318                     /* Existing string, existing option. Append the
1319                        option to the string, preceding it with a colon. */
1320                     changedOptString = [NSString stringWithFormat:@"%@:%@",changedOptString,thisOpt];
1321                 }
1322             }
1323         }
1324         
1325         /* Change the dislayed option string to reflect the new modified settings */
1326         [fDisplayX264Options setStringValue:changedOptString];    
1327     }
1328     else // if none exists, add it to the string
1329     {
1330         /* This is where options that aren't already in the string are handled. */
1331         if ([[fDisplayX264Options stringValue] isEqualToString: @""])
1332         {
1333             
1334             [fDisplayX264Options setStringValue:
1335                 [self X264AdvancedOptionsWidgetToString: optNameToChange withID: sender]];
1336         }
1337         else
1338         {
1339             /* The string isn't empty, and the option isn't already in it, so
1340                it will need to be appended to the current string with a colon,
1341                as long as the string to be appended isn't just blank (default). */
1342             if( [[self X264AdvancedOptionsWidgetToString: optNameToChange withID: sender] isEqualToString: @""] == false )
1343             {
1344                 [fDisplayX264Options setStringValue:
1345                     [NSString stringWithFormat:@"%@:%@",
1346                         currentOptString,
1347                         [self X264AdvancedOptionsWidgetToString: optNameToChange withID: sender] ]];                
1348             }
1349         }
1350     }
1351     
1352     /* We now need to reset the opt widgets since we changed some stuff */        
1353     [self X264AdvancedOptionsSet:sender];        
1354 }
1355
1356 @end