/* HBVideo+UIAdditions.m $
This file is part of the HandBrake source code.
Homepage: .
It may be used under the terms of the GNU General Public License. */
#import "HBVideo+UIAdditions.h"
#import "HBJob+Private.h"
#import "HBLocalizationUtilities.h"
#include "hb.h"
@implementation HBVideo (UIAdditions)
@dynamic qualityMaxValue;
@dynamic qualityMinValue;
@dynamic levels;
@dynamic tunes;
@dynamic presets;
@dynamic profiles;
#pragma mark - Possible values
+ (NSSet *)keyPathsForValuesAffectingEncoders
{
return [NSSet setWithObjects:@"job.container", nil];
}
- (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.job.container)
{
[encoders addObject:@(video_encoder->name)];
}
}
return [encoders copy];
}
- (NSArray *)frameRates
{
NSMutableArray *framerates = [NSMutableArray array];
[framerates addObject:HBKitLocalizedString(@"Same as source", @"HBVideo -> frame rates display name")];
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];
}
+ (NSSet *)keyPathsForValuesAffectingFastDecodeSupported
{
return [NSSet setWithObjects:@"encoder", nil];
}
- (BOOL)fastDecodeSupported
{
const char * const *tunes = hb_video_encoder_get_tunes(self.encoder);
for (int i = 0; tunes != NULL && tunes[i] != NULL; i++)
{
if (!strcasecmp(tunes[i], "fastdecode"))
{
return YES;
}
}
return NO;
}
+ (NSSet *)keyPathsForValuesAffectingTurboTwoPassSupported
{
return [NSSet setWithObjects:@"encoder", nil];
}
- (BOOL)turboTwoPassSupported
{
return ((self.encoder & HB_VCODEC_X264_MASK) ||
(self.encoder & HB_VCODEC_X265_MASK));
}
+ (NSSet *)keyPathsForValuesAffectingTwoPassSupported
{
return [NSSet setWithObjects:@"encoder", nil];
}
- (BOOL)twoPassSupported
{
return !((self.encoder & HB_VCODEC_FFMPEG_VT_H264) ||
(self.encoder & HB_VCODEC_FFMPEG_VT_H265));
}
+ (NSSet *)keyPathsForValuesAffectingConstantQualityLabel
{
return [NSSet setWithObjects:@"encoder", nil];
}
- (NSString *)constantQualityLabel
{
return @(hb_video_quality_get_name(self.encoder));
}
+ (NSSet *)keyPathsForValuesAffectingIsConstantQualitySupported
{
return [NSSet setWithObjects:@"encoder", nil];
}
- (BOOL)isConstantQualitySupported
{
return (self.qualityMaxValue == 0 && self.qualityMinValue == 0) == NO;
}
+ (NSSet *)keyPathsForValuesAffectingUnparseOptions
{
return [NSSet setWithObjects:@"encoder", @"preset", @"tune", @"profile", @"level",
@"videoOptionExtra", @"fastDecode", @"job.picture.width", @"job.picture.height", nil];
}
/**
* 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_MASK))
{
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;
// prepare the tune, advanced options, profile and level
if ([tmpString = self.completeTune 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(
hb_video_encoder_get_depth(self.encoder),
x264_preset, x264_tune, advanced_opts,
h264_profile, h264_level,
self.job.picture.width,
self.job.picture.height);
// update the text field
if (fX264PresetsUnparsedUTF8String != NULL)
{
tmpString = @(fX264PresetsUnparsedUTF8String);
free(fX264PresetsUnparsedUTF8String);
}
else
{
tmpString = @"";
}
return tmpString;
}
@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)
{
if (!strcmp(name, "23.976"))
{
return @"23.976 (NTSC Film)";
}
else if (!strcmp(name, "25"))
{
return @"25 (PAL Film/Video)";
}
else if (!strcmp(name, "29.97"))
{
return @"29.97 (NTSC Video)";
}
else
{
return @(name);
}
}
else
{
return HBKitLocalizedString(@"Same as source", @"HBVideo -> frame rates display name");
}
}
+ (BOOL)allowsReverseTransformation
{
return YES;
}
- (id)reverseTransformedValue:(id)value
{
if ([value isEqualTo:HBKitLocalizedString(@"Same as source", @"HBVideo -> frame rates display name")])
{
return @0;
}
else
{
return @(hb_video_framerate_get_from_name([value UTF8String]));
}
}
@end
@implementation HBPresetsTransformer
{
int _encoder;
}
- (instancetype)init
{
@throw nil;
}
- (instancetype)initWithEncoder:(int)encoder
{
self = [super init];
if (self)
{
_encoder = encoder;
}
return self;
}
+ (Class)transformedValueClass
{
return [NSNumber class];
}
- (id)transformedValue:(id)value
{
if (value)
{
const char * const *presets = hb_video_encoder_get_presets(_encoder);
for (int i = 0; presets != NULL && 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 != NULL && presets[i] != NULL; i++)
{
if (i == [value intValue])
{
return @(presets[i]);
}
}
return @"none";
}
@end
@implementation HBQualityTransformer
{
BOOL _reverse;
double _min;
double _max;
}
- (instancetype)init
{
return [self initWithReversedDirection:NO min:0 max:50];
}
- (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
@implementation HBVideo (EncoderAdditions)
- (BOOL)isUnparsedSupported:(int)encoder
{
return (encoder & HB_VCODEC_X264_MASK) != 0;
}
- (BOOL)isPresetSystemSupported:(int)encoder
{
return hb_video_encoder_get_presets(encoder) != NULL;
}
- (BOOL)isSimpleOptionsPanelSupported:(int)encoder
{
return (encoder & HB_VCODEC_FFMPEG_MASK) != 0;
}
- (void)qualityLimitsForEncoder:(int)encoder low:(float *)low high:(float *)high granularity:(float *)granularity direction:(int *)direction
{
hb_video_quality_get_limits(encoder, low, high, granularity, direction);
}
@end