summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--macosx/HBVideo.h83
-rw-r--r--macosx/HBVideo.m995
2 files changed, 1061 insertions, 17 deletions
diff --git a/macosx/HBVideo.h b/macosx/HBVideo.h
index 9e217611c..3d5b3a608 100644
--- a/macosx/HBVideo.h
+++ b/macosx/HBVideo.h
@@ -1,31 +1,94 @@
-//
-// HBVideo.h
-// HandBrake
-//
-// Created by Damiano Galassi on 12/08/14.
-//
-//
+/* HBVideo.h $
+
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
#import <Foundation/Foundation.h>
+#include "hb.h"
@interface HBVideo : NSObject
- (void)applySettingsFromPreset:(NSDictionary *)preset;
+- (void)prepareVideoForPreset:(NSMutableDictionary *)preset;
+
+- (void)applyVideoSettingsFromQueue:(NSDictionary *)queueToApply;
+- (void)prepareVideoForQueueFileJob:(NSMutableDictionary *)queueFileJob;
+
+- (void)prepareVideoForJobPreview:(hb_job_t *)job andTitle:(hb_title_t *)title;
-@property (nonatomic, readwrite) int videoEncoder;
+@property (nonatomic, readwrite) int encoder;
@property (nonatomic, readwrite) int qualityType;
@property (nonatomic, readwrite) int avgBitrate;
-@property (nonatomic, readwrite) float quality;
+@property (nonatomic, readwrite) double quality;
@property (nonatomic, readwrite) int frameRate;
@property (nonatomic, readwrite) int frameRateMode;
-
@property (nonatomic, readwrite) BOOL fastFirstPass;
@property (nonatomic, readwrite) BOOL twoPass;
@property (nonatomic, readwrite) BOOL turboTwoPass;
+/**
+ * Encoder specifics options
+ */
+
+@property (nonatomic, readwrite) BOOL advancedOptions;
+@property (nonatomic, readwrite, copy) NSString *preset;
+@property (nonatomic, readwrite, copy) NSString *tune;
+@property (nonatomic, readwrite, copy) NSString *profile;
+@property (nonatomic, readwrite, copy) NSString *level;
+
@property (nonatomic, readwrite, copy) NSString *videoOptionExtra;
+@property (nonatomic, readwrite) BOOL fastDecode;
+
+/**
+ * Arrays of possible options for the video properties.
+ */
+@property (nonatomic, readonly) NSArray *encoders;
+@property (nonatomic, readonly) NSArray *frameRates;
+
+@property (nonatomic, readonly) NSArray *presets;
+@property (nonatomic, readonly) NSArray *tunes;
+@property (nonatomic, readonly) NSArray *profiles;
+@property (nonatomic, readonly) NSArray *levels;
+
+@property (nonatomic, readonly) BOOL fastDecodeSupported;
+
+@property (nonatomic, readonly) NSString *unparseOptions;
+
+@property (nonatomic, readonly) double qualityMinValue;
+@property (nonatomic, readonly) double qualityMaxValue;
+
+/**
+ * Width and height for x264 unparse. Will be removed later.
+ */
+@property (nonatomic, readwrite) int widthForUnparse;
+@property (nonatomic, readwrite) int heightForUnparse;
+
+/**
+ * Current container, this will be removed later too.
+ */
+@property (nonatomic, readwrite) int container;
+
+@end
+
+/**
+ * A series of value trasformers to bridge the libhb enums
+ * to the textual rapresentations used in the interface.
+ */
+@interface HBVideoEncoderTransformer : NSValueTransformer
+@end
+
+@interface HBFrameRateTransformer : NSValueTransformer
+@end
+
+@interface HBPresetsTransformer : NSValueTransformer
+- (instancetype)initWithEncoder:(int)encoder;
+@end
+
+@interface HBQualityTransformer : NSValueTransformer
+- (instancetype)initWithReversedDirection:(BOOL)reverse min:(double)min max:(double)max;
@end
diff --git a/macosx/HBVideo.m b/macosx/HBVideo.m
index 9160934e9..0319174f7 100644
--- a/macosx/HBVideo.m
+++ b/macosx/HBVideo.m
@@ -1,18 +1,999 @@
-//
-// HBVideo.m
-// HandBrake
-//
-// Created by Damiano Galassi on 12/08/14.
-//
-//
+/* HBVideo.m $
+
+ This file is part of the HandBrake source code.
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License. */
#import "HBVideo.h"
+@interface HBVideo ()
+
+@property (nonatomic, readwrite) double qualityMinValue;
+@property (nonatomic, readwrite) double qualityMaxValue;
+
+@property (nonatomic, readwrite) NSUInteger mediumPresetIndex;
+
+@end
+
@implementation HBVideo
+- (instancetype)init
+{
+ self = [super init];
+ if (self) {
+ _encoder = HB_VCODEC_X264;
+ _avgBitrate = 1000;
+ _quality = 18.0;
+ _qualityMaxValue = 51.0f;
+
+ _widthForUnparse = 1;
+ _heightForUnparse = 1;
+
+ [self updateQualityBounds];
+ }
+ return self;
+}
+
+#pragma mark - Setters
+
+/**
+ * Updates the min/max quality values
+ */
+- (void)updateQualityBounds
+{
+ // Get the current slider maxValue to check for a change in slider scale
+ // later so that we can choose a new similar value on the new slider scale
+ double previousMaxValue = self.qualityMaxValue;
+ double previousPercentOfSliderScale = (self.quality / (self.qualityMaxValue - self.qualityMinValue + 1));
+
+ int direction;
+ float minValue, maxValue, granularity;
+ hb_video_quality_get_limits(self.encoder,
+ &minValue, &maxValue, &granularity, &direction);
+
+ self.qualityMinValue = minValue;
+ self.qualityMaxValue = maxValue;
+
+ // check to see if we have changed slider scales
+ if (previousMaxValue != maxValue)
+ {
+ // if so, convert the old setting to the new scale as close as possible
+ // based on percentages
+ self.quality = floor((maxValue - minValue + 1.) * (previousPercentOfSliderScale));
+ }
+}
+
+- (void)setEncoder:(int)encoder
+{
+ _encoder = encoder;
+ [self updateQualityBounds];
+ [self validatePresetsSettings];
+ [self validateAdvancedOptions];
+}
+
+- (void)setContainer:(int)container
+{
+ _container = container;
+
+ BOOL encoderSupported = NO;
+
+ for (const hb_encoder_t *video_encoder = hb_video_encoder_get_next(NULL);
+ video_encoder != NULL;
+ video_encoder = hb_video_encoder_get_next(video_encoder))
+ {
+ if (video_encoder->muxers & self.container)
+ {
+ if (video_encoder->codec == self.encoder)
+ {
+ encoderSupported = YES;
+ }
+ }
+ }
+
+ if (!encoderSupported)
+ {
+ self.encoder = HB_VCODEC_X264;
+ }
+}
+
+- (void)setTune:(NSString *)tune
+{
+ [_tune autorelease];
+
+ if (![tune isEqualToString:@"none"])
+ {
+ _tune = [tune copy];
+ }
+ else
+ {
+ _tune = @"";
+ }
+}
+
+- (void)validatePresetsSettings
+{
+ NSArray *presets = self.presets;
+ if (presets.count && ![presets containsObject:self.preset]) {
+ self.preset = presets[self.mediumPresetIndex];
+ }
+
+ NSArray *tunes = self.tunes;
+ if (tunes.count && ![tunes containsObject:self.tune]) {
+ self.tune = tunes.firstObject;
+ }
+
+ NSArray *profiles = self.profiles;
+ if (profiles.count && ![profiles containsObject:self.profile]) {
+ self.profile = profiles.firstObject;
+ }
+
+ NSArray *levels = self.levels;
+ if (levels.count && ![levels containsObject:self.level]) {
+ self.level = levels.firstObject;
+ }
+}
+
+- (void)validateAdvancedOptions
+{
+ if (self.encoder != HB_VCODEC_H264_MASK)
+ {
+ self.advancedOptions = NO;
+ }
+}
+
+#pragma mark - Possible values
+
+- (NSArray *)encoders
+{
+ NSMutableArray *encoders = [NSMutableArray array];
+ for (const hb_encoder_t *video_encoder = hb_video_encoder_get_next(NULL);
+ video_encoder != NULL;
+ video_encoder = hb_video_encoder_get_next(video_encoder))
+ {
+ if (video_encoder->muxers & self.container)
+ {
+ [encoders addObject:@(video_encoder->name)];
+ }
+ }
+ return [[encoders copy] autorelease];
+}
+
+- (NSArray *)frameRates
+{
+ NSMutableArray *framerates = [NSMutableArray array];
+
+ [framerates addObject:NSLocalizedString(@"Same as source", @"")];
+
+ for (const hb_rate_t *video_framerate = hb_video_framerate_get_next(NULL);
+ video_framerate != NULL;
+ video_framerate = hb_video_framerate_get_next(video_framerate))
+ {
+ NSString *itemTitle;
+ if (!strcmp(video_framerate->name, "23.976"))
+ {
+ itemTitle = @"23.976 (NTSC Film)";
+ }
+ else if (!strcmp(video_framerate->name, "25"))
+ {
+ itemTitle = @"25 (PAL Film/Video)";
+ }
+ else if (!strcmp(video_framerate->name, "29.97"))
+ {
+ itemTitle = @"29.97 (NTSC Video)";
+ }
+ else
+ {
+ itemTitle = @(video_framerate->name);
+ }
+
+ [framerates addObject:itemTitle];
+ }
+ return [[framerates copy] autorelease];
+}
+
+- (NSArray *)presets
+{
+ NSMutableArray *temp = [NSMutableArray array];
+
+ const char * const *presets = hb_video_encoder_get_presets(self.encoder);
+ for (int i = 0; presets != NULL && presets[i] != NULL; i++)
+ {
+ [temp addObject:@(presets[i])];
+ if (!strcasecmp(presets[i], "medium"))
+ {
+ self.mediumPresetIndex = i;
+ }
+ }
+
+ return [[temp copy] autorelease];
+}
+
+- (NSArray *)tunes
+{
+ NSMutableArray *temp = [NSMutableArray array];
+
+ [temp addObject:@"none"];
+
+ const char * const *tunes = hb_video_encoder_get_tunes(self.encoder);
+
+ for (int i = 0; tunes != NULL && tunes[i] != NULL; i++)
+ {
+ // we filter out "fastdecode" as we have a dedicated checkbox for it
+ if (strcasecmp(tunes[i], "fastdecode") != 0)
+ {
+ [temp addObject:@(tunes[i])];
+ }
+ }
+
+ return [[temp copy] autorelease];
+}
+
+- (NSArray *)profiles
+{
+ NSMutableArray *temp = [NSMutableArray array];
+
+ const char * const *profiles = hb_video_encoder_get_profiles(self.encoder);
+ for (int i = 0; profiles != NULL && profiles[i] != NULL; i++)
+ {
+ [temp addObject:@(profiles[i])];
+ }
+
+ return [[temp copy] autorelease];
+}
+
+- (NSArray *)levels
+{
+ NSMutableArray *temp = [NSMutableArray array];
+
+ const char * const *levels = hb_video_encoder_get_levels(self.encoder);
+ for (int i = 0; levels != NULL && levels[i] != NULL; i++)
+ {
+ [temp addObject:@(levels[i])];
+ }
+ if (!temp.count)
+ {
+ [temp addObject:@"auto"];
+ }
+
+ return [[temp copy] autorelease];
+}
+
+- (BOOL)fastDecodeSupported
+{
+ return (self.encoder == HB_VCODEC_X264);
+}
+
+/**
+ * This is called everytime a x264 widget in the video tab is changed to
+ * display the expanded options in a text field via outlet fDisplayX264PresetsUnparseTextField
+ */
+- (NSString *)unparseOptions
+{
+ if (self.encoder != HB_VCODEC_X264)
+ {
+ return @"";
+ }
+
+ /* API reference:
+ *
+ * char * hb_x264_param_unparse(const char *x264_preset,
+ * const char *x264_tune,
+ * const char *x264_encopts,
+ * const char *h264_profile,
+ * const char *h264_level,
+ * int width, int height);
+ */
+ NSString *tmpString;
+ const char *x264_preset = [self.preset UTF8String];
+ const char *x264_tune = NULL;
+ const char *advanced_opts = NULL;
+ const char *h264_profile = NULL;
+ const char *h264_level = NULL;
+
+ tmpString = self.tune;
+ if (self.fastDecode)
+ {
+ if (self.tune.length)
+ {
+ tmpString = [tmpString stringByAppendingString: @","];
+ }
+ tmpString = [tmpString stringByAppendingString: @"fastdecode"];
+ }
+
+ // prepare the tune, advanced options, profile and level
+ if ([tmpString length])
+ {
+ x264_tune = [tmpString UTF8String];
+ }
+ if ([(tmpString = self.videoOptionExtra) length])
+ {
+ advanced_opts = [tmpString UTF8String];
+ }
+ if ([(tmpString = self.profile) length])
+ {
+ h264_profile = [tmpString UTF8String];
+ }
+ if ([(tmpString = self.level) length])
+ {
+ h264_level = [tmpString UTF8String];
+ }
+
+ // now, unparse
+ char *fX264PresetsUnparsedUTF8String = hb_x264_param_unparse(x264_preset,
+ x264_tune,
+ advanced_opts,
+ h264_profile,
+ h264_level,
+ _widthForUnparse, _heightForUnparse);
+ // update the text field
+ if (fX264PresetsUnparsedUTF8String != NULL)
+ {
+ tmpString = [NSString stringWithUTF8String:fX264PresetsUnparsedUTF8String];
+ free(fX264PresetsUnparsedUTF8String);
+ }
+ else
+ {
+ tmpString = @"";
+ }
+
+ return tmpString;
+}
+
++ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
+{
+ NSSet *retval = nil;
+
+ // Tell KVO to reload the presets settings
+ // after a change to the encoder.
+ if ([key isEqualToString:@"presets"] ||
+ [key isEqualToString:@"tunes"] ||
+ [key isEqualToString:@"profiles"] ||
+ [key isEqualToString:@"levels"])
+ {
+ retval = [NSSet setWithObjects:@"encoder", nil];
+ }
+
+ // Tell KVO to reload the x264 unparse string
+ // after values changes.
+ if ([key isEqualToString:@"unparseOptions"])
+ {
+ retval = [NSSet setWithObjects:@"encoder", @"preset", @"tune", @"profile", @"level",
+ @"videoOptionExtra", @"fastDecode", @"widthForUnparse", @"heightForUnparse", nil];
+ }
+
+ if ([key isEqualToString:@"encoders"])
+ {
+ retval = [NSSet setWithObjects:@"container", nil];
+ }
+
+ if ([key isEqualToString:@"fastDecodeSupported"])
+ {
+ retval = [NSSet setWithObjects:@"encoder", nil];
+ }
+
+ return retval;
+}
+
+#pragma mark - Various conversion methods from dict/preset/queue/etc
+
- (void)applySettingsFromPreset:(NSDictionary *)preset
{
+ // map legacy encoder names via libhb.
+ const char *strValue = hb_video_encoder_sanitize_name([preset[@"VideoEncoder"] UTF8String]);
+ self.encoder = hb_video_encoder_get_from_name(strValue);
+
+ if (self.encoder == HB_VCODEC_X264 || self.encoder == HB_VCODEC_X265)
+ {
+ if (self.encoder == HB_VCODEC_X264 &&
+ (!preset[@"x264UseAdvancedOptions"] ||
+ [preset[@"x264UseAdvancedOptions"] intValue]))
+ {
+ // x264UseAdvancedOptions is not set (legacy preset)
+ // or set to 1 (enabled), so we use the old advanced panel.
+ if (preset[@"x264Option"])
+ {
+ // we set the advanced options string here if applicable.
+ self.advancedOptions = YES;
+ self.videoOptionExtra = preset[@"x264Option"];
+ }
+ else
+ {
+ self.videoOptionExtra = nil;
+ }
+ // preset does not use the x264 preset system, reset the widgets.
+ self.preset = nil;
+ self.tune = nil;
+ self.profile = nil;
+ self.level = nil;
+ }
+ else
+ {
+ // x264UseAdvancedOptions is set to 0 (disabled),
+ // so we use the new preset system and
+ // disable the advanced panel
+ self.advancedOptions = NO;
+
+ if (preset[@"x264Preset"])
+ {
+ // Read the old x264 preset keys
+ self.preset = preset[@"x264Preset"];
+ self.tune = preset[@"x264Tune"];
+ self.videoOptionExtra = preset[@"x264OptionExtra"];
+ self.profile = preset[@"h264Profile"];
+ self.level = preset[@"h264Level"];
+ }
+ else
+ {
+ // Read the new preset keys (0.10)
+ self.preset = preset[@"VideoPreset"];
+ self.tune = preset[@"VideoTune"];
+ self.videoOptionExtra = preset[@"VideoOptionExtra"];
+ self.profile = preset[@"VideoProfile"];
+ self.level = preset[@"VideoLevel"];
+ }
+ }
+ }
+ else
+ {
+ if (preset[@"lavcOption"])
+ {
+ // Load the old format
+ self.videoOptionExtra = preset[@"lavcOption"];
+ }
+ else
+ {
+ self.videoOptionExtra = preset[@"VideoOptionExtra"];
+ }
+ }
+
+ int qualityType = [preset[@"VideoQualityType"] intValue] - 1;
+ /* Note since the removal of Target Size encoding, the possible values for VideoQuality type are 0 - 1.
+ * Therefore any preset that uses the old 2 for Constant Quality would now use 1 since there is one less index
+ * for the fVidQualityMatrix. It should also be noted that any preset that used the deprecated Target Size
+ * setting of 0 would set us to 0 or ABR since ABR is now tagged 0. Fortunately this does not affect any built-in
+ * presets since they all use Constant Quality or Average Bitrate.*/
+ if (qualityType == -1)
+ {
+ qualityType = 0;
+ }
+ self.qualityType = qualityType;
+
+ self.avgBitrate = [preset[@"VideoAvgBitrate"] intValue];
+ self.quality = [preset[@"VideoQualitySlider"] floatValue];
+
+ // Video framerate
+ if ([preset[@"VideoFramerate"] isEqualToString:@"Same as source"])
+ {
+ // Now set the Video Frame Rate Mode to either vfr or cfr according to the preset.
+ if (!preset[@"VideoFramerateMode"] ||
+ [preset[@"VideoFramerateMode"] isEqualToString:@"vfr"])
+ {
+ self.frameRateMode = 0; // we want vfr
+ }
+ else
+ {
+ self.frameRateMode = 1; // we want cfr
+ }
+ }
+ else
+ {
+ // Now set the Video Frame Rate Mode to either pfr or cfr according to the preset.
+ if ([preset[@"VideoFramerateMode"] isEqualToString:@"pfr"] ||
+ [preset[@"VideoFrameratePFR"] intValue] == 1)
+ {
+ self.frameRateMode = 0; // we want pfr
+ }
+ else
+ {
+ self.frameRateMode = 1; // we want cfr
+ }
+ }
+ // map legacy names via libhb.
+ int intValue = hb_video_framerate_get_from_name([preset[@"VideoFramerate"] UTF8String]);
+ if (intValue == -1)
+ {
+ intValue = 0;
+ }
+ self.frameRate = intValue;
+ // 2 Pass Encoding.
+ self.twoPass = [preset[@"VideoTwoPass"] intValue];
+
+ // Turbo 1st pass for 2 Pass Encoding.
+ self.turboTwoPass = [preset[@"VideoTurboTwoPass"] intValue];
+}
+
+- (void)prepareVideoForPreset:(NSMutableDictionary *)preset
+{
+ preset[@"VideoEncoder"] = @(hb_video_encoder_get_name(self.encoder));
+
+ // x264 Options, this will either be advanced panel or the video tabs x264 presets panel with modded option string
+ if (self.advancedOptions)
+ {
+ // use the old advanced panel.
+ preset[@"x264UseAdvancedOptions"] = @1;
+ preset[@"x264Option"] = self.videoOptionExtra;
+ }
+ else if (self.encoder == HB_VCODEC_X264 || self.encoder == HB_VCODEC_X265)
+ {
+ // use the new preset system.
+ preset[@"x264UseAdvancedOptions"] = @0;
+ preset[@"VideoPreset"] = self.preset;
+ preset[@"VideoTune"] = self.tune;
+ preset[@"VideoOptionExtra"] = self.videoOptionExtra;
+ preset[@"VideoProfile"] = self.profile;
+ preset[@"VideoLevel"] = self.level;
+ }
+ else
+ {
+ // FFmpeg (lavc) Option String
+ preset[@"VideoOptionExtra"] = self.videoOptionExtra;
+ }
+
+ /* though there are actually only 0 - 1 types available in the ui we need to map to the old 0 - 2
+ * set of indexes from when we had 0 == Target , 1 == Abr and 2 == Constant Quality for presets
+ * to take care of any legacy presets. */
+ preset[@"VideoQualityType"] = @(self.qualityType + 1);
+ preset[@"VideoAvgBitrate"] = @(self.avgBitrate);
+ preset[@"VideoQualitySlider"] = @(self.quality);
+
+ /* Video framerate and framerate mode */
+ if (self.frameRateMode == 1)
+ {
+ preset[@"VideoFramerateMode"] = @"cfr";
+ }
+ if (self.frameRateMode == 0) // Same as source is selected
+ {
+ preset[@"VideoFramerate"] = @"Same as source";
+
+ if (self.frameRateMode == 0)
+ {
+ preset[@"VideoFramerateMode"] = @"vfr";
+ }
+ }
+ else // translate the rate (selected item's tag) to the official libhb name
+ {
+ preset[@"VideoFramerate"] = [NSString stringWithFormat:@"%s",
+ hb_video_framerate_get_name((int)self.frameRate)];
+
+ if (self.frameRateMode == 0)
+ {
+ preset[@"VideoFramerateMode"] = @"pfr";
+ }
+ }
+
+ preset[@"VideoTwoPass"] = @(self.twoPass);
+ preset[@"VideoTurboTwoPass"] = @(self.turboTwoPass);
+}
+
+- (void)applyVideoSettingsFromQueue:(NSDictionary *)queueToApply
+{
+ // Video encoder
+ self.encoder = [queueToApply[@"JobVideoEncoderVcodec"] intValue];
+
+ // Advanced x264 options
+ if ([queueToApply[@"x264UseAdvancedOptions"] intValue])
+ {
+ // we are using the advanced panel
+ self.preset = nil;
+ self.tune = nil;
+ self.videoOptionExtra = queueToApply[@"x264Option"];
+ self.profile = nil;
+ self.level = nil;
+ }
+ else if (self.encoder == HB_VCODEC_X264 || self.encoder == HB_VCODEC_X265)
+ {
+ // we are using the x264 preset system
+ self.preset = queueToApply[@"VideoPreset"];
+ self.tune = queueToApply[@"VideoTune"];
+ self.videoOptionExtra = queueToApply[@"VideoOptionExtra"];
+ self.profile = queueToApply[@"VideoProfile"];
+ self.level = queueToApply[@"VideoLevel"];
+ }
+ else
+ {
+ self.videoOptionExtra = queueToApply[@"VideoOptionExtra"];
+ }
+
+ self.qualityType = [queueToApply[@"VideoQualityType"] intValue] - 1;
+
+ self.avgBitrate = [queueToApply[@"VideoAvgBitrate"] intValue];
+ self.quality = [queueToApply[@"VideoQualitySlider"] doubleValue];
+
+ // Video framerate
+ if ([queueToApply[@"JobIndexVideoFramerate"] intValue] == 0)
+ {
+ // Now set the Video Frame Rate Mode to either vfr or cfr according to the preset
+ if ([queueToApply[@"VideoFramerateMode"] isEqualToString:@"vfr"])
+ {
+ self.frameRateMode = 0; // we want vfr
+ }
+ else
+ {
+ self.frameRateMode = 1; // we want cfr
+ }
+ }
+ else
+ {
+ // Now set the Video Frame Rate Mode to either pfr or cfr according to the preset
+ if ([queueToApply[@"VideoFramerateMode"] isEqualToString:@"pfr"])
+ {
+ self.frameRateMode = 0; // we want pfr
+ }
+ else
+ {
+ self.frameRateMode = 1; // we want cfr
+ }
+ }
+
+ self.frameRate = [queueToApply[@"JobIndexVideoFramerate"] intValue];
+
+ self.twoPass = [queueToApply[@"VideoTwoPass"] intValue];
+ self.turboTwoPass = [queueToApply[@"VideoTurboTwoPass"] intValue];
+}
+
+- (void)prepareVideoForQueueFileJob:(NSMutableDictionary *)queueFileJob
+{
+ // Video encoder.
+ queueFileJob[@"VideoEncoder"] = @(hb_video_encoder_get_name(self.encoder));
+ queueFileJob[@"JobVideoEncoderVcodec"] = @(self.encoder);
+
+ // x264 advanced options.
+ if (self.advancedOptions)
+ {
+ // we are using the advanced panel
+ queueFileJob[@"x264UseAdvancedOptions"] = @1;
+ queueFileJob[@"x264Option"] = self.videoOptionExtra;
+ }
+ else if (self.encoder == HB_VCODEC_X264 || self.encoder == HB_VCODEC_X265)
+ {
+ // we are using the x264/x265 preset system.
+ queueFileJob[@"x264UseAdvancedOptions"] = @0;
+ queueFileJob[@"VideoPreset"] = self.preset;
+ queueFileJob[@"VideoTune"] = self.tune;
+ queueFileJob[@"VideoOptionExtra"] = self.videoOptionExtra;
+ queueFileJob[@"VideoProfile"] = self.profile;
+ queueFileJob[@"VideoLevel"] = self.level;
+ }
+ else
+ {
+ // FFmpeg (lavc) Option String.
+ queueFileJob[@"VideoOptionExtra"] = self.videoOptionExtra;
+ }
+
+ queueFileJob[@"VideoQualityType"] = @(self.qualityType + 1);
+ queueFileJob[@"VideoAvgBitrate"] = @(self.avgBitrate);
+ queueFileJob[@"VideoQualitySlider"] = @(self.quality);
+
+ // Framerate
+ if (self.frameRate)
+ {
+ queueFileJob[@"VideoFramerate"] = @(hb_video_framerate_get_name(self.frameRate));
+ }
+ else
+ {
+ queueFileJob[@"VideoFramerate"] = @"Same as source";
+ }
+ queueFileJob[@"JobIndexVideoFramerate"] = @(self.frameRate);
+
+ // Frame Rate Mode
+ if (self.frameRateMode == 1) // if selected we are cfr regardless of the frame rate popup
+ {
+ queueFileJob[@"VideoFramerateMode"] = @"cfr";
+ }
+ else
+ {
+ if (self.frameRate == 0) // Same as source frame rate
+ {
+ queueFileJob[@"VideoFramerateMode"] = @"vfr";
+ }
+ else
+ {
+ queueFileJob[@"VideoFramerateMode"] = @"pfr";
+ }
+ }
+
+ // 2 Pass Encoding
+ queueFileJob[@"VideoTwoPass"] = @(self.twoPass);
+ queueFileJob[@"VideoTurboTwoPass"] = @(self.turboTwoPass);
+}
+
+- (void)prepareVideoForJobPreview:(hb_job_t *)job andTitle:(hb_title_t *)title
+{
+ job->vcodec = self.encoder;
+ job->fastfirstpass = 0;
+
+ job->chapter_markers = 0;
+
+ if (job->vcodec == HB_VCODEC_X264)
+ {
+ /* advanced x264 options */
+ NSString *tmpString;
+ // translate zero-length strings to NULL for libhb
+ const char *encoder_preset = NULL;
+ const char *encoder_tune = NULL;
+ const char *encoder_options = NULL;
+ const char *encoder_profile = NULL;
+ const char *encoder_level = NULL;
+ if (self.advancedOptions)
+ {
+ // we are using the advanced panel
+ if ([(tmpString = self.videoOptionExtra) length])
+ {
+ encoder_options = [tmpString UTF8String];
+ }
+ }
+ else
+ {
+ // we are using the x264 preset system
+ if ([(tmpString = self.tune) length])
+ {
+ encoder_tune = [tmpString UTF8String];
+ }
+ if ([(tmpString = self.videoOptionExtra) length])
+ {
+ encoder_options = [tmpString UTF8String];
+ }
+ if ([(tmpString = self.profile) length])
+ {
+ encoder_profile = [tmpString UTF8String];
+ }
+ if ([(tmpString = self.level) length])
+ {
+ encoder_level = [tmpString UTF8String];
+ }
+ encoder_preset = [self.preset UTF8String];
+ }
+ hb_job_set_encoder_preset (job, encoder_preset);
+ hb_job_set_encoder_tune (job, encoder_tune);
+ hb_job_set_encoder_options(job, encoder_options);
+ hb_job_set_encoder_profile(job, encoder_profile);
+ hb_job_set_encoder_level (job, encoder_level);
+ }
+ else if (job->vcodec & HB_VCODEC_FFMPEG_MASK)
+ {
+ hb_job_set_encoder_options(job, [self.videoOptionExtra UTF8String]);
+ }
+
+ /* Video settings */
+ int fps_mode, fps_num, fps_den;
+ if (self.frameRate > 0 )
+ {
+ /* a specific framerate has been chosen */
+ fps_num = 27000000;
+ fps_den = (int)self.frameRate;
+ if (self.frameRateMode == 1)
+ {
+ // CFR
+ fps_mode = 1;
+ }
+ else
+ {
+ // PFR
+ fps_mode = 2;
+ }
+ }
+ else
+ {
+ /* same as source */
+ fps_num = title->rate;
+ fps_den = title->rate_base;
+ if (self.frameRateMode == 1)
+ {
+ // CFR
+ fps_mode = 1;
+ }
+ else
+ {
+ // VFR
+ fps_mode = 0;
+ }
+ }
+
+ switch (self.qualityType)
+ {
+ case 0:
+ /* ABR */
+ job->vquality = -1.0;
+ job->vbitrate = self.avgBitrate;
+ break;
+ case 1:
+ /* Constant Quality */
+ job->vquality = self.quality;
+ job->vbitrate = 0;
+ break;
+ }
+
+ /* Add framerate shaping filter */
+ hb_filter_object_t *filter = hb_filter_init(HB_FILTER_VFR);
+ hb_add_filter(job, filter, [[NSString stringWithFormat:@"%d:%d:%d",
+ fps_mode, fps_num, fps_den] UTF8String]);
+}
+
+@end
+
+#pragma mark - Value Trasformers
+
+@implementation HBVideoEncoderTransformer
+
++ (Class)transformedValueClass
+{
+ return [NSString class];
+}
+
+- (id)transformedValue:(id)value
+{
+ const char *name = hb_video_encoder_get_name([value intValue]);
+ if (name)
+ {
+ return @(name);
+ }
+ else
+ {
+ return nil;
+ }
+}
+
++ (BOOL)allowsReverseTransformation
+{
+ return YES;
+}
+
+- (id)reverseTransformedValue:(id)value
+{
+ return @(hb_video_encoder_get_from_name([value UTF8String]));
+}
+
+@end
+
+@implementation HBFrameRateTransformer
+
++ (Class)transformedValueClass
+{
+ return [NSString class];
+}
+
+- (id)transformedValue:(id)value
+{
+ const char *name = hb_video_framerate_get_name([value intValue]);
+ if (name)
+ {
+ return @(name);
+ }
+ else
+ {
+ return NSLocalizedString(@"Same as source", @"");
+ }
+}
+
++ (BOOL)allowsReverseTransformation
+{
+ return YES;
+}
+
+- (id)reverseTransformedValue:(id)value
+{
+ if ([value isEqualTo:NSLocalizedString(@"Same as source", @"")])
+ {
+ return @0;
+ }
+ else
+ {
+ return @(hb_video_framerate_get_from_name([value UTF8String]));
+ }
}
@end
+
+@implementation HBPresetsTransformer
+{
+ int _encoder;
+}
+
+- (instancetype)initWithEncoder:(int)encoder
+{
+ self = [super init];
+ if (self)
+ {
+ _encoder = encoder;
+ }
+ return self;
+}
+
++ (Class)transformedValueClass
+{
+ return [NSNumber class];
+}
+
+- (id)transformedValue:(id)value
+{
+ const char * const *presets = hb_video_encoder_get_presets(_encoder);
+ for (int i = 0; presets[i] != NULL; i++)
+ {
+ if (!strcasecmp(presets[i], [value UTF8String]))
+ {
+ return @(i);
+ }
+ }
+
+ return @(-1);
+}
+
++ (BOOL)allowsReverseTransformation
+{
+ return YES;
+}
+
+- (id)reverseTransformedValue:(id)value
+{
+ const char * const *presets = hb_video_encoder_get_presets(_encoder);
+ for (int i = 0; presets[i] != NULL; i++)
+ {
+ if (i == [value intValue])
+ {
+ return @(presets[i]);
+ }
+ }
+
+ return @"none";
+}
+
+@end
+
+@implementation HBQualityTransformer
+{
+ BOOL _reverse;
+ double _min;
+ double _max;
+}
+
+- (instancetype)initWithReversedDirection:(BOOL)reverse min:(double)min max:(double)max
+{
+ self = [super init];
+ if (self)
+ {
+ _reverse = reverse;
+ _min = min;
+ _max = max;
+ }
+
+ return self;
+}
+
++ (Class)transformedValueClass
+{
+ return [NSNumber class];
+}
+
+- (id)transformedValue:(id)value
+{
+ if (_reverse)
+ {
+ double inverseValue = _min + _max - [value doubleValue];
+ return @(inverseValue);
+ }
+ else
+ {
+ return value;
+ }
+}
+
++ (BOOL)allowsReverseTransformation
+{
+ return YES;
+}
+
+- (id)reverseTransformedValue:(id)value
+{
+ if (_reverse)
+ {
+ double inverseValue = _min + _max - [value doubleValue];
+ return @(inverseValue);
+ }
+ else
+ {
+ return value;
+ }
+}
+
+@end \ No newline at end of file