public FFmpegComplexFilterBuilder WithHardwareAcceleration(HardwareAccelerationKind hardwareAccelerationKind) { _hardwareAccelerationKind = Some(hardwareAccelerationKind); return(this); }
public Option <FFmpegComplexFilter> Build() { var complexFilter = new StringBuilder(); var videoLabel = "0:V"; var audioLabel = "0:a"; HardwareAccelerationKind acceleration = _hardwareAccelerationKind.IfNone(HardwareAccelerationKind.None); bool isHardwareDecode = acceleration switch { HardwareAccelerationKind.Vaapi => _inputCodec != "mpeg4", HardwareAccelerationKind.Nvenc => true, HardwareAccelerationKind.Qsv => true, _ => false }; _audioDuration.IfSome( audioDuration => { complexFilter.Append($"[{audioLabel}]"); complexFilter.Append($"apad=whole_dur={audioDuration.TotalMilliseconds}ms"); audioLabel = "[a]"; complexFilter.Append(audioLabel); }); var filterQueue = new List <string>(); bool usesHardwareFilters = acceleration != HardwareAccelerationKind.None && !isHardwareDecode && (_deinterlace || _scaleToSize.IsSome); if (usesHardwareFilters) { filterQueue.Add("hwupload"); } if (_deinterlace) { string filter = acceleration switch { HardwareAccelerationKind.Qsv => "deinterlace_qsv", HardwareAccelerationKind.Nvenc => "", // TODO: yadif_cuda support in docker HardwareAccelerationKind.Vaapi => "deinterlace_vaapi", _ => "yadif=1" }; if (!string.IsNullOrWhiteSpace(filter)) { filterQueue.Add(filter); } } _scaleToSize.IfSome( size => { string filter = acceleration switch { HardwareAccelerationKind.Qsv => $"scale_qsv=w={size.Width}:h={size.Height}", HardwareAccelerationKind.Nvenc => $"scale_npp={size.Width}:{size.Height}", HardwareAccelerationKind.Vaapi => $"scale_vaapi=w={size.Width}:h={size.Height}", _ => $"scale={size.Width}:{size.Height}:flags=fast_bilinear" }; if (!string.IsNullOrWhiteSpace(filter)) { filterQueue.Add(filter); } }); if (_scaleToSize.IsSome || _padToSize.IsSome) { if (acceleration != HardwareAccelerationKind.None && (isHardwareDecode || usesHardwareFilters)) { filterQueue.Add("hwdownload"); string format = acceleration switch { HardwareAccelerationKind.Vaapi => "format=nv12|vaapi", _ => "format=nv12" }; filterQueue.Add(format); } filterQueue.Add("setsar=1"); } _padToSize.IfSome(size => filterQueue.Add($"pad={size.Width}:{size.Height}:(ow-iw)/2:(oh-ih)/2")); if ((_scaleToSize.IsSome || _padToSize.IsSome) && acceleration != HardwareAccelerationKind.None) { string upload = acceleration switch { HardwareAccelerationKind.Qsv => "hwupload=extra_hw_frames=64", _ => "hwupload" }; filterQueue.Add(upload); } if (filterQueue.Any()) { // TODO: any audio filter if (_audioDuration.IsSome) { complexFilter.Append(';'); } complexFilter.Append($"[{videoLabel}]"); complexFilter.Append(string.Join(",", filterQueue)); videoLabel = "[v]"; complexFilter.Append(videoLabel); } var filterResult = complexFilter.ToString(); return(string.IsNullOrWhiteSpace(filterResult) ? Option <FFmpegComplexFilter> .None : new FFmpegComplexFilter(filterResult, videoLabel, audioLabel)); } } }
public Option <FFmpegComplexFilter> Build( bool videoOnly, int videoInput, int videoStreamIndex, int audioInput, Option <int> audioStreamIndex, bool isSong) { // since .Contains is used on pixel format, we need it to be not null _pixelFormat ??= string.Empty; var complexFilter = new StringBuilder(); string videoLabel = $"{videoInput}:{(isSong ? "v" : videoStreamIndex.ToString())}"; string audioLabel = audioStreamIndex.Match(index => $"{audioInput}:{index}", () => "0:a"); HardwareAccelerationKind acceleration = _hardwareAccelerationKind.IfNone(HardwareAccelerationKind.None); bool isHardwareDecode = acceleration switch { HardwareAccelerationKind.Vaapi => !isSong && _inputCodec != "mpeg4" && (_deinterlace == false || !_pixelFormat.Contains("p10le")), // we need an initial hwupload_cuda when only padding with these pixel formats HardwareAccelerationKind.Nvenc when _scaleToSize.IsNone&& _padToSize.IsSome => !isSong && !_pixelFormat.Contains("p10le") && !_pixelFormat.Contains("444"), HardwareAccelerationKind.Nvenc => !isSong && (string.IsNullOrWhiteSpace(_videoDecoder) || _videoDecoder.Contains("cuvid")), HardwareAccelerationKind.Qsv => !isSong, HardwareAccelerationKind.VideoToolbox => false, _ => false }; bool nvencDeinterlace = acceleration == HardwareAccelerationKind.Nvenc && _videoDecoder == "mpeg2_cuvid" && _deinterlace; // mpeg2_cuvid will handle deinterlace and is "not" a hardware decode if (nvencDeinterlace) { _deinterlace = false; isHardwareDecode = false; } var audioFilterQueue = new List <string>(); var videoFilterQueue = new List <string>(); var watermarkPreprocess = new List <string>(); string watermarkOverlay = string.Empty; if (_normalizeLoudness) { audioFilterQueue.Add("loudnorm=I=-16:TP=-1.5:LRA=11"); } _audioDuration.IfSome( audioDuration => { var durationString = audioDuration.TotalMilliseconds.ToString(NumberFormatInfo.InvariantInfo); audioFilterQueue.Add($"apad=whole_dur={durationString}ms"); }); bool usesHardwareFilters = acceleration != HardwareAccelerationKind.None && acceleration != HardwareAccelerationKind.VideoToolbox && !isHardwareDecode && (_deinterlace || _scaleToSize.IsSome); if (isSong) { switch (acceleration) { case HardwareAccelerationKind.Qsv: videoFilterQueue.Add("format=nv12"); break; case HardwareAccelerationKind.Vaapi: videoFilterQueue.Add("format=nv12|vaapi"); break; default: videoFilterQueue.Add("format=yuv420p"); break; } } switch (usesHardwareFilters || isSong, acceleration) {