コード例 #1
0
ファイル: HbFunctions.cs プロジェクト: shkolnik/HandBrake
 public static extern int hb_subtitle_add(ref hb_job_s job, ref hb_subtitle_config_s subtitleConfig, int track);
コード例 #2
0
ファイル: HbFunctions.cs プロジェクト: shkolnik/HandBrake
 public static extern int hb_srt_add(ref hb_job_s job, ref hb_subtitle_config_s subtitleConfig, string lang);
コード例 #3
0
        /// <summary>
        /// Applies the encoding job to the native memory structure and returns a list of memory
        /// locations allocated during this.
        /// </summary>
        /// <param name="nativeJob">The native structure to apply to job info to.</param>
        /// <param name="job">The job info to apply.</param>
        /// <param name="preview">True if this is a preview encode.</param>
        /// <param name="previewNumber">The preview number (0-based) to encode.</param>
        /// <param name="previewSeconds">The number of seconds in the preview.</param>
        /// <param name="overallSelectedLengthSeconds">The currently selected encode length. Used in preview
        /// for calculating bitrate when the target size would be wrong.</param>
        /// <returns>The list of memory locations allocated for the job.</returns>
        private List<IntPtr> ApplyJob(ref hb_job_s nativeJob, EncodeJob job, bool preview, int previewNumber, int previewSeconds, double overallSelectedLengthSeconds)
        {
            var allocatedMemory = new List<IntPtr>();
            Title title = this.GetTitle(job.Title);
            hb_title_s originalTitle = this.GetOriginalTitle(job.Title);

            EncodingProfile profile = job.EncodingProfile;

            if (preview)
            {
                nativeJob.start_at_preview = previewNumber + 1;
                nativeJob.seek_points = this.previewCount;

                // There are 90,000 PTS per second.
                nativeJob.pts_to_stop = previewSeconds * 90000;
            }
            else
            {
                switch (job.RangeType)
                {
                    case VideoRangeType.Chapters:

                        if (job.ChapterStart > 0 && job.ChapterEnd > 0)
                        {
                            nativeJob.chapter_start = job.ChapterStart;
                            nativeJob.chapter_end = job.ChapterEnd;
                        }
                        else
                        {
                            nativeJob.chapter_start = 1;
                            nativeJob.chapter_end = title.Chapters.Count;
                        }

                        break;
                    case VideoRangeType.Seconds:
                        if (job.SecondsStart < 0 || job.SecondsEnd < 0 || job.SecondsStart >= job.SecondsEnd)
                        {
                            throw new ArgumentException("Seconds range " + job.SecondsStart + "-" + job.SecondsEnd + " is invalid.", "job");
                        }

                        // For some reason "pts_to_stop" actually means the number of pts to stop AFTER the start point.
                        nativeJob.pts_to_start = (int)(job.SecondsStart * 90000);
                        nativeJob.pts_to_stop = (int)((job.SecondsEnd - job.SecondsStart) * 90000);
                        break;
                    case VideoRangeType.Frames:
                        if (job.FramesStart < 0 || job.FramesEnd < 0 || job.FramesStart >= job.FramesEnd)
                        {
                            throw new ArgumentException("Frames range " + job.FramesStart + "-" + job.FramesEnd + " is invalid.", "job");
                        }

                        // "frame_to_stop" actually means the number of frames total to encode AFTER the start point.
                        nativeJob.frame_to_start = job.FramesStart;
                        nativeJob.frame_to_stop = job.FramesEnd - job.FramesStart;
                        break;
                }
            }

            nativeJob.chapter_markers = profile.IncludeChapterMarkers ? 1 : 0;

            Cropping crop;

            if (profile.CustomCropping)
            {
                crop = profile.Cropping;
            }
            else
            {
                crop = title.AutoCropDimensions;
            }

            nativeJob.crop[0] = crop.Top;
            nativeJob.crop[1] = crop.Bottom;
            nativeJob.crop[2] = crop.Left;
            nativeJob.crop[3] = crop.Right;

            if (profile.Deinterlace != Deinterlace.Off)
            {
                nativeJob.deinterlace = 1;
                string settings = null;

                switch (profile.Deinterlace)
                {
                    case Deinterlace.Fast:
                        settings = "-1";
                        break;
                    case Deinterlace.Slow:
                        settings = "2";
                        break;
                    case Deinterlace.Slower:
                        settings = "0";
                        break;
                    case Deinterlace.Custom:
                        settings = profile.CustomDeinterlace;
                        break;
                    default:
                        break;
                }

                this.AddFilter(nativeJob, NativeConstants.HB_FILTER_DEINTERLACE, settings, allocatedMemory);
            }
            else
            {
                nativeJob.deinterlace = 0;
            }

            if (profile.Detelecine != Detelecine.Off)
            {
                string settings = null;
                if (profile.Detelecine == Detelecine.Custom)
                {
                    settings = profile.CustomDetelecine;
                }

                this.AddFilter(nativeJob, NativeConstants.HB_FILTER_DETELECINE, settings, allocatedMemory);
            }

            if (profile.Decomb != Decomb.Off)
            {
                string settings = null;
                if (profile.Decomb == Decomb.Custom)
                {
                    settings = profile.CustomDecomb;
                }

                this.AddFilter(nativeJob, NativeConstants.HB_FILTER_DECOMB, settings, allocatedMemory);
            }

            if (profile.Deblock > 0)
            {
                this.AddFilter(nativeJob, NativeConstants.HB_FILTER_DEBLOCK, profile.Deblock.ToString(), allocatedMemory);
            }

            if (profile.Denoise != Denoise.Off)
            {
                string settings = null;
                switch (profile.Denoise)
                {
                    case Denoise.Weak:
                        settings = "2:1:2:3";
                        break;
                    case Denoise.Medium:
                        settings = "3:2:2:3";
                        break;
                    case Denoise.Strong:
                        settings = "7:7:5:5";
                        break;
                    case Denoise.Custom:
                        settings = profile.CustomDenoise;
                        break;
                    default:
                        break;
                }

                this.AddFilter(nativeJob, NativeConstants.HB_FILTER_DENOISE, settings, allocatedMemory);
            }

            int width = profile.Width;
            int height = profile.Height;

            int cropHorizontal = crop.Left + crop.Right;
            int cropVertical = crop.Top + crop.Bottom;

            if (width == 0)
            {
                width = title.Resolution.Width - cropHorizontal;
            }

            if (profile.MaxWidth > 0 && width > profile.MaxWidth)
            {
                width = profile.MaxWidth;
            }

            if (height == 0)
            {
                height = title.Resolution.Height - cropVertical;
            }

            if (profile.MaxHeight > 0 && height > profile.MaxHeight)
            {
                height = profile.MaxHeight;
            }

            nativeJob.grayscale = profile.Grayscale ? 1 : 0;

            switch (profile.Anamorphic)
            {
                case Anamorphic.None:
                    nativeJob.anamorphic.mode = 0;

                    Size outputSize = CalculateNonAnamorphicOutput(profile, title);
                    width = outputSize.Width;
                    height = outputSize.Height;

                    nativeJob.anamorphic.keep_display_aspect = profile.KeepDisplayAspect ? 1 : 0;
                    break;
                case Anamorphic.Strict:
                    nativeJob.anamorphic.mode = 1;
                    break;
                case Anamorphic.Loose:
                    nativeJob.anamorphic.mode = 2;
                    break;
                case Anamorphic.Custom:
                    nativeJob.anamorphic.mode = 3;

                    nativeJob.modulus = profile.Modulus;

                    if (profile.UseDisplayWidth)
                    {
                        if (profile.KeepDisplayAspect)
                        {
                            int cropWidth = title.Resolution.Width - cropHorizontal;
                            int cropHeight = title.Resolution.Height - cropVertical;

                            double displayAspect = ((double)(cropWidth * title.ParVal.Width)) / (cropHeight * title.ParVal.Height);
                            int displayWidth = profile.DisplayWidth;

                            if (profile.Height > 0)
                            {
                                displayWidth = (int)((double)profile.Height * displayAspect);
                            }
                            else if (displayWidth > 0)
                            {
                                height = (int)((double)displayWidth / displayAspect);
                            }
                            else
                            {
                                displayWidth = (int)((double)cropHeight * displayAspect);
                            }

                            nativeJob.anamorphic.dar_width = displayWidth;
                            nativeJob.anamorphic.dar_height = height;
                            nativeJob.anamorphic.keep_display_aspect = 1;
                        }

                        nativeJob.anamorphic.dar_width = profile.DisplayWidth;
                        nativeJob.anamorphic.dar_height = height;
                        nativeJob.anamorphic.keep_display_aspect = profile.KeepDisplayAspect ? 1 : 0;
                    }
                    else
                    {
                        nativeJob.anamorphic.par_width = profile.PixelAspectX;
                        nativeJob.anamorphic.par_height = profile.PixelAspectY;
                        nativeJob.anamorphic.keep_display_aspect = 0;
                    }

                    break;
                default:
                    break;
            }

            nativeJob.width = width;
            nativeJob.height = height;

            nativeJob.maxWidth = profile.MaxWidth;
            nativeJob.maxHeight = profile.MaxHeight;

            HBVideoEncoder videoEncoder = Encoders.VideoEncoders.FirstOrDefault(e => e.ShortName == profile.VideoEncoder);
            if (videoEncoder == null)
            {
                throw new ArgumentException("Video encoder " + profile.VideoEncoder + " not recognized.");
            }

            nativeJob.vcodec = videoEncoder.Id;

            if (profile.Framerate == 0)
            {
                nativeJob.cfr = 0;
            }
            else
            {
                if (profile.PeakFramerate)
                {
                    nativeJob.cfr = 2;
                }
                else
                {
                    nativeJob.cfr = 1;
                }

                nativeJob.vrate = 27000000;
                nativeJob.vrate_base = Converters.FramerateToVrate(profile.Framerate);
            }

            // vfr
            // areBframes
            // color_matrix
            List<hb_audio_s> titleAudio = InteropUtilities.ConvertList<hb_audio_s>(originalTitle.list_audio);

            var audioList = new List<hb_audio_s>();
            int numTracks = 0;

            List<Tuple<AudioEncoding, int>> outputTrackList = this.GetOutputTracks(job, title);

            if (profile.AudioEncoderFallback != null)
            {
                nativeJob.acodec_fallback = Encoders.GetAudioEncoder(profile.AudioEncoderFallback).Id;
            }

            nativeJob.acodec_copy_mask = (int)NativeConstants.HB_ACODEC_ANY;

            foreach (Tuple<AudioEncoding, int> outputTrack in outputTrackList)
            {
                audioList.Add(this.ConvertAudioBack(outputTrack.Item1, titleAudio[outputTrack.Item2 - 1], numTracks++, allocatedMemory));
            }

            NativeList nativeAudioList = InteropUtilities.ConvertListBack<hb_audio_s>(audioList);
            nativeJob.list_audio = nativeAudioList.ListPtr;
            allocatedMemory.AddRange(nativeAudioList.AllocatedMemory);

            // Create a new empty list
            int totalSubtitles = 0;
            if (job.Subtitles != null)
            {
                if (job.Subtitles.SourceSubtitles != null)
                {
                    totalSubtitles += job.Subtitles.SourceSubtitles.Count;
                }

                if (job.Subtitles.SrtSubtitles != null)
                {
                    totalSubtitles += job.Subtitles.SrtSubtitles.Count;
                }
            }

            NativeList nativeSubtitleList = InteropUtilities.CreateNativeList(totalSubtitles + 2);
            nativeJob.list_subtitle = nativeSubtitleList.ListPtr;
            allocatedMemory.AddRange(nativeSubtitleList.AllocatedMemory);

            if (job.Subtitles != null)
            {
                if (job.Subtitles.SourceSubtitles != null && job.Subtitles.SourceSubtitles.Count > 0)
                {
                    List<hb_subtitle_s> titleSubtitles = InteropUtilities.ConvertList<hb_subtitle_s>(originalTitle.list_subtitle);

                    foreach (SourceSubtitle sourceSubtitle in job.Subtitles.SourceSubtitles)
                    {
                        if (sourceSubtitle.TrackNumber == 0)
                        {
                            // Use subtitle search.
                            nativeJob.select_subtitle_config.force = sourceSubtitle.Forced ? 1 : 0;
                            nativeJob.select_subtitle_config.default_track = sourceSubtitle.Default ? 1 : 0;

                            if (!sourceSubtitle.BurnedIn)
                            {
                                nativeJob.select_subtitle_config.dest = hb_subtitle_config_s_subdest.PASSTHRUSUB;
                            }

                            nativeJob.indepth_scan = 1;
                        }
                        else
                        {
                            // Use specified subtitle.
                            hb_subtitle_s nativeSubtitle = titleSubtitles[sourceSubtitle.TrackNumber - 1];
                            var subtitleConfig = new hb_subtitle_config_s();

                            subtitleConfig.force = sourceSubtitle.Forced ? 1 : 0;
                            subtitleConfig.default_track = sourceSubtitle.Default ? 1 : 0;

                            bool supportsBurn = nativeSubtitle.source == hb_subtitle_s_subsource.VOBSUB || nativeSubtitle.source == hb_subtitle_s_subsource.SSASUB;
                            if (supportsBurn && sourceSubtitle.BurnedIn)
                            {
                                subtitleConfig.dest = hb_subtitle_config_s_subdest.RENDERSUB;
                            }
                            else
                            {
                                subtitleConfig.dest = hb_subtitle_config_s_subdest.PASSTHRUSUB;
                            }

                            int subtitleAddSucceded = HBFunctions.hb_subtitle_add(ref nativeJob, ref subtitleConfig, sourceSubtitle.TrackNumber - 1);
                            if (subtitleAddSucceded == 0)
                            {
                                System.Diagnostics.Debug.WriteLine("Subtitle add failed");
                            }
                        }
                    }
                }

                if (job.Subtitles.SrtSubtitles != null)
                {
                    foreach (SrtSubtitle srtSubtitle in job.Subtitles.SrtSubtitles)
                    {
                        var subtitleConfig = new hb_subtitle_config_s();

                        subtitleConfig.src_codeset = srtSubtitle.CharacterCode;
                        subtitleConfig.src_filename = srtSubtitle.FileName;
                        subtitleConfig.offset = srtSubtitle.Offset;
                        subtitleConfig.default_track = srtSubtitle.Default ? 1 : 0;

                        int srtAddSucceded = HBFunctions.hb_srt_add(ref nativeJob, ref subtitleConfig, srtSubtitle.LanguageCode);
                        if (srtAddSucceded == 0)
                        {
                            System.Diagnostics.Debug.WriteLine("SRT add failed");
                        }
                    }
                }
            }

            if (profile.OutputFormat == Container.Mp4)
            {
                nativeJob.mux = NativeConstants.HB_MUX_MP4;
            }
            else
            {
                nativeJob.mux = NativeConstants.HB_MUX_MKV;
            }

            nativeJob.file = job.OutputPath;

            nativeJob.largeFileSize = profile.LargeFile ? 1 : 0;
            nativeJob.mp4_optimize = profile.Optimize ? 1 : 0;
            nativeJob.ipod_atom = profile.IPod5GSupport ? 1 : 0;

            if (title.AngleCount > 1)
            {
                nativeJob.angle = job.Angle;
            }

            switch (profile.VideoEncodeRateType)
            {
                case VideoEncodeRateType.ConstantQuality:
                    nativeJob.vquality = (float)profile.Quality;
                    nativeJob.vbitrate = 0;
                    break;
                case VideoEncodeRateType.AverageBitrate:
                    nativeJob.vquality = -1;
                    nativeJob.vbitrate = profile.VideoBitrate;
                    break;
                case VideoEncodeRateType.TargetSize:
                    nativeJob.vquality = -1;
                    nativeJob.vbitrate = this.CalculateBitrate(job, profile.TargetSize, overallSelectedLengthSeconds);
                    break;
                default:
                    break;
            }

            // frames_to_skip

            return allocatedMemory;
        }
コード例 #4
0
 public static extern int hb_srt_add(ref hb_job_s job, ref hb_subtitle_config_s subtitleConfig, string lang);
コード例 #5
0
 public static extern int hb_subtitle_add(ref hb_job_s job, ref hb_subtitle_config_s subtitleConfig, int track);
コード例 #6
0
        /// <summary>
        /// Applies the encoding job to the native memory structure and returns a list of memory
        /// locations allocated during this.
        /// </summary>
        /// <param name="nativeJob">The native structure to apply to job info to.</param>
        /// <param name="job">The job info to apply.</param>
        /// <param name="preview">True if this is a preview encode.</param>
        /// <param name="previewNumber">The preview number (0-based) to encode.</param>
        /// <param name="previewSeconds">The number of seconds in the preview.</param>
        /// <param name="overallSelectedLengthSeconds">The currently selected encode length. Used in preview
        /// for calculating bitrate when the target size would be wrong.</param>
        /// <returns>The list of memory locations allocated for the job.</returns>
        private List<IntPtr> ApplyJob(ref hb_job_s nativeJob, EncodeJob job, bool preview, int previewNumber, int previewSeconds, double overallSelectedLengthSeconds)
        {
            var allocatedMemory = new List<IntPtr>();
            Title title = this.GetTitle(job.Title);
            hb_title_s originalTitle = this.GetOriginalTitle(job.Title);

            EncodingProfile profile = job.EncodingProfile;

            if (preview)
            {
                nativeJob.start_at_preview = previewNumber + 1;
                nativeJob.seek_points = this.previewCount;

                // There are 90,000 PTS per second.
                nativeJob.pts_to_stop = previewSeconds * 90000;
            }
            else
            {
                switch (job.RangeType)
                {
                    case VideoRangeType.All:
                        break;
                    case VideoRangeType.Chapters:
                        if (job.ChapterStart > 0 && job.ChapterEnd > 0)
                        {
                            nativeJob.chapter_start = job.ChapterStart;
                            nativeJob.chapter_end = job.ChapterEnd;
                        }
                        else
                        {
                            nativeJob.chapter_start = 1;
                            nativeJob.chapter_end = title.Chapters.Count;
                        }

                        break;
                    case VideoRangeType.Seconds:
                        if (job.SecondsStart < 0 || job.SecondsEnd < 0 || job.SecondsStart >= job.SecondsEnd)
                        {
                            throw new ArgumentException("Seconds range " + job.SecondsStart + "-" + job.SecondsEnd + " is invalid.", "job");
                        }

                        // If they've selected the "full" title duration, leave off the arguments to make it clean
                        if (job.SecondsStart > 0 || job.SecondsEnd < title.Duration.TotalSeconds)
                        {
                            // For some reason "pts_to_stop" actually means the number of pts to stop AFTER the start point.
                            nativeJob.pts_to_start = (int)(job.SecondsStart * 90000);
                            nativeJob.pts_to_stop = (int)((job.SecondsEnd - job.SecondsStart) * 90000);
                        }
                        break;
                    case VideoRangeType.Frames:
                        if (job.FramesStart < 0 || job.FramesEnd < 0 || job.FramesStart >= job.FramesEnd)
                        {
                            throw new ArgumentException("Frames range " + job.FramesStart + "-" + job.FramesEnd + " is invalid.", "job");
                        }

                        // "frame_to_stop" actually means the number of frames total to encode AFTER the start point.
                        nativeJob.frame_to_start = job.FramesStart;
                        nativeJob.frame_to_stop = job.FramesEnd - job.FramesStart;
                        break;
                }
            }

            // Chapter markers
            nativeJob.chapter_markers = profile.IncludeChapterMarkers ? 1 : 0;

            List<IntPtr> nativeChapters = nativeJob.list_chapter.ToIntPtrList();

            if (!preview && profile.IncludeChapterMarkers)
            {
                int numChapters = title.Chapters.Count;

                if (job.UseDefaultChapterNames)
                {
                    for (int i = 0; i < numChapters; i++)
                    {
                        if (i < nativeChapters.Count)
                        {
                            HBFunctions.hb_chapter_set_title(nativeChapters[i], "Chapter " + (i + 1));
                        }
                    }
                }
                else
                {
                    for (int i = 0; i < numChapters; i++)
                    {
                        if (i < nativeChapters.Count && i < job.CustomChapterNames.Count)
                        {
                            IntPtr chapterNamePtr;

                            if (string.IsNullOrWhiteSpace(job.CustomChapterNames[i]))
                            {
                                chapterNamePtr = InteropUtilities.CreateUtf8Ptr("Chapter " + (i + 1));
                            }
                            else
                            {
                                chapterNamePtr = InteropUtilities.CreateUtf8Ptr(job.CustomChapterNames[i]);
                            }

                            HBFunctions.hb_chapter_set_title__ptr(nativeChapters[i], chapterNamePtr);
                            Marshal.FreeHGlobal(chapterNamePtr);
                        }
                    }
                }
            }

            Cropping crop = GetCropping(profile, title);

            nativeJob.crop[0] = crop.Top;
            nativeJob.crop[1] = crop.Bottom;
            nativeJob.crop[2] = crop.Left;
            nativeJob.crop[3] = crop.Right;

            var filterList = new List<hb_filter_object_s>();

            // FILTERS: These must be added in the correct order since we cannot depend on the automatic ordering in hb_add_filter . Ordering is determined
            // by the order they show up in the filters enum.

            // Detelecine
            if (profile.Detelecine != Detelecine.Off)
            {
                string settings = null;
                if (profile.Detelecine == Detelecine.Custom)
                {
                    settings = profile.CustomDetelecine;
                }

                this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_DETELECINE, settings, allocatedMemory);
            }

            // Decomb
            if (profile.Decomb != Decomb.Off)
            {
                string settings = null;
                switch (profile.Decomb)
                {
                    case Decomb.Default:
                        break;
                    case Decomb.Fast:
                        settings = "7:2:6:9:1:80";
                        break;
                    case Decomb.Bob:
                        settings = "455";
                        break;
                    case Decomb.Custom:
                        settings = profile.CustomDecomb;
                        break;
                    default:
                        break;
                }

                this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_DECOMB, settings, allocatedMemory);
            }

            // Deinterlace
            if (profile.Deinterlace != Deinterlace.Off)
            {
                nativeJob.deinterlace = 1;
                string settings = null;

                switch (profile.Deinterlace)
                {
                    case Deinterlace.Fast:
                        settings = "0";
                        break;
                    case Deinterlace.Slow:
                        settings = "1";
                        break;
                    case Deinterlace.Slower:
                        settings = "3";
                        break;
                    case Deinterlace.Bob:
                        settings = "15";
                        break;
                    case Deinterlace.Custom:
                        settings = profile.CustomDeinterlace;
                        break;
                    default:
                        break;
                }

                this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_DEINTERLACE, settings, allocatedMemory);
            }
            else
            {
                nativeJob.deinterlace = 0;
            }

            // VFR
            if (profile.Framerate == 0)
            {
                if (profile.ConstantFramerate)
                {
                    // CFR with "Same as Source". Use the title rate
                    nativeJob.cfr = 1;
                    nativeJob.vrate = originalTitle.rate;
                    nativeJob.vrate_base = originalTitle.rate_base;
                }
                else
                {
                    // Pure VFR "Same as Source"
                    nativeJob.cfr = 0;
                }
            }
            else
            {
                // Specified framerate
                if (profile.ConstantFramerate)
                {
                    // Mark as pure CFR
                    nativeJob.cfr = 1;
                }
                else
                {
                    // Mark as peak framerate
                    nativeJob.cfr = 2;
                }

                nativeJob.vrate = 27000000;
                nativeJob.vrate_base = Converters.FramerateToVrate(profile.Framerate);
            }

            string vfrSettings = string.Format(CultureInfo.InvariantCulture, "{0}:{1}:{2}", nativeJob.cfr, nativeJob.vrate, nativeJob.vrate_base);
            this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_VFR, vfrSettings, allocatedMemory);

            // Deblock
            if (profile.Deblock > 0)
            {
                this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_DEBLOCK, profile.Deblock.ToString(CultureInfo.InvariantCulture), allocatedMemory);
            }

            // Denoise
            if (profile.Denoise != Denoise.Off)
            {
                string settings = null;
                switch (profile.Denoise)
                {
                    case Denoise.Weak:
                        settings = "2:1:1:2:3:3";
                        break;
                    case Denoise.Medium:
                        settings = "3:2:2:2:3:3";
                        break;
                    case Denoise.Strong:
                        settings = "7:7:7:5:5:5";
                        break;
                    case Denoise.Custom:
                        settings = profile.CustomDenoise;
                        break;
                    default:
                        break;
                }

                this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_DENOISE, settings, allocatedMemory);
            }

            // Crop/scale
            int width = profile.Width;
            int height = profile.Height;

            int cropHorizontal = crop.Left + crop.Right;
            int cropVertical = crop.Top + crop.Bottom;

            if (width == 0)
            {
                width = title.Resolution.Width - cropHorizontal;
            }

            if (profile.MaxWidth > 0 && width > profile.MaxWidth)
            {
                width = profile.MaxWidth;
            }

            if (height == 0)
            {
                height = title.Resolution.Height - cropVertical;
            }

            if (profile.MaxHeight > 0 && height > profile.MaxHeight)
            {
                height = profile.MaxHeight;
            }

            // The job width can sometimes start not clean, due to interference from
            // preview generation. We reset it here to allow good calculations.
            nativeJob.width = width;
            nativeJob.height = height;

            nativeJob.grayscale = profile.Grayscale ? 1 : 0;

            switch (profile.Anamorphic)
            {
                case Anamorphic.None:
                    nativeJob.anamorphic.mode = 0;

                    Size outputSize = CalculateNonAnamorphicOutput(profile, title);
                    width = outputSize.Width;
                    height = outputSize.Height;

                    nativeJob.anamorphic.keep_display_aspect = profile.KeepDisplayAspect ? 1 : 0;

                    nativeJob.width = width;
                    nativeJob.height = height;

                    nativeJob.maxWidth = profile.MaxWidth;
                    nativeJob.maxHeight = profile.MaxHeight;

                    break;
                case Anamorphic.Strict:
                    nativeJob.anamorphic.mode = 1;

                    nativeJob.anamorphic.par_width = title.ParVal.Width;
                    nativeJob.anamorphic.par_height = title.ParVal.Height;
                    break;
                case Anamorphic.Loose:
                    nativeJob.anamorphic.mode = 2;

                    nativeJob.modulus = profile.Modulus;

                    nativeJob.width = width;

                    nativeJob.maxWidth = profile.MaxWidth;

                    nativeJob.anamorphic.par_width = title.ParVal.Width;
                    nativeJob.anamorphic.par_height = title.ParVal.Height;
                    break;
                case Anamorphic.Custom:
                    nativeJob.anamorphic.mode = 3;

                    nativeJob.modulus = profile.Modulus;

                    if (profile.UseDisplayWidth)
                    {
                        if (profile.KeepDisplayAspect)
                        {
                            int cropWidth = title.Resolution.Width - cropHorizontal;
                            int cropHeight = title.Resolution.Height - cropVertical;

                            double displayAspect = ((double)(cropWidth * title.ParVal.Width)) / (cropHeight * title.ParVal.Height);
                            int displayWidth = profile.DisplayWidth;

                            if (profile.Height > 0)
                            {
                                displayWidth = (int)((double)profile.Height * displayAspect);
                            }
                            else if (displayWidth > 0)
                            {
                                height = (int)((double)displayWidth / displayAspect);
                            }
                            else
                            {
                                displayWidth = (int)((double)height * displayAspect);
                            }

                            nativeJob.anamorphic.dar_width = displayWidth;
                            nativeJob.anamorphic.dar_height = height;
                            nativeJob.anamorphic.keep_display_aspect = 1;
                        }
                        else
                        {
                            nativeJob.anamorphic.dar_width = profile.DisplayWidth;
                            nativeJob.anamorphic.dar_height = height;
                            nativeJob.anamorphic.keep_display_aspect = 0;
                        }
                    }
                    else
                    {
                        nativeJob.anamorphic.par_width = profile.PixelAspectX;
                        nativeJob.anamorphic.par_height = profile.PixelAspectY;
                        nativeJob.anamorphic.keep_display_aspect = 0;
                    }

                    nativeJob.width = width;
                    nativeJob.height = height;

                    nativeJob.maxWidth = profile.MaxWidth;
                    nativeJob.maxHeight = profile.MaxHeight;

                    break;
                default:
                    break;
            }

            // Need to fix up values before adding crop/scale filter
            if (profile.Anamorphic != Anamorphic.None)
            {
                int anamorphicWidth = 0, anamorphicHeight = 0, anamorphicParWidth = 0, anamorphicParHeight = 0;

                HBFunctions.hb_set_anamorphic_size(ref nativeJob, ref anamorphicWidth, ref anamorphicHeight, ref anamorphicParWidth, ref anamorphicParHeight);
                nativeJob.width = anamorphicWidth;
                nativeJob.height = anamorphicHeight;
                nativeJob.anamorphic.par_width = anamorphicParWidth;
                nativeJob.anamorphic.par_height = anamorphicParHeight;
            }

            string cropScaleSettings = string.Format(
                CultureInfo.InvariantCulture,
                "{0}:{1}:{2}:{3}:{4}:{5}",
                nativeJob.width,
                nativeJob.height,
                nativeJob.crop[0],
                nativeJob.crop[1],
                nativeJob.crop[2],
                nativeJob.crop[3]);
            this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_CROP_SCALE, cropScaleSettings, allocatedMemory);

            HBVideoEncoder videoEncoder = Encoders.VideoEncoders.FirstOrDefault(e => e.ShortName == profile.VideoEncoder);
            if (videoEncoder == null)
            {
                throw new ArgumentException("Video encoder " + profile.VideoEncoder + " not recognized.");
            }

            nativeJob.vcodec = videoEncoder.Id;

            // areBframes
            // color_matrix
            List<hb_audio_s> titleAudio = originalTitle.list_audio.ToList<hb_audio_s>();

            var audioList = new List<hb_audio_s>();
            int numTracks = 0;

            List<Tuple<AudioEncoding, int>> outputTrackList = this.GetOutputTracks(job, title);

            if (!string.IsNullOrEmpty(profile.AudioEncoderFallback))
            {
                HBAudioEncoder audioEncoder = Encoders.GetAudioEncoder(profile.AudioEncoderFallback);
                if (audioEncoder == null)
                {
                    throw new ArgumentException("Unrecognized fallback audio encoder: " + profile.AudioEncoderFallback);
                }

                nativeJob.acodec_fallback = Encoders.GetAudioEncoder(profile.AudioEncoderFallback).Id;
            }

            nativeJob.acodec_copy_mask = (int)NativeConstants.HB_ACODEC_ANY;

            foreach (Tuple<AudioEncoding, int> outputTrack in outputTrackList)
            {
                audioList.Add(this.ConvertAudioBack(outputTrack.Item1, titleAudio[outputTrack.Item2 - 1], numTracks++, allocatedMemory));
            }

            NativeList nativeAudioList = InteropUtilities.ConvertListBack<hb_audio_s>(audioList);
            nativeJob.list_audio = nativeAudioList.Ptr;
            allocatedMemory.AddRange(nativeAudioList.AllocatedMemory);

            if (job.Subtitles != null)
            {
                if (job.Subtitles.SourceSubtitles != null && job.Subtitles.SourceSubtitles.Count > 0)
                {
                    List<hb_subtitle_s> titleSubtitles = originalTitle.list_subtitle.ToList<hb_subtitle_s>();

                    foreach (SourceSubtitle sourceSubtitle in job.Subtitles.SourceSubtitles)
                    {
                        if (sourceSubtitle.TrackNumber == 0)
                        {
                            // Use subtitle search.
                            nativeJob.select_subtitle_config.force = sourceSubtitle.Forced ? 1 : 0;
                            nativeJob.select_subtitle_config.default_track = sourceSubtitle.Default ? 1 : 0;

                            if (!sourceSubtitle.BurnedIn)
                            {
                                nativeJob.select_subtitle_config.dest = hb_subtitle_config_s_subdest.PASSTHRUSUB;
                            }

                            nativeJob.indepth_scan = 1;
                        }
                        else
                        {
                            // Use specified subtitle.
                            hb_subtitle_s nativeSubtitle = titleSubtitles[sourceSubtitle.TrackNumber - 1];
                            var subtitleConfig = new hb_subtitle_config_s();

                            subtitleConfig.force = sourceSubtitle.Forced ? 1 : 0;
                            subtitleConfig.default_track = sourceSubtitle.Default ? 1 : 0;

                            bool supportsBurn = nativeSubtitle.source == hb_subtitle_s_subsource.VOBSUB || nativeSubtitle.source == hb_subtitle_s_subsource.SSASUB || nativeSubtitle.source == hb_subtitle_s_subsource.PGSSUB;
                            if (supportsBurn && sourceSubtitle.BurnedIn)
                            {
                                subtitleConfig.dest = hb_subtitle_config_s_subdest.RENDERSUB;
                            }
                            else
                            {
                                subtitleConfig.dest = hb_subtitle_config_s_subdest.PASSTHRUSUB;
                            }

                            int subtitleAddSucceded = HBFunctions.hb_subtitle_add(ref nativeJob, ref subtitleConfig, sourceSubtitle.TrackNumber - 1);
                            if (subtitleAddSucceded == 0)
                            {
                                System.Diagnostics.Debug.WriteLine("Subtitle add failed");
                            }
                        }
                    }
                }

                if (job.Subtitles.SrtSubtitles != null)
                {
                    foreach (SrtSubtitle srtSubtitle in job.Subtitles.SrtSubtitles)
                    {
                        var subtitleConfig = new hb_subtitle_config_s();

                        subtitleConfig.src_codeset = srtSubtitle.CharacterCode;
                        subtitleConfig.src_filename = srtSubtitle.FileName;
                        subtitleConfig.offset = srtSubtitle.Offset;
                        subtitleConfig.default_track = srtSubtitle.Default ? 1 : 0;

                        int srtAddSucceded = HBFunctions.hb_srt_add(ref nativeJob, ref subtitleConfig, srtSubtitle.LanguageCode);
                        if (srtAddSucceded == 0)
                        {
                            System.Diagnostics.Debug.WriteLine("SRT add failed");
                        }
                    }
                }

                bool hasBurnedSubtitle = job.Subtitles.SourceSubtitles != null && job.Subtitles.SourceSubtitles.Any(s => s.BurnedIn);
                if (hasBurnedSubtitle)
                {
                    this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_RENDER_SUB, string.Format(CultureInfo.InvariantCulture, "{0}:{1}:{2}:{3}", crop.Top, crop.Bottom, crop.Left, crop.Right), allocatedMemory);
                }
            }

            // Construct final filter list
            nativeJob.list_filter = this.ConvertFilterListToNative(filterList, allocatedMemory).Ptr;

            if (profile.ScaleMethod == ScaleMethod.Bicubic)
            {
                HBFunctions.hb_get_opencl_env();
                nativeJob.use_opencl = 1;
            }
            else
            {
                nativeJob.use_opencl = 0;
            }

            nativeJob.qsv.decode = profile.QsvDecode ? 1 : 0;
            nativeJob.use_hwd = job.DxvaDecoding ? 1 : 0;

            #pragma warning disable 612, 618
            if (profile.OutputFormat == Container.Mp4)
            {
                nativeJob.mux = HBFunctions.hb_container_get_from_name("av_mp4");
            }
            else if (profile.OutputFormat == Container.Mkv)
            {
                nativeJob.mux = HBFunctions.hb_container_get_from_name("av_mkv");
            }
            #pragma warning restore 612, 618

            if (profile.ContainerName != null)
            {
                nativeJob.mux = HBFunctions.hb_container_get_from_name(profile.ContainerName);
            }

            if (job.OutputPath == null)
            {
                nativeJob.file = IntPtr.Zero;
            }
            else
            {
                IntPtr outputPathPtr = InteropUtilities.CreateUtf8Ptr(job.OutputPath);
                allocatedMemory.Add(outputPathPtr);
                nativeJob.file = outputPathPtr;
            }

            nativeJob.largeFileSize = profile.LargeFile ? 1 : 0;
            nativeJob.mp4_optimize = profile.Optimize ? 1 : 0;
            nativeJob.ipod_atom = profile.IPod5GSupport ? 1 : 0;

            if (title.AngleCount > 1)
            {
                nativeJob.angle = job.Angle;
            }

            switch (profile.VideoEncodeRateType)
            {
                case VideoEncodeRateType.ConstantQuality:
                    nativeJob.vquality = (float)profile.Quality;
                    nativeJob.vbitrate = 0;
                    break;
                case VideoEncodeRateType.AverageBitrate:
                    nativeJob.vquality = -1;
                    nativeJob.vbitrate = profile.VideoBitrate;
                    break;
                case VideoEncodeRateType.TargetSize:
                    nativeJob.vquality = -1;
                    nativeJob.vbitrate = this.CalculateBitrate(job, profile.TargetSize, overallSelectedLengthSeconds);
                    break;
                default:
                    break;
            }

            // frames_to_skip

            return allocatedMemory;
        }