diff --git a/.editorconfig b/.editorconfig index 33410c9..7bbd8e3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -31,6 +31,9 @@ dotnet_diagnostic.CA1056.severity = none # Asp.Net Core Pages uses strings na # CA1303: Do not pass literals as localized parameters dotnet_diagnostic.CA1303.severity = none # Don't need translated exceptions +# CA1308: Normalize strings to uppercase +dotnet_diagnostic.CA1308.severity = none # Also to lower is required + # CA1707: Identifiers should not contain underscores dotnet_diagnostic.CA1707.severity = none # I like underscores into constants name diff --git a/src/EthernaVideoImporter.Core/Services/EncoderService.cs b/src/EthernaVideoImporter.Core/Services/EncoderService.cs index cd382b4..077a598 100644 --- a/src/EthernaVideoImporter.Core/Services/EncoderService.cs +++ b/src/EthernaVideoImporter.Core/Services/EncoderService.cs @@ -44,7 +44,40 @@ public async Task> EncodeVideosAsync( Console.WriteLine($"Encoding resolution {heightResolution}..."); - // Get scaled height +#pragma warning disable IDE0028 // Simplify collection initialization + // Build args. + var args = new List(); + + //input + args.Add("-i"); args.Add(sourceVideoFile.FilePath); + + //audio codec + args.Add("-c:a"); args.Add("aac"); + + //video codec + args.Add("-c:v"); args.Add(options.FFMpegHWAccelerationType switch + { + FFMpegHWAccelerationType.None => "libx264", + FFMpegHWAccelerationType.Cuda => "h264_nvenc", + _ => throw new InvalidOperationException() + }); + + //preset + args.Add("-preset"); args.Add("slow"); + + //hw acceleration + switch (options.FFMpegHWAccelerationType) + { + case FFMpegHWAccelerationType.Cuda: + args.Add("-hwaccel"); args.Add("cuda"); + break; + default: break; + } + + //flags + args.Add("-movflags"); args.Add("faststart"); + + //filters var scaledWidth = (int)Math.Round(heightResolution * resolutionRatio, 0); var roundedScaledWidth = (scaledWidth % 4) switch { @@ -54,26 +87,40 @@ public async Task> EncodeVideosAsync( 3 => scaledWidth + 1, _ => throw new InvalidOperationException() }; + args.Add("-vf"); + { + var filters = new List(); - var fileName = $"{CommonConsts.TempDirectory.FullName}/{fileNameGuid}_{heightResolution}.mp4"; - var args = new string[] { - "-i", sourceVideoFile.FilePath, - "-c:a", "aac", - "-c:v", ffMpegHWAccelerationType switch + //memory management + switch (options.FFMpegHWAccelerationType) { - FFMpegHWAccelerationType.None => "libx264", - FFMpegHWAccelerationType.Cuda => "h264_nvenc" - }, - ffMpegHWAccelerationType switch + case FFMpegHWAccelerationType.Cuda: + filters.Add("hwupload_cuda"); + break; + default: break; + } + + //scale + switch (options.FFMpegHWAccelerationType) { - FFMpegHWAccelerationType.None => "", - FFMpegHWAccelerationType.Cuda => "-hwaccel cuda -hwaccel_output_format cuda" - }, - "-movflags", "faststart", - "-vf", $"scale={roundedScaledWidth}:{heightResolution}", - "-loglevel", "info", - fileName - }; + case FFMpegHWAccelerationType.Cuda: + filters.Add($"scale_npp={roundedScaledWidth}:{heightResolution}"); + break; + default: + filters.Add($"scale={roundedScaledWidth}:{heightResolution}"); + break; + } + + args.Add($"\"{filters.Aggregate((r, f) => $"{r},{f}")}\""); + } + + //logs + args.Add("-loglevel"); args.Add("info"); + + //output + var fileName = $"{CommonConsts.TempDirectory.FullName}/{fileNameGuid}_{heightResolution}.mp4"; + args.Add(fileName); +#pragma warning restore IDE0028 // Simplify collection initialization var command = Command.Run(FFMpegBinaryPath, args); diff --git a/src/EthernaVideoImporter.Devcon/Program.cs b/src/EthernaVideoImporter.Devcon/Program.cs index 88a5276..d5c725b 100644 --- a/src/EthernaVideoImporter.Devcon/Program.cs +++ b/src/EthernaVideoImporter.Devcon/Program.cs @@ -40,7 +40,7 @@ internal static class Program "\n" + "Options:\n" + $" -ff\tPath FFmpeg (default dir: {CommonConsts.DefaultFFmpegFolder})\n" + - $" -hw\tUse NVIDIA CUDA hardware acceleration on FFmpeg (default: false)\n" + + $" -hw\tUse hardware acceleration on FFmpeg (default: {nameof(FFMpegHWAccelerationType.None).ToLowerInvariant()}). Valid values: [{Enum.GetNames().Aggregate((r, i) => $"{r}, {i}").ToLowerInvariant()}]\n" + $" -t\tTTL (days) Postage Stamp (default value: {DefaultTTLPostageStamp} days)\n" + " -o\tOffer video downloads to everyone\n" + " -p\tPin videos\n" + @@ -175,7 +175,7 @@ static async Task Main(string[] args) case "-skip720": skip720 = true; break; case "-skip480": skip480 = true; break; case "-skip360": skip360 = true; break; - case "-hw": ffMpegHWAccelerationType = FFMpegHWAccelerationType.Cuda; break; + case "-hw": ffMpegHWAccelerationType = Enum.Parse(args[++i], true); break; default: throw new ArgumentException(args[i] + " is not a valid argument"); } } @@ -250,7 +250,7 @@ static async Task Main(string[] args) { if (customFFMpegFolderPath is not null) encoderOptions.FFMpegFolderPath = customFFMpegFolderPath; - + encoderOptions.FFMpegHWAccelerationType = ffMpegHWAccelerationType; encoderOptions.IncludeAudioTrack = includeAudioTrack; encoderOptions.Skip1440 = skip1440; encoderOptions.Skip1080 = skip1080; @@ -266,7 +266,13 @@ static async Task Main(string[] args) }, useBeeNativeNode, authResult.RefreshTokenHandler); - services.AddTransient(); + services.AddTransient(_ => + ffMpegHWAccelerationType switch + { + FFMpegHWAccelerationType.None => new YoutubeClient(), + FFMpegHWAccelerationType.Cuda => new YoutubeClient("cuda"), + _ => throw new InvalidOperationException() + }); services.AddTransient(); services.AddTransient(); diff --git a/src/EthernaVideoImporter/Program.cs b/src/EthernaVideoImporter/Program.cs index 72aa979..12a4da5 100644 --- a/src/EthernaVideoImporter/Program.cs +++ b/src/EthernaVideoImporter/Program.cs @@ -46,7 +46,7 @@ internal static class Program "\n" + "Options:\n" + $" -ff\tPath FFmpeg (default dir: {CommonConsts.DefaultFFmpegFolder})\n" + - $" -hw\tUse NVIDIA CUDA hardware acceleration on FFmpeg (default: false)\n" + + $" -hw\tUse hardware acceleration on FFmpeg (default: {nameof(FFMpegHWAccelerationType.None).ToLowerInvariant()}). Valid values: [{Enum.GetNames().Aggregate((r, i) => $"{r}, {i}").ToLowerInvariant()}]\n" + $" -t\tTTL (days) Postage Stamp (default value: {DefaultTTLPostageStamp} days)\n" + " -o\tOffer video downloads to everyone\n" + " -p\tPin videos\n" + @@ -198,7 +198,7 @@ static async Task Main(string[] args) case "-skip720": skip720 = true; break; case "-skip480": skip480 = true; break; case "-skip360": skip360 = true; break; - case "-hw": ffMpegHWAccelerationType = FFMpegHWAccelerationType.Cuda; break; + case "-hw": ffMpegHWAccelerationType = Enum.Parse(args[++i], true); break; default: throw new ArgumentException(args[i] + " is not a valid argument"); } } @@ -272,7 +272,7 @@ static async Task Main(string[] args) { if (customFFMpegFolderPath is not null) encoderOptions.FFMpegFolderPath = customFFMpegFolderPath; - + encoderOptions.FFMpegHWAccelerationType = ffMpegHWAccelerationType; encoderOptions.IncludeAudioTrack = includeAudioTrack; encoderOptions.Skip1440 = skip1440; encoderOptions.Skip1080 = skip1080; @@ -313,8 +313,7 @@ static async Task Main(string[] args) services.AddSingleton, YouTubeChannelVideoProviderOptionsValidation>(); //services - services.AddTransient(); - services.AddTransient(); + AddYoutubeDownloader(services, ffMpegHWAccelerationType); services.AddTransient(); break; case SourceType.YouTubeVideo: @@ -326,8 +325,7 @@ static async Task Main(string[] args) services.AddSingleton, YouTubeSingleVideoProviderOptionsValidation>(); //services - services.AddTransient(); - services.AddTransient(); + AddYoutubeDownloader(services, ffMpegHWAccelerationType); services.AddTransient(); break; default: @@ -347,5 +345,20 @@ await importer.RunAsync( userEthAddr, unpinRemovedVideos); } + + // Helpers. + private static void AddYoutubeDownloader( + ServiceCollection services, + FFMpegHWAccelerationType ffMpegHWAccelerationType) + { + services.AddTransient(_ => + ffMpegHWAccelerationType switch + { + FFMpegHWAccelerationType.None => new YoutubeClient(), + FFMpegHWAccelerationType.Cuda => new YoutubeClient("cuda"), + _ => throw new InvalidOperationException() + }); + services.AddTransient(); + } } }