using HandBrake.Interop.HbLib;\r
using HandBrake.Interop.Helpers;\r
using HandBrake.Interop.Interfaces;\r
+ using HandBrake.Interop.Json.Encode;\r
using HandBrake.Interop.Json.Factories;\r
using HandBrake.Interop.Json.Scan;\r
using HandBrake.Interop.Json.State;\r
/// </summary>\r
public class HandBrakeInstance : IHandBrakeInstance, IDisposable\r
{\r
- /// <summary>\r
- /// The modulus for picture size when auto-sizing dimensions.\r
- /// </summary>\r
- private const int PictureAutoSizeModulus = 2;\r
-\r
/// <summary>\r
/// The number of MS between status polls when scanning.\r
/// </summary>\r
/// </summary>\r
private const double EncodePollIntervalMs = 200;\r
\r
- /// <summary>\r
- /// X264 options to add for a turbo first pass.\r
- /// </summary>\r
- private const string TurboX264Opts = "ref=1:subme=2:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0";\r
-\r
/// <summary>\r
/// The native handle to the HandBrake instance.\r
/// </summary>\r
/// </summary>\r
private Timer encodePollTimer;\r
\r
- /// <summary>\r
- /// The list of original titles in native structure form.\r
- /// </summary>\r
- private List<hb_title_s> originalTitles;\r
-\r
/// <summary>\r
/// The list of titles on this instance.\r
/// </summary>\r
private List<Title> titles;\r
\r
- /// <summary>\r
- /// The current encode job for this instance.\r
- /// </summary>\r
- private EncodeJob currentJob;\r
-\r
- /// <summary>\r
- /// True if the current job is scanning for subtitles.\r
- /// </summary>\r
- private bool subtitleScan;\r
-\r
/// <summary>\r
/// The index of the default title.\r
/// </summary>\r
private int featureTitle;\r
\r
- /// <summary>\r
- /// A list of native memory locations allocated by this instance.\r
- /// </summary>\r
- private List<IntPtr> encodeAllocatedMemory;\r
-\r
/// <summary>\r
/// A value indicating whether this object has been disposed or not.\r
/// </summary>\r
/// <returns>An image with the requested preview.</returns>\r
public BitmapImage GetPreview(EncodeJob job, int previewNumber)\r
{\r
- IntPtr nativeJobPtr = HBFunctions.hb_job_init_by_index(this.hbHandle, this.GetTitleIndex(job.Title));\r
- var nativeJob = InteropUtilities.ToStructureFromPtr<hb_job_s>(nativeJobPtr);\r
\r
- List<IntPtr> allocatedMemory = this.ApplyJob(ref nativeJob, job);\r
+ Title title = this.Titles.FirstOrDefault(t => t.TitleNumber == job.Title);\r
+ Validate.NotNull(title, "GetPreview: Title should not have been null. This is probably a bug.");\r
+\r
+ hb_geometry_settings_s uiGeometry = new hb_geometry_settings_s\r
+ {\r
+ crop = new[] { job.EncodingProfile.Cropping.Top, job.EncodingProfile.Cropping.Bottom, job.EncodingProfile.Cropping.Left, job.EncodingProfile.Cropping.Right },\r
+ itu_par = 0,\r
+ keep = (int)AnamorphicFactory.KeepSetting.HB_KEEP_WIDTH + (job.EncodingProfile.KeepDisplayAspect ? 0x04 : 0), // TODO Keep Width?\r
+ maxWidth = job.EncodingProfile.MaxWidth,\r
+ maxHeight = job.EncodingProfile.MaxHeight,\r
+ mode = (int)(hb_anamorphic_mode_t)job.EncodingProfile.Anamorphic,\r
+ modulus = job.EncodingProfile.Modulus,\r
+ geometry = new hb_geometry_s { height = job.EncodingProfile.Height, width = job.EncodingProfile.Width, \r
+ par = job.EncodingProfile.Anamorphic != Anamorphic.Custom \r
+ ? new hb_rational_t { den = title.ParVal.Height, num = title.ParVal.Width }\r
+ : new hb_rational_t { den = job.EncodingProfile.PixelAspectY, num = job.EncodingProfile.PixelAspectX } }\r
+ };\r
+\r
+ // Sanatise the input.\r
+ Json.Anamorphic.Geometry resultGeometry = AnamorphicFactory.CreateGeometry(job, title, AnamorphicFactory.KeepSetting.HB_KEEP_WIDTH); // TODO this keep isn't right.\r
\r
- // There are some problems with getting previews with deinterlacing. Disabling for now.\r
- nativeJob.deinterlace = 0;\r
+ int outputWidth = resultGeometry.Width;\r
+ int outputHeight = resultGeometry.Height;\r
+ int imageBufferSize = outputWidth * outputHeight * 4;\r
+ IntPtr nativeBuffer = Marshal.AllocHGlobal(imageBufferSize);\r
\r
- int outputWidth = nativeJob.width;\r
- int outputHeight = nativeJob.height;\r
- int imageBufferSize = outputWidth * outputHeight * 4;\r
- IntPtr nativeBuffer = Marshal.AllocHGlobal(imageBufferSize);\r
- allocatedMemory.Add(nativeBuffer);\r
- HBFunctions.hb_get_preview(this.hbHandle, ref nativeJob, previewNumber, nativeBuffer);\r
\r
- // We've used the job to get the preview. Clean up the job.\r
- InteropUtilities.CloseJob(nativeJobPtr);\r
+ HBFunctions.hb_get_preview2(this.hbHandle, job.Title, previewNumber, ref uiGeometry, 0);\r
\r
// Copy the filled image buffer to a managed array.\r
byte[] managedBuffer = new byte[imageBufferSize];\r
Marshal.Copy(nativeBuffer, managedBuffer, 0, imageBufferSize);\r
\r
- // We've copied the data out of unmanaged memory. Clean up that memory now.\r
- InteropUtilities.FreeMemory(allocatedMemory);\r
-\r
var bitmap = new Bitmap(outputWidth, outputHeight);\r
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, outputWidth, outputHeight), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);\r
\r
IntPtr ptr = bitmapData.Scan0;\r
\r
- for (int i = 0; i < nativeJob.height; i++)\r
+ for (int i = 0; i < job.EncodingProfile.Height; i++)\r
{\r
- Marshal.Copy(managedBuffer, i * nativeJob.width * 4, ptr, nativeJob.width * 4);\r
+ Marshal.Copy(managedBuffer, i * job.EncodingProfile.Width * 4, ptr, job.EncodingProfile.Width * 4);\r
ptr = IntPtr.Add(ptr, bitmapData.Stride);\r
}\r
\r
/// <summary>\r
/// Starts an encode with the given job.\r
/// </summary>\r
- /// <param name="job">\r
- /// The job to start.\r
- /// </param>\r
- /// <param name="preview">\r
- /// True if this is a preview encode.\r
- /// </param>\r
- /// <param name="previewNumber">\r
- /// The preview number to start the encode at (0-based).\r
- /// </param>\r
- /// <param name="previewSeconds">\r
- /// The number of seconds in the preview.\r
- /// </param>\r
- /// <param name="overallSelectedLengthSeconds">\r
- /// The currently selected encode length. Used in preview\r
- /// for calculating bitrate when the target size would be wrong.\r
- /// </param>\r
- /// <param name="scanPreviewCount">\r
- /// The scan Preview Count.\r
- /// </param>\r
+ /// <param name="job">The job to start.</param>\r
+ /// <param name="preview">The scan Preview Count.</param>\r
+ /// <param name="previewNumber">Preview Feature: Preview to encode</param>\r
+ /// <param name="previewSeconds">Number of seconds to encode for the preview</param>\r
+ /// <param name="overallSelectedLengthSeconds"></param>\r
+ /// <param name="scanPreviewCount">Number of previews</param>\r
public void StartEncode(EncodeJob job, bool preview, int previewNumber, int previewSeconds, double overallSelectedLengthSeconds, int scanPreviewCount)\r
{\r
- EncodingProfile profile = job.EncodingProfile;\r
- if (job.ChosenAudioTracks == null)\r
- {\r
- throw new ArgumentException("job.ChosenAudioTracks cannot be null.");\r
- }\r
-\r
this.previewCount = scanPreviewCount;\r
- this.currentJob = job;\r
-\r
- IntPtr nativeJobPtr = HBFunctions.hb_job_init_by_index(this.hbHandle, this.GetTitleIndex(job.Title));\r
- var nativeJob = InteropUtilities.ToStructureFromPtr<hb_job_s>(nativeJobPtr);\r
-\r
- this.encodeAllocatedMemory = this.ApplyJob(ref nativeJob, job, preview, previewNumber, previewSeconds, overallSelectedLengthSeconds);\r
-\r
- this.subtitleScan = false;\r
- if (job.Subtitles != null && job.Subtitles.SourceSubtitles != null)\r
- {\r
- foreach (SourceSubtitle subtitle in job.Subtitles.SourceSubtitles)\r
- {\r
- if (subtitle.TrackNumber == 0)\r
- {\r
- this.subtitleScan = true;\r
- break;\r
- }\r
- }\r
- }\r
-\r
- string videoOptions = profile.VideoOptions ?? string.Empty;\r
- IntPtr originalVideoOptions = Marshal.StringToHGlobalAnsi(videoOptions);\r
- this.encodeAllocatedMemory.Add(originalVideoOptions);\r
-\r
- if (!string.IsNullOrEmpty(profile.VideoProfile))\r
- {\r
- nativeJob.encoder_profile = Marshal.StringToHGlobalAnsi(profile.VideoProfile);\r
- this.encodeAllocatedMemory.Add(nativeJob.encoder_profile);\r
- }\r
-\r
- if (!string.IsNullOrEmpty(profile.VideoPreset))\r
- {\r
- nativeJob.encoder_preset = Marshal.StringToHGlobalAnsi(profile.VideoPreset);\r
- this.encodeAllocatedMemory.Add(nativeJob.encoder_preset);\r
- }\r
-\r
- if (profile.VideoTunes != null && profile.VideoTunes.Count > 0)\r
- {\r
- nativeJob.encoder_tune = Marshal.StringToHGlobalAnsi(string.Join(",", profile.VideoTunes));\r
- this.encodeAllocatedMemory.Add(nativeJob.encoder_tune);\r
- }\r
-\r
- if (!string.IsNullOrEmpty(job.EncodingProfile.VideoLevel))\r
- {\r
- nativeJob.encoder_level = Marshal.StringToHGlobalAnsi(job.EncodingProfile.VideoLevel);\r
- this.encodeAllocatedMemory.Add(nativeJob.encoder_level);\r
- }\r
\r
- if (this.subtitleScan)\r
- {\r
- // If we need to scan subtitles, enqueue a pre-processing job to do that.\r
- nativeJob.pass = -1;\r
- nativeJob.indepth_scan = 1;\r
-\r
- nativeJob.encoder_options = IntPtr.Zero;\r
-\r
- HBFunctions.hb_add(this.hbHandle, ref nativeJob);\r
- }\r
+ JsonEncodeObject encodeObject = EncodeFactory.Create(job, lastScan);\r
\r
- nativeJob.indepth_scan = 0;\r
-\r
- if (job.EncodingProfile.VideoEncodeRateType != VideoEncodeRateType.ConstantQuality && job.EncodingProfile.TwoPass)\r
+ JsonSerializerSettings settings = new JsonSerializerSettings\r
{\r
- // First pass. Apply turbo options if needed.\r
- nativeJob.pass = 1;\r
- string firstPassAdvancedOptions = videoOptions;\r
- if (job.EncodingProfile.TurboFirstPass)\r
- {\r
- if (firstPassAdvancedOptions == string.Empty)\r
- {\r
- firstPassAdvancedOptions = TurboX264Opts;\r
- }\r
- else\r
- {\r
- firstPassAdvancedOptions += ":" + TurboX264Opts;\r
- }\r
- }\r
-\r
- nativeJob.encoder_options = Marshal.StringToHGlobalAnsi(firstPassAdvancedOptions);\r
- this.encodeAllocatedMemory.Add(nativeJob.encoder_options);\r
-\r
- HBFunctions.hb_add(this.hbHandle, ref nativeJob);\r
-\r
- // Second pass. Apply normal options.\r
- nativeJob.pass = 2;\r
- nativeJob.encoder_options = originalVideoOptions;\r
-\r
- HBFunctions.hb_add(this.hbHandle, ref nativeJob);\r
- }\r
- else\r
- {\r
- // One pass job.\r
- nativeJob.pass = 0;\r
- nativeJob.encoder_options = originalVideoOptions;\r
-\r
- HBFunctions.hb_add(this.hbHandle, ref nativeJob);\r
- }\r
+ NullValueHandling = NullValueHandling.Ignore\r
+ };\r
\r
+ string encode = JsonConvert.SerializeObject(encodeObject, Formatting.Indented, settings);\r
+ HBFunctions.hb_add_json(this.hbHandle, Marshal.StringToHGlobalAnsi(encode));\r
HBFunctions.hb_start(this.hbHandle);\r
\r
- // Should be safe to clean up the job we started with; a copy is in the queue now.\r
- InteropUtilities.CloseJob(nativeJobPtr);\r
-\r
- this.encodePollTimer = new Timer();\r
+ this.encodePollTimer = new System.Timers.Timer();\r
this.encodePollTimer.Interval = EncodePollIntervalMs;\r
\r
this.encodePollTimer.Elapsed += (o, e) =>\r
}\r
}\r
\r
- /// <summary>\r
- /// Gets the final size for a given encode job.\r
- /// </summary>\r
- /// <param name="job">The encode job to use.</param>\r
- /// <param name="width">The storage width.</param>\r
- /// <param name="height">The storage height.</param>\r
- /// <param name="parWidth">The pixel aspect X number.</param>\r
- /// <param name="parHeight">The pixel aspect Y number.</param>\r
- public void GetSize(EncodeJob job, out int width, out int height, out int parWidth, out int parHeight)\r
- {\r
- Title title = this.GetTitle(job.Title);\r
-\r
- if (job.EncodingProfile.Anamorphic == Anamorphic.None)\r
- {\r
- Size storageDimensions = CalculateNonAnamorphicOutput(job.EncodingProfile, title);\r
-\r
- width = storageDimensions.Width;\r
- height = storageDimensions.Height;\r
-\r
- parWidth = 1;\r
- parHeight = 1;\r
-\r
- return;\r
- }\r
-\r
- IntPtr nativeJobPtr = HBFunctions.hb_job_init_by_index(this.hbHandle, this.GetTitleIndex(title));\r
- var nativeJob = InteropUtilities.ToStructureFromPtr<hb_job_s>(nativeJobPtr);\r
-\r
- List<IntPtr> allocatedMemory = this.ApplyJob(ref nativeJob, job);\r
- InteropUtilities.FreeMemory(allocatedMemory);\r
-\r
- InteropUtilities.CloseJob(nativeJobPtr);\r
-\r
- // During the ApplyJob call, it modified nativeJob to have the correct width, height and PAR.\r
- // We use those for the size.\r
- width = nativeJob.width;\r
- height = nativeJob.height;\r
- parWidth = nativeJob.anamorphic.par_width;\r
- parHeight = nativeJob.anamorphic.par_height;\r
- }\r
-\r
-\r
-\r
/// <summary>\r
/// Frees any resources associated with this object.\r
/// </summary>\r
this.disposed = true;\r
}\r
\r
- /// <summary>\r
- /// Calculates the output size for a non-anamorphic job.\r
- /// </summary>\r
- /// <param name="profile">The encoding profile for the job.</param>\r
- /// <param name="title">The title being encoded.</param>\r
- /// <returns>The dimensions of the final encode.</returns>\r
- private static Size CalculateNonAnamorphicOutput(EncodingProfile profile, Title title)\r
- {\r
- int sourceWidth = title.Resolution.Width;\r
- int sourceHeight = title.Resolution.Height;\r
-\r
- int width = profile.Width;\r
- int height = profile.Height;\r
-\r
- Cropping crop;\r
- switch (profile.CroppingType)\r
- {\r
- case CroppingType.Automatic:\r
- crop = title.AutoCropDimensions;\r
- break;\r
- case CroppingType.Custom:\r
- crop = profile.Cropping;\r
- break;\r
- default:\r
- crop = new Cropping();\r
- break;\r
- }\r
-\r
- sourceWidth -= crop.Left;\r
- sourceWidth -= crop.Right;\r
-\r
- sourceHeight -= crop.Top;\r
- sourceHeight -= crop.Bottom;\r
-\r
- double croppedAspectRatio = ((double)sourceWidth * title.ParVal.Width) / (sourceHeight * title.ParVal.Height);\r
-\r
- if (width == 0)\r
- {\r
- width = sourceWidth;\r
- }\r
-\r
- if (profile.MaxWidth > 0 && width > profile.MaxWidth)\r
- {\r
- width = profile.MaxWidth;\r
- }\r
-\r
- if (height == 0)\r
- {\r
- height = sourceHeight;\r
- }\r
-\r
- if (profile.MaxHeight > 0 && height > profile.MaxHeight)\r
- {\r
- height = profile.MaxHeight;\r
- }\r
-\r
- if (profile.KeepDisplayAspect)\r
- {\r
- if ((profile.Width == 0 && profile.Height == 0) || profile.Width == 0)\r
- {\r
- width = (int)((double)height * croppedAspectRatio);\r
- if (profile.MaxWidth > 0 && width > profile.MaxWidth)\r
- {\r
- width = profile.MaxWidth;\r
- height = (int)((double)width / croppedAspectRatio);\r
- height = GetNearestValue(height, PictureAutoSizeModulus);\r
- }\r
-\r
- width = GetNearestValue(width, PictureAutoSizeModulus);\r
- }\r
- else if (profile.Height == 0)\r
- {\r
- height = (int)((double)width / croppedAspectRatio);\r
- if (profile.MaxHeight > 0 && height > profile.MaxHeight)\r
- {\r
- height = profile.MaxHeight;\r
- width = (int)((double)height * croppedAspectRatio);\r
- width = GetNearestValue(width, PictureAutoSizeModulus);\r
- }\r
-\r
- height = GetNearestValue(height, PictureAutoSizeModulus);\r
- }\r
- }\r
-\r
- return new Size(width, height);\r
- }\r
-\r
- /// <summary>\r
- /// Gets the closest value to the given number divisible by the given modulus.\r
- /// </summary>\r
- /// <param name="number">The number to approximate.</param>\r
- /// <param name="modulus">The modulus.</param>\r
- /// <returns>The closest value to the given number divisible by the given modulus.</returns>\r
- private static int GetNearestValue(int number, int modulus)\r
- {\r
- return modulus * ((number + modulus / 2) / modulus);\r
- }\r
-\r
/// <summary>\r
/// Checks the status of the ongoing scan.\r
/// </summary>\r
{\r
this.titles = new List<Title>();\r
\r
- IntPtr titleSetPtr = HBFunctions.hb_get_title_set(this.hbHandle);\r
var jsonMsg = HBFunctions.hb_get_title_set_json(this.hbHandle);\r
string scanJson = Marshal.PtrToStringAnsi(jsonMsg);\r
JsonScanObject scanObject = JsonConvert.DeserializeObject<JsonScanObject>(scanJson);\r
this.titles.Add(title);\r
}\r
\r
- hb_title_set_s titleSet = InteropUtilities.ToStructureFromPtr<hb_title_set_s>(titleSetPtr);\r
- this.originalTitles = titleSet.list_title.ToListFromHandBrakeList<hb_title_s>();\r
-\r
this.scanPollTimer.Stop();\r
\r
if (this.ScanCompleted != null)\r
}\r
}\r
\r
+ /// <summary>\r
+ /// Checks the status of the ongoing encode.\r
+ /// </summary>\r
/// <summary>\r
/// Checks the status of the ongoing encode.\r
/// </summary>\r
private void PollEncodeProgress()\r
{\r
- hb_state_s state = new hb_state_s();\r
- HBFunctions.hb_get_state(this.hbHandle, ref state);\r
+ IntPtr json = HBFunctions.hb_get_state_json(this.hbHandle);\r
+ string statusJson = Marshal.PtrToStringAnsi(json);\r
+ JsonState state = JsonConvert.DeserializeObject<JsonState>(statusJson);\r
\r
- if (state.state == NativeConstants.HB_STATE_WORKING)\r
+ if (state.State == NativeConstants.HB_STATE_WORKING)\r
{\r
if (this.EncodeProgress != null)\r
{\r
- int pass = 1;\r
- int rawJobNumber = state.param.working.job_cur;\r
-\r
- if (this.currentJob.EncodingProfile.VideoEncodeRateType != VideoEncodeRateType.ConstantQuality && this.currentJob.EncodingProfile.TwoPass)\r
- {\r
- if (this.subtitleScan)\r
- {\r
- switch (rawJobNumber)\r
- {\r
- case 1:\r
- pass = -1;\r
- break;\r
- case 2:\r
- pass = 1;\r
- break;\r
- case 3:\r
- pass = 2;\r
- break;\r
- default:\r
- break;\r
- }\r
- }\r
- else\r
- {\r
- switch (rawJobNumber)\r
- {\r
- case 1:\r
- pass = 1;\r
- break;\r
- case 2:\r
- pass = 2;\r
- break;\r
- default:\r
- break;\r
- }\r
- }\r
- }\r
- else\r
- {\r
- if (this.subtitleScan)\r
- {\r
- switch (rawJobNumber)\r
- {\r
- case 1:\r
- pass = -1;\r
- break;\r
- case 2:\r
- pass = 1;\r
- break;\r
- default:\r
- break;\r
- }\r
- }\r
- else\r
- {\r
- pass = 1;\r
- }\r
- }\r
-\r
var progressEventArgs = new EncodeProgressEventArgs\r
{\r
- FractionComplete = state.param.working.progress,\r
- CurrentFrameRate = state.param.working.rate_cur,\r
- AverageFrameRate = state.param.working.rate_avg,\r
- EstimatedTimeLeft = new TimeSpan(state.param.working.hours, state.param.working.minutes, state.param.working.seconds),\r
- Pass = pass\r
+ FractionComplete = state.Working.Progress,\r
+ CurrentFrameRate = state.Working.Rate,\r
+ AverageFrameRate = state.Working.RateAvg,\r
+ EstimatedTimeLeft = new TimeSpan(state.Working.Hours, state.Working.Minutes, state.Working.Seconds),\r
+ Pass = 1, // TODO\r
};\r
\r
this.EncodeProgress(this, progressEventArgs);\r
}\r
}\r
- else if (state.state == NativeConstants.HB_STATE_WORKDONE)\r
+ else if (state.State == NativeConstants.HB_STATE_WORKDONE)\r
{\r
- InteropUtilities.FreeMemory(this.encodeAllocatedMemory);\r
this.encodePollTimer.Stop();\r
\r
- if (this.EncodeCompleted != null)\r
- {\r
- this.EncodeCompleted(this, new EncodeCompletedEventArgs { Error = state.param.workdone.error != hb_error_code.HB_ERROR_NONE });\r
- }\r
- }\r
- }\r
-\r
- /// <summary>\r
- /// Applies the encoding job to the native memory structure and returns a list of memory\r
- /// locations allocated during this.\r
- /// </summary>\r
- /// <param name="nativeJob">The native structure to apply to job info to.</param>\r
- /// <param name="job">The job info to apply.</param>\r
- /// <returns>The list of memory locations allocated for the job.</returns>\r
- private List<IntPtr> ApplyJob(ref hb_job_s nativeJob, EncodeJob job)\r
- {\r
- return this.ApplyJob(ref nativeJob, job, false, 0, 0, 0);\r
- }\r
-\r
- /// <summary>\r
- /// Applies the encoding job to the native memory structure and returns a list of memory\r
- /// locations allocated during this.\r
- /// </summary>\r
- /// <param name="nativeJob">The native structure to apply to job info to.</param>\r
- /// <param name="job">The job info to apply.</param>\r
- /// <param name="preview">True if this is a preview encode.</param>\r
- /// <param name="previewNumber">The preview number (0-based) to encode.</param>\r
- /// <param name="previewSeconds">The number of seconds in the preview.</param>\r
- /// <param name="overallSelectedLengthSeconds">The currently selected encode length. Used in preview\r
- /// for calculating bitrate when the target size would be wrong.</param>\r
- /// <returns>The list of memory locations allocated for the job.</returns>\r
- private List<IntPtr> ApplyJob(ref hb_job_s nativeJob, EncodeJob job, bool preview, int previewNumber, int previewSeconds, double overallSelectedLengthSeconds)\r
- {\r
- var allocatedMemory = new List<IntPtr>();\r
- Title title = this.GetTitle(job.Title);\r
- hb_title_s originalTitle = this.GetOriginalTitle(job.Title);\r
-\r
- EncodingProfile profile = job.EncodingProfile;\r
-\r
- if (preview)\r
- {\r
- nativeJob.start_at_preview = previewNumber + 1;\r
- nativeJob.seek_points = this.previewCount;\r
-\r
- // There are 90,000 PTS per second.\r
- nativeJob.pts_to_stop = previewSeconds * 90000;\r
- }\r
- else\r
- {\r
- switch (job.RangeType)\r
+ if (this.EncodeCompleted != null)\r
{\r
- case VideoRangeType.All:\r
- break;\r
- case VideoRangeType.Chapters:\r
- if (job.ChapterStart > 0 && job.ChapterEnd > 0)\r
- {\r
- nativeJob.chapter_start = job.ChapterStart;\r
- nativeJob.chapter_end = job.ChapterEnd;\r
- }\r
- else\r
- {\r
- nativeJob.chapter_start = 1;\r
- nativeJob.chapter_end = title.Chapters.Count;\r
- }\r
-\r
- break;\r
- case VideoRangeType.Seconds:\r
- if (job.SecondsStart < 0 || job.SecondsEnd < 0 || job.SecondsStart >= job.SecondsEnd)\r
- {\r
- throw new ArgumentException("Seconds range " + job.SecondsStart + "-" + job.SecondsEnd + " is invalid.", "job");\r
- }\r
-\r
- // If they've selected the "full" title duration, leave off the arguments to make it clean\r
- if (job.SecondsStart > 0 || job.SecondsEnd < title.Duration.TotalSeconds)\r
- {\r
- // For some reason "pts_to_stop" actually means the number of pts to stop AFTER the start point.\r
- nativeJob.pts_to_start = (int)(job.SecondsStart * 90000);\r
- nativeJob.pts_to_stop = (int)((job.SecondsEnd - job.SecondsStart) * 90000);\r
- }\r
- break;\r
- case VideoRangeType.Frames:\r
- if (job.FramesStart < 0 || job.FramesEnd < 0 || job.FramesStart >= job.FramesEnd)\r
- {\r
- throw new ArgumentException("Frames range " + job.FramesStart + "-" + job.FramesEnd + " is invalid.", "job");\r
- }\r
-\r
- // "frame_to_stop" actually means the number of frames total to encode AFTER the start point.\r
- nativeJob.frame_to_start = job.FramesStart;\r
- nativeJob.frame_to_stop = job.FramesEnd - job.FramesStart;\r
- break;\r
- }\r
- }\r
-\r
- // Chapter markers\r
- nativeJob.chapter_markers = profile.IncludeChapterMarkers ? 1 : 0;\r
-\r
- List<IntPtr> nativeChapters = nativeJob.list_chapter.ToIntPtrList();\r
-\r
- if (!preview && profile.IncludeChapterMarkers)\r
- {\r
- int numChapters = title.Chapters.Count;\r
-\r
- if (job.UseDefaultChapterNames)\r
- {\r
- for (int i = 0; i < numChapters; i++)\r
- {\r
- if (i < nativeChapters.Count)\r
- {\r
- HBFunctions.hb_chapter_set_title(nativeChapters[i], "Chapter " + (i + 1));\r
- }\r
- }\r
- }\r
- else\r
- {\r
- for (int i = 0; i < numChapters; i++)\r
+ this.EncodeCompleted(this, new EncodeCompletedEventArgs\r
{\r
- if (i < nativeChapters.Count && i < job.CustomChapterNames.Count)\r
- {\r
- IntPtr chapterNamePtr;\r
-\r
- if (string.IsNullOrWhiteSpace(job.CustomChapterNames[i]))\r
- {\r
- chapterNamePtr = InteropUtilities.ToUtf8PtrFromString("Chapter " + (i + 1));\r
- }\r
- else\r
- {\r
- chapterNamePtr = InteropUtilities.ToUtf8PtrFromString(job.CustomChapterNames[i]);\r
- }\r
-\r
- HBFunctions.hb_chapter_set_title__ptr(nativeChapters[i], chapterNamePtr);\r
- Marshal.FreeHGlobal(chapterNamePtr);\r
- }\r
- }\r
- }\r
- }\r
-\r
- Cropping crop = GetCropping(profile, title);\r
-\r
- nativeJob.crop[0] = crop.Top;\r
- nativeJob.crop[1] = crop.Bottom;\r
- nativeJob.crop[2] = crop.Left;\r
- nativeJob.crop[3] = crop.Right;\r
-\r
- var filterList = new List<hb_filter_object_s>();\r
-\r
- // FILTERS: These must be added in the correct order since we cannot depend on the automatic ordering in hb_add_filter . Ordering is determined\r
- // by the order they show up in the filters enum.\r
-\r
- // Detelecine\r
- if (profile.Detelecine != Detelecine.Off)\r
- {\r
- string settings = null;\r
- if (profile.Detelecine == Detelecine.Custom)\r
- {\r
- settings = profile.CustomDetelecine;\r
- }\r
-\r
- this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_DETELECINE, settings, allocatedMemory);\r
- }\r
-\r
- // Decomb\r
- if (profile.Decomb != Decomb.Off)\r
- {\r
- string settings = null;\r
- switch (profile.Decomb)\r
- {\r
- case Decomb.Default:\r
- break;\r
- case Decomb.Fast:\r
- settings = "7:2:6:9:1:80";\r
- break;\r
- case Decomb.Bob:\r
- settings = "455";\r
- break;\r
- case Decomb.Custom:\r
- settings = profile.CustomDecomb;\r
- break;\r
- default:\r
- break;\r
- }\r
-\r
- this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_DECOMB, settings, allocatedMemory);\r
- }\r
-\r
- // Deinterlace\r
- if (profile.Deinterlace != Deinterlace.Off)\r
- {\r
- nativeJob.deinterlace = 1;\r
- string settings = null;\r
-\r
- switch (profile.Deinterlace)\r
- {\r
- case Deinterlace.Fast:\r
- settings = "0";\r
- break;\r
- case Deinterlace.Slow:\r
- settings = "1";\r
- break;\r
- case Deinterlace.Slower:\r
- settings = "3";\r
- break;\r
- case Deinterlace.Bob:\r
- settings = "15";\r
- break;\r
- case Deinterlace.Custom:\r
- settings = profile.CustomDeinterlace;\r
- break;\r
- default:\r
- break;\r
- }\r
-\r
- this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_DEINTERLACE, settings, allocatedMemory);\r
- }\r
- else\r
- {\r
- nativeJob.deinterlace = 0;\r
- }\r
-\r
- // VFR\r
- if (profile.Framerate == 0)\r
- {\r
- if (profile.ConstantFramerate)\r
- {\r
- // CFR with "Same as Source". Use the title rate\r
- nativeJob.cfr = 1;\r
- nativeJob.vrate = originalTitle.vrate.num;\r
- nativeJob.vrate_base = originalTitle.vrate.den;\r
- }\r
- else\r
- {\r
- // Pure VFR "Same as Source"\r
- nativeJob.cfr = 0;\r
- }\r
- }\r
- else\r
- {\r
- // Specified framerate\r
- if (profile.ConstantFramerate)\r
- {\r
- // Mark as pure CFR\r
- nativeJob.cfr = 1;\r
- }\r
- else\r
- {\r
- // Mark as peak framerate\r
- nativeJob.cfr = 2;\r
- }\r
-\r
- nativeJob.vrate = 27000000;\r
- nativeJob.vrate_base = Converters.Converters.FramerateToVrate(profile.Framerate);\r
- }\r
-\r
- string vfrSettings = string.Format(CultureInfo.InvariantCulture, "{0}:{1}:{2}", nativeJob.cfr, nativeJob.vrate, nativeJob.vrate_base);\r
- this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_VFR, vfrSettings, allocatedMemory);\r
-\r
- // Deblock\r
- if (profile.Deblock > 0)\r
- {\r
- this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_DEBLOCK, profile.Deblock.ToString(CultureInfo.InvariantCulture), allocatedMemory);\r
- }\r
-\r
- // Denoise\r
- if (profile.Denoise != Denoise.Off)\r
- {\r
- int filterType = profile.Denoise == Denoise.hqdn3d ? (int)hb_filter_ids.HB_FILTER_HQDN3D : (int)hb_filter_ids.HB_FILTER_NLMEANS;\r
-\r
- this.AddFilterFromPreset(filterList, filterType, profile.DenoisePreset, profile.DenoiseTune);\r
- }\r
-\r
- // Crop/scale\r
- int width = profile.Width;\r
- int height = profile.Height;\r
-\r
- int cropHorizontal = crop.Left + crop.Right;\r
- int cropVertical = crop.Top + crop.Bottom;\r
-\r
- if (width == 0)\r
- {\r
- width = title.Resolution.Width - cropHorizontal;\r
- }\r
-\r
- if (profile.MaxWidth > 0 && width > profile.MaxWidth)\r
- {\r
- width = profile.MaxWidth;\r
- }\r
-\r
- if (height == 0)\r
- {\r
- height = title.Resolution.Height - cropVertical;\r
- }\r
-\r
- if (profile.MaxHeight > 0 && height > profile.MaxHeight)\r
- {\r
- height = profile.MaxHeight;\r
- }\r
-\r
- // The job width can sometimes start not clean, due to interference from\r
- // preview generation. We reset it here to allow good calculations.\r
- nativeJob.width = width;\r
- nativeJob.height = height;\r
-\r
- nativeJob.grayscale = profile.Grayscale ? 1 : 0;\r
-\r
- switch (profile.Anamorphic)\r
- {\r
- case Anamorphic.None:\r
- nativeJob.anamorphic.mode = 0;\r
-\r
- Size outputSize = CalculateNonAnamorphicOutput(profile, title);\r
- width = outputSize.Width;\r
- height = outputSize.Height;\r
-\r
- nativeJob.anamorphic.keep_display_aspect = 0;\r
- nativeJob.anamorphic.par_width = 1;\r
- nativeJob.anamorphic.par_height = 1;\r
-\r
- nativeJob.width = width;\r
- nativeJob.height = height;\r
-\r
- nativeJob.maxWidth = profile.MaxWidth;\r
- nativeJob.maxHeight = profile.MaxHeight;\r
-\r
- nativeJob.modulus = 2;\r
-\r
- break;\r
- case Anamorphic.Strict:\r
- nativeJob.anamorphic.mode = hb_anamorphic_mode_t.HB_ANAMORPHIC_STRICT;\r
-\r
- nativeJob.anamorphic.par_width = title.ParVal.Width;\r
- nativeJob.anamorphic.par_height = title.ParVal.Height;\r
- break;\r
- case Anamorphic.Loose:\r
- nativeJob.anamorphic.mode = hb_anamorphic_mode_t.HB_ANAMORPHIC_LOOSE;\r
-\r
- nativeJob.modulus = profile.Modulus;\r
-\r
- nativeJob.width = width;\r
-\r
- nativeJob.maxWidth = profile.MaxWidth;\r
-\r
- nativeJob.anamorphic.par_width = title.ParVal.Width;\r
- nativeJob.anamorphic.par_height = title.ParVal.Height;\r
- break;\r
- case Anamorphic.Custom:\r
- nativeJob.anamorphic.mode = hb_anamorphic_mode_t.HB_ANAMORPHIC_CUSTOM;\r
-\r
- nativeJob.modulus = profile.Modulus;\r
-\r
- if (profile.UseDisplayWidth)\r
- {\r
- if (profile.KeepDisplayAspect)\r
- {\r
- int cropWidth = title.Resolution.Width - cropHorizontal;\r
- int cropHeight = title.Resolution.Height - cropVertical;\r
-\r
- double displayAspect = ((double)(cropWidth * title.ParVal.Width)) / (cropHeight * title.ParVal.Height);\r
- int displayWidth = profile.DisplayWidth;\r
-\r
- if (profile.Height > 0)\r
- {\r
- displayWidth = (int)((double)profile.Height * displayAspect);\r
- }\r
- else if (displayWidth > 0)\r
- {\r
- height = (int)((double)displayWidth / displayAspect);\r
- }\r
- else\r
- {\r
- displayWidth = (int)((double)height * displayAspect);\r
- }\r
-\r
- nativeJob.anamorphic.dar_width = displayWidth;\r
- nativeJob.anamorphic.dar_height = height;\r
- nativeJob.anamorphic.keep_display_aspect = 1;\r
- }\r
- else\r
- {\r
- nativeJob.anamorphic.dar_width = profile.DisplayWidth;\r
- nativeJob.anamorphic.dar_height = height;\r
- nativeJob.anamorphic.keep_display_aspect = 0;\r
- }\r
- }\r
- else\r
- {\r
- nativeJob.anamorphic.par_width = profile.PixelAspectX;\r
- nativeJob.anamorphic.par_height = profile.PixelAspectY;\r
- nativeJob.anamorphic.keep_display_aspect = 0;\r
- }\r
-\r
- nativeJob.width = width;\r
- nativeJob.height = height;\r
-\r
- nativeJob.maxWidth = profile.MaxWidth;\r
- nativeJob.maxHeight = profile.MaxHeight;\r
-\r
- break;\r
- default:\r
- break;\r
- }\r
-\r
- // Need to fix up values before adding crop/scale filter\r
- if (profile.Anamorphic != Anamorphic.None)\r
- {\r
- int anamorphicWidth = 0, anamorphicHeight = 0, anamorphicParWidth = 0, anamorphicParHeight = 0;\r
-\r
- HBFunctions.hb_set_anamorphic_size(ref nativeJob, ref anamorphicWidth, ref anamorphicHeight, ref anamorphicParWidth, ref anamorphicParHeight);\r
- nativeJob.width = anamorphicWidth;\r
- nativeJob.height = anamorphicHeight;\r
- nativeJob.anamorphic.par_width = anamorphicParWidth;\r
- nativeJob.anamorphic.par_height = anamorphicParHeight;\r
- }\r
-\r
- string cropScaleSettings = string.Format(\r
- CultureInfo.InvariantCulture,\r
- "{0}:{1}:{2}:{3}:{4}:{5}",\r
- nativeJob.width,\r
- nativeJob.height,\r
- nativeJob.crop[0],\r
- nativeJob.crop[1],\r
- nativeJob.crop[2],\r
- nativeJob.crop[3]);\r
- this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_CROP_SCALE, cropScaleSettings, allocatedMemory);\r
-\r
-\r
-\r
-\r
-\r
- HBVideoEncoder videoEncoder = Encoders.VideoEncoders.FirstOrDefault(e => e.ShortName == profile.VideoEncoder);\r
- if (videoEncoder == null)\r
- {\r
- throw new ArgumentException("Video encoder " + profile.VideoEncoder + " not recognized.");\r
- }\r
-\r
- nativeJob.vcodec = videoEncoder.Id;\r
-\r
-\r
-\r
- // areBframes\r
- // color_matrix\r
- List<hb_audio_s> titleAudio = originalTitle.list_audio.ToListFromHandBrakeList<hb_audio_s>();\r
-\r
- var audioList = new List<hb_audio_s>();\r
- int numTracks = 0;\r
-\r
- List<Tuple<AudioEncoding, int>> outputTrackList = this.GetOutputTracks(job, title);\r
-\r
- if (!string.IsNullOrEmpty(profile.AudioEncoderFallback))\r
- {\r
- HBAudioEncoder audioEncoder = Encoders.GetAudioEncoder(profile.AudioEncoderFallback);\r
- if (audioEncoder == null)\r
- {\r
- throw new ArgumentException("Unrecognized fallback audio encoder: " + profile.AudioEncoderFallback);\r
- }\r
-\r
- nativeJob.acodec_fallback = Encoders.GetAudioEncoder(profile.AudioEncoderFallback).Id;\r
- }\r
-\r
- nativeJob.acodec_copy_mask = (int)NativeConstants.HB_ACODEC_ANY;\r
-\r
- foreach (Tuple<AudioEncoding, int> outputTrack in outputTrackList)\r
- {\r
- AudioEncoding encoding = outputTrack.Item1;\r
- int trackNumber = outputTrack.Item2;\r
-\r
- audioList.Add(this.ConvertAudioBack(encoding, titleAudio[trackNumber - 1], numTracks++, allocatedMemory));\r
- }\r
-\r
- NativeList nativeAudioList = InteropUtilities.ToHandBrakeListFromList<hb_audio_s>(audioList);\r
- nativeJob.list_audio = nativeAudioList.Ptr;\r
- allocatedMemory.AddRange(nativeAudioList.AllocatedMemory);\r
-\r
- if (job.Subtitles != null)\r
- {\r
- if (job.Subtitles.SourceSubtitles != null && job.Subtitles.SourceSubtitles.Count > 0)\r
- {\r
- List<hb_subtitle_s> titleSubtitles = originalTitle.list_subtitle.ToListFromHandBrakeList<hb_subtitle_s>();\r
-\r
- foreach (SourceSubtitle sourceSubtitle in job.Subtitles.SourceSubtitles)\r
- {\r
- if (sourceSubtitle.TrackNumber == 0)\r
- {\r
- // Use subtitle search.\r
- nativeJob.select_subtitle_config.force = sourceSubtitle.Forced ? 1 : 0;\r
- nativeJob.select_subtitle_config.default_track = sourceSubtitle.Default ? 1 : 0;\r
-\r
- if (!sourceSubtitle.BurnedIn)\r
- {\r
- nativeJob.select_subtitle_config.dest = hb_subtitle_config_s_subdest.PASSTHRUSUB;\r
- }\r
-\r
- nativeJob.indepth_scan = 1;\r
- }\r
- else\r
- {\r
- // Use specified subtitle.\r
- hb_subtitle_s nativeSubtitle = titleSubtitles[sourceSubtitle.TrackNumber - 1];\r
- var subtitleConfig = new hb_subtitle_config_s();\r
-\r
- subtitleConfig.force = sourceSubtitle.Forced ? 1 : 0;\r
- subtitleConfig.default_track = sourceSubtitle.Default ? 1 : 0;\r
-\r
- bool supportsBurn = nativeSubtitle.source == hb_subtitle_s_subsource.VOBSUB || nativeSubtitle.source == hb_subtitle_s_subsource.SSASUB || nativeSubtitle.source == hb_subtitle_s_subsource.PGSSUB;\r
- if (supportsBurn && sourceSubtitle.BurnedIn)\r
- {\r
- subtitleConfig.dest = hb_subtitle_config_s_subdest.RENDERSUB;\r
- }\r
- else\r
- {\r
- subtitleConfig.dest = hb_subtitle_config_s_subdest.PASSTHRUSUB;\r
- }\r
-\r
- int subtitleAddSucceded = HBFunctions.hb_subtitle_add(ref nativeJob, ref subtitleConfig, sourceSubtitle.TrackNumber - 1);\r
- if (subtitleAddSucceded == 0)\r
- {\r
- Debug.WriteLine("Subtitle add failed");\r
- }\r
- }\r
- }\r
- }\r
-\r
- if (job.Subtitles.SrtSubtitles != null)\r
- {\r
- foreach (SrtSubtitle srtSubtitle in job.Subtitles.SrtSubtitles)\r
- {\r
- var subtitleConfig = new hb_subtitle_config_s();\r
-\r
- subtitleConfig.dest = srtSubtitle.BurnedIn ? hb_subtitle_config_s_subdest.RENDERSUB : hb_subtitle_config_s_subdest.PASSTHRUSUB;\r
- subtitleConfig.src_codeset = srtSubtitle.CharacterCode;\r
- subtitleConfig.src_filename = srtSubtitle.FileName;\r
- subtitleConfig.offset = srtSubtitle.Offset;\r
- subtitleConfig.default_track = srtSubtitle.Default ? 1 : 0;\r
-\r
- int srtAddSucceded = HBFunctions.hb_srt_add(ref nativeJob, ref subtitleConfig, srtSubtitle.LanguageCode);\r
- if (srtAddSucceded == 0)\r
- {\r
- Debug.WriteLine("SRT add failed");\r
- }\r
- }\r
- }\r
-\r
- bool hasBurnedSubtitle = job.Subtitles.SourceSubtitles != null && job.Subtitles.SourceSubtitles.Any(s => s.BurnedIn);\r
- if (hasBurnedSubtitle)\r
- {\r
- 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);\r
+ Error = state.WorkDone.Error != (int)hb_error_code.HB_ERROR_NONE\r
+ });\r
}\r
}\r
-\r
- // Rotate\r
- if (profile.FlipHorizontal || profile.FlipVertical || profile.Rotation != PictureRotation.None)\r
- {\r
- bool rotate90 = false;\r
- bool flipHorizontal = profile.FlipHorizontal;\r
- bool flipVertical = profile.FlipVertical;\r
-\r
- switch (profile.Rotation)\r
- {\r
- case PictureRotation.Clockwise90:\r
- rotate90 = true;\r
- break;\r
- case PictureRotation.Clockwise180:\r
- flipHorizontal = !flipHorizontal;\r
- flipVertical = !flipVertical;\r
- break;\r
- case PictureRotation.Clockwise270:\r
- rotate90 = true;\r
- flipHorizontal = !flipHorizontal;\r
- flipVertical = !flipVertical;\r
- break;\r
- }\r
-\r
- int rotateSetting = 0;\r
- if (flipVertical)\r
- {\r
- rotateSetting |= 1;\r
- }\r
-\r
- if (flipHorizontal)\r
- {\r
- rotateSetting |= 2;\r
- }\r
-\r
- if (rotate90)\r
- {\r
- rotateSetting |= 4;\r
- }\r
-\r
- this.AddFilter(filterList, (int)hb_filter_ids.HB_FILTER_ROTATE, rotateSetting.ToString(CultureInfo.InvariantCulture), allocatedMemory);\r
- }\r
-\r
- // Construct final filter list\r
- nativeJob.list_filter = this.ConvertFilterListToNative(filterList, allocatedMemory).Ptr;\r
-\r
- if (profile.ScaleMethod == ScaleMethod.Bicubic)\r
- {\r
- HBFunctions.hb_get_opencl_env();\r
- nativeJob.use_opencl = 1;\r
- }\r
- else\r
- {\r
- nativeJob.use_opencl = 0;\r
- }\r
-\r
- nativeJob.qsv.decode = profile.QsvDecode ? 1 : 0;\r
- nativeJob.use_hwd = job.DxvaDecoding ? 1 : 0;\r
-\r
- if (profile.ContainerName != null)\r
- {\r
- nativeJob.mux = HBFunctions.hb_container_get_from_name(profile.ContainerName);\r
- }\r
-\r
- if (job.OutputPath == null)\r
- {\r
- nativeJob.file = IntPtr.Zero;\r
- }\r
- else\r
- {\r
- IntPtr outputPathPtr = InteropUtilities.ToUtf8PtrFromString(job.OutputPath);\r
- allocatedMemory.Add(outputPathPtr);\r
- nativeJob.file = outputPathPtr;\r
- }\r
-\r
- nativeJob.largeFileSize = profile.LargeFile ? 1 : 0;\r
- nativeJob.mp4_optimize = profile.Optimize ? 1 : 0;\r
- nativeJob.ipod_atom = profile.IPod5GSupport ? 1 : 0;\r
-\r
- if (title.AngleCount > 1)\r
- {\r
- nativeJob.angle = job.Angle;\r
- }\r
-\r
- switch (profile.VideoEncodeRateType)\r
- {\r
- case VideoEncodeRateType.ConstantQuality:\r
- nativeJob.vquality = (float)profile.Quality;\r
- nativeJob.vbitrate = 0;\r
- break;\r
- case VideoEncodeRateType.AverageBitrate:\r
- nativeJob.vquality = -1;\r
- nativeJob.vbitrate = profile.VideoBitrate;\r
- break;\r
- case VideoEncodeRateType.TargetSize:\r
- nativeJob.vquality = -1;\r
- nativeJob.vbitrate = this.CalculateBitrate(job, profile.TargetSize, overallSelectedLengthSeconds);\r
- break;\r
- default:\r
- break;\r
- }\r
-\r
- // frames_to_skip\r
-\r
- return allocatedMemory;\r
}\r
\r
/// <summary>\r
return list;\r
}\r
\r
- /// <summary>\r
- /// Adds a filter to the given filter list.\r
- /// </summary>\r
- /// <param name="filterList">The list to add the filter to.</param>\r
- /// <param name="filterType">The type of filter.</param>\r
- /// <param name="settings">Settings for the filter.</param>\r
- /// <param name="allocatedMemory">The list of allocated memory.</param>\r
- private void AddFilter(List<hb_filter_object_s> filterList, int filterType, string settings, List<IntPtr> allocatedMemory)\r
- {\r
- IntPtr settingsNativeString = Marshal.StringToHGlobalAnsi(settings);\r
- hb_filter_object_s filter = InteropUtilities.ToStructureFromPtr<hb_filter_object_s>(HBFunctions.hb_filter_init(filterType));\r
- filter.settings = settingsNativeString;\r
-\r
- allocatedMemory.Add(settingsNativeString);\r
- filterList.Add(filter);\r
- }\r
-\r
- /// <summary>\r
- /// Adds a filter to the given filter list with the provided preset and tune.\r
- /// </summary>\r
- /// <param name="filterList">The list to add the filter to.</param>\r
- /// <param name="filterType">The type of filter.</param>\r
- /// <param name="preset">The preset name.</param>\r
- /// <param name="tune">The tune name.</param>\r
- private void AddFilterFromPreset(List<hb_filter_object_s> filterList, int filterType, string preset, string tune)\r
- {\r
- IntPtr settingsPtr = HBFunctions.hb_generate_filter_settings(filterType, preset, tune);\r
-\r
- hb_filter_object_s filter = InteropUtilities.ToStructureFromPtr<hb_filter_object_s>(HBFunctions.hb_filter_init(filterType));\r
- filter.settings = settingsPtr;\r
-\r
- filterList.Add(filter);\r
- }\r
-\r
- /// <summary>\r
- /// Converts the given filter list to a native list.\r
- /// </summary>\r
- /// <remarks>Sorts the list by filter ID before converting to a native list, as HB expects it that way.\r
- /// The list memory itself will be added to the allocatedMemory list.</remarks>\r
- /// <param name="filterList">The filter list to convert.</param>\r
- /// <param name="allocatedMemory">The list of allocated memory to add to.</param>\r
- /// <returns>The converted list.</returns>\r
- private NativeList ConvertFilterListToNative(List<hb_filter_object_s> filterList, List<IntPtr> allocatedMemory)\r
- {\r
- var filterPtrList = new List<IntPtr>();\r
-\r
- var sortedList = filterList.OrderBy(f => f.id);\r
- foreach (var filter in sortedList)\r
- {\r
- IntPtr filterPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(hb_filter_object_s)));\r
- Marshal.StructureToPtr(filter, filterPtr, false);\r
-\r
- allocatedMemory.Add(filterPtr);\r
- filterPtrList.Add(filterPtr);\r
- }\r
-\r
- NativeList filterListNative = InteropUtilities.ToHandBrakeListFromPtrList(filterPtrList);\r
- allocatedMemory.AddRange(filterListNative.AllocatedMemory);\r
-\r
- return filterListNative;\r
- }\r
-\r
/// <summary>\r
/// Gets the title, given the 1-based title number.\r
/// </summary>\r
{\r
return this.Titles.SingleOrDefault(title => title.TitleNumber == titleNumber);\r
}\r
-\r
- /// <summary>\r
- /// Gets the 1-based title index of the given title.\r
- /// </summary>\r
- /// <param name="titleNumber">The 1-based title title number.</param>\r
- /// <returns>The 1-based title index.</returns>\r
- private int GetTitleIndex(int titleNumber)\r
- {\r
- Title title = this.GetTitle(titleNumber);\r
- return this.GetTitleIndex(title);\r
- }\r
-\r
- /// <summary>\r
- /// Gets the 1-based title index of the given title.\r
- /// </summary>\r
- /// <param name="title">The title to look up</param>\r
- /// <returns>The 1-based title index of the given title.</returns>\r
- private int GetTitleIndex(Title title)\r
- {\r
- return this.Titles.IndexOf(title) + 1;\r
- }\r
-\r
- /// <summary>\r
- /// Gets the native title object from the title index.\r
- /// </summary>\r
- /// <param name="titleIndex">The index of the title (1-based).</param>\r
- /// <returns>Gets the native title object for the given index.</returns>\r
- private hb_title_s GetOriginalTitle(int titleIndex)\r
- {\r
- List<hb_title_s> matchingTitles = this.originalTitles.Where(title => title.index == titleIndex).ToList();\r
- if (matchingTitles.Count == 0)\r
- {\r
- throw new ArgumentException("Could not find specified title.");\r
- }\r
-\r
- if (matchingTitles.Count > 1)\r
- {\r
- throw new ArgumentException("Multiple titles matched.");\r
- }\r
-\r
- return matchingTitles[0];\r
- }\r
-\r
- /// <summary>\r
- /// Applies an audio encoding to a native audio encoding base structure.\r
- /// </summary>\r
- /// <param name="encoding">The encoding to apply.</param>\r
- /// <param name="baseStruct">The base native structure.</param>\r
- /// <param name="outputTrack">The output track number (0-based).</param>\r
- /// <param name="allocatedMemory">The collection of allocated memory.</param>\r
- /// <returns>The resulting native audio structure.</returns>\r
- private hb_audio_s ConvertAudioBack(AudioEncoding encoding, hb_audio_s baseStruct, int outputTrack, List<IntPtr> allocatedMemory)\r
- {\r
- hb_audio_s nativeAudio = baseStruct;\r
- HBAudioEncoder encoder = Encoders.GetAudioEncoder(encoding.Encoder);\r
-\r
- if (encoder == null)\r
- {\r
- throw new InvalidOperationException("Could not find audio encoder " + encoding.Name);\r
- }\r
-\r
- bool isPassthrough = encoder.IsPassthrough;\r
-\r
- HBAudioEncoder inputCodec = Encoders.GetAudioEncoder((int)baseStruct.config.input.codec);\r
-\r
- uint outputCodec = (uint)encoder.Id;\r
- if (encoding.PassthroughIfPossible && \r
- (encoder.Id == baseStruct.config.input.codec || \r
- inputCodec != null && (inputCodec.ShortName.ToLowerInvariant().Contains("aac") && encoder.ShortName.ToLowerInvariant().Contains("aac") ||\r
- inputCodec.ShortName.ToLowerInvariant().Contains("mp3") && encoder.ShortName.ToLowerInvariant().Contains("mp3"))) &&\r
- (inputCodec.Id & NativeConstants.HB_ACODEC_PASS_MASK) > 0)\r
- {\r
- outputCodec = baseStruct.config.input.codec | NativeConstants.HB_ACODEC_PASS_FLAG;\r
- isPassthrough = true;\r
- }\r
-\r
- nativeAudio.config.output.track = outputTrack;\r
- nativeAudio.config.output.codec = outputCodec;\r
- nativeAudio.config.output.compression_level = -1;\r
- nativeAudio.config.output.samplerate = nativeAudio.config.input.samplerate;\r
- nativeAudio.config.output.dither_method = -1;\r
-\r
- if (!isPassthrough)\r
- {\r
- if (encoding.SampleRateRaw != 0)\r
- {\r
- nativeAudio.config.output.samplerate = encoding.SampleRateRaw;\r
- }\r
-\r
- HBMixdown mixdown = Encoders.GetMixdown(encoding.Mixdown);\r
- nativeAudio.config.output.mixdown = mixdown.Id;\r
-\r
- if (encoding.EncodeRateType == AudioEncodeRateType.Bitrate)\r
- {\r
- // Disable quality targeting.\r
- nativeAudio.config.output.quality = -3;\r
-\r
- if (encoding.Bitrate == 0)\r
- {\r
- // Bitrate of 0 means auto: choose the default for this codec, sample rate and mixdown.\r
- nativeAudio.config.output.bitrate = HBFunctions.hb_audio_bitrate_get_default(\r
- nativeAudio.config.output.codec,\r
- nativeAudio.config.output.samplerate,\r
- nativeAudio.config.output.mixdown);\r
- }\r
- else\r
- {\r
- nativeAudio.config.output.bitrate = encoding.Bitrate;\r
- }\r
- }\r
- else if (encoding.EncodeRateType == AudioEncodeRateType.Quality)\r
- {\r
- // Bitrate of -1 signals quality targeting.\r
- nativeAudio.config.output.bitrate = -1;\r
- nativeAudio.config.output.quality = encoding.Quality;\r
- }\r
-\r
- // If this encoder supports compression level, pass it in.\r
- if (encoder.SupportsCompression)\r
- {\r
- nativeAudio.config.output.compression_level = encoding.Compression;\r
- }\r
-\r
- nativeAudio.config.output.dynamic_range_compression = encoding.Drc;\r
- nativeAudio.config.output.gain = encoding.Gain;\r
- }\r
-\r
- if (!string.IsNullOrEmpty(encoding.Name))\r
- {\r
- IntPtr encodingNamePtr = Marshal.StringToHGlobalAnsi(encoding.Name);\r
- nativeAudio.config.output.name = encodingNamePtr;\r
- allocatedMemory.Add(encodingNamePtr);\r
- }\r
-\r
- if (nativeAudio.padding == null)\r
- {\r
- nativeAudio.padding = new byte[MarshalingConstants.AudioPaddingBytes];\r
- }\r
-\r
- return nativeAudio;\r
- }\r
-\r
- /// <summary>\r
- /// Gets the cropping to use for the given encoding profile and title.\r
- /// </summary>\r
- /// <param name="profile">The encoding profile to use.</param>\r
- /// <param name="title">The title being encoded.</param>\r
- /// <returns>The cropping to use for the encode.</returns>\r
- private static Cropping GetCropping(EncodingProfile profile, Title title)\r
- {\r
- Cropping crop;\r
- switch (profile.CroppingType)\r
- {\r
- case CroppingType.Automatic:\r
- crop = title.AutoCropDimensions;\r
- break;\r
- case CroppingType.Custom:\r
- crop = profile.Cropping;\r
- break;\r
- default:\r
- crop = new Cropping();\r
- break;\r
- }\r
- return crop;\r
- }\r
}\r
}\r