summaryrefslogtreecommitdiffstats
path: root/macosx/HBJob.m
diff options
context:
space:
mode:
Diffstat (limited to 'macosx/HBJob.m')
-rw-r--r--macosx/HBJob.m525
1 files changed, 520 insertions, 5 deletions
diff --git a/macosx/HBJob.m b/macosx/HBJob.m
index 03aafa2f8..0358929e7 100644
--- a/macosx/HBJob.m
+++ b/macosx/HBJob.m
@@ -5,13 +5,12 @@
It may be used under the terms of the GNU General Public License. */
#import "HBJob.h"
-#import "HBTitle.h"
-
-#import "HBAudioDefaults.h"
-#import "HBSubtitlesDefaults.h"
-
#import "HBPreset.h"
+#import "HBAudio.h"
+#import "HBAudioController.h"
+#import "HBSubtitlesController.h"
+
#include "lang.h"
@implementation HBJob
@@ -27,6 +26,8 @@
_title = title;
_fileURL = [fileURL copy];
+ _fileFormat = HB_MUX_MP4;
+
_audioDefaults = [[HBAudioDefaults alloc] init];
_subtitlesDefaults = [[HBSubtitlesDefaults alloc] init];
@@ -34,6 +35,9 @@
_picture = [[HBPicture alloc] initWithTitle:title];
_filters = [[HBFilters alloc] init];
+ _audioTracks = [[NSMutableArray alloc] init];
+ _subtitlesTracks = [[NSMutableArray alloc] init];
+
[self applyPreset:preset];
}
@@ -46,6 +50,517 @@
withObject:preset.content];
}
+/**
+ * Prepares a hb_job_t
+ */
+- (hb_job_t *)hb_job
+{
+ hb_title_t *title = self.title.hb_title;
+ hb_job_t *job = hb_job_init(title);
+
+ hb_job_set_file(job, self.destURL.path.fileSystemRepresentation);
+
+ // Title Angle for dvdnav
+ job->angle = 1; //FIXME
+
+ /*
+ if([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 0)
+ {
+ // Chapter selection
+ [HBUtilities writeToActivityLog: "Start / Stop set to chapters"];
+ job->chapter_start = [[queueToApply objectForKey:@"ChapterStart"] intValue];
+ job->chapter_end = [[queueToApply objectForKey:@"ChapterEnd"] intValue];
+ }
+ else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 1)
+ {
+ // we are pts based start / stop
+ [HBUtilities writeToActivityLog: "Start / Stop set to seconds…"];
+
+ // Point A to Point B. Time to time in seconds.
+ // get the start seconds from the start seconds field
+ int start_seconds = [[queueToApply objectForKey:@"StartSeconds"] intValue];
+ 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 = [[queueToApply objectForKey:@"StopSeconds"] intValue];
+ job->pts_to_stop = stop_seconds * 90000LL;
+
+ }
+ else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 2)
+ {
+ // we are frame based start / stop
+ [HBUtilities writeToActivityLog: "Start / Stop set to frames…"];
+
+ //Point A to Point B. Frame to frame
+ // get the start frame from the start frame field
+ int start_frame = [[queueToApply objectForKey:@"StartFrame"] intValue];
+ job->frame_to_start = start_frame;
+ // get the frame to stop on from the end frame field
+ int stop_frame = [[queueToApply objectForKey:@"StopFrame"] intValue];
+ job->frame_to_stop = stop_frame;
+
+ }*/
+
+ // Format (Muxer) and Video Encoder
+ job->mux = self.fileFormat;
+ job->vcodec = self.video.encoder;
+
+ // We set http optimized mp4 here
+ job->mp4_optimize = self.mp4HttpOptimize;
+
+ // 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 (NSString *name in self.chapterNames)
+ {
+ hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item(job->list_chapter, i);
+ if (chapter != NULL)
+ {
+ hb_chapter_set_title(chapter, name.UTF8String);
+ }
+ i++;
+ }
+ }
+ else
+ {
+ job->chapter_markers = 0;
+ }
+
+ if (job->vcodec == HB_VCODEC_X264 || job->vcodec == HB_VCODEC_X265)
+ {
+ // iPod 5G atom
+ job->ipod_atom = self.mp4iPodCompatible;
+
+ // set fastfirstpass if 2-pass and Turbo are enabled
+ if (self.video.twoPass)
+ {
+ job->fastfirstpass = self.video.turboTwoPass;
+ }
+
+ // 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.video.advancedOptions)
+ {
+ // we are using the advanced panel
+ if ([(tmpString = self.video.videoOptionExtra) length])
+ {
+ encoder_options = tmpString.UTF8String;
+ }
+ }
+ else
+ {
+ // we are using the x264 preset system
+ if ([(tmpString = self.video.tune) 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->width = self.picture.width;
+ job->height = self.picture.height;
+
+ job->anamorphic.keep_display_aspect = self.picture.keepDisplayAspect;
+ job->anamorphic.mode = self.picture.anamorphicMode;
+ job->modulus = self.picture.modulus;
+ job->par.num = self.picture.parWidth;
+ job->par.den = self.picture.parHeight;
+
+ // Here we use the crop values saved at the time the preset was saved
+ job->crop[0] = self.picture.cropTop;
+ job->crop[1] = self.picture.cropBottom;
+ job->crop[2] = self.picture.cropLeft;
+ job->crop[3] = self.picture.cropRight;
+
+ // 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;
+ }
+ }
+
+ if (self.video.qualityType != 2)
+ {
+ job->vquality = -1.0;
+ job->vbitrate = self.video.avgBitrate;
+ }
+ if (self.video.qualityType == 2)
+ {
+ job->vquality = self.video.quality;
+ job->vbitrate = 0;
+
+ }
+
+ job->grayscale = self.filters.grayscale;
+
+ // Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle
+ BOOL one_burned = NO;
+ int i = 0;
+
+ for (NSDictionary *subtitleDict in self.subtitlesTracks)
+ {
+ int subtitle = [subtitleDict[keySubTrackIndex] intValue];
+ int force = [subtitleDict[keySubTrackForced] intValue];
+ int burned = [subtitleDict[keySubTrackBurned] intValue];
+ int def = [subtitleDict[keySubTrackDefault] intValue];
+
+ // if i is 0, then we are in the first item of the subtitles which we need to
+ // check for the "Foreign Audio Search" which would be keySubTrackIndex of -1
+
+ // if we are on the first track and using "Foreign Audio Search"
+ if (i == 0 && subtitle == -1)
+ {
+ job->indepth_scan = 1;
+
+ if (burned != 1)
+ {
+ job->select_subtitle_config.dest = PASSTHRUSUB;
+ }
+ else
+ {
+ job->select_subtitle_config.dest = RENDERSUB;
+ }
+
+ job->select_subtitle_config.force = force;
+ job->select_subtitle_config.default_track = def;
+ }
+ else
+ {
+ // if we are getting the subtitles from an external srt file
+ if ([subtitleDict[keySubTrackType] intValue] == SRTSUB)
+ {
+ hb_subtitle_config_t sub_config;
+
+ sub_config.offset = [subtitleDict[keySubTrackSrtOffset] intValue];
+
+ // we need to srncpy file name and codeset
+ strncpy(sub_config.src_filename, [subtitleDict[keySubTrackSrtFilePath] UTF8String], 255);
+ sub_config.src_filename[255] = 0;
+ strncpy(sub_config.src_codeset, [subtitleDict[keySubTrackSrtCharCode] UTF8String], 39);
+ sub_config.src_codeset[39] = 0;
+
+ if (!burned && hb_subtitle_can_pass(SRTSUB, job->mux))
+ {
+ sub_config.dest = PASSTHRUSUB;
+ }
+ else if (hb_subtitle_can_burn(SRTSUB))
+ {
+ // Only allow one subtitle to be burned into the video
+ if (one_burned)
+ continue;
+ one_burned = TRUE;
+ sub_config.dest = RENDERSUB;
+ }
+
+ sub_config.force = 0;
+ sub_config.default_track = def;
+ hb_srt_add( job, &sub_config, [subtitleDict[keySubTrackLanguageIsoCode] UTF8String]);
+ continue;
+ }
+
+ // 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, subtitle);
+
+ if (subt != NULL)
+ {
+ hb_subtitle_config_t sub_config = subt->config;
+
+ if (!burned && hb_subtitle_can_pass(subt->source, job->mux))
+ {
+ sub_config.dest = PASSTHRUSUB;
+ }
+ else if (hb_subtitle_can_burn(subt->source))
+ {
+ // Only allow one subtitle to be burned into the video
+ if (one_burned)
+ continue;
+ one_burned = TRUE;
+ sub_config.dest = RENDERSUB;
+ }
+
+ sub_config.force = force;
+ sub_config.default_track = def;
+ hb_subtitle_add(job, &sub_config, subtitle);
+ }
+ }
+ i++;
+ }
+
+ if (one_burned)
+ {
+ hb_filter_object_t *filter = hb_filter_init( HB_FILTER_RENDER_SUB );
+ hb_add_filter( job, filter, [[NSString stringWithFormat:@"%d:%d:%d:%d",
+ job->crop[0], job->crop[1],
+ job->crop[2], job->crop[3]] UTF8String] );
+ }
+
+ // Audio Defaults
+ job->acodec_copy_mask = 0;
+
+ if (self.audioDefaults.allowAACPassthru)
+ {
+ job->acodec_copy_mask |= HB_ACODEC_FFAAC;
+ }
+ if (self.audioDefaults.allowAC3Passthru)
+ {
+ job->acodec_copy_mask |= HB_ACODEC_AC3;
+ }
+ if (self.audioDefaults.allowDTSHDPassthru)
+ {
+ job->acodec_copy_mask |= HB_ACODEC_DCA_HD;
+ }
+ if (self.audioDefaults.allowDTSPassthru)
+ {
+ job->acodec_copy_mask |= HB_ACODEC_DCA;
+ }
+ if (self.audioDefaults.allowMP3Passthru)
+ {
+ job->acodec_copy_mask |= HB_ACODEC_MP3;
+ }
+ job->acodec_fallback = self.audioDefaults.encoderFallback;
+
+ // Audio tracks and mixdowns
+ // Now lets add our new tracks to the audio list here
+ for (HBAudio *audioTrack in self.audioTracks)
+ {
+ if (audioTrack.enabled)
+ {
+ hb_audio_config_t *audio = (hb_audio_config_t *)calloc(1, sizeof(*audio));
+ hb_audio_config_init(audio);
+
+ NSNumber *sampleRateToUse = ([audioTrack.sampleRate[keyAudioSamplerate] intValue] == 0 ?
+ audioTrack.track[keyAudioInputSampleRate] :
+ audioTrack.sampleRate[keyAudioSamplerate]);
+
+ audio->in.track = [audioTrack.track[keyAudioTrackIndex] intValue] -1;
+
+ // We go ahead and assign values to our audio->out.<properties>
+ audio->out.track = audio->in.track;
+ audio->out.codec = [audioTrack.codec[keyAudioCodec] intValue];
+ audio->out.compression_level = hb_audio_compression_get_default(audio->out.codec);
+ audio->out.mixdown = [audioTrack.mixdown[keyAudioMixdown] intValue];
+ audio->out.normalize_mix_level = 0;
+ audio->out.bitrate = [audioTrack.bitRate[keyAudioBitrate] intValue];
+ audio->out.samplerate = [sampleRateToUse intValue];
+ audio->out.dither_method = hb_audio_dither_get_default();
+
+ // output is not passthru so apply gain
+ if (!([[audioTrack codec][keyAudioCodec] intValue] & HB_ACODEC_PASS_FLAG))
+ {
+ audio->out.gain = [audioTrack.gain doubleValue];
+ }
+ 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([audioTrack.track[keyAudioInputCodec] intValue],
+ [audioTrack.track[keyAudioInputCodecParam] intValue],
+ [audioTrack.codec[keyAudioCodec] intValue]))
+ {
+ audio->out.dynamic_range_compression = [audioTrack.drc doubleValue];
+ }
+ 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);
+ free(audio);
+ }
+ }
+
+ // Now lets call the filters if applicable.
+ // The order of the filters is critical
+
+ // Detelecine
+ hb_filter_object_t *filter;
+ filter = hb_filter_init(HB_FILTER_DETELECINE);
+ if (self.filters.detelecine == 1)
+ {
+ // use a custom detelecine string
+ hb_add_filter(job, filter, self.filters.detelecineCustomString.UTF8String);
+ }
+ else if (self.filters.detelecine == 2)
+ {
+ // Use libhb's default values
+ hb_add_filter(job, filter, NULL);
+ }
+
+ if (self.filters.useDecomb)
+ {
+ // Decomb
+ filter = hb_filter_init(HB_FILTER_DECOMB);
+ if (self.filters.decomb == 1)
+ {
+ // use a custom decomb string */
+ hb_add_filter(job, filter, self.filters.decombCustomString.UTF8String);
+ }
+ else if (self.filters.decomb == 2)
+ {
+ // use libhb defaults
+ hb_add_filter(job, filter, NULL);
+ }
+ else if (self.filters.decomb == 3)
+ {
+ // use old defaults (decomb fast)
+ hb_add_filter(job, filter, "7:2:6:9:1:80");
+ }
+ else if (self.filters.decomb == 4)
+ {
+ // decomb 3 with bobbing enabled
+ hb_add_filter(job, filter, "455");
+ }
+ }
+ else
+ {
+ // Deinterlace
+ filter = hb_filter_init(HB_FILTER_DEINTERLACE);
+ if (self.filters.deinterlace == 1)
+ {
+ // we add the custom string if present
+ hb_add_filter(job, filter, self.filters.deinterlaceCustomString.UTF8String);
+ }
+ else if (self.filters.deinterlace == 2)
+ {
+ // Run old deinterlacer fd by default
+ hb_add_filter(job, filter, "0");
+ }
+ else if (self.filters.deinterlace == 3)
+ {
+ // Yadif mode 0 (without spatial deinterlacing)
+ hb_add_filter(job, filter, "1");
+ }
+ else if (self.filters.deinterlace == 4)
+ {
+ // Yadif (with spatial deinterlacing)
+ hb_add_filter(job, filter, "3");
+ }
+ else if (self.filters.deinterlace == 5)
+ {
+ // Yadif (with spatial deinterlacing and bobbing)
+ hb_add_filter(job, filter, "15");
+ }
+ }
+ // Denoise
+ if (![self.filters.denoise isEqualToString:@"off"])
+ {
+ int filter_id = HB_FILTER_HQDN3D;
+ if ([self.filters.denoise isEqualToString:@"nlmeans"])
+ filter_id = HB_FILTER_NLMEANS;
+
+ if ([self.filters.denoisePreset isEqualToString:@"none"])
+ {
+ const char *filter_str;
+ filter_str = self.filters.denoiseCustomString.UTF8String;
+ filter = hb_filter_init(filter_id);
+ hb_add_filter(job, filter, filter_str);
+ }
+ else
+ {
+ const char *filter_str, *preset, *tune;
+ preset = self.filters.denoisePreset.UTF8String;
+ tune = self.filters.denoiseTune.UTF8String;
+ filter_str = hb_generate_filter_settings(filter_id, preset, tune);
+ filter = hb_filter_init(filter_id);
+ hb_add_filter(job, filter, filter_str);
+ }
+ }
+
+ // Deblock (uses pp7 default)
+ // NOTE: even though there is a valid deblock setting of 0 for the filter, for
+ // the macgui's purposes a value of 0 actually means to not even use the filter
+ // current hb_filter_deblock.settings valid ranges are from 5 - 15
+ filter = hb_filter_init(HB_FILTER_DEBLOCK);
+ if (self.filters.deblock != 0)
+ {
+ hb_add_filter(job, filter, [NSString stringWithFormat:@"%ld", (long)self.filters.deblock].UTF8String);
+ }
+
+ // Add Crop/Scale filter
+ filter = hb_filter_init(HB_FILTER_CROP_SCALE);
+ hb_add_filter( job, filter, [NSString stringWithFormat:@"%d:%d:%d:%d:%d:%d",
+ job->width, job->height,
+ job->crop[0], job->crop[1],
+ job->crop[2], job->crop[3]].UTF8String);
+
+ // Add framerate shaping filter
+ filter = hb_filter_init(HB_FILTER_VFR);
+ hb_add_filter(job, filter, [[NSString stringWithFormat:@"%d:%d:%d",
+ fps_mode, fps_num, fps_den] UTF8String]);
+
+ return job;
+}
+
#pragma mark - NSCoding
- (void)encodeWithCoder:(NSCoder *)coder