mirror of https://github.com/HandBrake/HandBrake
682 lines
24 KiB
Objective-C
682 lines
24 KiB
Objective-C
/* HBJob+HBJobConversion.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 "HBJob+HBJobConversion.h"
|
|
|
|
#import "HBAudioDefaults.h"
|
|
#import "HBAudioTrack.h"
|
|
|
|
#import "HBSubtitlesTrack.h"
|
|
|
|
#import "HBChapter.h"
|
|
|
|
#import "HBTitle+Private.h"
|
|
#import "HBMutablePreset.h"
|
|
|
|
#import "HBRange.h"
|
|
#import "HBVideo.h"
|
|
#import "HBPicture.h"
|
|
#import "HBFilters.h"
|
|
#import "HBAudio.h"
|
|
#import "HBSubtitles.h"
|
|
|
|
#import "NSDictionary+HBAdditions.h"
|
|
|
|
@implementation HBJob (HBJobConversion)
|
|
|
|
- (NSDictionary *)jobDict
|
|
{
|
|
NSAssert(self.title, @"HBJob: calling jobDict without a valid title loaded");
|
|
|
|
HBMutablePreset *preset = [[HBMutablePreset alloc] init];
|
|
[self writeToPreset:preset];
|
|
|
|
return [self.title jobSettingsWithPreset:preset];
|
|
}
|
|
|
|
/**
|
|
* Prepares a hb_job_t
|
|
*/
|
|
- (hb_job_t *)hb_job
|
|
{
|
|
NSAssert(self.title, @"HBJob: calling hb_job without a valid title loaded");
|
|
NSAssert(self.destinationURL, @"HBJob: calling hb_job without a valid destination");
|
|
|
|
hb_title_t *title = self.title.hb_title;
|
|
hb_job_t *job = hb_job_init(title);
|
|
|
|
hb_job_set_file(job, self.destinationURL.fileSystemRepresentation);
|
|
|
|
if (self.hwDecodeUsage == HBJobHardwareDecoderUsageFullPathOnly)
|
|
{
|
|
job->hw_decode = HB_DECODE_VIDEOTOOLBOX;
|
|
}
|
|
else if (self.hwDecodeUsage == HBJobHardwareDecoderUsageAlways)
|
|
{
|
|
job->hw_decode = HB_DECODE_VIDEOTOOLBOX | HB_DECODE_FORCE_HW;
|
|
}
|
|
|
|
// Title Angle for dvdnav
|
|
job->angle = self.angle;
|
|
|
|
if (self.range.type == HBRangeTypeChapters)
|
|
{
|
|
// Chapter selection
|
|
job->chapter_start = self.range.chapterStart + 1;
|
|
job->chapter_end = self.range.chapterStop + 1;
|
|
}
|
|
else if (self.range.type == HBRangeTypeSeconds)
|
|
{
|
|
// we are pts based start / stop
|
|
// Point A to Point B. Time to time in seconds.
|
|
// get the start seconds from the start seconds field
|
|
int start_seconds = self.range.secondsStart;
|
|
job->pts_to_start = start_seconds * 90000LL;
|
|
// Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds
|
|
int stop_seconds = self.range.secondsStop;
|
|
job->pts_to_stop = (stop_seconds - start_seconds) * 90000LL;
|
|
}
|
|
else if (self.range.type == HBRangeTypeFrames)
|
|
{
|
|
// we are frame based start / stop
|
|
// Point A to Point B. Frame to frame
|
|
// get the start frame from the start frame field
|
|
int start_frame = self.range.frameStart;
|
|
job->frame_to_start = start_frame;
|
|
// get the frame to stop on from the end frame field
|
|
int stop_frame = self.range.frameStop;
|
|
job->frame_to_stop = stop_frame - start_frame + 1;
|
|
}
|
|
else if (self.range.type == HBRangePreviewIndex)
|
|
{
|
|
job->start_at_preview = self.range.previewIndex;
|
|
job->seek_points = self.range.previewsCount;
|
|
job->pts_to_stop = self.range.ptsToStop;
|
|
}
|
|
|
|
// Format (Muxer) and Video Encoder
|
|
job->mux = self.container;
|
|
job->vcodec = self.video.encoder;
|
|
|
|
if (self.video.colorRange != HBVideoColorRangeAuto)
|
|
{
|
|
job->color_range = (int)self.video.colorRange;
|
|
}
|
|
|
|
job->optimize = self.optimize;
|
|
|
|
if (self.container & HB_MUX_MASK_MP4)
|
|
{
|
|
job->align_av_start = self.alignAVStart;
|
|
}
|
|
|
|
// We set the chapter marker extraction here based on the format being
|
|
// mpeg4 or mkv and the checkbox being checked.
|
|
if (self.chaptersEnabled)
|
|
{
|
|
job->chapter_markers = 1;
|
|
|
|
// now lets get our saved chapter names out the array in the queue file
|
|
// and insert them back into the title chapter list. We have it here,
|
|
// because unless we are inserting chapter markers there is no need to
|
|
// spend the overhead of iterating through the chapter names array imo
|
|
// Also, note that if for some reason we don't apply chapter names, the
|
|
// chapters just come out 001, 002, etc. etc.
|
|
int i = 0;
|
|
for (HBChapter *jobChapter in self.chapterTitles)
|
|
{
|
|
hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item(job->list_chapter, i);
|
|
if (chapter != NULL)
|
|
{
|
|
hb_chapter_set_title(chapter, jobChapter.title.UTF8String);
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
job->chapter_markers = 0;
|
|
}
|
|
|
|
if (self.metadataPassthru == NO && job->metadata)
|
|
{
|
|
if (job->metadata->dict)
|
|
{
|
|
hb_dict_clear(job->metadata->dict);
|
|
}
|
|
if (job->metadata->list_coverart)
|
|
{
|
|
int count = hb_list_count(job->metadata->list_coverart);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
hb_metadata_rem_coverart(job->metadata, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (job->vcodec & HB_VCODEC_H264_MASK)
|
|
{
|
|
// iPod 5G atom
|
|
job->ipod_atom = self.mp4iPodCompatible;
|
|
}
|
|
|
|
if (self.video.multiPass && ((self.video.encoder & HB_VCODEC_X264_MASK) ||
|
|
(self.video.encoder & HB_VCODEC_X265_MASK)))
|
|
{
|
|
job->fastanalysispass = self.video.turboMultiPass;
|
|
}
|
|
job->multipass = self.video.multiPass;
|
|
|
|
switch (self.video.passthruHDRDynamicMetadata)
|
|
{
|
|
case HBVideoHDRDynamicMetadataPassthruOff:
|
|
job->passthru_dynamic_hdr_metadata = HB_HDR_DYNAMIC_METADATA_NONE;
|
|
break;
|
|
case HBVideoHDRDynamicMetadataPassthruHDR10Plus:
|
|
job->passthru_dynamic_hdr_metadata = HB_HDR_DYNAMIC_METADATA_HDR10PLUS;
|
|
break;
|
|
case HBVideoHDRDynamicMetadataPassthruDolbyVision:
|
|
job->passthru_dynamic_hdr_metadata = HB_HDR_DYNAMIC_METADATA_DOVI;
|
|
break;
|
|
case HBVideoHDRDynamicMetadataPassthruAll:
|
|
default:
|
|
job->passthru_dynamic_hdr_metadata = HB_HDR_DYNAMIC_METADATA_ALL;
|
|
break;
|
|
}
|
|
|
|
if (hb_video_encoder_get_presets(self.video.encoder) != NULL)
|
|
{
|
|
// advanced x264/x265 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;
|
|
|
|
// we are using the x264/x265 preset system
|
|
if ([(tmpString = self.video.completeTune) length])
|
|
{
|
|
encoder_tune = [tmpString UTF8String];
|
|
}
|
|
if ([(tmpString = self.video.videoOptionExtra) length])
|
|
{
|
|
encoder_options = [tmpString UTF8String];
|
|
}
|
|
if ([(tmpString = self.video.profile) length])
|
|
{
|
|
encoder_profile = [tmpString UTF8String];
|
|
}
|
|
if ([(tmpString = self.video.level) length])
|
|
{
|
|
encoder_level = [tmpString UTF8String];
|
|
}
|
|
encoder_preset = self.video.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.video.videoOptionExtra.UTF8String);
|
|
}
|
|
|
|
// Picture Size Settings
|
|
job->par.num = self.picture.parNum;
|
|
job->par.den = self.picture.parDen;
|
|
|
|
// Video settings
|
|
// Framerate
|
|
int fps_mode, fps_num, fps_den;
|
|
if (self.video.frameRate > 0)
|
|
{
|
|
// a specific framerate has been chosen
|
|
fps_num = 27000000;
|
|
fps_den = self.video.frameRate;
|
|
if (self.video.frameRateMode == 1)
|
|
{
|
|
// CFR
|
|
fps_mode = 1;
|
|
}
|
|
else
|
|
{
|
|
// PFR
|
|
fps_mode = 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// same as source
|
|
fps_num = title->vrate.num;
|
|
fps_den = title->vrate.den;
|
|
if (self.video.frameRateMode == 1)
|
|
{
|
|
// CFR
|
|
fps_mode = 1;
|
|
}
|
|
else
|
|
{
|
|
// VFR
|
|
fps_mode = 0;
|
|
}
|
|
}
|
|
|
|
switch (self.video.qualityType)
|
|
{
|
|
case 0:
|
|
// ABR
|
|
job->vquality = HB_INVALID_VIDEO_QUALITY;
|
|
job->vbitrate = self.video.avgBitrate;
|
|
break;
|
|
case 1:
|
|
// Constant Quality
|
|
job->vquality = self.video.quality;
|
|
job->vbitrate = 0;
|
|
break;
|
|
}
|
|
|
|
// Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle
|
|
for (HBSubtitlesTrack *subTrack in self.subtitles.tracks)
|
|
{
|
|
if (subTrack.isEnabled)
|
|
{
|
|
// Subtract 2 to the source indexes to compensate
|
|
// for the none and foreign audio search tracks.
|
|
int sourceIdx = ((int)subTrack.sourceTrackIdx) - 2;
|
|
|
|
// we need to check for the "Foreign Audio Search" which would be have an index of -1
|
|
if (sourceIdx == -1)
|
|
{
|
|
job->indepth_scan = 1;
|
|
|
|
if (subTrack.burnedIn)
|
|
{
|
|
job->select_subtitle_config.dest = RENDERSUB;
|
|
}
|
|
else
|
|
{
|
|
job->select_subtitle_config.dest = PASSTHRUSUB;
|
|
}
|
|
|
|
job->select_subtitle_config.force = subTrack.forcedOnly;
|
|
job->select_subtitle_config.default_track = subTrack.def;
|
|
}
|
|
else
|
|
{
|
|
// if we are getting the subtitles from an external file
|
|
if (subTrack.type == IMPORTSRT || subTrack.type == IMPORTSSA)
|
|
{
|
|
if (subTrack.fileURL)
|
|
{
|
|
hb_subtitle_config_t sub_config = {0};
|
|
sub_config.name = subTrack.title.UTF8String;
|
|
sub_config.offset = subTrack.offset;
|
|
|
|
// we need to strncpy file name and codeset
|
|
sub_config.src_filename = subTrack.fileURL.fileSystemRepresentation;
|
|
if (subTrack.charCode)
|
|
{
|
|
size_t len = sizeof(sub_config.src_codeset) - 1;
|
|
strncpy(sub_config.src_codeset, subTrack.charCode.UTF8String, len);
|
|
sub_config.src_codeset[len] = 0;
|
|
}
|
|
|
|
if (!subTrack.burnedIn && hb_subtitle_can_pass(subTrack.type, job->mux))
|
|
{
|
|
sub_config.dest = PASSTHRUSUB;
|
|
}
|
|
else if (hb_subtitle_can_burn(subTrack.type))
|
|
{
|
|
sub_config.dest = RENDERSUB;
|
|
}
|
|
|
|
sub_config.force = 0;
|
|
sub_config.default_track = subTrack.def;
|
|
hb_import_subtitle_add(job, &sub_config, subTrack.isoLanguage.UTF8String, subTrack.type);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We are setting a source subtitle so access the source subtitle info
|
|
hb_subtitle_t *subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, sourceIdx);
|
|
|
|
if (subt != NULL)
|
|
{
|
|
hb_subtitle_config_t sub_config = subt->config;
|
|
sub_config.name = subTrack.title.UTF8String;
|
|
|
|
if (!subTrack.burnedIn && hb_subtitle_can_pass(subt->source, job->mux))
|
|
{
|
|
sub_config.dest = PASSTHRUSUB;
|
|
}
|
|
else if (hb_subtitle_can_burn(subt->source))
|
|
{
|
|
sub_config.dest = RENDERSUB;
|
|
}
|
|
|
|
sub_config.force = subTrack.forcedOnly;
|
|
sub_config.default_track = subTrack.def;
|
|
hb_subtitle_add(job, &sub_config, sourceIdx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Audio Defaults (Selection Behavior)
|
|
job->acodec_copy_mask = 0;
|
|
|
|
HBAudioDefaults *audioDefaults = self.audio.defaults;
|
|
|
|
if (audioDefaults.allowAACPassthru)
|
|
{
|
|
job->acodec_copy_mask |= HB_ACODEC_AAC_PASS;
|
|
}
|
|
if (audioDefaults.allowAC3Passthru)
|
|
{
|
|
job->acodec_copy_mask |= HB_ACODEC_AC3_PASS;
|
|
}
|
|
if (audioDefaults.allowEAC3Passthru)
|
|
{
|
|
job->acodec_copy_mask |= HB_ACODEC_EAC3_PASS;
|
|
}
|
|
if (audioDefaults.allowDTSHDPassthru)
|
|
{
|
|
job->acodec_copy_mask |= HB_ACODEC_DCA_HD_PASS;
|
|
}
|
|
if (audioDefaults.allowDTSPassthru)
|
|
{
|
|
job->acodec_copy_mask |= HB_ACODEC_DCA_PASS;
|
|
}
|
|
if (audioDefaults.allowMP2Passthru)
|
|
{
|
|
job->acodec_copy_mask |= HB_ACODEC_MP2_PASS;
|
|
}
|
|
if (audioDefaults.allowMP3Passthru)
|
|
{
|
|
job->acodec_copy_mask |= HB_ACODEC_MP3_PASS;
|
|
}
|
|
if (audioDefaults.allowVorbisPassthru)
|
|
{
|
|
job->acodec_copy_mask |= HB_ACODEC_VORBIS_PASS;
|
|
}
|
|
if (audioDefaults.allowOpusPassthru)
|
|
{
|
|
job->acodec_copy_mask |= HB_ACODEC_OPUS_PASS;
|
|
}
|
|
if (audioDefaults.allowTrueHDPassthru)
|
|
{
|
|
job->acodec_copy_mask |= HB_ACODEC_TRUEHD_PASS;
|
|
}
|
|
if (audioDefaults.allowALACPassthru)
|
|
{
|
|
job->acodec_copy_mask |= HB_ACODEC_ALAC_PASS;
|
|
}
|
|
if (audioDefaults.allowFLACPassthru)
|
|
{
|
|
job->acodec_copy_mask |= HB_ACODEC_FLAC_PASS;
|
|
}
|
|
|
|
job->acodec_fallback = audioDefaults.encoderFallback;
|
|
|
|
// Audio tracks and mixdowns
|
|
// Now lets add our new tracks to the audio list here
|
|
for (HBAudioTrack *audioTrack in self.audio.tracks)
|
|
{
|
|
if (audioTrack.isEnabled)
|
|
{
|
|
hb_audio_config_t audio = {0};
|
|
hb_audio_config_init(&audio);
|
|
|
|
HBTitleAudioTrack *inputTrack = self.audio.sourceTracks[audioTrack.sourceTrackIdx];
|
|
|
|
int sampleRateToUse = (audioTrack.sampleRate == 0 ?
|
|
inputTrack.sampleRate :
|
|
audioTrack.sampleRate);
|
|
|
|
audio.index = (int)audioTrack.sourceTrackIdx - 1;
|
|
|
|
// We go ahead and assign values to our audio->out.<properties>
|
|
audio.out.track = audio.in.track;
|
|
audio.out.codec = audioTrack.encoder;
|
|
audio.out.compression_level = hb_audio_compression_get_default(audio.out.codec);
|
|
audio.out.mixdown = audioTrack.mixdown;
|
|
audio.out.normalize_mix_level = 0;
|
|
audio.out.bitrate = audioTrack.bitRate;
|
|
audio.out.samplerate = sampleRateToUse;
|
|
audio.out.dither_method = hb_audio_dither_get_default();
|
|
audio.out.name = audioTrack.title.UTF8String;
|
|
|
|
// output is not passthru so apply gain
|
|
if (!(audioTrack.encoder & HB_ACODEC_PASS_FLAG))
|
|
{
|
|
audio.out.gain = audioTrack.gain;
|
|
}
|
|
else
|
|
{
|
|
// output is passthru - the Gain dial is disabled so don't apply its value
|
|
audio.out.gain = 0;
|
|
}
|
|
|
|
if (hb_audio_can_apply_drc(inputTrack.codec,
|
|
inputTrack.codecParam,
|
|
audioTrack.encoder))
|
|
{
|
|
audio.out.dynamic_range_compression = audioTrack.drc;
|
|
}
|
|
else
|
|
{
|
|
// source isn't AC3 or output is passthru - the DRC dial is disabled so don't apply its value
|
|
audio.out.dynamic_range_compression = 0;
|
|
}
|
|
|
|
hb_audio_add(job, &audio);
|
|
hb_audio_config_close(&audio);
|
|
}
|
|
}
|
|
|
|
// Now lets call the filters if applicable.
|
|
hb_filter_object_t *filter;
|
|
|
|
// Detelecine
|
|
if (![self.filters.detelecine isEqualToString:@"off"])
|
|
{
|
|
int filter_id = HB_FILTER_DETELECINE;
|
|
hb_dict_t *filter_dict = hb_generate_filter_settings(filter_id,
|
|
self.filters.detelecine.UTF8String,
|
|
NULL,
|
|
self.filters.detelecineCustomString.UTF8String);
|
|
filter = hb_filter_init(filter_id);
|
|
hb_add_filter_dict(job, filter, filter_dict);
|
|
hb_value_free(&filter_dict);
|
|
}
|
|
|
|
// Comb Detection
|
|
if (![self.filters.combDetection isEqualToString:@"off"])
|
|
{
|
|
int filter_id = HB_FILTER_COMB_DETECT;
|
|
hb_dict_t *filter_dict = hb_generate_filter_settings(filter_id,
|
|
self.filters.combDetection.UTF8String,
|
|
NULL,
|
|
self.filters.combDetectionCustomString.UTF8String);
|
|
filter = hb_filter_init(filter_id);
|
|
hb_add_filter_dict(job, filter, filter_dict);
|
|
hb_value_free(&filter_dict);
|
|
}
|
|
|
|
// Deinterlace
|
|
if (![self.filters.deinterlace isEqualToString:@"off"])
|
|
{
|
|
int filter_id = HB_FILTER_DECOMB;
|
|
if ([self.filters.deinterlace isEqualToString:@"deinterlace"])
|
|
{
|
|
filter_id = HB_FILTER_YADIF;
|
|
}
|
|
else if ([self.filters.deinterlace isEqualToString:@"bwdif"])
|
|
{
|
|
filter_id = HB_FILTER_BWDIF;
|
|
}
|
|
|
|
hb_dict_t *filter_dict = hb_generate_filter_settings(filter_id,
|
|
self.filters.deinterlacePreset.UTF8String,
|
|
NULL,
|
|
self.filters.deinterlaceCustomString.UTF8String);
|
|
filter = hb_filter_init(filter_id);
|
|
hb_add_filter_dict(job, filter, filter_dict);
|
|
hb_value_free(&filter_dict);
|
|
}
|
|
|
|
// Add framerate shaping filter
|
|
filter = hb_filter_init(HB_FILTER_VFR);
|
|
hb_add_filter(job, filter, [[NSString stringWithFormat:@"mode=%d:rate=%d/%d",
|
|
fps_mode, fps_num, fps_den] UTF8String]);
|
|
|
|
// Deblock
|
|
if (![self.filters.deblock isEqualToString:@"off"])
|
|
{
|
|
int filter_id = HB_FILTER_DEBLOCK;
|
|
hb_dict_t *filter_dict = hb_generate_filter_settings(filter_id,
|
|
self.filters.deblock.UTF8String,
|
|
self.filters.deblockTune.UTF8String,
|
|
self.filters.deblockCustomString.UTF8String);
|
|
filter = hb_filter_init(filter_id);
|
|
hb_add_filter_dict(job, filter, filter_dict);
|
|
hb_value_free(&filter_dict);
|
|
}
|
|
|
|
// Denoise
|
|
if (![self.filters.denoise isEqualToString:@"off"])
|
|
{
|
|
int filter_id = HB_FILTER_HQDN3D;
|
|
if ([self.filters.denoise isEqualToString:@"nlmeans"])
|
|
{
|
|
filter_id = HB_FILTER_NLMEANS;
|
|
}
|
|
|
|
hb_dict_t *filter_dict = hb_generate_filter_settings(filter_id,
|
|
self.filters.denoisePreset.UTF8String,
|
|
self.filters.denoiseTune.UTF8String,
|
|
self.filters.denoiseCustomString.UTF8String);
|
|
filter = hb_filter_init(filter_id);
|
|
hb_add_filter_dict(job, filter, filter_dict);
|
|
hb_dict_free(&filter_dict);
|
|
}
|
|
|
|
// Chroma Smooth
|
|
if (![self.filters.chromaSmooth isEqualToString:@"off"])
|
|
{
|
|
int filter_id = HB_FILTER_CHROMA_SMOOTH;
|
|
hb_dict_t *filter_dict = hb_generate_filter_settings(filter_id,
|
|
self.filters.chromaSmooth.UTF8String,
|
|
self.filters.chromaSmoothTune.UTF8String,
|
|
self.filters.chromaSmoothCustomString.UTF8String);
|
|
filter = hb_filter_init(filter_id);
|
|
hb_add_filter_dict(job, filter, filter_dict);
|
|
hb_value_free(&filter_dict);
|
|
}
|
|
|
|
// Add Crop/Scale filter
|
|
filter = hb_filter_init(HB_FILTER_CROP_SCALE);
|
|
hb_add_filter( job, filter,
|
|
[NSString stringWithFormat:
|
|
@"width=%d:height=%d:crop-top=%d:crop-bottom=%d:crop-left=%d:crop-right=%d",
|
|
self.picture.width, self.picture.height,
|
|
self.picture.cropTop, self.picture.cropBottom,
|
|
self.picture.cropLeft, self.picture.cropRight].UTF8String);
|
|
|
|
// Sharpen
|
|
if (![self.filters.sharpen isEqualToString:@"off"])
|
|
{
|
|
int filter_id = HB_FILTER_UNSHARP;
|
|
if ([self.filters.sharpen isEqualToString:@"lapsharp"])
|
|
{
|
|
filter_id = HB_FILTER_LAPSHARP;
|
|
}
|
|
|
|
hb_dict_t *filter_dict = hb_generate_filter_settings(filter_id,
|
|
self.filters.sharpenPreset.UTF8String,
|
|
self.filters.sharpenTune.UTF8String,
|
|
self.filters.sharpenCustomString.UTF8String);
|
|
filter = hb_filter_init(filter_id);
|
|
hb_add_filter_dict(job, filter, filter_dict);
|
|
hb_dict_free(&filter_dict);
|
|
}
|
|
|
|
// Grayscale
|
|
if (self.filters.grayscale)
|
|
{
|
|
filter = hb_filter_init(HB_FILTER_GRAYSCALE);
|
|
hb_add_filter(job, filter, NULL);
|
|
}
|
|
|
|
// Rotate
|
|
if (self.picture.angle || self.picture.flip)
|
|
{
|
|
int filter_id = HB_FILTER_ROTATE;
|
|
hb_dict_t *filter_dict = hb_generate_filter_settings(filter_id,
|
|
NULL, NULL,
|
|
[NSString stringWithFormat:@"angle=%d:hflip=%d",
|
|
self.picture.angle, self.picture.flip].UTF8String);
|
|
|
|
filter = hb_filter_init(filter_id);
|
|
hb_add_filter_dict(job, filter, filter_dict);
|
|
hb_dict_free(&filter_dict);
|
|
}
|
|
|
|
// Pad
|
|
if (self.picture.padMode != HBPicturePadModeNone)
|
|
{
|
|
int filter_id = HB_FILTER_PAD;
|
|
NSString *color;
|
|
switch (self.picture.padColorMode) {
|
|
case HBPicturePadColorModeBlack:
|
|
color = @"black";
|
|
break;
|
|
case HBPicturePadColorModeDarkGray:
|
|
color = @"darkslategray";
|
|
break;
|
|
case HBPicturePadColorModeGray:
|
|
color = @"slategray";
|
|
break;
|
|
case HBPicturePadColorModeWhite:
|
|
color = @"white";
|
|
break;
|
|
case HBPicturePadColorModeCustom:
|
|
color = self.picture.padColorCustom;
|
|
break;
|
|
}
|
|
|
|
NSString *settings = [NSString stringWithFormat:@"color=%@:top=%d:bottom=%d:left=%d:right=%d",
|
|
color, self.picture.padTop, self.picture.padBottom, self.picture.padLeft, self.picture.padRight];
|
|
hb_dict_t *filter_dict = hb_generate_filter_settings(filter_id, NULL, NULL, settings.UTF8String);
|
|
|
|
filter = hb_filter_init(filter_id);
|
|
hb_add_filter_dict(job, filter, filter_dict);
|
|
hb_dict_free(&filter_dict);
|
|
}
|
|
|
|
// Colorspace
|
|
if (![self.filters.colorspace isEqualToString:@"off"])
|
|
{
|
|
int filter_id = HB_FILTER_COLORSPACE;
|
|
hb_dict_t *filter_dict = hb_generate_filter_settings(filter_id,
|
|
self.filters.colorspace.UTF8String,
|
|
NULL,
|
|
self.filters.colorspaceCustomString.UTF8String);
|
|
filter = hb_filter_init(filter_id);
|
|
hb_add_filter_dict(job, filter, filter_dict);
|
|
hb_value_free(&filter_dict);
|
|
}
|
|
|
|
return job;
|
|
}
|
|
|
|
@end
|