From 919325033dff8a9901808480e24f0577be5a22cb Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Fri, 26 Nov 2021 21:39:10 -0600 Subject: [PATCH] use subtitles instead of drawtext for songs (#510) --- ...layoutItemProcessByChannelNumberHandler.cs | 11 +++-- .../FFmpeg/FFmpegComplexFilterBuilder.cs | 41 ++++++++++--------- ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs | 8 ++-- ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs | 4 +- ErsatzTV.Core/FFmpeg/TempFileCategory.cs | 2 +- .../FFmpeg/IFFmpegProcessService.cs | 2 +- 6 files changed, 36 insertions(+), 32 deletions(-) diff --git a/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs b/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs index 08689be2f..042f18c13 100644 --- a/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs +++ b/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs @@ -142,7 +142,7 @@ namespace ErsatzTV.Application.Streaming.Queries if (playoutItemWithPath.PlayoutItem.MediaItem is Song song) { - Option drawtextFile = None; + Option subtitleFile = None; videoVersion = new FallbackMediaVersion { @@ -175,10 +175,13 @@ namespace ErsatzTV.Application.Streaming.Queries // use thumbnail (cover art) if present foreach (SongMetadata metadata in song.SongMetadata) { - string fileName = _tempFilePool.GetNextTempFile(TempFileCategory.DrawText); - drawtextFile = fileName; + string fileName = _tempFilePool.GetNextTempFile(TempFileCategory.Subtitle); + subtitleFile = fileName; var sb = new StringBuilder(); + sb.AppendLine("1"); + sb.AppendLine("00:00:00,000 --> 99:99:99,999"); + if (!string.IsNullOrWhiteSpace(metadata.Artist)) { sb.AppendLine(metadata.Artist); @@ -231,7 +234,7 @@ namespace ErsatzTV.Application.Streaming.Queries Either maybeSongImage = await _ffmpegProcessService.GenerateSongImage( ffmpegPath, - drawtextFile, + subtitleFile, channel, maybeGlobalWatermark, videoVersion, diff --git a/ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs b/ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs index 965b79971..7e4592a1c 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs @@ -28,7 +28,7 @@ namespace ErsatzTV.Core.FFmpeg private Option _watermarkIndex; private string _pixelFormat; private string _videoEncoder; - private Option _drawtext; + private Option _subtitle; public FFmpegComplexFilterBuilder WithHardwareAcceleration(HardwareAccelerationKind hardwareAccelerationKind) { @@ -97,21 +97,21 @@ namespace ErsatzTV.Core.FFmpeg return this; } - public FFmpegComplexFilterBuilder WithDrawtextFile( + public FFmpegComplexFilterBuilder WithSubtitleFile( MediaVersion videoVersion, - Option drawtextFile) + Option subtitleFile) { - foreach (string file in drawtextFile) + foreach (string file in subtitleFile) { string effectiveFile = file; if (videoVersion is FallbackMediaVersion or CoverArtMediaVersion) { - string fontPath = Path.Combine(FileSystemLayout.ResourcesCacheFolder, "OPTIKabel-Heavy.otf"); + string fontsDir = FileSystemLayout.ResourcesCacheFolder; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - fontPath = fontPath + fontsDir = fontsDir .Replace(@"\", @"/\") .Replace(@":/", @"\\:/"); @@ -120,27 +120,31 @@ namespace ErsatzTV.Core.FFmpeg .Replace(@":/", @"\\:/"); } - var horizontalMarginPercent = 3; + var leftMarginPercent = 3; + var rightMarginPercent = 3; const int VERTICAL_MARGIN_PERCENT = 5; foreach (ChannelWatermark watermark in _watermark) { - horizontalMarginPercent = watermark.HorizontalMarginPercent; + leftMarginPercent = rightMarginPercent = watermark.HorizontalMarginPercent; switch (watermark.Location) { case ChannelWatermarkLocation.BottomLeft: - horizontalMarginPercent += watermark.WidthPercent + watermark.HorizontalMarginPercent; + leftMarginPercent += watermark.WidthPercent + watermark.HorizontalMarginPercent; + break; + case ChannelWatermarkLocation.BottomRight: + rightMarginPercent += watermark.WidthPercent + watermark.HorizontalMarginPercent; break; } } - double horizontalMargin = Math.Round(horizontalMarginPercent / 100.0 * _resolution.Width); + double leftMargin = Math.Round(leftMarginPercent / 100.0 * _resolution.Width); + double rightMargin = Math.Round(rightMarginPercent / 100.0 * _resolution.Width); double verticalMargin = Math.Round(VERTICAL_MARGIN_PERCENT / 100.0 * _resolution.Height); - - var location = $"x={horizontalMargin}:y=H-text_h-{verticalMargin}"; - - _drawtext = - $"drawtext=fontfile={fontPath}:textfile={effectiveFile}:{location}:fontsize=(h/25):fontcolor=white:line_spacing=7"; + double fontSize = Math.Round(_resolution.Height / 20.0); + + _subtitle = + $"subtitles={effectiveFile}:fontsdir={fontsDir}:force_style='PlayResX={_resolution.Width},PlayResY={_resolution.Height},Fontname=OPTIKabel-Heavy,Fontsize={fontSize},PrimaryColour=&HFFFFFF,OutlineColour=&H555555,Alignment=0,MarginR={rightMargin},MarginL={leftMargin},MarginV={verticalMargin},Shadow=3'"; } } @@ -308,9 +312,6 @@ namespace ErsatzTV.Core.FFmpeg $"palettegen=max_colors=8,crop=1:1:{next}:0,scale={_resolution.Width}:{_resolution.Height},setsar=1:1"; } - filter += - "[b];[b]split[b1][b2];[b1]format=rgba,geq=r=0:g=0:b=0:a=70[fg];[b2][fg]overlay=format=auto"; - videoFilterQueue.Add(filter); } @@ -365,9 +366,9 @@ namespace ErsatzTV.Core.FFmpeg _padToSize.IfSome(size => videoFilterQueue.Add($"pad={size.Width}:{size.Height}:(ow-iw)/2:(oh-ih)/2")); - foreach (string drawtext in _drawtext) + foreach (string subtitle in _subtitle) { - videoFilterQueue.Add(drawtext); + videoFilterQueue.Add(subtitle); } string outputPixelFormat = null; diff --git a/ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs b/ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs index 15876971d..381030b9f 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs @@ -221,13 +221,13 @@ namespace ErsatzTV.Core.FFmpeg return this; } - public FFmpegProcessBuilder WithDrawtextFile( + public FFmpegProcessBuilder WithSubtitleFile( MediaVersion videoVersion, - Option drawtextFile) + Option subtitleFile) { - _complexFilterBuilder = _complexFilterBuilder.WithDrawtextFile( + _complexFilterBuilder = _complexFilterBuilder.WithSubtitleFile( videoVersion, - drawtextFile); + subtitleFile); return this; } diff --git a/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs b/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs index d402233d4..846069d8a 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs @@ -238,7 +238,7 @@ namespace ErsatzTV.Core.FFmpeg public async Task> GenerateSongImage( string ffmpegPath, - Option drawtextFile, + Option subtitleFile, Channel channel, Option globalWatermark, MediaVersion videoVersion, @@ -273,7 +273,7 @@ namespace ErsatzTV.Core.FFmpeg .WithFormatFlags(playbackSettings.FormatFlags) .WithSongInput(videoPath, videoStream.Codec, videoStream.PixelFormat) .WithWatermark(watermarkOptions, channel.FFmpegProfile.Resolution) - .WithDrawtextFile(videoVersion, drawtextFile); + .WithSubtitleFile(videoVersion, subtitleFile); foreach (IDisplaySize scaledSize in scalePlaybackSettings.ScaledSize) { diff --git a/ErsatzTV.Core/FFmpeg/TempFileCategory.cs b/ErsatzTV.Core/FFmpeg/TempFileCategory.cs index ea2a9e210..ef82c1ac8 100644 --- a/ErsatzTV.Core/FFmpeg/TempFileCategory.cs +++ b/ErsatzTV.Core/FFmpeg/TempFileCategory.cs @@ -2,7 +2,7 @@ { public enum TempFileCategory { - DrawText = 0, + Subtitle = 0, SongBackground = 1, CoverArt = 2 } diff --git a/ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs b/ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs index bca5e5b78..db5c04348 100644 --- a/ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs +++ b/ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs @@ -42,7 +42,7 @@ namespace ErsatzTV.Core.Interfaces.FFmpeg Task> GenerateSongImage( string ffmpegPath, - Option drawtextFile, + Option subtitleFile, Channel channel, Option globalWatermark, MediaVersion videoVersion,