1 // --------------------------------------------------------------------------------------------------------------------
2 // <copyright file="EncodeTaskFactory.cs" company="HandBrake Project (http://handbrake.fr)">
3 //   This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.
4 // </copyright>
5 // <summary>
6 //   The encode factory.
7 // </summary>
8 // --------------------------------------------------------------------------------------------------------------------
9 
10 namespace HandBrakeWPF.Services.Encode.Factories
11 {
12     using System.Collections.Generic;
13     using System.Globalization;
14     using System.Linq;
15     using System.Text.Json;
16 
17     using HandBrake.Interop.Interop;
18     using HandBrake.Interop.Interop.HbLib;
19     using HandBrake.Interop.Interop.Interfaces.Model;
20     using HandBrake.Interop.Interop.Interfaces.Model.Encoders;
21     using HandBrake.Interop.Interop.Json.Encode;
22     using HandBrake.Interop.Interop.Json.Shared;
23 
24     using HandBrakeWPF.Helpers;
25     using HandBrakeWPF.Model.Filters;
26     using HandBrakeWPF.Services.Interfaces;
27     using HandBrakeWPF.Utilities;
28 
29     using AudioEncoder = Model.Models.AudioEncoder;
30     using AudioEncoderRateType = Model.Models.AudioEncoderRateType;
31     using AudioTrack = Model.Models.AudioTrack;
32     using ChapterMarker = Model.Models.ChapterMarker;
33     using EncodeTask = Model.EncodeTask;
34     using FramerateMode = Model.Models.FramerateMode;
35     using OutputFormat = Model.Models.OutputFormat;
36     using PointToPointMode = Model.Models.PointToPointMode;
37     using Subtitle = HandBrake.Interop.Interop.Json.Encode.Subtitles;
38     using SubtitleTrack = Model.Models.SubtitleTrack;
39     using Validate = Helpers.Validate;
40     using VideoEncoder = HandBrakeWPF.Model.Video.VideoEncoder;
41     using VideoEncodeRateType = HandBrakeWPF.Model.Video.VideoEncodeRateType;
42 
43     /// <summary>
44     /// This factory takes the internal EncodeJob object and turns it into a set of JSON models
45     /// that can be deserialized by libhb.
46     /// </summary>
47     internal class EncodeTaskFactory
48     {
49         private readonly IUserSettingService userSettingService;
50 
EncodeTaskFactory(IUserSettingService userSettingService)51         public EncodeTaskFactory(IUserSettingService userSettingService)
52         {
53             this.userSettingService = userSettingService;
54         }
55 
Create(EncodeTask job, HBConfiguration configuration)56         internal JsonEncodeObject Create(EncodeTask job, HBConfiguration configuration)
57         {
58             JsonEncodeObject encode = new JsonEncodeObject
59                                       {
60                                           SequenceID = 0,
61                                           Audio = CreateAudio(job),
62                                           Destination = CreateDestination(job),
63                                           Filters = CreateFilters(job),
64                                           PAR = CreatePAR(job),
65                                           Metadata = CreateMetadata(job),
66                                           Source = CreateSource(job, configuration),
67                                           Subtitle = CreateSubtitle(job),
68                                           Video = CreateVideo(job, configuration)
69                                       };
70 
71             return encode;
72         }
73 
CreateSource(EncodeTask job, HBConfiguration configuration)74         private Source CreateSource(EncodeTask job, HBConfiguration configuration)
75         {
76             Range range = new Range();
77             switch (job.PointToPointMode)
78             {
79                 case PointToPointMode.Chapters:
80                     range.Type = "chapter";
81                     range.Start = job.StartPoint;
82                     range.End = job.EndPoint;
83                     break;
84                 case PointToPointMode.Seconds:
85                     range.Type = "time";
86                     range.Start = job.StartPoint * 90000;
87                     range.End = job.EndPoint * 90000;
88                     break;
89                 case PointToPointMode.Frames:
90                     range.Type = "frame";
91                     range.Start = job.StartPoint;
92                     range.End = job.EndPoint;
93                     break;
94                 case PointToPointMode.Preview:
95                     range.Type = "preview";
96                     range.Start = job.PreviewEncodeStartAt;
97                     range.SeekPoints = configuration.PreviewScanCount;
98                     range.End = job.PreviewEncodeDuration * 90000;
99                     break;
100             }
101 
102             Source source = new Source
103             {
104                 Title = job.Title,
105                 Range = range,
106                 Angle = job.Angle,
107                 Path = job.Source,
108             };
109             return source;
110         }
111 
CreateDestination(EncodeTask job)112         private Destination CreateDestination(EncodeTask job)
113         {
114             Destination destination = new Destination
115             {
116                 File = job.Destination,
117                 Mp4Options = new Mp4Options
118                 {
119                     IpodAtom = VideoEncoderHelpers.IsH264(job.VideoEncoder) ? job.IPod5GSupport : false,
120                     Mp4Optimize = job.OptimizeMP4
121                 },
122                 ChapterMarkers = job.IncludeChapterMarkers,
123                 AlignAVStart = job.AlignAVStart,
124                 Mux = EnumHelper<OutputFormat>.GetShortName(job.OutputFormat),
125                 ChapterList = new List<Chapter>()
126             };
127 
128             if (job.IncludeChapterMarkers)
129             {
130                 foreach (ChapterMarker item in job.ChapterNames)
131                 {
132                     Chapter chapter = new Chapter { Name = item.ChapterName };
133                     destination.ChapterList.Add(chapter);
134                 }
135             }
136 
137             return destination;
138         }
139 
CreatePAR(EncodeTask job)140         private PAR CreatePAR(EncodeTask job)
141         {
142             return new PAR { Num = job.PixelAspectX, Den = job.PixelAspectY };
143         }
144 
CreateSubtitle(EncodeTask job)145         private Subtitle CreateSubtitle(EncodeTask job)
146         {
147             Subtitles subtitle = new Subtitles
148             {
149                 Search =
150                         new SubtitleSearch
151                         {
152                             Enable = false,
153                             Default = false,
154                             Burn = false,
155                             Forced = false
156                         },
157                 SubtitleList = new List<HandBrake.Interop.Interop.Json.Encode.SubtitleTrack>()
158             };
159 
160             foreach (SubtitleTrack item in job.SubtitleTracks)
161             {
162                 if (!item.IsSrtSubtitle)
163                 {
164                     // Handle Foreign Audio Search
165                     if (item.SourceTrack.TrackNumber == 0)
166                     {
167                         subtitle.Search.Enable = true;
168                         subtitle.Search.Burn = item.Burned;
169                         subtitle.Search.Default = item.Default;
170                         subtitle.Search.Forced = item.Forced;
171                     }
172                     else
173                     {
174                         HandBrake.Interop.Interop.Json.Encode.SubtitleTrack track = new HandBrake.Interop.Interop.Json.Encode.SubtitleTrack
175                         {
176                             Burn = item.Burned,
177                             Default = item.Default,
178                             Forced = item.Forced,
179                             ID = item.SourceTrack.TrackNumber,
180                             Track = (item.SourceTrack.TrackNumber - 1),
181                             Name = item.Name
182                         };
183 
184                         subtitle.SubtitleList.Add(track);
185                     }
186                 }
187                 else
188                 {
189                     HandBrake.Interop.Interop.Json.Encode.SubtitleTrack track = new HandBrake.Interop.Interop.Json.Encode.SubtitleTrack
190                     {
191                         Track = -1, // Indicates SRT
192                         Default = item.Default,
193                         Offset = item.SrtOffset,
194                         Burn = item.Burned,
195                         Name = item.Name,
196                         Import =
197                             new SubImport
198                             {
199                                 Format = item.SrtPath.EndsWith("srt") ? "SRT" : "SSA",
200                                 Filename = item.SrtPath,
201                                 Codeset = item.SrtCharCode,
202                                 Language = item.SrtLangCode
203                             }
204                     };
205 
206                     subtitle.SubtitleList.Add(track);
207                 }
208             }
209 
210             return subtitle;
211         }
212 
CreateVideo(EncodeTask job, HBConfiguration configuration)213         private Video CreateVideo(EncodeTask job, HBConfiguration configuration)
214         {
215             Video video = new Video();
216 
217             HBVideoEncoder videoEncoder = HandBrakeEncoderHelpers.VideoEncoders.FirstOrDefault(e => e.ShortName == EnumHelper<VideoEncoder>.GetShortName(job.VideoEncoder));
218             Validate.NotNull(videoEncoder, "Video encoder " + job.VideoEncoder + " not recognized.");
219             if (videoEncoder != null)
220             {
221                 video.Encoder = videoEncoder.ShortName;
222             }
223 
224             video.Level = job.VideoLevel?.ShortName;
225             video.Preset = job.VideoPreset?.ShortName;
226             video.Profile = job.VideoProfile?.ShortName;
227 
228             if (job.VideoTunes != null && job.VideoTunes.Count > 0)
229             {
230                 foreach (var item in job.VideoTunes)
231                 {
232                     video.Tune += string.IsNullOrEmpty(video.Tune) ? item.ShortName : "," + item.ShortName;
233                 }
234             }
235 
236             if (job.VideoEncodeRateType == VideoEncodeRateType.ConstantQuality)
237             {
238                 video.Quality = (decimal?)job.Quality;
239             }
240 
241             if (job.VideoEncodeRateType == VideoEncodeRateType.AverageBitrate)
242             {
243                 video.Bitrate = job.VideoBitrate;
244                 video.TwoPass = job.TwoPass;
245                 video.Turbo = job.TurboFirstPass;
246             }
247 
248             video.QSV.Decode = HandBrakeHardwareEncoderHelper.IsQsvAvailable && configuration.EnableQuickSyncDecoding;
249 
250             // The use of the QSV decoder is configurable for non QSV encoders.
251             if (video.QSV.Decode && job.VideoEncoder != VideoEncoder.QuickSync && job.VideoEncoder != VideoEncoder.QuickSyncH265 && job.VideoEncoder != VideoEncoder.QuickSyncH26510b)
252             {
253                 video.QSV.Decode = configuration.UseQSVDecodeForNonQSVEnc;
254             }
255 
256             video.Options = job.ExtraAdvancedArguments;
257 
258             if (HandBrakeHardwareEncoderHelper.IsQsvAvailable && (HandBrakeHardwareEncoderHelper.QsvHardwareGeneration > 6) && (job.VideoEncoder == VideoEncoder.QuickSync || job.VideoEncoder == VideoEncoder.QuickSyncH265 || job.VideoEncoder == VideoEncoder.QuickSyncH26510b))
259             {
260                 if (configuration.EnableQsvLowPower && !video.Options.Contains("lowpower"))
261                 {
262                     video.Options = string.IsNullOrEmpty(video.Options) ? "lowpower=1" : string.Concat(video.Options, ":lowpower=1");
263                 }
264                 else if(!configuration.EnableQsvLowPower && !video.Options.Contains("lowpower"))
265                 {
266                     video.Options = string.IsNullOrEmpty(video.Options) ? "lowpower=0" : string.Concat(video.Options, ":lowpower=0");
267                 }
268             }
269 
270             return video;
271         }
272 
CreateAudio(EncodeTask job)273         private Audio CreateAudio(EncodeTask job)
274         {
275             Audio audio = new Audio();
276 
277             List<string> copyMaskList = new List<string>();
278             if (job.AudioPassthruOptions.AudioAllowAACPass) copyMaskList.Add(EnumHelper<AudioEncoder>.GetShortName(AudioEncoder.AacPassthru));
279             if (job.AudioPassthruOptions.AudioAllowAC3Pass) copyMaskList.Add(EnumHelper<AudioEncoder>.GetShortName(AudioEncoder.Ac3Passthrough));
280             if (job.AudioPassthruOptions.AudioAllowDTSHDPass) copyMaskList.Add(EnumHelper<AudioEncoder>.GetShortName(AudioEncoder.DtsHDPassthrough));
281             if (job.AudioPassthruOptions.AudioAllowDTSPass) copyMaskList.Add(EnumHelper<AudioEncoder>.GetShortName(AudioEncoder.DtsPassthrough));
282             if (job.AudioPassthruOptions.AudioAllowEAC3Pass) copyMaskList.Add(EnumHelper<AudioEncoder>.GetShortName(AudioEncoder.EAc3Passthrough));
283             if (job.AudioPassthruOptions.AudioAllowFlacPass) copyMaskList.Add(EnumHelper<AudioEncoder>.GetShortName(AudioEncoder.FlacPassthru));
284             if (job.AudioPassthruOptions.AudioAllowMP3Pass) copyMaskList.Add(EnumHelper<AudioEncoder>.GetShortName(AudioEncoder.Mp3Passthru));
285             if (job.AudioPassthruOptions.AudioAllowTrueHDPass) copyMaskList.Add(EnumHelper<AudioEncoder>.GetShortName(AudioEncoder.TrueHDPassthrough));
286             if (job.AudioPassthruOptions.AudioAllowMP2Pass) copyMaskList.Add(EnumHelper<AudioEncoder>.GetShortName(AudioEncoder.Mp2Passthru));
287 
288             audio.CopyMask = copyMaskList.ToArray();
289 
290             HBAudioEncoder audioEncoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper<AudioEncoder>.GetShortName(job.AudioPassthruOptions.AudioEncoderFallback));
291             audio.FallbackEncoder = audioEncoder?.ShortName;
292 
293             Validate.NotNull(audio.FallbackEncoder, string.Format("Unrecognized audio encoder: {0} \n", job.AudioPassthruOptions.AudioEncoderFallback));
294 
295             audio.AudioList = new List<HandBrake.Interop.Interop.Json.Encode.AudioTrack>();
296             foreach (AudioTrack item in job.AudioTracks)
297             {
298                 HBAudioEncoder encoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper<AudioEncoder>.GetShortName(item.Encoder));
299                 Validate.NotNull(encoder, "Unrecognized audio encoder:" + item.Encoder);
300 
301                 if (item.IsPassthru && (item.ScannedTrack.Codec & encoder.Id) == 0)
302                 {
303                     // We have an unsupported passthru. Rather than let libhb drop the track, switch it to the fallback.
304                     encoder = HandBrakeEncoderHelpers.GetAudioEncoder(EnumHelper<AudioEncoder>.GetShortName(job.AudioPassthruOptions.AudioEncoderFallback));
305                 }
306 
307                 HBMixdown mixdown = HandBrakeEncoderHelpers.GetMixdown(item.MixDown);
308 
309                 HBRate sampleRate = HandBrakeEncoderHelpers.AudioSampleRates.FirstOrDefault(s => s.Name == item.SampleRate.ToString(CultureInfo.InvariantCulture));
310 
311                 HandBrake.Interop.Interop.Json.Encode.AudioTrack audioTrack = new HandBrake.Interop.Interop.Json.Encode.AudioTrack
312                 {
313                     Track = (item.Track.HasValue ? item.Track.Value : 0) - 1,
314                     DRC = item.DRC,
315                     Encoder = encoder.ShortName,
316                     Gain = item.Gain,
317                     Mixdown = mixdown != null ? mixdown.Id : -1,
318                     NormalizeMixLevel = false,
319                     Samplerate = sampleRate != null ? sampleRate.Rate : 0,
320                     Name = !string.IsNullOrEmpty(item.TrackName) ? item.TrackName : null,
321                 };
322 
323                 if (!item.IsPassthru)
324                 {
325                     if (item.EncoderRateType == AudioEncoderRateType.Quality)
326                     {
327                         audioTrack.Quality = item.Quality;
328                     }
329 
330                     if (item.EncoderRateType == AudioEncoderRateType.Bitrate)
331                     {
332                         audioTrack.Bitrate = item.Bitrate;
333                     }
334                 }
335 
336                 audio.AudioList.Add(audioTrack);
337             }
338 
339             return audio;
340         }
341 
CreateFilters(EncodeTask job)342         private Filters CreateFilters(EncodeTask job)
343         {
344             Filters filter = new Filters
345             {
346                 FilterList = new List<Filter>(),
347             };
348 
349             // Note, order is important.
350 
351             // Detelecine
352             if (job.Detelecine != Detelecine.Off)
353             {
354                 string unparsedJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)hb_filter_ids.HB_FILTER_DETELECINE, null, null, job.CustomDetelecine);
355                 if (!string.IsNullOrEmpty(unparsedJson))
356                 {
357                     JsonDocument settings = JsonDocument.Parse(unparsedJson);
358 
359                     Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_DETELECINE, Settings = settings };
360                     filter.FilterList.Add(filterItem);
361                 }
362             }
363 
364             // Deinterlace
365             if (job.DeinterlaceFilter == DeinterlaceFilter.Yadif)
366             {
367                 string unparsedJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)hb_filter_ids.HB_FILTER_DEINTERLACE, job.DeinterlacePreset?.ShortName, null, job.CustomDeinterlaceSettings);
368                 if (!string.IsNullOrEmpty(unparsedJson))
369                 {
370                     JsonDocument root = JsonDocument.Parse(unparsedJson);
371 
372                     Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_DEINTERLACE, Settings = root };
373                     filter.FilterList.Add(filterItem);
374                 }
375             }
376 
377             // Decomb
378             if (job.DeinterlaceFilter == DeinterlaceFilter.Decomb)
379             {
380                 string unparsedJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)hb_filter_ids.HB_FILTER_DECOMB, job.DeinterlacePreset?.ShortName, null, job.CustomDeinterlaceSettings);
381                 if (!string.IsNullOrEmpty(unparsedJson))
382                 {
383                     JsonDocument settings = JsonDocument.Parse(unparsedJson);
384 
385                     Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_DECOMB, Settings = settings };
386                     filter.FilterList.Add(filterItem);
387                 }
388             }
389 
390             if (job.DeinterlaceFilter == DeinterlaceFilter.Decomb || job.DeinterlaceFilter == DeinterlaceFilter.Yadif)
391             {
392                 if (job.CombDetect != CombDetect.Off)
393                 {
394                     string unparsedJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)hb_filter_ids.HB_FILTER_COMB_DETECT, EnumHelper<CombDetect>.GetShortName(job.CombDetect), null, job.CustomCombDetect);
395                     if (!string.IsNullOrEmpty(unparsedJson))
396                     {
397                         JsonDocument settings = JsonDocument.Parse(unparsedJson);
398 
399                         Filter filterItem = new Filter
400                                                 {
401                                                     ID = (int)hb_filter_ids.HB_FILTER_COMB_DETECT,
402                                                     Settings = settings
403                                                 };
404                         filter.FilterList.Add(filterItem);
405                     }
406                 }
407             }
408 
409             // Denoise
410             if (job.Denoise != Denoise.Off)
411             {
412                 hb_filter_ids id = job.Denoise == Denoise.hqdn3d
413                     ? hb_filter_ids.HB_FILTER_HQDN3D
414                     : hb_filter_ids.HB_FILTER_NLMEANS;
415 
416                 string unparsedJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)id, job.DenoisePreset.ToString().ToLower().Replace(" ", string.Empty), job.DenoiseTune.ToString().ToLower().Replace(" ", string.Empty), job.CustomDenoise);
417 
418                 if (!string.IsNullOrEmpty(unparsedJson))
419                 {
420                     JsonDocument settings = JsonDocument.Parse(unparsedJson);
421 
422                     Filter filterItem = new Filter { ID = (int)id, Settings = settings };
423                     filter.FilterList.Add(filterItem);
424                 }
425             }
426 
427             // Sharpen
428             if (job.Sharpen != Sharpen.Off)
429             {
430                 hb_filter_ids id = job.Sharpen == Sharpen.LapSharp
431                     ? hb_filter_ids.HB_FILTER_LAPSHARP
432                     : hb_filter_ids.HB_FILTER_UNSHARP;
433 
434                 string unparsedJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)id, job.SharpenPreset.Key, job.SharpenTune.Key, job.SharpenCustom);
435 
436                 if (!string.IsNullOrEmpty(unparsedJson))
437                 {
438                     JsonDocument settings = JsonDocument.Parse(unparsedJson);
439 
440                     Filter filterItem = new Filter { ID = (int)id, Settings = settings };
441                     filter.FilterList.Add(filterItem);
442                 }
443             }
444 
445             // Deblock
446             if (job.DeblockPreset != null && job.DeblockPreset.Key != "off")
447             {
448                 string unparsedJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)hb_filter_ids.HB_FILTER_DEBLOCK, job.DeblockPreset.Key, job.DeblockTune.Key, job.CustomDeblock);
449                 if (!string.IsNullOrEmpty(unparsedJson))
450                 {
451                     JsonDocument settings = JsonDocument.Parse(unparsedJson);
452 
453                     Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_DEBLOCK, Settings = settings };
454                     filter.FilterList.Add(filterItem);
455                 }
456             }
457 
458             // CropScale Filter
459             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);
460             string unparsedCropSettingsJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)hb_filter_ids.HB_FILTER_CROP_SCALE, null, null, cropSettings);
461             if (!string.IsNullOrEmpty(unparsedCropSettingsJson))
462             {
463                 JsonDocument cropSettingsJson = JsonDocument.Parse(unparsedCropSettingsJson);
464 
465                 Filter cropScale = new Filter
466                                        {
467                                            ID = (int)hb_filter_ids.HB_FILTER_CROP_SCALE,
468                                            Settings = cropSettingsJson
469                                        };
470                 filter.FilterList.Add(cropScale);
471             }
472 
473             // Padding Filter
474             if (job.Padding.Enabled)
475             {
476                 // Calculate the new Width / Height
477                 int? width = job.Width;
478                 int? height = job.Height;
479                 if (job.Padding.Enabled)
480                 {
481                     width = width + job.Padding.W;
482                     height = height + job.Padding.H;
483                 }
484 
485                 // Setup the filter.
486                 string padSettings = string.Format("width={0}:height={1}:color={2}:x={3}:y={4}", width, height, job.Padding.Color, job.Padding.X, job.Padding.Y);
487                 string unparsedPadSettingsJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)hb_filter_ids.HB_FILTER_PAD, null, null, padSettings);
488                 if (!string.IsNullOrEmpty(unparsedPadSettingsJson))
489                 {
490                     JsonDocument PadSettingsJson = JsonDocument.Parse(unparsedPadSettingsJson);
491 
492                     Filter padding = new Filter
493                                        {
494                                            ID = (int)hb_filter_ids.HB_FILTER_PAD,
495                                            Settings = PadSettingsJson
496                     };
497                     filter.FilterList.Add(padding);
498                 }
499             }
500 
501             // Colourspace
502             if (job.Colourspace != null && job.Colourspace.Key != "off")
503             {
504                 string unparsedJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)hb_filter_ids.HB_FILTER_COLORSPACE, job.Colourspace.Key, null, job.CustomColourspace);
505                 if (!string.IsNullOrEmpty(unparsedJson))
506                 {
507                     JsonDocument settings = JsonDocument.Parse(unparsedJson);
508 
509                     Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_COLORSPACE, Settings = settings };
510                     filter.FilterList.Add(filterItem);
511                 }
512             }
513 
514             if (job.ChromaSmooth != null && job.ChromaSmooth.Key != "off")
515             {
516                 string unparsedJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)hb_filter_ids.HB_FILTER_CHROMA_SMOOTH, job.ChromaSmooth.Key, job.ChromaSmoothTune?.Key, job.CustomChromaSmooth);
517                 if (!string.IsNullOrEmpty(unparsedJson))
518                 {
519                     JsonDocument settings = JsonDocument.Parse(unparsedJson);
520 
521                     Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_CHROMA_SMOOTH, Settings = settings };
522                     filter.FilterList.Add(filterItem);
523                 }
524             }
525 
526 
527             // Grayscale
528             if (job.Grayscale)
529             {
530                 Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_GRAYSCALE, Settings = null };
531                 filter.FilterList.Add(filterItem);
532             }
533 
534             // Rotate
535             if (job.Rotation != 0 || job.FlipVideo)
536             {
537                 string rotateSettings = string.Format("angle={0}:hflip={1}", job.Rotation, job.FlipVideo ? "1" : "0");
538                 string unparsedJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)hb_filter_ids.HB_FILTER_ROTATE, null, null, rotateSettings);
539                 if (!string.IsNullOrEmpty(unparsedJson))
540                 {
541                     JsonDocument settings = JsonDocument.Parse(unparsedJson);
542 
543                     Filter filterItem = new Filter { ID = (int)hb_filter_ids.HB_FILTER_ROTATE, Settings = settings };
544                     filter.FilterList.Add(filterItem);
545                 }
546             }
547 
548             // Framerate shaping filter
549             int fm = job.FramerateMode == FramerateMode.CFR ? 1 : job.FramerateMode == FramerateMode.PFR ? 2 : 0;
550             int? num = null, den = null;
551             if (job.Framerate != null)
552             {
553                 int vrate = HandBrakeUnitConversionHelpers.GetFramerateFromName(job.Framerate.Value.ToString(CultureInfo.InvariantCulture));
554 
555                 if (vrate > 0)
556                 {
557                     num = 27000000;
558                     den = vrate;
559                 }
560             }
561 
562             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
563             string unparsedFramerateJson = HandBrakeFilterHelpers.GenerateFilterSettingJson((int)hb_filter_ids.HB_FILTER_VFR, null, null, framerateString);
564             if (!string.IsNullOrEmpty(unparsedFramerateJson))
565             {
566                 JsonDocument framerateSettings = JsonDocument.Parse(unparsedFramerateJson);
567 
568                 Filter framerateShaper = new Filter
569                                              {
570                                                  ID = (int)hb_filter_ids.HB_FILTER_VFR,
571                                                  Settings = framerateSettings
572                                              };
573                 filter.FilterList.Add(framerateShaper);
574             }
575 
576             return filter;
577         }
578 
CreateMetadata(EncodeTask job)579         private Metadata CreateMetadata(EncodeTask job)
580         {
581             if (job.MetaData != null && job.MetaData.PassthruMetadataEnabled)
582             {
583                 Metadata metaData = new Metadata
584                                     {
585                                         Artist = job.MetaData.Artist,
586                                         Album = job.MetaData.Album,
587                                         AlbumArtist = job.MetaData.AlbumArtist,
588                                         Comment = job.MetaData.Comment,
589                                         Composer = job.MetaData.Composer,
590                                         Description = job.MetaData.Description,
591                                         Genre = job.MetaData.Genre,
592                                         LongDescription = job.MetaData.LongDescription,
593                                         Name = job.MetaData.Name,
594                                         ReleaseDate = job.MetaData.ReleaseDate
595                                     };
596                 return metaData;
597             }
598 
599             return new Metadata(); // Empty Metadata will not pass through to the destination.
600         }
601     }
602 }
603