WinGui: Fix another instance of the Caliburn vs Json.net sillyness where objects...
[HandBrake.git] / macosx / HBVideo.m
blobf264a16b561cbd2e3eb2f3c641cda6ca45837eae
1 /*  HBVideo.m $
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. */
7 #import "HBVideo.h"
8 #import "HBJob.h"
9 #import "HBCodingUtilities.h"
10 #include "hb.h"
12 NSString * const HBVideoChangedNotification = @"HBVideoChangedNotification";
14 @interface HBVideo ()
16 @property (nonatomic, readwrite) double qualityMinValue;
17 @property (nonatomic, readwrite) double qualityMaxValue;
19 @property (nonatomic, readwrite) NSUInteger mediumPresetIndex;
21 @property (nonatomic, readwrite, getter=areNotificationsEnabled) BOOL notificationsEnabled;
23 @end
25 @implementation HBVideo
27 - (instancetype)initWithJob:(HBJob *)job;
29     self = [super init];
30     if (self) {
31         _encoder = HB_VCODEC_X264;
32         _avgBitrate = 1000;
33         _quality = 18.0;
34         _qualityMaxValue = 51.0f;
35         _job = job;
37         _preset = @"medium";
38         _tune = @"";
39         _profile = @"auto";
40         _level = @"auto";
42         [self updateQualityBounds];
44         _notificationsEnabled = YES;
45     }
46     return self;
49 - (void)postChangedNotification
51     if (self.areNotificationsEnabled)
52     {
53         [[NSNotificationCenter defaultCenter] postNotification: [NSNotification notificationWithName:HBVideoChangedNotification
54                                                                                               object:self
55                                                                                             userInfo:nil]];
56     }
59 #pragma mark - Setters
61 /**
62  *  Updates the min/max quality values
63  */
64 - (void)updateQualityBounds
66     // Get the current slider maxValue to check for a change in slider scale
67     // later so that we can choose a new similar value on the new slider scale
68     double previousMaxValue             = self.qualityMaxValue;
69     double previousPercentOfSliderScale = (self.quality / (self.qualityMaxValue - self.qualityMinValue + 1));
71     int direction;
72     float minValue, maxValue, granularity;
73     hb_video_quality_get_limits(self.encoder,
74                                 &minValue, &maxValue, &granularity, &direction);
76     self.qualityMinValue = minValue;
77     self.qualityMaxValue = maxValue;
79     // check to see if we have changed slider scales
80     if (previousMaxValue != maxValue)
81     {
82         // if so, convert the old setting to the new scale as close as possible
83         // based on percentages
84         self.quality = floor((maxValue - minValue + 1.) * (previousPercentOfSliderScale));
85     }
88 - (void)setEncoder:(int)encoder
90     _encoder = encoder;
91     [self updateQualityBounds];
92     [self validatePresetsSettings];
93     [self validateAdvancedOptions];
95     [self postChangedNotification];
98 - (void)setQualityType:(int)qualityType
100     _qualityType = qualityType;
101     [self postChangedNotification];
104 - (void)setAvgBitrate:(int)avgBitrate
106     _avgBitrate = avgBitrate;
107     [self postChangedNotification];
110 - (void)setQuality:(double)quality
112     _quality = quality;
113     [self postChangedNotification];
116 - (void)setFrameRate:(int)frameRate
118     _frameRate = frameRate;
119     [self postChangedNotification];
122 - (void)setFrameRateMode:(int)frameRateMode
124     _frameRateMode = frameRateMode;
125     [self postChangedNotification];
128 - (void)setTwoPass:(BOOL)twoPass
130     _twoPass = twoPass;
131     [self postChangedNotification];
134 - (void)setTurboTwoPass:(BOOL)turboTwoPass
136     _turboTwoPass = turboTwoPass;
137     [self postChangedNotification];
140 - (void)containerChanged
142     BOOL encoderSupported = NO;
144     for (const hb_encoder_t *video_encoder = hb_video_encoder_get_next(NULL);
145          video_encoder != NULL;
146          video_encoder  = hb_video_encoder_get_next(video_encoder))
147     {
148         if (video_encoder->muxers & self.job.container)
149         {
150             if (video_encoder->codec == self.encoder)
151             {
152                 encoderSupported = YES;
153             }
154         }
155     }
157     if (!encoderSupported)
158     {
159         self.encoder = HB_VCODEC_X264;
160     }
163 - (void)setPreset:(NSString *)preset
165     _preset = [preset copy];
166     [self postChangedNotification];
169 - (void)setTune:(NSString *)tune
171     if (![tune isEqualToString:@"none"])
172     {
173         _tune = [tune copy];
174     }
175     else
176     {
177         _tune = @"";
178     }
180     [self postChangedNotification];
183 - (void)setProfile:(NSString *)profile
185     _profile = [profile copy];
186     [self postChangedNotification];
189 - (void)setLevel:(NSString *)level
191     _level = [level copy];
192     [self postChangedNotification];
195 - (void)setVideoOptionExtra:(NSString *)videoOptionExtra
197     if (videoOptionExtra != nil)
198     {
199         _videoOptionExtra = [videoOptionExtra copy];
200     }
201     else
202     {
203         _videoOptionExtra = @"";
204     }
205     [self postChangedNotification];
208 - (void)setFastDecode:(BOOL)fastDecode
210     _fastDecode = fastDecode;
211     [self postChangedNotification];
214 - (void)validatePresetsSettings
216     NSArray *presets = self.presets;
217     if (presets.count && ![presets containsObject:self.preset]) {
218         self.preset = presets[self.mediumPresetIndex];
219     }
221     NSArray *tunes = self.tunes;
222     if (tunes.count && ![tunes containsObject:self.tune]) {
223         self.tune = tunes.firstObject;
224     }
226     NSArray *profiles = self.profiles;
227     if (profiles.count && ![profiles containsObject:self.profile]) {
228         self.profile = profiles.firstObject;
229     }
231     NSArray *levels = self.levels;
232     if (levels.count && ![levels containsObject:self.level]) {
233         self.level = levels.firstObject;
234     }
237 - (void)validateAdvancedOptions
239     if (self.encoder != HB_VCODEC_H264_MASK)
240     {
241         self.advancedOptions = NO;
242     }
245 + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
247     NSSet *retval = nil;
249     // Tell KVO to reload the presets settings
250     // after a change to the encoder.
251     if ([key isEqualToString:@"presets"] ||
252         [key isEqualToString:@"tunes"] ||
253         [key isEqualToString:@"profiles"] ||
254         [key isEqualToString:@"levels"])
255     {
256         retval = [NSSet setWithObjects:@"encoder", nil];
257     }
259     // Tell KVO to reload the x264 unparse string
260     // after values changes.
261     if ([key isEqualToString:@"unparseOptions"])
262     {
263         retval = [NSSet setWithObjects:@"encoder", @"preset", @"tune", @"profile", @"level",
264                   @"videoOptionExtra", @"fastDecode", @"job.picture.width", @"job.picture.height", nil];
265     }
267     if ([key isEqualToString:@"encoders"])
268     {
269         retval = [NSSet setWithObjects:@"job.container", nil];
270     }
272     if ([key isEqualToString:@"fastDecodeSupported"] ||
273         [key isEqualToString:@"turboTwoPassSupported"])
274     {
275         retval = [NSSet setWithObjects:@"encoder", nil];
276     }
278     return retval;
281 - (void)setNilValueForKey:(NSString *)key
283     [self setValue:@0 forKey:key];
286 #pragma mark -
288 - (NSArray *)presets
290     NSMutableArray *temp = [NSMutableArray array];
292     const char * const *presets = hb_video_encoder_get_presets(self.encoder);
293     for (int i = 0; presets != NULL && presets[i] != NULL; i++)
294     {
295         [temp addObject:@(presets[i])];
296         if (!strcasecmp(presets[i], "medium"))
297         {
298             self.mediumPresetIndex = i;
299         }
300     }
302     return [temp copy];
305 - (NSArray *)tunes
307     NSMutableArray *temp = [NSMutableArray array];
309     [temp addObject:@"none"];
311     const char * const *tunes = hb_video_encoder_get_tunes(self.encoder);
313     for (int i = 0; tunes != NULL && tunes[i] != NULL; i++)
314     {
315         // we filter out "fastdecode" as we have a dedicated checkbox for it
316         if (strcasecmp(tunes[i], "fastdecode") != 0)
317         {
318             [temp addObject:@(tunes[i])];
319         }
320     }
322     return [temp copy];
325 - (NSArray *)profiles
327     NSMutableArray *temp = [NSMutableArray array];
329     const char * const *profiles = hb_video_encoder_get_profiles(self.encoder);
330     for (int i = 0; profiles != NULL && profiles[i] != NULL; i++)
331     {
332         [temp addObject:@(profiles[i])];
333     }
334     if (!temp.count)
335     {
336         [temp addObject:@"auto"];
337     }
339     return [temp copy];
342 - (NSArray *)levels
344     NSMutableArray *temp = [NSMutableArray array];
346     const char * const *levels = hb_video_encoder_get_levels(self.encoder);
347     for (int i = 0; levels != NULL && levels[i] != NULL; i++)
348     {
349         [temp addObject:@(levels[i])];
350     }
351     if (!temp.count)
352     {
353         [temp addObject:@"auto"];
354     }
356     return [temp copy];
359 #pragma mark - NSCopying
361 - (instancetype)copyWithZone:(NSZone *)zone
363     HBVideo *copy = [[[self class] alloc] init];
365     if (copy)
366     {
367         copy->_encoder = _encoder;
369         copy->_qualityType = _qualityType;
370         copy->_avgBitrate = _avgBitrate;
371         copy->_quality = _quality;
373         copy->_qualityMinValue = _qualityMinValue;
374         copy->_qualityMaxValue = _qualityMaxValue;
376         copy->_frameRate = _frameRate;
377         copy->_frameRateMode = _frameRateMode;
379         copy->_twoPass = _twoPass;
380         copy->_turboTwoPass = _turboTwoPass;
382         copy->_advancedOptions = _advancedOptions;
383         copy->_preset = [_preset copy];
384         copy->_tune = [_tune copy];
385         copy->_profile = [_profile copy];
386         copy->_level = [_level copy];
387         copy->_videoOptionExtra = [_videoOptionExtra copy];
388         copy->_fastDecode = _fastDecode;
390         copy->_notificationsEnabled = _notificationsEnabled;
391     }
393     return copy;
396 #pragma mark - NSCoding
398 + (BOOL)supportsSecureCoding
400     return YES;
403 - (void)encodeWithCoder:(NSCoder *)coder
405     [coder encodeInt:1 forKey:@"HBVideoVersion"];
407     encodeInt(_encoder);
409     encodeInt(_qualityType);
410     encodeInt(_avgBitrate);
411     encodeDouble(_quality);
413     encodeDouble(_qualityMinValue);
414     encodeDouble(_qualityMaxValue);
416     encodeInt(_frameRate);
417     encodeInt(_frameRateMode);
419     encodeBool(_twoPass);
420     encodeBool(_turboTwoPass);
422     encodeBool(_advancedOptions);
423     encodeObject(_preset);
424     encodeObject(_tune);
425     encodeObject(_profile);
426     encodeObject(_level);
428     encodeObject(_videoOptionExtra);
430     encodeBool(_fastDecode);
433 - (instancetype)initWithCoder:(NSCoder *)decoder
435     self = [super init];
437     decodeInt(_encoder);
439     decodeInt(_qualityType);
440     decodeInt(_avgBitrate);
441     decodeDouble(_quality);
443     decodeDouble(_qualityMinValue);
444     decodeDouble(_qualityMaxValue);
446     decodeInt(_frameRate);
447     decodeInt(_frameRateMode);
449     decodeBool(_twoPass);
450     decodeBool(_turboTwoPass);
452     decodeBool(_advancedOptions);
453     decodeObject(_preset, NSString);
454     decodeObject(_tune, NSString);
455     decodeObject(_profile, NSString);
456     decodeObject(_level, NSString);
458     decodeObject(_videoOptionExtra, NSString);
460     decodeBool(_fastDecode);
462     _notificationsEnabled = YES;
464     return self;
467 #pragma mark - Various conversion methods from dict/preset/queue/etc
470  *  Returns a string minus the fastdecode string.
471  */
472 - (NSString *)stripFastDecodeFromString:(NSString *)tune
474     // filter out fastdecode
475     tune = [tune stringByReplacingOccurrencesOfString:@"," withString:@""];
476     tune = [tune stringByReplacingOccurrencesOfString:@"fastdecode" withString:@""];
478     return tune;
482  *  Retuns the tune string plus the fastdecode option (if enabled)
483  */
484 - (NSString *)completeTune
486     NSMutableString *string = [[NSMutableString alloc] init];
488     if (self.tune)
489     {
490         [string appendString:self.tune];
491     }
493     if (self.fastDecode)
494     {
495         if (string.length)
496         {
497             [string appendString:@","];
498         }
500         [string appendString:@"fastdecode"];
501     }
503     return string;
506 - (void)applyPreset:(NSDictionary *)preset
508     self.notificationsEnabled = NO;
510     // map legacy encoder names via libhb.
511     self.encoder = hb_video_encoder_get_from_name([preset[@"VideoEncoder"] UTF8String]);
513     if (hb_video_encoder_get_presets(self.encoder) != NULL)
514     {
515         if (self.encoder == HB_VCODEC_X264 && [preset[@"x264UseAdvancedOptions"] boolValue])
516         {
517             // preset does not use the x264 preset system, reset the widgets.
518             self.preset = @"medium";
519             self.tune = @"";
520             self.profile = @"auto";
521             self.level = @"auto";
522             self.fastDecode = NO;
524             self.videoOptionExtra = preset[@"VideoOptionExtra"];
525             self.advancedOptions = YES;
526         }
527         else
528         {
529             // x264UseAdvancedOptions is set to 0 (disabled),
530             // so we use the new preset system and
531             // disable the advanced panel
532             self.advancedOptions = NO;
534             self.preset = preset[@"VideoPreset"];
535             self.tune   = preset[@"VideoTune"];
536             self.videoOptionExtra = preset[@"VideoOptionExtra"];
537             self.profile = preset[@"VideoProfile"];
538             self.level   = preset[@"VideoLevel"];
540             if ([self.tune rangeOfString:@"fastdecode"].location != NSNotFound)
541             {
542                 self.fastDecode = YES;;
543             }
544             else
545             {
546                 self.fastDecode = NO;
547             }
549             self.tune = [self stripFastDecodeFromString:self.tune];
550         }
551     }
552     else
553     {
554         self.videoOptionExtra = preset[@"VideoOptionExtra"];
555     }
557     int qualityType = [preset[@"VideoQualityType"] intValue] - 1;
558     /* Note since the removal of Target Size encoding, the possible values for VideoQuality type are 0 - 1.
559      * Therefore any preset that uses the old 2 for Constant Quality would now use 1 since there is one less index
560      * for the fVidQualityMatrix. It should also be noted that any preset that used the deprecated Target Size
561      * setting of 0 would set us to 0 or ABR since ABR is now tagged 0. Fortunately this does not affect any built-in
562      * presets since they all use Constant Quality or Average Bitrate.*/
563     if (qualityType == -1)
564     {
565         qualityType = 0;
566     }
567     self.qualityType = qualityType;
569     self.avgBitrate = [preset[@"VideoAvgBitrate"] intValue];
570     self.quality = [preset[@"VideoQualitySlider"] floatValue];
572     // Video framerate
573     if ([preset[@"VideoFramerate"] isEqualToString:@"auto"])
574     {
575         // Now set the Video Frame Rate Mode to either vfr or cfr according to the preset.
576         if (!preset[@"VideoFramerateMode"] ||
577             [preset[@"VideoFramerateMode"] isEqualToString:@"vfr"])
578         {
579             self.frameRateMode = 0; // we want vfr
580         }
581         else
582         {
583             self.frameRateMode = 1; // we want cfr
584         }
585     }
586     else
587     {
588         // Now set the Video Frame Rate Mode to either pfr or cfr according to the preset.
589         if ([preset[@"VideoFramerateMode"] isEqualToString:@"pfr"] ||
590             [preset[@"VideoFrameratePFR"]  intValue] == 1)
591         {
592             self.frameRateMode = 0; // we want pfr
593         }
594         else
595         {
596             self.frameRateMode = 1; // we want cfr
597         }
598     }
599     // map legacy names via libhb.
600     int intValue = hb_video_framerate_get_from_name([preset[@"VideoFramerate"] UTF8String]);
601     if (intValue == -1)
602     {
603         intValue = 0;
604     }
605     self.frameRate = intValue;
607     // 2 Pass Encoding.
608     self.twoPass = [preset[@"VideoTwoPass"] boolValue];
610     // Turbo 1st pass for 2 Pass Encoding.
611     self.turboTwoPass = [preset[@"VideoTurboTwoPass"] boolValue];
613     self.notificationsEnabled = YES;
616 - (void)writeToPreset:(NSMutableDictionary *)preset
618     preset[@"VideoEncoder"] = @(hb_video_encoder_get_short_name(self.encoder));
620     if (hb_video_encoder_get_presets(self.encoder) != NULL)
621     {
622         preset[@"VideoPreset"]      = self.preset;
623         preset[@"VideoTune"]        = [self completeTune];
624         preset[@"VideoOptionExtra"] = self.videoOptionExtra;
625         preset[@"VideoProfile"]     = self.profile;
626         preset[@"VideoLevel"]       = self.level;
628         // x264 Options, this will either be advanced panel or the video tabs x264 presets panel with modded option string
629         if (self.advancedOptions)
630         {
631             // use the old advanced panel.
632             preset[@"x264UseAdvancedOptions"] = @YES;
633         }
634         else
635         {
636             preset[@"x264UseAdvancedOptions"] = @NO;
637         }
638     }
639     else
640     {
641         // FFmpeg (lavc) Option String
642         preset[@"VideoOptionExtra"] = self.videoOptionExtra;
643     }
645     /* though there are actually only 0 - 1 types available in the ui we need to map to the old 0 - 2
646      * set of indexes from when we had 0 == Target , 1 == Abr and 2 == Constant Quality for presets
647      * to take care of any legacy presets. */
648     preset[@"VideoQualityType"] = @(self.qualityType + 1);
649     preset[@"VideoAvgBitrate"] = @(self.avgBitrate);
650     preset[@"VideoQualitySlider"] = @(self.quality);
652     /* Video framerate and framerate mode */
653     if (self.frameRateMode == 1)
654     {
655         preset[@"VideoFramerateMode"] = @"cfr";
656     }
657     if (self.frameRate == 0) // Same as source is selected
658     {
659         preset[@"VideoFramerate"] = @"auto";
661         if (self.frameRateMode == 0)
662         {
663             preset[@"VideoFramerateMode"] = @"vfr";
664         }
665     }
666     else // translate the rate (selected item's tag) to the official libhb name
667     {
668         preset[@"VideoFramerate"] = [NSString stringWithFormat:@"%s",
669                                      hb_video_framerate_get_name((int)self.frameRate)];
671         if (self.frameRateMode == 0)
672         {
673             preset[@"VideoFramerateMode"] = @"pfr";
674         }
675     }
677     preset[@"VideoTwoPass"] = @(self.twoPass);
678     preset[@"VideoTurboTwoPass"] = @(self.turboTwoPass);
681 @end