Browse Source

fix remote stream durations in playouts (#2515)

pull/2517/head
Jason Dove 3 months ago committed by GitHub
parent
commit
144b3fe80b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 19
      ErsatzTV.Core/Extensions/MediaItemExtensions.cs
  3. 13
      ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs
  4. 15
      ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutFillerBuilder.cs
  5. 2
      ErsatzTV.Core/Scheduling/Engine/ISchedulingEngine.cs
  6. 15
      ErsatzTV.Core/Scheduling/Engine/SchedulingEngine.cs
  7. 25
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs
  8. 3
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs
  9. 3
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs
  10. 3
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerMultiple.cs
  11. 3
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerOne.cs
  12. 3
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutAllHandler.cs
  13. 11
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutContentHandler.cs
  14. 3
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutCountHandler.cs
  15. 3
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutDurationHandler.cs
  16. 3
      ErsatzTV/Controllers/Api/ScriptedScheduleController.cs

1
CHANGELOG.md

@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Fixed
- Fix NVIDIA startup errors on arm64
- Fix remote stream durations in playouts created using block, sequential or scripted schedules
### Changed
- Do not use graphics engine for single, permanent watermark

19
ErsatzTV.Core/Extensions/MediaItemExtensions.cs

@ -24,6 +24,25 @@ public static class MediaItemExtensions @@ -24,6 +24,25 @@ public static class MediaItemExtensions
return maybeDuration.Any(duration => duration == TimeSpan.Zero) ? Option<TimeSpan>.None : maybeDuration;
}
public static TimeSpan GetDurationForPlayout(this MediaItem mediaItem)
{
if (mediaItem is Image image)
{
return TimeSpan.FromSeconds(image.ImageMetadata.Head().DurationSeconds ?? Image.DefaultSeconds);
}
MediaVersion version = mediaItem.GetHeadVersion();
if (mediaItem is RemoteStream remoteStream)
{
return version.Duration == TimeSpan.Zero && remoteStream.Duration.HasValue
? remoteStream.Duration.Value
: version.Duration;
}
return version.Duration;
}
public static MediaVersion GetHeadVersion(this MediaItem mediaItem) =>
mediaItem switch
{

13
ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs

@ -192,7 +192,7 @@ public class BlockPlayoutBuilder( @@ -192,7 +192,7 @@ public class BlockPlayoutBuilder(
mediaItem.Id,
PlayoutBuilder.DisplayTitle(mediaItem));
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
// item will never fit in block
var blockDuration = TimeSpan.FromMinutes(effectiveBlock.Block.Minutes);
@ -439,15 +439,4 @@ public class BlockPlayoutBuilder( @@ -439,15 +439,4 @@ public class BlockPlayoutBuilder(
return result;
}
private static TimeSpan DurationForMediaItem(MediaItem mediaItem)
{
if (mediaItem is Image image)
{
return TimeSpan.FromSeconds(image.ImageMetadata.Head().DurationSeconds ?? Image.DefaultSeconds);
}
MediaVersion version = mediaItem.GetHeadVersion();
return version.Duration;
}
}

15
ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutFillerBuilder.cs

@ -203,7 +203,7 @@ public class BlockPlayoutFillerBuilder( @@ -203,7 +203,7 @@ public class BlockPlayoutFillerBuilder(
{
foreach (MediaItem mediaItem in enumerator.Current)
{
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
var filler = new PlayoutItem
{
@ -348,7 +348,7 @@ public class BlockPlayoutFillerBuilder( @@ -348,7 +348,7 @@ public class BlockPlayoutFillerBuilder(
{
foreach (MediaItem mediaItem in enumerator.Current)
{
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
// add filler from deco to unscheduled period
var filler = new PlayoutItem
@ -528,16 +528,5 @@ public class BlockPlayoutFillerBuilder( @@ -528,16 +528,5 @@ public class BlockPlayoutFillerBuilder(
}
}
private static TimeSpan DurationForMediaItem(MediaItem mediaItem)
{
if (mediaItem is Image image)
{
return TimeSpan.FromSeconds(image.ImageMetadata.Head().DurationSeconds ?? Image.DefaultSeconds);
}
MediaVersion version = mediaItem.GetHeadVersion();
return version.Duration;
}
private record ItemAndHistory(PlayoutItem PlayoutItem, PlayoutHistory History);
}

2
ErsatzTV.Core/Scheduling/Engine/ISchedulingEngine.cs

@ -143,6 +143,4 @@ public interface ISchedulingEngine @@ -143,6 +143,4 @@ public interface ISchedulingEngine
PlayoutAnchor GetAnchor();
ISchedulingEngineState GetState();
TimeSpan DurationForMediaItem(MediaItem mediaItem);
}

15
ErsatzTV.Core/Scheduling/Engine/SchedulingEngine.cs

@ -725,7 +725,7 @@ public class SchedulingEngine( @@ -725,7 +725,7 @@ public class SchedulingEngine(
foreach (MediaItem mediaItem in enumeratorDetails.Enumerator.Current)
{
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
var playoutItem = new PlayoutItem
{
@ -906,7 +906,7 @@ public class SchedulingEngine( @@ -906,7 +906,7 @@ public class SchedulingEngine(
foreach (MediaItem mediaItem in enumeratorDetails.Enumerator.Current)
{
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
// create a playout item
var playoutItem = new PlayoutItem
@ -1368,17 +1368,6 @@ public class SchedulingEngine( @@ -1368,17 +1368,6 @@ public class SchedulingEngine(
}
}
public TimeSpan DurationForMediaItem(MediaItem mediaItem)
{
if (mediaItem is Image image)
{
return TimeSpan.FromSeconds(image.ImageMetadata.Head().DurationSeconds ?? Image.DefaultSeconds);
}
MediaVersion version = mediaItem.GetHeadVersion();
return version.Duration;
}
private List<PlayoutHistory> GetHistoryForItem(
EnumeratorDetails enumeratorDetails,
PlayoutItem playoutItem,

25
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs

@ -144,7 +144,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe @@ -144,7 +144,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
{
MediaItem mediaItem = enumerator.Current.ValueUnsafe();
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
TimeSpan inPoint = InPointForMediaItem(mediaItem);
if (nextState.CurrentTime + itemDuration > nextItemStart)
@ -230,25 +230,6 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe @@ -230,25 +230,6 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
return Tuple(nextState, newItems);
}
protected static TimeSpan DurationForMediaItem(MediaItem mediaItem)
{
if (mediaItem is Image image)
{
return TimeSpan.FromSeconds(image.ImageMetadata.Head().DurationSeconds ?? Image.DefaultSeconds);
}
MediaVersion version = mediaItem.GetHeadVersion();
if (mediaItem is RemoteStream remoteStream)
{
return version.Duration == TimeSpan.Zero && remoteStream.Duration.HasValue
? remoteStream.Duration.Value
: version.Duration;
}
return version.Duration;
}
private static TimeSpan InPointForMediaItem(MediaItem mediaItem) =>
mediaItem switch
{
@ -784,7 +765,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe @@ -784,7 +765,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
{
foreach (MediaItem mediaItem in enumerator.Current)
{
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
TimeSpan inPoint = InPointForMediaItem(mediaItem);
var playoutItem = new PlayoutItem
@ -828,7 +809,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe @@ -828,7 +809,7 @@ public abstract class PlayoutModeSchedulerBase<T> : IPlayoutModeScheduler<T> whe
{
foreach (MediaItem mediaItem in enumerator.Current)
{
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
TimeSpan inPoint = InPointForMediaItem(mediaItem);
if (remainingToFill - itemDuration >= TimeSpan.Zero)

3
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Scheduling;
using LanguageExt.UnsafeValueAccess;
using Microsoft.Extensions.Logging;
@ -74,7 +75,7 @@ public class PlayoutModeSchedulerDuration : PlayoutModeSchedulerBase<ProgramSche @@ -74,7 +75,7 @@ public class PlayoutModeSchedulerDuration : PlayoutModeSchedulerBase<ProgramSche
durationUntil = nextState.DurationFinish;
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
List<MediaChapter> itemChapters = ChaptersForMediaItem(mediaItem);
if (itemDuration > scheduleItem.PlayoutDuration)

3
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Scheduling;
using LanguageExt.UnsafeValueAccess;
using Microsoft.Extensions.Logging;
@ -46,7 +47,7 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul @@ -46,7 +47,7 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul
break;
}
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
// never block scheduling when there is only one schedule item (with fixed start and flood)
DateTimeOffset peekScheduleItemStart =

3
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerMultiple.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Scheduling;
using LanguageExt.UnsafeValueAccess;
using Microsoft.Extensions.Logging;
@ -86,7 +87,7 @@ public class PlayoutModeSchedulerMultiple : PlayoutModeSchedulerBase<ProgramSche @@ -86,7 +87,7 @@ public class PlayoutModeSchedulerMultiple : PlayoutModeSchedulerBase<ProgramSche
// find when we should start this item, based on the current time
DateTimeOffset itemStartTime = GetStartTimeAfter(nextState, scheduleItem, Option<ILogger>.Some(Logger));
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
List<MediaChapter> itemChapters = ChaptersForMediaItem(mediaItem);
var playoutItem = new PlayoutItem

3
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerOne.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Scheduling;
using Microsoft.Extensions.Logging;
@ -34,7 +35,7 @@ public class PlayoutModeSchedulerOne : PlayoutModeSchedulerBase<ProgramScheduleI @@ -34,7 +35,7 @@ public class PlayoutModeSchedulerOne : PlayoutModeSchedulerBase<ProgramScheduleI
break;
}
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
List<MediaChapter> itemChapters = ChaptersForMediaItem(mediaItem);
var playoutItem = new PlayoutItem

3
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutAllHandler.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Domain.Scheduling;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Scheduling;
using ErsatzTV.Core.Scheduling.YamlScheduling.Models;
using Microsoft.Extensions.Logging;
@ -41,7 +42,7 @@ public class YamlPlayoutAllHandler(EnumeratorCache enumeratorCache) : YamlPlayou @@ -41,7 +42,7 @@ public class YamlPlayoutAllHandler(EnumeratorCache enumeratorCache) : YamlPlayou
foreach (MediaItem mediaItem in enumerator.Current)
{
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
// create a playout item
var playoutItem = new PlayoutItem

11
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutContentHandler.cs

@ -137,17 +137,6 @@ public abstract class YamlPlayoutContentHandler(EnumeratorCache enumeratorCache) @@ -137,17 +137,6 @@ public abstract class YamlPlayoutContentHandler(EnumeratorCache enumeratorCache)
return result;
}
protected static TimeSpan DurationForMediaItem(MediaItem mediaItem)
{
if (mediaItem is Image image)
{
return TimeSpan.FromSeconds(image.ImageMetadata.Head().DurationSeconds ?? Image.DefaultSeconds);
}
MediaVersion version = mediaItem.GetHeadVersion();
return version.Duration;
}
protected static FillerKind GetFillerKind(YamlPlayoutInstruction instruction, YamlPlayoutContext context)
{
if (!string.IsNullOrWhiteSpace(instruction.FillerKind) &&

3
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutCountHandler.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Domain.Scheduling;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Scheduling;
using ErsatzTV.Core.Scheduling.YamlScheduling.Models;
using Microsoft.Extensions.Logging;
@ -66,7 +67,7 @@ public class YamlPlayoutCountHandler(EnumeratorCache enumeratorCache) : YamlPlay @@ -66,7 +67,7 @@ public class YamlPlayoutCountHandler(EnumeratorCache enumeratorCache) : YamlPlay
foreach (MediaItem mediaItem in enumerator.Current)
{
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
// create a playout item
var playoutItem = new PlayoutItem

3
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutDurationHandler.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Domain.Scheduling;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Scheduling;
using ErsatzTV.Core.Scheduling.YamlScheduling.Models;
using Microsoft.Extensions.Logging;
@ -110,7 +111,7 @@ public class YamlPlayoutDurationHandler(EnumeratorCache enumeratorCache) : YamlP @@ -110,7 +111,7 @@ public class YamlPlayoutDurationHandler(EnumeratorCache enumeratorCache) : YamlP
foreach (MediaItem mediaItem in enumerator.Current)
{
TimeSpan itemDuration = DurationForMediaItem(mediaItem);
TimeSpan itemDuration = mediaItem.GetDurationForPlayout();
var playoutItem = new PlayoutItem
{

3
ErsatzTV/Controllers/Api/ScriptedScheduleController.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using ErsatzTV.Core.Api.ScriptedPlayout;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Scheduling;
using ErsatzTV.Core.Scheduling.Engine;
using Microsoft.AspNetCore.Mvc;
@ -426,7 +427,7 @@ public class ScriptedScheduleController(IScriptedPlayoutBuilderService scriptedP @@ -426,7 +427,7 @@ public class ScriptedScheduleController(IScriptedPlayoutBuilderService scriptedP
return new PeekItemDuration
{
Content = content,
Milliseconds = (long)engine.DurationForMediaItem(mediaItem).TotalMilliseconds
Milliseconds = (long)mediaItem.GetDurationForPlayout().TotalMilliseconds
};
}

Loading…
Cancel
Save