// --------------------------------------------------------------------------------------------------------------------
//
// 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 HandBrake.ApplicationServices.Services.Encode.Factories
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using HandBrake.ApplicationServices.Interop;
using HandBrake.ApplicationServices.Interop.HbLib;
using HandBrake.ApplicationServices.Interop.Helpers;
using HandBrake.ApplicationServices.Interop.Json.Encode;
using HandBrake.ApplicationServices.Interop.Json.Shared;
using HandBrake.ApplicationServices.Interop.Model.Encoding;
using HandBrake.ApplicationServices.Model;
using HandBrake.ApplicationServices.Services.Encode.Model;
using HandBrake.ApplicationServices.Services.Encode.Model.Models;
using HandBrake.ApplicationServices.Utilities;
///
/// 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
{
/*
* TODO:
* 1. OpenCL and HWD Support
* 2. Rotate Support
*/
///
/// 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),
Filter = CreateFilter(job),
PAR = CreatePAR(job),
MetaData = CreateMetaData(job),
Source = CreateSource(job, configuration),
Subtitle = CreateSubtitle(job),
Video = CreateVideo(job)
};
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.ChapterEnd = job.EndPoint;
range.ChapterStart = job.StartPoint;
break;
case PointToPointMode.Seconds:
range.PtsToStart = job.StartPoint * 90000;
range.PtsToStop = (job.EndPoint - job.StartPoint) * 90000;
break;
case PointToPointMode.Frames:
range.FrameToStart = job.StartPoint;
range.FrameToStop = job.EndPoint;
break;
case PointToPointMode.Preview:
range.StartAtPreview = job.PreviewEncodeStartAt;
range.SeekPoints = configuration.PreviewScanCount;
range.PtsToStop = 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,
Mux = HBFunctions.hb_container_get_from_name(job.OutputFormat == OutputFormat.Mp4 ? "av_mp4" : "av_mkv"), // TODO tidy up.
ChapterList = new List()
};
if (job.IncludeChapterMarkers)
{
foreach (ChapterMarker item in job.ChapterNames)
{
ChapterList chapter = new ChapterList { 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 Subtitles 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
{
SubtitleList track = new SubtitleList { Burn = item.Burned, Default = item.Default, Force = item.Forced, ID = item.SourceTrack.TrackNumber, Track = (item.SourceTrack.TrackNumber - 1) };
subtitle.SubtitleList.Add(track);
}
}
else
{
SubtitleList track = new SubtitleList
{
Track = -1, // Indicates SRT
Default = item.Default,
Offset = item.SrtOffset,
Burn = item.Burned,
SRT =
new SRT
{
Filename = item.SrtPath,
Codeset = item.SrtCharCode,
Language = item.SrtLang
}
};
subtitle.SubtitleList.Add(track);
}
}
return subtitle;
}
///
/// The create video.
///
///
/// The job.
///
///
/// The .
///
private static Video CreateVideo(EncodeTask job)
{
Video video = new Video();
HBVideoEncoder videoEncoder = HandBrakeEncoderHelpers.VideoEncoders.FirstOrDefault(e => e.ShortName == ApplicationServices.Utilities.Converters.GetVideoEncoder(job.VideoEncoder));
Validate.NotNull(videoEncoder, "Video encoder " + job.VideoEncoder + " not recognized.");
if (videoEncoder != null)
{
video.Codec = videoEncoder.Id;
}
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.VideoEncodeRateType == VideoEncodeRateType.ConstantQuality) video.Quality = job.Quality;
if (job.VideoEncodeRateType == VideoEncodeRateType.AverageBitrate)
{
video.Bitrate = job.VideoBitrate;
video.TwoPass = job.TwoPass;
video.Turbo = job.TurboFirstPass;
}
if (job.VideoTunes != null && job.VideoTunes.Count > 0)
{
foreach (var item in job.VideoTunes)
{
video.Tune += string.IsNullOrEmpty(video.Tune) ? item.ShortName : "," + item.ShortName;
}
}
return video;
}
///
/// The create audio.
///
///
/// The job.
///
///
/// The .
///
private static Audio CreateAudio(EncodeTask job)
{
Audio audio = new Audio();
int copyMask = 0;
if (job.AllowedPassthruOptions.AudioAllowAACPass) copyMask = (int)NativeConstants.HB_ACODEC_AAC_PASS;
if (job.AllowedPassthruOptions.AudioAllowAC3Pass) copyMask |= (int)NativeConstants.HB_ACODEC_AC3_PASS;
if (job.AllowedPassthruOptions.AudioAllowDTSHDPass) copyMask |= (int)NativeConstants.HB_ACODEC_DCA_HD_PASS;
if (job.AllowedPassthruOptions.AudioAllowDTSPass) copyMask |= (int)NativeConstants.HB_ACODEC_DCA_PASS;
if (job.AllowedPassthruOptions.AudioAllowEAC3Pass) copyMask |= (int)NativeConstants.HB_ACODEC_EAC3_PASS;
if (job.AllowedPassthruOptions.AudioAllowFlacPass) copyMask |= (int)NativeConstants.HB_ACODEC_FLAC_PASS;
if (job.AllowedPassthruOptions.AudioAllowMP3Pass) copyMask |= (int)NativeConstants.HB_ACODEC_MP3_PASS;
if (job.AllowedPassthruOptions.AudioAllowTrueHDPass) copyMask |= (int)NativeConstants.HB_ACODEC_TRUEHD_PASS;
audio.CopyMask = copyMask;
HBAudioEncoder audioEncoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper.GetShortName(job.AllowedPassthruOptions.AudioEncoderFallback));
audio.FallbackEncoder = audioEncoder.Id;
audio.AudioList = new List();
foreach (AudioTrack item in job.AudioTracks)
{
HBAudioEncoder encoder = HandBrakeEncoderHelpers.GetAudioEncoder(ApplicationServices.Utilities.Converters.GetCliAudioEncoder(item.Encoder) );
Validate.NotNull(encoder, "Unrecognized audio encoder:" + item.Encoder);
HBMixdown mixdown = HandBrakeEncoderHelpers.GetMixdown(ApplicationServices.Utilities.Converters.GetCliMixDown(item.MixDown));
Validate.NotNull(mixdown, "Unrecognized audio mixdown:" + ApplicationServices.Utilities.Converters.GetCliMixDown(item.MixDown));
AudioList audioTrack = new AudioList
{
Track = (item.Track.HasValue ? item.Track.Value : 0) - 1,
DRC = item.DRC,
Encoder = encoder.Id,
Gain = item.Gain,
Mixdown = mixdown.Id,
NormalizeMixLevel = false,
Samplerate = GetSampleRateRaw(item.SampleRate),
Name = item.TrackName,
};
if (!item.IsPassthru)
{
// TODO Impiment Quality and Compression. We only support bitrate right now.
//if (item.EncodeRateType == AudioEncodeRateType.Quality)
//{
// audioTrack.Quality = item.Quality;
//}
//if (item.EncodeRateType == AudioEncodeRateType.Compression)
//{
// audioTrack.CompressionLevel = item.Compression;
//}
//if (item.EncodeRateType == AudioEncodeRateType.Bitrate)
// {
audioTrack.Bitrate = item.Bitrate;
// }
}
audio.AudioList.Add(audioTrack);
}
return audio;
}
///
/// The get sample rate raw.
///
///
/// The rate.
///
///
/// The .
///
private static int GetSampleRateRaw(double rate)
{
if (rate == 22.05)
return 22050;
else if (rate == 24)
return 24000;
else if (rate == 44.1)
return 32000;
else if (rate == 48)
return 48000;
else return 48000;
}
///
/// The create filter.
///
///
/// The job.
///
///
/// The .
///
private static Filters CreateFilter(EncodeTask job)
{
Filters filter = new Filters
{
FilterList = new List(),
Grayscale = job.Grayscale
};
// Detelecine
if (job.Detelecine != Detelecine.Off)
{
Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_DETELECINE, Settings = job.CustomDetelecine };
filter.FilterList.Add(filterItem);
}
// Decomb
if (job.Decomb != Decomb.Off)
{
string options;
if (job.Decomb == Decomb.Fast)
{
options = "7:2:6:9:1:80";
}
else if (job.Decomb == Decomb.Bob)
{
options = "455";
}
else
{
options = job.CustomDecomb;
}
Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_DECOMB, Settings = options };
filter.FilterList.Add(filterItem);
}
// Deinterlace
if (job.Deinterlace != Deinterlace.Off)
{
string options;
if (job.Deinterlace == Deinterlace.Fast)
{
options = "0";
}
else if (job.Deinterlace == Deinterlace.Slow)
{
options = "1";
}
else if (job.Deinterlace == Deinterlace.Slower)
{
options = "3";
}
else if (job.Deinterlace == Deinterlace.Bob)
{
options = "15";
}
else
{
options = job.CustomDeinterlace;
}
Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_DEINTERLACE, Settings = options };
filter.FilterList.Add(filterItem);
}
// VFR / CFR
int fm = job.FramerateMode == FramerateMode.CFR ? 1 : job.FramerateMode == FramerateMode.PFR ? 2 : 0;
IntPtr frameratePrt = Marshal.StringToHGlobalAnsi(job.Framerate.ToString()); // TODO check culture
int vrate = HBFunctions.hb_video_framerate_get_from_name(frameratePrt);
int? num = null;
int? den = null;
if (vrate > 0)
{
num = 27000000;
den = vrate;
}
string framerateString = num.HasValue ? string.Format("{0}:{1}:{2}", fm, num, den) : string.Format("{0}", fm); // filter_cfr, filter_vrate.num, filter_vrate.den
Filter framerateShaper = new Filter { ID = (int)hb_filter_ids.HB_FILTER_VFR, Settings = framerateString };
filter.FilterList.Add(framerateShaper);
// Deblock
if (job.Deblock >= 5)
{
Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_DEBLOCK, Settings = job.Deblock.ToString() };
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;
string settings;
if (!string.IsNullOrEmpty(job.CustomDenoise))
{
settings = job.CustomDenoise;
}
else
{
IntPtr settingsPtr = HBFunctions.hb_generate_filter_settings((int)id, job.DenoisePreset.ToString().ToLower().Replace(" ", string.Empty), job.DenoiseTune.ToString().ToLower().Replace(" ", string.Empty));
settings = Marshal.PtrToStringAnsi(settingsPtr);
}
Filter filterItem = new Filter { ID = (int)id, Settings = settings };
filter.FilterList.Add(filterItem);
}
// CropScale Filter
Filter cropScale = new Filter
{
ID = (int)hb_filter_ids.HB_FILTER_CROP_SCALE,
Settings =
string.Format(
"{0}:{1}:{2}:{3}:{4}:{5}",
job.Width,
job.Height,
job.Cropping.Top,
job.Cropping.Bottom,
job.Cropping.Left,
job.Cropping.Right)
};
filter.FilterList.Add(cropScale);
// Rotate
/* TODO NOT SUPPORTED YET. */
return filter;
}
///
/// The create meta data.
///
///
/// The job.
///
///
/// The .
///
private static MetaData CreateMetaData(EncodeTask job)
{
MetaData metaData = new MetaData();
/* TODO NOT SUPPORTED YET. */
return metaData;
}
}
}