// --------------------------------------------------------------------------------------------------------------------
//
// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
//
//
// The encode factory.
//
// --------------------------------------------------------------------------------------------------------------------
namespace HandBrakeWPF.Services.Encode.Factories
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using HandBrake.Interop.Interop;
using HandBrake.Interop.Interop.HbLib;
using HandBrake.Interop.Interop.Json.Encode;
using HandBrake.Interop.Interop.Json.Shared;
using HandBrake.Interop.Interop.Model.Encoding;
using HandBrake.Interop.Model;
using HandBrakeWPF.Utilities;
using Newtonsoft.Json.Linq;
using AudioEncoder = HandBrakeWPF.Services.Encode.Model.Models.AudioEncoder;
using AudioEncoderRateType = HandBrakeWPF.Services.Encode.Model.Models.AudioEncoderRateType;
using AudioTrack = HandBrakeWPF.Services.Encode.Model.Models.AudioTrack;
using ChapterMarker = HandBrakeWPF.Services.Encode.Model.Models.ChapterMarker;
using EncodeTask = HandBrakeWPF.Services.Encode.Model.EncodeTask;
using FramerateMode = HandBrakeWPF.Services.Encode.Model.Models.FramerateMode;
using OutputFormat = HandBrakeWPF.Services.Encode.Model.Models.OutputFormat;
using PointToPointMode = HandBrakeWPF.Services.Encode.Model.Models.PointToPointMode;
using Subtitle = HandBrake.Interop.Interop.Json.Encode.Subtitles;
using SubtitleTrack = HandBrakeWPF.Services.Encode.Model.Models.SubtitleTrack;
using SystemInfo = HandBrake.Interop.Utilities.SystemInfo;
using Validate = HandBrakeWPF.Helpers.Validate;
///
/// This factory takes the internal EncodeJob object and turns it into a set of JSON models
/// that can be deserialized by libhb.
///
internal class EncodeFactory
{
///
/// The create.
///
///
/// The encode job.
///
///
/// The configuration.
///
///
/// The .
///
internal static JsonEncodeObject Create(EncodeTask job, HBConfiguration configuration)
{
JsonEncodeObject encode = new JsonEncodeObject
{
SequenceID = 0,
Audio = CreateAudio(job),
Destination = CreateDestination(job),
Filters = CreateFilters(job),
PAR = CreatePAR(job),
Metadata = CreateMetadata(job),
Source = CreateSource(job, configuration),
Subtitle = CreateSubtitle(job),
Video = CreateVideo(job, configuration)
};
return encode;
}
///
/// The create source.
///
///
/// The job.
///
///
/// The configuration.
///
///
/// The .
///
private static Source CreateSource(EncodeTask job, HBConfiguration configuration)
{
Range range = new Range();
switch (job.PointToPointMode)
{
case PointToPointMode.Chapters:
range.Type = "chapter";
range.Start = job.StartPoint;
range.End = job.EndPoint;
break;
case PointToPointMode.Seconds:
range.Type = "time";
range.Start = job.StartPoint * 90000;
range.End = job.EndPoint * 90000;
break;
case PointToPointMode.Frames:
range.Type = "frame";
range.Start = job.StartPoint;
range.End = job.EndPoint;
break;
case PointToPointMode.Preview:
range.Type = "preview";
range.Start = job.PreviewEncodeStartAt;
range.SeekPoints = configuration.PreviewScanCount;
range.End = job.PreviewEncodeDuration * 90000;
break;
}
Source source = new Source
{
Title = job.Title,
Range = range,
Angle = job.Angle,
Path = job.Source,
};
return source;
}
///
/// The create destination.
///
///
/// The job.
///
///
/// The .
///
private static Destination CreateDestination(EncodeTask job)
{
Destination destination = new Destination
{
File = job.Destination,
Mp4Options = new Mp4Options
{
IpodAtom = job.IPod5GSupport,
Mp4Optimize = job.OptimizeMP4
},
ChapterMarkers = job.IncludeChapterMarkers,
AlignAVStart = job.AlignAVStart,
Mux = EnumHelper.GetShortName(job.OutputFormat),
ChapterList = new List()
};
if (job.IncludeChapterMarkers)
{
foreach (ChapterMarker item in job.ChapterNames)
{
Chapter chapter = new Chapter { Name = item.ChapterName };
destination.ChapterList.Add(chapter);
}
}
return destination;
}
///
/// Create the PAR object
///
///
/// The Job
///
///
/// The produced PAR object.
///
private static PAR CreatePAR(EncodeTask job)
{
return new PAR { Num = job.PixelAspectX, Den = job.PixelAspectY };
}
///
/// The create subtitle.
///
///
/// The job.
///
///
/// The .
///
private static Subtitle CreateSubtitle(EncodeTask job)
{
Subtitles subtitle = new Subtitles
{
Search =
new SubtitleSearch
{
Enable = false,
Default = false,
Burn = false,
Forced = false
},
SubtitleList = new List()
};
foreach (SubtitleTrack item in job.SubtitleTracks)
{
if (!item.IsSrtSubtitle)
{
// Handle Foreign Audio Search
if (item.SourceTrack.TrackNumber == 0)
{
subtitle.Search.Enable = true;
subtitle.Search.Burn = item.Burned;
subtitle.Search.Default = item.Default;
subtitle.Search.Forced = item.Forced;
}
else
{
HandBrake.Interop.Interop.Json.Encode.SubtitleTrack track = new HandBrake.Interop.Interop.Json.Encode.SubtitleTrack
{
Burn = item.Burned,
Default = item.Default,
Forced = item.Forced,
ID = item.SourceTrack.TrackNumber,
Track = (item.SourceTrack.TrackNumber - 1)
};
subtitle.SubtitleList.Add(track);
}
}
else
{
HandBrake.Interop.Interop.Json.Encode.SubtitleTrack track = new HandBrake.Interop.Interop.Json.Encode.SubtitleTrack
{
Track = -1, // Indicates SRT
Default = item.Default,
Offset = item.SrtOffset,
Burn = item.Burned,
SRT =
new SRT
{
Filename = item.SrtPath,
Codeset = item.SrtCharCode,
Language = item.SrtLangCode
}
};
subtitle.SubtitleList.Add(track);
}
}
return subtitle;
}
///
/// The create video.
///
///
/// The job.
///
///
/// The configuration.
///
///
/// The .
///
private static Video CreateVideo(EncodeTask job, HBConfiguration configuration)
{
Video video = new Video();
HBVideoEncoder videoEncoder = HandBrakeEncoderHelpers.VideoEncoders.FirstOrDefault(e => e.ShortName == EnumHelper.GetShortName(job.VideoEncoder));
Validate.NotNull(videoEncoder, "Video encoder " + job.VideoEncoder + " not recognized.");
if (videoEncoder != null)
{
video.Encoder = videoEncoder.ShortName;
}
string advancedOptions = job.ShowAdvancedTab ? job.AdvancedEncoderOptions : string.Empty;
if (!string.IsNullOrEmpty(advancedOptions))
{
video.Options = advancedOptions;
}
else
{
video.Level = job.VideoLevel != null ? job.VideoLevel.ShortName : null;
video.Options = job.ExtraAdvancedArguments;
video.Preset = job.VideoPreset != null ? job.VideoPreset.ShortName : null;
video.Profile = job.VideoProfile != null ? job.VideoProfile.ShortName : null;
if (job.VideoTunes != null && job.VideoTunes.Count > 0)
{
foreach (var item in job.VideoTunes)
{
video.Tune += string.IsNullOrEmpty(video.Tune) ? item.ShortName : "," + item.ShortName;
}
}
}
if (job.VideoEncodeRateType == VideoEncodeRateType.ConstantQuality)
{
video.Quality = job.Quality;
}
if (job.VideoEncodeRateType == VideoEncodeRateType.AverageBitrate)
{
video.Bitrate = job.VideoBitrate;
video.TwoPass = job.TwoPass;
video.Turbo = job.TurboFirstPass;
}
video.QSV.Decode = SystemInfo.IsQsvAvailable && configuration.EnableQuickSyncDecoding;
// The use of the QSV decoder is configurable for non QSV encoders.
if (video.QSV.Decode && job.VideoEncoder != VideoEncoder.QuickSync && job.VideoEncoder != VideoEncoder.QuickSyncH265 && job.VideoEncoder != VideoEncoder.QuickSyncH26510b)
{
video.QSV.Decode = configuration.UseQSVDecodeForNonQSVEnc;
}
return video;
}
///
/// The create audio.
///
///
/// The job.
///
///
/// The .
///
private static Audio CreateAudio(EncodeTask job)
{
Audio audio = new Audio();
List copyMaskList = new List();
if (job.AllowedPassthruOptions.AudioAllowAACPass) copyMaskList.Add(EnumHelper.GetShortName(AudioEncoder.AacPassthru));
if (job.AllowedPassthruOptions.AudioAllowAC3Pass) copyMaskList.Add(EnumHelper.GetShortName(AudioEncoder.Ac3Passthrough));
if (job.AllowedPassthruOptions.AudioAllowDTSHDPass) copyMaskList.Add(EnumHelper.GetShortName(AudioEncoder.DtsHDPassthrough));
if (job.AllowedPassthruOptions.AudioAllowDTSPass) copyMaskList.Add(EnumHelper.GetShortName(AudioEncoder.DtsPassthrough));
if (job.AllowedPassthruOptions.AudioAllowEAC3Pass) copyMaskList.Add(EnumHelper.GetShortName(AudioEncoder.EAc3Passthrough));
if (job.AllowedPassthruOptions.AudioAllowFlacPass) copyMaskList.Add(EnumHelper.GetShortName(AudioEncoder.FlacPassthru));
if (job.AllowedPassthruOptions.AudioAllowMP3Pass) copyMaskList.Add(EnumHelper.GetShortName(AudioEncoder.Mp3Passthru));
if (job.AllowedPassthruOptions.AudioAllowTrueHDPass) copyMaskList.Add(EnumHelper.GetShortName(AudioEncoder.TrueHDPassthrough));
audio.CopyMask = copyMaskList.ToArray();
HBAudioEncoder audioEncoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper.GetShortName(job.AllowedPassthruOptions.AudioEncoderFallback));
audio.FallbackEncoder = audioEncoder.ShortName;
audio.AudioList = new List();
foreach (AudioTrack item in job.AudioTracks)
{
HBAudioEncoder encoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper.GetShortName(item.Encoder));
Validate.NotNull(encoder, "Unrecognized audio encoder:" + item.Encoder);
if (item.IsPassthru && (item.ScannedTrack.Codec & encoder.Id) == 0)
{
// We have an unsupported passthru. Rather than let libhb drop the track, switch it to the fallback.
encoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper.GetShortName(job.AllowedPassthruOptions.AudioEncoderFallback));
}
HBMixdown mixdown = HandBrakeEncoderHelpers.GetMixdown(item.MixDown);
HBRate sampleRate = HandBrakeEncoderHelpers.AudioSampleRates.FirstOrDefault(s => s.Name == item.SampleRate.ToString(CultureInfo.InvariantCulture));
HandBrake.Interop.Interop.Json.Encode.AudioTrack audioTrack = new HandBrake.Interop.Interop.Json.Encode.AudioTrack
{
Track = (item.Track.HasValue ? item.Track.Value : 0) - 1,
DRC = item.DRC,
Encoder = encoder.ShortName,
Gain = item.Gain,
Mixdown = mixdown != null ? mixdown.Id : -1,
NormalizeMixLevel = false,
Samplerate = sampleRate != null ? sampleRate.Rate : 0,
Name = !string.IsNullOrEmpty(item.TrackName) ? item.TrackName : null,
};
if (!item.IsPassthru)
{
if (item.EncoderRateType == AudioEncoderRateType.Quality)
{
audioTrack.Quality = item.Quality;
}
if (item.EncoderRateType == AudioEncoderRateType.Bitrate)
{
audioTrack.Bitrate = item.Bitrate;
}
}
audio.AudioList.Add(audioTrack);
}
return audio;
}
///
/// The create filter.
///
///
/// The job.
///
///
/// The .
///
private static Filters CreateFilters(EncodeTask job)
{
Filters filter = new Filters
{
FilterList = new List(),
};
// Note, order is important.
// Detelecine
if (job.Detelecine != Detelecine.Off)
{
IntPtr settingsPtr = HBFunctions.hb_generate_filter_settings_json((int)hb_filter_ids.HB_FILTER_DETELECINE, null, null, job.CustomDetelecine);
string unparsedJson = Marshal.PtrToStringAnsi(settingsPtr);
if (!string.IsNullOrEmpty(unparsedJson))
{
JToken settings = JObject.Parse(unparsedJson);
Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_DETELECINE, Settings = settings };
filter.FilterList.Add(filterItem);
}
}
// Deinterlace
if (job.DeinterlaceFilter == DeinterlaceFilter.Yadif)
{
IntPtr settingsPtr = HBFunctions.hb_generate_filter_settings_json((int)hb_filter_ids.HB_FILTER_DEINTERLACE, EnumHelper.GetShortName(job.Deinterlace), null, job.CustomDeinterlace);
string unparsedJson = Marshal.PtrToStringAnsi(settingsPtr);
if (!string.IsNullOrEmpty(unparsedJson))
{
JToken root = JObject.Parse(unparsedJson);
Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_DEINTERLACE, Settings = root };
filter.FilterList.Add(filterItem);
}
}
// Decomb
if (job.DeinterlaceFilter == DeinterlaceFilter.Decomb)
{
IntPtr settingsPtr = HBFunctions.hb_generate_filter_settings_json((int)hb_filter_ids.HB_FILTER_DECOMB, EnumHelper.GetShortName(job.Decomb), null, job.CustomDecomb);
string unparsedJson = Marshal.PtrToStringAnsi(settingsPtr);
if (!string.IsNullOrEmpty(unparsedJson))
{
JToken settings = JObject.Parse(unparsedJson);
Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_DECOMB, Settings = settings };
filter.FilterList.Add(filterItem);
}
}
if (job.DeinterlaceFilter == DeinterlaceFilter.Decomb || job.DeinterlaceFilter == DeinterlaceFilter.Yadif)
{
if (job.CombDetect != CombDetect.Off)
{
IntPtr settingsPtr = HBFunctions.hb_generate_filter_settings_json((int)hb_filter_ids.HB_FILTER_COMB_DETECT, EnumHelper.GetShortName(job.CombDetect), null, job.CustomCombDetect);
string unparsedJson = Marshal.PtrToStringAnsi(settingsPtr);
if (!string.IsNullOrEmpty(unparsedJson))
{
JToken settings = JObject.Parse(unparsedJson);
Filter filterItem = new Filter
{
ID = (int)hb_filter_ids.HB_FILTER_COMB_DETECT,
Settings = settings
};
filter.FilterList.Add(filterItem);
}
}
}
// Denoise
if (job.Denoise != Denoise.Off)
{
hb_filter_ids id = job.Denoise == Denoise.hqdn3d
? hb_filter_ids.HB_FILTER_HQDN3D
: hb_filter_ids.HB_FILTER_NLMEANS;
IntPtr settingsPtr = HBFunctions.hb_generate_filter_settings_json((int)id, job.DenoisePreset.ToString().ToLower().Replace(" ", string.Empty), job.DenoiseTune.ToString().ToLower().Replace(" ", string.Empty), job.CustomDenoise);
string unparsedJson = Marshal.PtrToStringAnsi(settingsPtr);
if (!string.IsNullOrEmpty(unparsedJson))
{
JToken settings = JObject.Parse(unparsedJson);
Filter filterItem = new Filter { ID = (int)id, Settings = settings };
filter.FilterList.Add(filterItem);
}
}
// Sharpen
if (job.Sharpen != Sharpen.Off)
{
hb_filter_ids id = job.Sharpen == Sharpen.LapSharp
? hb_filter_ids.HB_FILTER_LAPSHARP
: hb_filter_ids.HB_FILTER_UNSHARP;
IntPtr settingsPtr = HBFunctions.hb_generate_filter_settings_json((int)id, job.SharpenPreset.Key, job.SharpenTune.Key, job.SharpenCustom);
string unparsedJson = Marshal.PtrToStringAnsi(settingsPtr);
if (!string.IsNullOrEmpty(unparsedJson))
{
JToken settings = JObject.Parse(unparsedJson);
Filter filterItem = new Filter { ID = (int)id, Settings = settings };
filter.FilterList.Add(filterItem);
}
}
// Deblock
if (job.Deblock >= 5)
{
IntPtr settingsPtr = HBFunctions.hb_generate_filter_settings_json((int)hb_filter_ids.HB_FILTER_DEBLOCK, null, null, string.Format("qp={0}", job.Deblock));
string unparsedJson = Marshal.PtrToStringAnsi(settingsPtr);
if (!string.IsNullOrEmpty(unparsedJson))
{
JToken settings = JObject.Parse(unparsedJson);
Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_DEBLOCK, Settings = settings };
filter.FilterList.Add(filterItem);
}
}
// CropScale Filter
string cropSettings = string.Format("width={0}:height={1}:crop-top={2}:crop-bottom={3}:crop-left={4}:crop-right={5}", job.Width, job.Height, job.Cropping.Top, job.Cropping.Bottom, job.Cropping.Left, job.Cropping.Right);
IntPtr cropSettingsPtr = HBFunctions.hb_generate_filter_settings_json((int)hb_filter_ids.HB_FILTER_CROP_SCALE, null, null, cropSettings);
string unparsedCropSettingsJson = Marshal.PtrToStringAnsi(cropSettingsPtr);
if (!string.IsNullOrEmpty(unparsedCropSettingsJson))
{
JToken cropSettingsJson = JObject.Parse(unparsedCropSettingsJson);
Filter cropScale = new Filter
{
ID = (int)hb_filter_ids.HB_FILTER_CROP_SCALE,
Settings = cropSettingsJson
};
filter.FilterList.Add(cropScale);
}
// Grayscale
if (job.Grayscale)
{
Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_GRAYSCALE, Settings = null };
filter.FilterList.Add(filterItem);
}
// Rotate
if (job.Rotation != 0 || job.FlipVideo)
{
string rotateSettings = string.Format("angle={0}:hflip={1}", job.Rotation, job.FlipVideo ? "1" : "0");
IntPtr settingsPtr = HBFunctions.hb_generate_filter_settings_json((int)hb_filter_ids.HB_FILTER_ROTATE, null, null, rotateSettings);
string unparsedJson = Marshal.PtrToStringAnsi(settingsPtr);
if (!string.IsNullOrEmpty(unparsedJson))
{
JToken settings = JObject.Parse(unparsedJson);
Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_ROTATE, Settings = settings };
filter.FilterList.Add(filterItem);
}
}
// Framerate shaping filter
int fm = job.FramerateMode == FramerateMode.CFR ? 1 : job.FramerateMode == FramerateMode.PFR ? 2 : 0;
int? num = null, den = null;
if (job.Framerate != null)
{
IntPtr frameratePrt = Marshal.StringToHGlobalAnsi(job.Framerate.Value.ToString(CultureInfo.InvariantCulture));
int vrate = HBFunctions.hb_video_framerate_get_from_name(frameratePrt);
if (vrate > 0)
{
num = 27000000;
den = vrate;
}
}
string framerateString = num.HasValue ? string.Format("mode={0}:rate={1}/{2}", fm, num, den) : string.Format("mode={0}", fm); // filter_cfr, filter_vrate.num, filter_vrate.den
IntPtr framerateSettingsPtr = HBFunctions.hb_generate_filter_settings_json((int)hb_filter_ids.HB_FILTER_VFR, null, null, framerateString);
string unparsedFramerateJson = Marshal.PtrToStringAnsi(framerateSettingsPtr);
if (!string.IsNullOrEmpty(unparsedFramerateJson))
{
JToken framerateSettings = JObject.Parse(unparsedFramerateJson);
Filter framerateShaper = new Filter
{
ID = (int)hb_filter_ids.HB_FILTER_VFR,
Settings = framerateSettings
};
filter.FilterList.Add(framerateShaper);
}
return filter;
}
///
/// The create meta data.
///
///
/// The job.
///
///
/// The .
///
private static Metadata CreateMetadata(EncodeTask job)
{
Metadata metaData = new Metadata();
if (job.MetaData != null)
{
metaData.Artist = job.MetaData.Artist;
metaData.Album = job.MetaData.Album;
metaData.AlbumArtist = job.MetaData.AlbumArtist;
metaData.Comment = job.MetaData.Comment;
metaData.Composer = job.MetaData.Composer;
metaData.Description = job.MetaData.Description;
metaData.Genre = job.MetaData.Genre;
metaData.LongDescription = job.MetaData.LongDescription;
metaData.Name = job.MetaData.Name;
metaData.ReleaseDate = job.MetaData.ReleaseDate;
}
return metaData;
}
}
}