Browse Source

use subtitles to display errors (#512)

* use subtitles to display errors

* fix margin calculation
pull/514/head
Jason Dove 5 years ago committed by GitHub
parent
commit
1e35e9a5b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 6
      ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs
  3. 28
      ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs
  4. 43
      ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs
  5. 30
      ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs
  6. 2
      ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs

1
CHANGELOG.md

@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix song page links in UI
- Show song artist in playout detail
- Include song artist and cover art in channel guide (xmltv)
- Use subtitles to display errors, which fixes many edge cases of unescaped characters
### Changed
- Randomly place song cover art on left or right side of screen

6
ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs

@ -193,7 +193,7 @@ namespace ErsatzTV.Application.Streaming.Queries @@ -193,7 +193,7 @@ namespace ErsatzTV.Application.Streaming.Queries
case UnableToLocatePlayoutItem:
if (channel.FFmpegProfile.Transcode)
{
Process errorProcess = _ffmpegProcessService.ForError(
Process errorProcess = await _ffmpegProcessService.ForError(
ffmpegPath,
channel,
maybeDuration,
@ -212,7 +212,7 @@ namespace ErsatzTV.Application.Streaming.Queries @@ -212,7 +212,7 @@ namespace ErsatzTV.Application.Streaming.Queries
case PlayoutItemDoesNotExistOnDisk:
if (channel.FFmpegProfile.Transcode)
{
Process errorProcess = _ffmpegProcessService.ForError(
Process errorProcess = await _ffmpegProcessService.ForError(
ffmpegPath,
channel,
maybeDuration,
@ -231,7 +231,7 @@ namespace ErsatzTV.Application.Streaming.Queries @@ -231,7 +231,7 @@ namespace ErsatzTV.Application.Streaming.Queries
default:
if (channel.FFmpegProfile.Transcode)
{
Process errorProcess = _ffmpegProcessService.ForError(
Process errorProcess = await _ffmpegProcessService.ForError(
ffmpegPath,
channel,
maybeDuration,

28
ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs

@ -108,31 +108,25 @@ namespace ErsatzTV.Core.FFmpeg @@ -108,31 +108,25 @@ namespace ErsatzTV.Core.FFmpeg
return this;
}
public FFmpegComplexFilterBuilder WithSubtitleFile(
MediaVersion videoVersion,
Option<string> subtitleFile)
public FFmpegComplexFilterBuilder WithSubtitleFile(Option<string> subtitleFile)
{
foreach (string file in subtitleFile)
{
string effectiveFile = file;
string fontsDir = FileSystemLayout.ResourcesCacheFolder;
if (videoVersion is FallbackMediaVersion or CoverArtMediaVersion)
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
string fontsDir = FileSystemLayout.ResourcesCacheFolder;
fontsDir = fontsDir
.Replace(@"\", @"/\")
.Replace(@":/", @"\\:/");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
fontsDir = fontsDir
.Replace(@"\", @"/\")
.Replace(@":/", @"\\:/");
effectiveFile = effectiveFile
.Replace(@"\", @"/\")
.Replace(@":/", @"\\:/");
}
_subtitle = $"subtitles={effectiveFile}:fontsdir={fontsDir}";
effectiveFile = effectiveFile
.Replace(@"\", @"/\")
.Replace(@":/", @"\\:/");
}
_subtitle = $"subtitles={effectiveFile}:fontsdir={fontsDir}";
}
return this;

43
ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs

@ -221,14 +221,9 @@ namespace ErsatzTV.Core.FFmpeg @@ -221,14 +221,9 @@ namespace ErsatzTV.Core.FFmpeg
return this;
}
public FFmpegProcessBuilder WithSubtitleFile(
MediaVersion videoVersion,
Option<string> subtitleFile)
public FFmpegProcessBuilder WithSubtitleFile(Option<string> subtitleFile)
{
_complexFilterBuilder = _complexFilterBuilder.WithSubtitleFile(
videoVersion,
subtitleFile);
_complexFilterBuilder = _complexFilterBuilder.WithSubtitleFile(subtitleFile);
return this;
}
@ -301,24 +296,6 @@ namespace ErsatzTV.Core.FFmpeg @@ -301,24 +296,6 @@ namespace ErsatzTV.Core.FFmpeg
return this;
}
public FFmpegProcessBuilder WithFiltergraph(string graph)
{
_arguments.Add("-vf");
_arguments.Add($"{graph}");
return this;
}
public FFmpegProcessBuilder WithFilterComplex(string filter, string finalVideo, string finalAudio)
{
_arguments.Add("-filter_complex");
_arguments.Add($"{filter}");
_arguments.Add("-map");
_arguments.Add(finalVideo);
_arguments.Add("-map");
_arguments.Add(finalAudio);
return this;
}
public FFmpegProcessBuilder WithConcat(string concatPlaylist)
{
_isConcat = true;
@ -372,22 +349,6 @@ namespace ErsatzTV.Core.FFmpeg @@ -372,22 +349,6 @@ namespace ErsatzTV.Core.FFmpeg
return this;
}
public FFmpegProcessBuilder WithErrorText(IDisplaySize desiredResolution, string text)
{
string fontPath = Path.Combine(FileSystemLayout.ResourcesCacheFolder, "Roboto-Regular.ttf");
var fontFile = $"fontfile={fontPath}";
const string FONT_COLOR = "fontcolor=white";
const string X = "x=(w-text_w)/2";
const string Y = "y=(h-text_h)/3*2";
string fontSize = text.Length > 80 ? "fontsize=30" : text.Length > 60 ? "fontsize=40" : "fontsize=60";
return WithFilterComplex(
$"[0:0]scale={desiredResolution.Width}:{desiredResolution.Height},drawtext={fontFile}:{fontSize}:{FONT_COLOR}:{X}:{Y}:text='{text}'[v]",
"[v]",
"1:a");
}
public FFmpegProcessBuilder WithDuration(TimeSpan duration)
{
_arguments.Add("-t");

30
ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs

@ -167,7 +167,7 @@ namespace ErsatzTV.Core.FFmpeg @@ -167,7 +167,7 @@ namespace ErsatzTV.Core.FFmpeg
}
}
public Process ForError(
public async Task<Process> ForError(
string ffmpegPath,
Channel channel,
Option<TimeSpan> duration,
@ -179,6 +179,22 @@ namespace ErsatzTV.Core.FFmpeg @@ -179,6 +179,22 @@ namespace ErsatzTV.Core.FFmpeg
IDisplaySize desiredResolution = channel.FFmpegProfile.Resolution;
var fontSize = (int)Math.Round(channel.FFmpegProfile.Resolution.Height / 20.0);
var margin = (int)Math.Round(channel.FFmpegProfile.Resolution.Height * 0.05);
string subtitleFile = await new SubtitleBuilder(_tempFilePool)
.WithResolution(desiredResolution)
.WithFontName("Roboto")
.WithFontSize(fontSize)
.WithAlignment(2)
.WithMarginV(margin)
.WithPrimaryColor("&HFFFFFF")
.WithFormattedContent(errorMessage.Replace(Environment.NewLine, "\\N"))
.BuildFile();
var videoStream = new MediaStream { Index = 0 };
var audioStream = new MediaStream { Index = 0 };
FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath, false, _logger)
.WithThreads(1)
.WithQuiet()
@ -187,12 +203,18 @@ namespace ErsatzTV.Core.FFmpeg @@ -187,12 +203,18 @@ namespace ErsatzTV.Core.FFmpeg
.WithLoopedImage(Path.Combine(FileSystemLayout.ResourcesCacheFolder, "background.png"))
.WithLibavfilter()
.WithInput("anullsrc")
.WithErrorText(desiredResolution, errorMessage)
.WithSubtitleFile(subtitleFile)
.WithFilterComplex(
videoStream,
audioStream,
Path.Combine(FileSystemLayout.ResourcesCacheFolder, "background.png"),
"fake-audio-path",
playbackSettings.VideoCodec)
.WithPixfmt("yuv420p")
.WithPlaybackArgs(playbackSettings)
.WithMetadata(channel, None);
duration.IfSome(d => builder = builder.WithDuration(d));
await duration.IfSomeAsync(d => builder = builder.WithDuration(d));
switch (channel.StreamingMode)
{
@ -292,7 +314,7 @@ namespace ErsatzTV.Core.FFmpeg @@ -292,7 +314,7 @@ namespace ErsatzTV.Core.FFmpeg
.WithFormatFlags(playbackSettings.FormatFlags)
.WithSongInput(videoPath, videoStream.Codec, videoStream.PixelFormat, boxBlur, randomColor)
.WithWatermark(watermarkOptions, channel.FFmpegProfile.Resolution)
.WithSubtitleFile(videoVersion, subtitleFile);
.WithSubtitleFile(subtitleFile);
foreach (IDisplaySize scaledSize in scalePlaybackSettings.ScaledSize)
{

2
ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs

@ -29,7 +29,7 @@ namespace ErsatzTV.Core.Interfaces.FFmpeg @@ -29,7 +29,7 @@ namespace ErsatzTV.Core.Interfaces.FFmpeg
TimeSpan inPoint,
TimeSpan outPoint);
Process ForError(
Task<Process> ForError(
string ffmpegPath,
Channel channel,
Option<TimeSpan> duration,

Loading…
Cancel
Save