use output duration flag (#49)
* re-enable output duration flag * calculate appropriate duration for offline image
This commit is contained in:
+6
-5
@@ -10,7 +10,6 @@ using ErsatzTV.Core.FFmpeg;
|
|||||||
using ErsatzTV.Core.Interfaces.Repositories;
|
using ErsatzTV.Core.Interfaces.Repositories;
|
||||||
using LanguageExt;
|
using LanguageExt;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using static LanguageExt.Prelude;
|
|
||||||
|
|
||||||
namespace ErsatzTV.Application.Streaming.Queries
|
namespace ErsatzTV.Application.Streaming.Queries
|
||||||
{
|
{
|
||||||
@@ -69,18 +68,20 @@ namespace ErsatzTV.Application.Streaming.Queries
|
|||||||
playoutItem.StartOffset,
|
playoutItem.StartOffset,
|
||||||
now);
|
now);
|
||||||
},
|
},
|
||||||
() =>
|
async () =>
|
||||||
{
|
{
|
||||||
if (channel.FFmpegProfile.Transcode)
|
if (channel.FFmpegProfile.Transcode)
|
||||||
{
|
{
|
||||||
return Right<BaseError, Process>(_ffmpegProcessService.ForOfflineImage(ffmpegPath, channel))
|
Option<TimeSpan> maybeDuration = await _playoutRepository.GetNextItemStart(channel.Id, now)
|
||||||
.AsTask();
|
.MapT(nextStart => nextStart - now);
|
||||||
|
|
||||||
|
return _ffmpegProcessService.ForOfflineImage(ffmpegPath, channel, maybeDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
var message =
|
var message =
|
||||||
$"Unable to locate playout item for channel {channel.Number}; offline image is unavailable because transcoding is disabled in ffmpeg profile '{channel.FFmpegProfile.Name}'";
|
$"Unable to locate playout item for channel {channel.Number}; offline image is unavailable because transcoding is disabled in ffmpeg profile '{channel.FFmpegProfile.Name}'";
|
||||||
|
|
||||||
return Left<BaseError, Process>(BaseError.New(message)).AsTask();
|
return BaseError.New(message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -227,10 +227,12 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
"1:a");
|
"1:a");
|
||||||
}
|
}
|
||||||
|
|
||||||
public FFmpegProcessBuilder WithDuration(TimeSpan duration) =>
|
public FFmpegProcessBuilder WithDuration(TimeSpan duration)
|
||||||
// _arguments.Add("-t");
|
{
|
||||||
// _arguments.Add($"{duration:c}");
|
_arguments.Add("-t");
|
||||||
this;
|
_arguments.Add($"{duration:c}");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public FFmpegProcessBuilder WithFormat(string format)
|
public FFmpegProcessBuilder WithFormat(string format)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
using ErsatzTV.Core.Interfaces.FFmpeg;
|
using ErsatzTV.Core.Interfaces.FFmpeg;
|
||||||
|
using LanguageExt;
|
||||||
|
|
||||||
namespace ErsatzTV.Core.FFmpeg
|
namespace ErsatzTV.Core.FFmpeg
|
||||||
{
|
{
|
||||||
@@ -83,14 +84,14 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Process ForOfflineImage(string ffmpegPath, Channel channel)
|
public Process ForOfflineImage(string ffmpegPath, Channel channel, Option<TimeSpan> duration)
|
||||||
{
|
{
|
||||||
FFmpegPlaybackSettings playbackSettings =
|
FFmpegPlaybackSettings playbackSettings =
|
||||||
_playbackSettingsCalculator.CalculateErrorSettings(channel.FFmpegProfile);
|
_playbackSettingsCalculator.CalculateErrorSettings(channel.FFmpegProfile);
|
||||||
|
|
||||||
IDisplaySize desiredResolution = channel.FFmpegProfile.Resolution;
|
IDisplaySize desiredResolution = channel.FFmpegProfile.Resolution;
|
||||||
|
|
||||||
return new FFmpegProcessBuilder(ffmpegPath)
|
FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath)
|
||||||
.WithThreads(1)
|
.WithThreads(1)
|
||||||
.WithQuiet()
|
.WithQuiet()
|
||||||
.WithFormatFlags(playbackSettings.FormatFlags)
|
.WithFormatFlags(playbackSettings.FormatFlags)
|
||||||
@@ -102,10 +103,11 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
.WithPixfmt("yuv420p")
|
.WithPixfmt("yuv420p")
|
||||||
.WithPlaybackArgs(playbackSettings)
|
.WithPlaybackArgs(playbackSettings)
|
||||||
.WithMetadata(channel)
|
.WithMetadata(channel)
|
||||||
.WithFormat("mpegts")
|
.WithFormat("mpegts");
|
||||||
.WithDuration(TimeSpan.FromSeconds(10)) // TODO: figure out when we're back online
|
|
||||||
.WithPipe()
|
duration.IfSome(d => builder = builder.WithDuration(d));
|
||||||
.Build();
|
|
||||||
|
return builder.WithPipe().Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Process ConcatChannel(string ffmpegPath, Channel channel, string scheme, string host)
|
public Process ConcatChannel(string ffmpegPath, Channel channel, string scheme, string host)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace ErsatzTV.Core.Interfaces.Repositories
|
|||||||
Task<Option<Playout>> Get(int id);
|
Task<Option<Playout>> Get(int id);
|
||||||
Task<Option<Playout>> GetFull(int id);
|
Task<Option<Playout>> GetFull(int id);
|
||||||
Task<Option<PlayoutItem>> GetPlayoutItem(int channelId, DateTimeOffset now);
|
Task<Option<PlayoutItem>> GetPlayoutItem(int channelId, DateTimeOffset now);
|
||||||
|
Task<Option<DateTimeOffset>> GetNextItemStart(int channelId, DateTimeOffset now);
|
||||||
Task<List<PlayoutItem>> GetPlayoutItems(int playoutId);
|
Task<List<PlayoutItem>> GetPlayoutItems(int playoutId);
|
||||||
Task<List<Playout>> GetAll();
|
Task<List<Playout>> GetAll();
|
||||||
Task Update(Playout playout);
|
Task Update(Playout playout);
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
|||||||
.OrderBy(p => p.Id) // https://github.com/dotnet/efcore/issues/22579#issuecomment-694772289
|
.OrderBy(p => p.Id) // https://github.com/dotnet/efcore/issues/22579#issuecomment-694772289
|
||||||
.SingleOrDefaultAsync(p => p.Id == id);
|
.SingleOrDefaultAsync(p => p.Id == id);
|
||||||
|
|
||||||
public async Task<Option<PlayoutItem>> GetPlayoutItem(int channelId, DateTimeOffset now) =>
|
public Task<Option<PlayoutItem>> GetPlayoutItem(int channelId, DateTimeOffset now) =>
|
||||||
await _dbContext.PlayoutItems
|
_dbContext.PlayoutItems
|
||||||
.Where(pi => pi.Playout.ChannelId == channelId)
|
.Where(pi => pi.Playout.ChannelId == channelId)
|
||||||
.Where(pi => pi.Start <= now.UtcDateTime && pi.Finish > now.UtcDateTime)
|
.Where(pi => pi.Start <= now.UtcDateTime && pi.Finish > now.UtcDateTime)
|
||||||
.Include(i => i.MediaItem)
|
.Include(i => i.MediaItem)
|
||||||
@@ -55,7 +55,17 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
|||||||
.ThenInclude(mi => (mi as Movie).MediaVersions)
|
.ThenInclude(mi => (mi as Movie).MediaVersions)
|
||||||
.ThenInclude(mv => mv.MediaFiles)
|
.ThenInclude(mv => mv.MediaFiles)
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.SingleOrDefaultAsync();
|
.SingleOrDefaultAsync()
|
||||||
|
.Map(Optional);
|
||||||
|
|
||||||
|
public Task<Option<DateTimeOffset>> GetNextItemStart(int channelId, DateTimeOffset now) =>
|
||||||
|
_dbContext.PlayoutItems
|
||||||
|
.Where(pi => pi.Playout.ChannelId == channelId)
|
||||||
|
.Where(pi => pi.Finish > now.UtcDateTime)
|
||||||
|
.OrderBy(pi => pi.Finish)
|
||||||
|
.FirstOrDefaultAsync()
|
||||||
|
.Map(Optional)
|
||||||
|
.MapT(pi => pi.StartOffset);
|
||||||
|
|
||||||
public Task<List<PlayoutItem>> GetPlayoutItems(int playoutId) =>
|
public Task<List<PlayoutItem>> GetPlayoutItems(int playoutId) =>
|
||||||
_dbContext.PlayoutItems
|
_dbContext.PlayoutItems
|
||||||
|
|||||||
Reference in New Issue
Block a user