From b986854fbeab91bdec052f0c38c4700568e850be Mon Sep 17 00:00:00 2001 From: lay295 Date: Thu, 20 Aug 2020 00:32:38 -0400 Subject: [PATCH] Add luma mask option --- TwitchDownloaderCLI/Options.cs | 2 + TwitchDownloaderCLI/Program.cs | 1 + TwitchDownloaderCLI/README.md | 5 +- TwitchDownloaderCore/ChatRenderer.cs | 48 +++++++++++++++++++ .../Options/ChatRenderOptions.cs | 14 ++++++ TwitchDownloaderWPF/App.config | 5 +- TwitchDownloaderWPF/MainWindow.xaml | 2 +- TwitchDownloaderWPF/PageChatRender.xaml | 2 + TwitchDownloaderWPF/PageChatRender.xaml.cs | 14 +++++- .../Properties/Settings.Designer.cs | 14 +++++- .../Properties/Settings.settings | 3 ++ 11 files changed, 104 insertions(+), 6 deletions(-) diff --git a/TwitchDownloaderCLI/Options.cs b/TwitchDownloaderCLI/Options.cs index 4626568c..b4120b75 100644 --- a/TwitchDownloaderCLI/Options.cs +++ b/TwitchDownloaderCLI/Options.cs @@ -51,6 +51,8 @@ class Options public bool FfzEmotes { get; set; } [Option("outline", Default = false, HelpText = "Enable outline in chat render.")] public bool Outline { get; set; } + [Option("generate-mask", Default = false, HelpText = "Generates a mask file in addition to the regular chat file.")] + public bool GenerateMask { get; set; } [Option("outline-size", Default = 4, HelpText = "Size of outline in chat render.")] public double OutlineSize { get; set; } [Option('f', "font", Default = "ariel", HelpText = "Font to use in chat render.")] diff --git a/TwitchDownloaderCLI/Program.cs b/TwitchDownloaderCLI/Program.cs index edabf087..25e15666 100644 --- a/TwitchDownloaderCLI/Program.cs +++ b/TwitchDownloaderCLI/Program.cs @@ -197,6 +197,7 @@ private static void RenderChat(Options inputOptions) renderOptions.UpdateRate = inputOptions.UpdateRate; renderOptions.PaddingLeft = inputOptions.PaddingLeft; renderOptions.Framerate = inputOptions.Framerate; + renderOptions.GenerateMask = inputOptions.GenerateMask; renderOptions.InputArgs = inputOptions.InputArgs; renderOptions.OutputArgs = inputOptions.OutputArgs; renderOptions.FfmpegPath = inputOptions.FfmpegPath == null || inputOptions.FfmpegPath == "" ? ffmpegPath : inputOptions.FfmpegPath; diff --git a/TwitchDownloaderCLI/README.md b/TwitchDownloaderCLI/README.md index b1e55402..53b374ff 100644 --- a/TwitchDownloaderCLI/README.md +++ b/TwitchDownloaderCLI/README.md @@ -38,7 +38,7 @@ Extra example, if I wanted only seconds 3-6 in a 10 second stream I would do -b **-t/-\-threads** (Default: 10) Number of download threads. -**-o/-\-oauth** +**-\-oauth** OAuth to be passed when downloading a VOD. Used when downloading sub only VODs. ## Arguments for mode ClipDownload **-u/-\-id** @@ -109,6 +109,9 @@ Path to JSON chat file input. **-\-timestamp** Enables timestamps to left of messages, similar to VOD chat on Twitch. +**-\-generate-mask** +Generates a mask file in addition to the regular chat file. + **-\-framerate** (Default: 30) Framerate of chat render output. diff --git a/TwitchDownloaderCore/ChatRenderer.cs b/TwitchDownloaderCore/ChatRenderer.cs index ee17755e..38fed8fa 100644 --- a/TwitchDownloaderCore/ChatRenderer.cs +++ b/TwitchDownloaderCore/ChatRenderer.cs @@ -221,6 +221,36 @@ private void RenderVideo(ChatRenderOptions renderOptions, Queue f Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); + Process maskProcess = null; + BinaryWriter maskStream = null; + if (renderOptions.GenerateMask) + { + string outputArgsMask = renderOptions.OutputArgs.Replace("{fps}", renderOptions.Framerate.ToString()) + .Replace("{height}", renderOptions.ChatHeight.ToString()).Replace("{width}", renderOptions.ChatWidth.ToString()) + .Replace("{save_path}", renderOptions.OutputFileMask).Replace("{max_int}", int.MaxValue.ToString()); + maskProcess = new Process + { + StartInfo = + { + FileName = ffmpegFile, + Arguments = $"{inputArgs} {outputArgsMask}", + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true + } + }; + + if (File.Exists(renderOptions.OutputFileMask)) + File.Delete(renderOptions.OutputFileMask); + + maskProcess.Start(); + maskProcess.BeginErrorReadLine(); + maskProcess.BeginOutputReadLine(); + maskStream = new BinaryWriter(maskProcess.StandardInput.BaseStream); + } + using (var ffmpegStream = new BinaryWriter(process.StandardInput.BaseStream)) { bufferCanvas.Clear(renderOptions.BackgroundColor); @@ -363,6 +393,19 @@ private void RenderVideo(ChatRenderOptions renderOptions, Queue f var data = SKData.Create(pix.GetPixels(), pix.Info.BytesSize); var bytes = data.ToArray(); ffmpegStream.Write(bytes); + if (renderOptions.GenerateMask) + { + SKBitmap maskBitmap = new SKBitmap(renderOptions.ChatWidth, renderOptions.ChatHeight); + using (SKCanvas maskCanvas = new SKCanvas(maskBitmap)) + { + maskCanvas.Clear(SKColors.White); + maskCanvas.DrawBitmap(bufferBitmap, 0, 0, new SKPaint() { BlendMode = SKBlendMode.DstIn }); + } + var pixMask = maskBitmap.PeekPixels(); + var dataMask = SKData.Create(pixMask.GetPixels(), pixMask.Info.BytesSize); + var bytesMask = dataMask.ToArray(); + maskStream.Write(bytesMask); + } foreach (var emote in displayedGifs) { @@ -381,6 +424,11 @@ private void RenderVideo(ChatRenderOptions renderOptions, Queue f } } } + if (renderOptions.GenerateMask) + { + maskStream.Dispose(); + maskProcess.WaitForExit(); + } stopwatch.Stop(); progress.Report(new ProgressReport() { reportType = ReportType.Log, data = $"FINISHED. RENDER TIME: {(int)stopwatch.Elapsed.TotalSeconds}s SPEED: {(duration / stopwatch.Elapsed.TotalSeconds).ToString("0.##")}x" }); process.WaitForExit(); diff --git a/TwitchDownloaderCore/Options/ChatRenderOptions.cs b/TwitchDownloaderCore/Options/ChatRenderOptions.cs index eb43008b..8593f0e6 100644 --- a/TwitchDownloaderCore/Options/ChatRenderOptions.cs +++ b/TwitchDownloaderCore/Options/ChatRenderOptions.cs @@ -1,6 +1,7 @@ using SkiaSharp; using System; using System.Collections.Generic; +using System.IO; using System.Text; namespace TwitchDownloaderCore.Options @@ -49,6 +50,19 @@ public int UpdateFrame return (int)Math.Floor(UpdateRate / (1.0 / Framerate)); } } + public bool GenerateMask { get; set; } + public string OutputFileMask + { + get + { + if (OutputFile == "" || GenerateMask == false) + return OutputFile; + + string extension = Path.GetExtension(OutputFile); + int lastIndex = OutputFile.LastIndexOf(extension); + return OutputFile.Substring(0, lastIndex) + "_mask" + extension; + } + } public string InputArgs { get; set; } public string OutputArgs { get; set; } public string FfmpegPath { get; set; } diff --git a/TwitchDownloaderWPF/App.config b/TwitchDownloaderWPF/App.config index cfb2674f..56d4f0dd 100644 --- a/TwitchDownloaderWPF/App.config +++ b/TwitchDownloaderWPF/App.config @@ -85,11 +85,14 @@ 255 - + False + + False + diff --git a/TwitchDownloaderWPF/MainWindow.xaml b/TwitchDownloaderWPF/MainWindow.xaml index e8bfd963..d232ab61 100644 --- a/TwitchDownloaderWPF/MainWindow.xaml +++ b/TwitchDownloaderWPF/MainWindow.xaml @@ -5,7 +5,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:TwitchDownloaderWPF" mc:Ignorable="d" - Title="Twitch Downloader" Height="450" Width="800" Loaded="Window_Loaded" Closing="Window_Closing"> + Title="Twitch Downloader" Height="450" Width="850" Loaded="Window_Loaded" Closing="Window_Closing"> diff --git a/TwitchDownloaderWPF/PageChatRender.xaml b/TwitchDownloaderWPF/PageChatRender.xaml index 709aff2a..e63300fa 100644 --- a/TwitchDownloaderWPF/PageChatRender.xaml +++ b/TwitchDownloaderWPF/PageChatRender.xaml @@ -65,12 +65,14 @@ + Generate Mask: Generates a seperate video file as a mask for transparency. Click for a tutorial on how to use in Premiere and Vegas.(?)