1 /* HBJob+HBJobConversion.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 "HBJob+HBJobConversion.h"
9 #import "HBAudioDefaults.h"
10 #import "HBAudioTrack.h"
12 #import "HBTitlePrivate.h"
14 @implementation HBJob (HBJobConversion)
21 NSAssert(self.title, @"HBJob: calling hb_job without a valid title loaded");
22 NSAssert(self.destURL, @"HBJob: calling hb_job without a valid destination");
24 hb_title_t *title = self.title.hb_title;
25 hb_job_t *job = hb_job_init(title);
27 hb_job_set_file(job, self.destURL.path.fileSystemRepresentation);
29 // Title Angle for dvdnav
30 job->angle = self.angle;
32 if (self.range.type == HBRangeTypeChapters)
35 job->chapter_start = self.range.chapterStart + 1;
36 job->chapter_end = self.range.chapterStop + 1;
38 else if (self.range.type == HBRangeTypeSeconds)
40 // we are pts based start / stop
41 // Point A to Point B. Time to time in seconds.
42 // get the start seconds from the start seconds field
43 int start_seconds = self.range.secondsStart;
44 job->pts_to_start = start_seconds * 90000LL;
45 // Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds
46 int stop_seconds = self.range.secondsStop;
47 job->pts_to_stop = (stop_seconds - start_seconds) * 90000LL;
49 else if (self.range.type == HBRangeTypeFrames)
51 // we are frame based start / stop
52 //Point A to Point B. Frame to frame
53 // get the start frame from the start frame field
54 int start_frame = self.range.frameStart;
55 job->frame_to_start = start_frame;
56 // get the frame to stop on from the end frame field
57 int stop_frame = self.range.frameStop;
58 job->frame_to_stop = stop_frame - start_frame;
60 else if (self.range.type == HBRangePreviewIndex)
62 job->start_at_preview = self.range.previewIndex;
63 job->seek_points = self.range.previewsCount;
64 job->pts_to_stop = self.range.ptsToStop;
67 // Format (Muxer) and Video Encoder
68 job->mux = self.container;
69 job->vcodec = self.video.encoder;
71 // We set http optimized mp4 here
72 job->mp4_optimize = self.mp4HttpOptimize;
74 // We set the chapter marker extraction here based on the format being
75 // mpeg4 or mkv and the checkbox being checked.
76 if (self.chaptersEnabled)
78 job->chapter_markers = 1;
80 // now lets get our saved chapter names out the array in the queue file
81 // and insert them back into the title chapter list. We have it here,
82 // because unless we are inserting chapter markers there is no need to
83 // spend the overhead of iterating through the chapter names array imo
84 // Also, note that if for some reason we don't apply chapter names, the
85 // chapters just come out 001, 002, etc. etc.
87 for (NSString *name in self.chapterTitles)
89 hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item(job->list_chapter, i);
92 hb_chapter_set_title(chapter, name.UTF8String);
99 job->chapter_markers = 0;
102 if (job->vcodec & HB_VCODEC_H264_MASK)
105 job->ipod_atom = self.mp4iPodCompatible;
108 if (self.video.twoPass && (self.video.encoder == HB_VCODEC_X264 ||
109 self.video.encoder == HB_VCODEC_X265))
111 job->fastfirstpass = self.video.turboTwoPass;
113 job->twopass = self.video.twoPass;
115 if (hb_video_encoder_get_presets(self.video.encoder) != NULL)
117 // advanced x264/x265 options
119 // translate zero-length strings to NULL for libhb
120 const char *encoder_preset = NULL;
121 const char *encoder_tune = NULL;
122 const char *encoder_options = NULL;
123 const char *encoder_profile = NULL;
124 const char *encoder_level = NULL;
125 if (self.video.advancedOptions)
127 // we are using the advanced panel
128 if ([(tmpString = self.video.videoOptionExtra) length])
130 encoder_options = tmpString.UTF8String;
135 // we are using the x264/x265 preset system
136 if ([(tmpString = self.video.completeTune) length])
138 encoder_tune = [tmpString UTF8String];
140 if ([(tmpString = self.video.videoOptionExtra) length])
142 encoder_options = [tmpString UTF8String];
144 if ([(tmpString = self.video.profile) length])
146 encoder_profile = [tmpString UTF8String];
148 if ([(tmpString = self.video.level) length])
150 encoder_level = [tmpString UTF8String];
152 encoder_preset = self.video.preset.UTF8String;
154 hb_job_set_encoder_preset (job, encoder_preset);
155 hb_job_set_encoder_tune (job, encoder_tune);
156 hb_job_set_encoder_options(job, encoder_options);
157 hb_job_set_encoder_profile(job, encoder_profile);
158 hb_job_set_encoder_level (job, encoder_level);
160 else if (job->vcodec & HB_VCODEC_FFMPEG_MASK)
162 hb_job_set_encoder_options(job, self.video.videoOptionExtra.UTF8String);
165 // Picture Size Settings
166 job->par.num = self.picture.parWidth;
167 job->par.den = self.picture.parHeight;
171 int fps_mode, fps_num, fps_den;
172 if (self.video.frameRate > 0)
174 // a specific framerate has been chosen
176 fps_den = self.video.frameRate;
177 if (self.video.frameRateMode == 1)
191 fps_num = title->vrate.num;
192 fps_den = title->vrate.den;
193 if (self.video.frameRateMode == 1)
205 switch (self.video.qualityType)
209 job->vquality = -1.0;
210 job->vbitrate = self.video.avgBitrate;
214 job->vquality = self.video.quality;
219 job->grayscale = self.filters.grayscale;
221 // Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle
222 BOOL one_burned = NO;
223 for (NSDictionary *subtitleDict in self.subtitles.tracks)
225 int subtitle = [subtitleDict[keySubTrackIndex] intValue];
226 BOOL force = [subtitleDict[keySubTrackForced] boolValue];
227 BOOL burned = [subtitleDict[keySubTrackBurned] boolValue];
228 BOOL def = [subtitleDict[keySubTrackDefault] boolValue];
230 // Skip the "None" track.
236 // we need to check for the "Foreign Audio Search" which would be keySubTrackIndex of -1
239 job->indepth_scan = 1;
243 job->select_subtitle_config.dest = PASSTHRUSUB;
247 job->select_subtitle_config.dest = RENDERSUB;
250 job->select_subtitle_config.force = force;
251 job->select_subtitle_config.default_track = def;
255 // if we are getting the subtitles from an external srt file
256 if ([subtitleDict[keySubTrackType] intValue] == SRTSUB)
258 hb_subtitle_config_t sub_config;
260 sub_config.offset = [subtitleDict[keySubTrackSrtOffset] intValue];
262 // we need to strncpy file name and codeset
263 strncpy(sub_config.src_filename, [subtitleDict[keySubTrackSrtFilePath] UTF8String], 255);
264 sub_config.src_filename[255] = 0;
265 strncpy(sub_config.src_codeset, [subtitleDict[keySubTrackSrtCharCode] UTF8String], 39);
266 sub_config.src_codeset[39] = 0;
268 if (!burned && hb_subtitle_can_pass(SRTSUB, job->mux))
270 sub_config.dest = PASSTHRUSUB;
272 else if (hb_subtitle_can_burn(SRTSUB))
274 // Only allow one subtitle to be burned into the video
278 sub_config.dest = RENDERSUB;
281 sub_config.force = 0;
282 sub_config.default_track = def;
283 hb_srt_add( job, &sub_config, [subtitleDict[keySubTrackLanguageIsoCode] UTF8String]);
287 // We are setting a source subtitle so access the source subtitle info
288 hb_subtitle_t * subt = (hb_subtitle_t *) hb_list_item(title->list_subtitle, subtitle);
292 hb_subtitle_config_t sub_config = subt->config;
294 if (!burned && hb_subtitle_can_pass(subt->source, job->mux))
296 sub_config.dest = PASSTHRUSUB;
298 else if (hb_subtitle_can_burn(subt->source))
300 // Only allow one subtitle to be burned into the video
304 sub_config.dest = RENDERSUB;
307 sub_config.force = force;
308 sub_config.default_track = def;
309 hb_subtitle_add(job, &sub_config, subtitle);
316 hb_filter_object_t *filter = hb_filter_init( HB_FILTER_RENDER_SUB );
317 hb_add_filter(job, filter, [NSString stringWithFormat:@"%d:%d:%d:%d",
318 self.picture.cropTop, self.picture.cropBottom,
319 self.picture.cropLeft, self.picture.cropRight].UTF8String);
323 job->acodec_copy_mask = 0;
325 HBAudioDefaults *audioDefaults = self.audio.defaults;
327 if (audioDefaults.allowAACPassthru)
329 job->acodec_copy_mask |= HB_ACODEC_AAC_PASS;
331 if (audioDefaults.allowAC3Passthru)
333 job->acodec_copy_mask |= HB_ACODEC_AC3_PASS;
335 if (audioDefaults.allowEAC3Passthru)
337 job->acodec_copy_mask |= HB_ACODEC_EAC3_PASS;
339 if (audioDefaults.allowDTSHDPassthru)
341 job->acodec_copy_mask |= HB_ACODEC_DCA_HD_PASS;
343 if (audioDefaults.allowDTSPassthru)
345 job->acodec_copy_mask |= HB_ACODEC_DCA_PASS;
347 if (audioDefaults.allowMP3Passthru)
349 job->acodec_copy_mask |= HB_ACODEC_MP3_PASS;
351 if (audioDefaults.allowTrueHDPassthru)
353 job->acodec_copy_mask |= HB_ACODEC_TRUEHD_PASS;
355 if (audioDefaults.allowFLACPassthru)
357 job->acodec_copy_mask |= HB_ACODEC_FLAC_PASS;
360 job->acodec_fallback = audioDefaults.encoderFallback;
362 // Audio tracks and mixdowns
363 // Now lets add our new tracks to the audio list here
364 for (HBAudioTrack *audioTrack in self.audio.tracks)
366 if (audioTrack.enabled)
368 hb_audio_config_t *audio = (hb_audio_config_t *)calloc(1, sizeof(*audio));
369 hb_audio_config_init(audio);
371 NSNumber *sampleRateToUse = ([audioTrack.sampleRate[keyAudioSamplerate] intValue] == 0 ?
372 audioTrack.track[keyAudioInputSampleRate] :
373 audioTrack.sampleRate[keyAudioSamplerate]);
375 audio->in.track = [audioTrack.track[keyAudioTrackIndex] intValue] -1;
377 // We go ahead and assign values to our audio->out.<properties>
378 audio->out.track = audio->in.track;
379 audio->out.codec = [audioTrack.codec[keyAudioCodec] intValue];
380 audio->out.compression_level = hb_audio_compression_get_default(audio->out.codec);
381 audio->out.mixdown = [audioTrack.mixdown[keyAudioMixdown] intValue];
382 audio->out.normalize_mix_level = 0;
383 audio->out.bitrate = [audioTrack.bitRate[keyAudioBitrate] intValue];
384 audio->out.samplerate = [sampleRateToUse intValue];
385 audio->out.dither_method = hb_audio_dither_get_default();
387 // output is not passthru so apply gain
388 if (!([[audioTrack codec][keyAudioCodec] intValue] & HB_ACODEC_PASS_FLAG))
390 audio->out.gain = [audioTrack.gain doubleValue];
394 // output is passthru - the Gain dial is disabled so don't apply its value
398 if (hb_audio_can_apply_drc([audioTrack.track[keyAudioInputCodec] intValue],
399 [audioTrack.track[keyAudioInputCodecParam] intValue],
400 [audioTrack.codec[keyAudioCodec] intValue]))
402 audio->out.dynamic_range_compression = [audioTrack.drc doubleValue];
406 // source isn't AC3 or output is passthru - the DRC dial is disabled so don't apply its value
407 audio->out.dynamic_range_compression = 0;
410 hb_audio_add(job, audio);
415 // Now lets call the filters if applicable.
416 // The order of the filters is critical
419 hb_filter_object_t *filter;
420 if (![self.filters.detelecine isEqualToString:@"off"])
422 int filter_id = HB_FILTER_DETELECINE;
423 const char *filter_str = hb_generate_filter_settings(filter_id,
424 self.filters.detelecine.UTF8String,
425 self.filters.detelecineCustomString.UTF8String);
426 filter = hb_filter_init(filter_id);
427 hb_add_filter(job, filter, filter_str);
430 if (self.filters.useDecomb && ![self.filters.decomb isEqualToString:@"off"])
433 int filter_id = HB_FILTER_DECOMB;
434 const char *filter_str = hb_generate_filter_settings(filter_id,
435 self.filters.decomb.UTF8String,
436 self.filters.decombCustomString.UTF8String);
437 filter = hb_filter_init(filter_id);
438 hb_add_filter(job, filter, filter_str);
440 else if (!self.filters.useDecomb && ![self.filters.deinterlace isEqualToString:@"off"])
443 int filter_id = HB_FILTER_DEINTERLACE;
444 const char *filter_str = hb_generate_filter_settings(filter_id,
445 self.filters.deinterlace.UTF8String,
446 self.filters.deinterlaceCustomString.UTF8String);
447 filter = hb_filter_init(filter_id);
448 hb_add_filter(job, filter, filter_str);
452 if (![self.filters.denoise isEqualToString:@"off"])
454 int filter_id = HB_FILTER_HQDN3D;
455 if ([self.filters.denoise isEqualToString:@"nlmeans"])
456 filter_id = HB_FILTER_NLMEANS;
458 if ([self.filters.denoisePreset isEqualToString:@"custom"])
460 const char *filter_str;
461 filter_str = self.filters.denoiseCustomString.UTF8String;
462 filter = hb_filter_init(filter_id);
463 hb_add_filter(job, filter, filter_str);
467 const char *filter_str, *preset, *tune;
468 preset = self.filters.denoisePreset.UTF8String;
469 tune = self.filters.denoiseTune.UTF8String;
470 filter_str = hb_generate_filter_settings(filter_id, preset, tune);
471 filter = hb_filter_init(filter_id);
472 hb_add_filter(job, filter, filter_str);
476 // Deblock (uses pp7 default)
477 if (self.filters.deblock)
479 filter = hb_filter_init(HB_FILTER_DEBLOCK);
480 hb_add_filter(job, filter, [NSString stringWithFormat:@"%d", self.filters.deblock].UTF8String);
483 // Add Crop/Scale filter
484 filter = hb_filter_init(HB_FILTER_CROP_SCALE);
485 hb_add_filter( job, filter, [NSString stringWithFormat:@"%d:%d:%d:%d:%d:%d",
486 self.picture.width, self.picture.height,
487 self.picture.cropTop, self.picture.cropBottom,
488 self.picture.cropLeft, self.picture.cropRight].UTF8String);
490 // Add framerate shaping filter
491 filter = hb_filter_init(HB_FILTER_VFR);
492 hb_add_filter(job, filter, [[NSString stringWithFormat:@"%d:%d:%d",
493 fps_mode, fps_num, fps_den] UTF8String]);