Browse Source

properly fix infinite playout build loop (#952)

pull/953/head
Jason Dove 3 years ago committed by GitHub
parent
commit
df281758b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      CHANGELOG.md
  2. 17
      ErsatzTV.Core.Tests/Scheduling/PlayoutBuilderTests.cs
  3. 92
      ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerFloodTests.cs
  4. 10
      ErsatzTV.Core/Scheduling/PlayoutBuilder.cs
  5. 3
      ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs

3
CHANGELOG.md

@ -12,7 +12,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -12,7 +12,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix bug with XMLTV that caused some filler to display with primary content details
- Multiple fixes for content scaling with `Nvidia`, `Qsv` and `Vaapi` accelerations
- Properly scale image-based subtitles
- Abort when an infinite playout building loop is detected; proper bug fix will be released soon
- Fix bug where a schedule containing a single item (fixed start and flood) would never finish building a playout
- Logic was also added to detect infinite playout build loops in the future and stop them
### Added
- Add `Preferred Audio Title` feature

17
ErsatzTV.Core.Tests/Scheduling/PlayoutBuilderTests.cs

@ -515,6 +515,7 @@ public class PlayoutBuilderTests @@ -515,6 +515,7 @@ public class PlayoutBuilderTests
{
new ProgramScheduleItemFlood
{
Id = 1,
Index = 1,
Collection = floodCollection,
CollectionId = floodCollection.Id,
@ -523,6 +524,7 @@ public class PlayoutBuilderTests @@ -523,6 +524,7 @@ public class PlayoutBuilderTests
},
new ProgramScheduleItemOne
{
Id = 2,
Index = 2,
Collection = fixedCollection,
CollectionId = fixedCollection.Id,
@ -605,6 +607,7 @@ public class PlayoutBuilderTests @@ -605,6 +607,7 @@ public class PlayoutBuilderTests
{
new ProgramScheduleItemFlood
{
Id = 1,
Index = 1,
Collection = floodCollection,
CollectionId = floodCollection.Id,
@ -613,6 +616,7 @@ public class PlayoutBuilderTests @@ -613,6 +616,7 @@ public class PlayoutBuilderTests
},
new ProgramScheduleItemOne
{
Id = 2,
Index = 2,
Collection = fixedCollection,
CollectionId = fixedCollection.Id,
@ -742,6 +746,7 @@ public class PlayoutBuilderTests @@ -742,6 +746,7 @@ public class PlayoutBuilderTests
{
new ProgramScheduleItemFlood
{
Id = 1,
Index = 1,
Collection = floodCollection,
CollectionId = floodCollection.Id,
@ -750,6 +755,7 @@ public class PlayoutBuilderTests @@ -750,6 +755,7 @@ public class PlayoutBuilderTests
},
new ProgramScheduleItemMultiple
{
Id = 2,
Index = 2,
Collection = fixedCollection,
CollectionId = fixedCollection.Id,
@ -839,6 +845,7 @@ public class PlayoutBuilderTests @@ -839,6 +845,7 @@ public class PlayoutBuilderTests
{
new ProgramScheduleItemFlood
{
Id = 1,
Index = 1,
Collection = floodCollection,
CollectionId = floodCollection.Id,
@ -847,6 +854,7 @@ public class PlayoutBuilderTests @@ -847,6 +854,7 @@ public class PlayoutBuilderTests
},
new ProgramScheduleItemOne
{
Id = 2,
Index = 2,
Collection = fixedCollection,
CollectionId = fixedCollection.Id,
@ -934,6 +942,7 @@ public class PlayoutBuilderTests @@ -934,6 +942,7 @@ public class PlayoutBuilderTests
{
new ProgramScheduleItemFlood
{
Id = 1,
Index = 1,
Collection = floodCollection,
CollectionId = floodCollection.Id,
@ -942,6 +951,7 @@ public class PlayoutBuilderTests @@ -942,6 +951,7 @@ public class PlayoutBuilderTests
},
new ProgramScheduleItemOne
{
Id = 2,
Index = 2,
Collection = fixedCollection,
CollectionId = fixedCollection.Id,
@ -1040,6 +1050,7 @@ public class PlayoutBuilderTests @@ -1040,6 +1050,7 @@ public class PlayoutBuilderTests
{
new ProgramScheduleItemFlood
{
Id = 1,
Index = 1,
Collection = floodCollection,
CollectionId = floodCollection.Id,
@ -1048,6 +1059,7 @@ public class PlayoutBuilderTests @@ -1048,6 +1059,7 @@ public class PlayoutBuilderTests
},
new ProgramScheduleItemDuration
{
Id = 2,
Index = 2,
Collection = fixedCollection,
CollectionId = fixedCollection.Id,
@ -1141,6 +1153,7 @@ public class PlayoutBuilderTests @@ -1141,6 +1153,7 @@ public class PlayoutBuilderTests
{
new ProgramScheduleItemMultiple
{
Id = 1,
Index = 1,
Collection = multipleCollection,
CollectionId = multipleCollection.Id,
@ -1150,6 +1163,7 @@ public class PlayoutBuilderTests @@ -1150,6 +1163,7 @@ public class PlayoutBuilderTests
},
new ProgramScheduleItemDuration
{
Id = 2,
Index = 2,
Collection = dynamicCollection,
CollectionId = dynamicCollection.Id,
@ -2329,6 +2343,7 @@ public class PlayoutBuilderTests @@ -2329,6 +2343,7 @@ public class PlayoutBuilderTests
{
new ProgramScheduleItemFlood
{
Id = 1,
Index = 1,
Collection = floodCollection,
CollectionId = floodCollection.Id,
@ -2337,6 +2352,7 @@ public class PlayoutBuilderTests @@ -2337,6 +2352,7 @@ public class PlayoutBuilderTests
},
new ProgramScheduleItemOne
{
Id = 2,
Index = 2,
Collection = fixedCollection,
CollectionId = fixedCollection.Id,
@ -2622,6 +2638,7 @@ public class PlayoutBuilderTests @@ -2622,6 +2638,7 @@ public class PlayoutBuilderTests
private static ProgramScheduleItem Flood(Collection mediaCollection, PlaybackOrder playbackOrder) =>
new ProgramScheduleItemFlood
{
Id = 1,
Index = 1,
Collection = mediaCollection,
CollectionId = mediaCollection.Id,

92
ErsatzTV.Core.Tests/Scheduling/PlayoutModeSchedulerFloodTests.cs

@ -85,6 +85,98 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase @@ -85,6 +85,98 @@ public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
playoutItems[2].FillerKind.Should().Be(FillerKind.None);
playoutItems[2].CustomTitle.Should().Be("CustomTitle");
}
[Test]
public void Should_Schedule_Single_Item_Fixed_Start_Flood()
{
Collection collectionOne = TwoItemCollection(1, 2, TimeSpan.FromHours(1));
var scheduleItem = new ProgramScheduleItemFlood
{
Id = 1,
Index = 1,
Collection = collectionOne,
CollectionId = collectionOne.Id,
StartTime = TimeSpan.Zero,
PlaybackOrder = PlaybackOrder.Chronological,
TailFiller = null,
FallbackFiller = null,
CustomTitle = "CustomTitle"
};
var enumerator = new ChronologicalMediaCollectionEnumerator(
collectionOne.MediaItems,
new CollectionEnumeratorState());
var sortedScheduleItems = new List<ProgramScheduleItem>
{
scheduleItem
};
var scheduleItemsEnumerator = new OrderedScheduleItemsEnumerator(
sortedScheduleItems,
new CollectionEnumeratorState());
PlayoutBuilderState startState = StartState(scheduleItemsEnumerator);
var scheduler = new PlayoutModeSchedulerFlood(new Mock<ILogger>().Object);
(PlayoutBuilderState playoutBuilderState, List<PlayoutItem> playoutItems) = scheduler.Schedule(
startState,
CollectionEnumerators(scheduleItem, enumerator),
scheduleItem,
scheduleItem,
HardStop(scheduleItemsEnumerator));
playoutBuilderState.CurrentTime.Should().Be(startState.CurrentTime.AddHours(6));
playoutItems.Last().FinishOffset.Should().Be(playoutBuilderState.CurrentTime);
playoutBuilderState.NextGuideGroup.Should().Be(2); // one guide group here because of custom title
playoutBuilderState.DurationFinish.IsNone.Should().BeTrue();
playoutBuilderState.InFlood.Should().BeTrue();
playoutBuilderState.MultipleRemaining.IsNone.Should().BeTrue();
playoutBuilderState.InDurationFiller.Should().BeFalse();
playoutBuilderState.ScheduleItemsEnumerator.State.Index.Should().Be(0);
enumerator.State.Index.Should().Be(0);
playoutItems.Count.Should().Be(6);
playoutItems[0].MediaItemId.Should().Be(1);
playoutItems[0].StartOffset.Should().Be(startState.CurrentTime);
playoutItems[0].GuideGroup.Should().Be(1);
playoutItems[0].FillerKind.Should().Be(FillerKind.None);
playoutItems[0].CustomTitle.Should().Be("CustomTitle");
playoutItems[1].MediaItemId.Should().Be(2);
playoutItems[1].StartOffset.Should().Be(startState.CurrentTime.AddHours(1));
playoutItems[1].GuideGroup.Should().Be(1);
playoutItems[1].FillerKind.Should().Be(FillerKind.None);
playoutItems[1].CustomTitle.Should().Be("CustomTitle");
playoutItems[2].MediaItemId.Should().Be(1);
playoutItems[2].StartOffset.Should().Be(startState.CurrentTime.AddHours(2));
playoutItems[2].GuideGroup.Should().Be(1);
playoutItems[2].FillerKind.Should().Be(FillerKind.None);
playoutItems[2].CustomTitle.Should().Be("CustomTitle");
playoutItems[3].MediaItemId.Should().Be(2);
playoutItems[3].StartOffset.Should().Be(startState.CurrentTime.AddHours(3));
playoutItems[3].GuideGroup.Should().Be(1);
playoutItems[3].FillerKind.Should().Be(FillerKind.None);
playoutItems[3].CustomTitle.Should().Be("CustomTitle");
playoutItems[4].MediaItemId.Should().Be(1);
playoutItems[4].StartOffset.Should().Be(startState.CurrentTime.AddHours(4));
playoutItems[4].GuideGroup.Should().Be(1);
playoutItems[4].FillerKind.Should().Be(FillerKind.None);
playoutItems[4].CustomTitle.Should().Be("CustomTitle");
playoutItems[5].MediaItemId.Should().Be(2);
playoutItems[5].StartOffset.Should().Be(startState.CurrentTime.AddHours(5));
playoutItems[5].GuideGroup.Should().Be(1);
playoutItems[5].FillerKind.Should().Be(FillerKind.None);
playoutItems[5].CustomTitle.Should().Be("CustomTitle");
}
[Test]
public void Should_Fill_Exactly_To_Next_Schedule_Item_Flood()

10
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

@ -423,21 +423,21 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -423,21 +423,21 @@ public class PlayoutBuilder : IPlayoutBuilder
var schedulerDuration = new PlayoutModeSchedulerDuration(_logger);
var schedulerFlood = new PlayoutModeSchedulerFlood(_logger);
var timeHash = new Dictionary<DateTimeOffset, int>();
var timeCount = new Dictionary<DateTimeOffset, int>();
// loop until we're done filling the desired amount of time
while (playoutBuilderState.CurrentTime < playoutFinish)
{
if (timeHash.TryGetValue(playoutBuilderState.CurrentTime, out int count))
if (timeCount.TryGetValue(playoutBuilderState.CurrentTime, out int count))
{
timeHash[playoutBuilderState.CurrentTime] = count + 1;
timeCount[playoutBuilderState.CurrentTime] = count + 1;
}
else
{
timeHash[playoutBuilderState.CurrentTime] = 1;
timeCount[playoutBuilderState.CurrentTime] = 1;
}
if (timeHash[playoutBuilderState.CurrentTime] == 6)
if (timeCount[playoutBuilderState.CurrentTime] == 6)
{
_logger.LogWarning(
"Failed to schedule beyond {Time}; aborting playout build - this is a bug",

3
ErsatzTV.Core/Scheduling/PlayoutModeSchedulerFlood.cs

@ -67,8 +67,9 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul @@ -67,8 +67,9 @@ public class PlayoutModeSchedulerFlood : PlayoutModeSchedulerBase<ProgramSchedul
SubtitleMode = scheduleItem.SubtitleMode
};
// never block scheduling when there is only one schedule item (with fixed start and flood)
DateTimeOffset peekScheduleItemStart =
peekScheduleItem.StartType == StartType.Fixed
scheduleItem.Id != peekScheduleItem.Id && peekScheduleItem.StartType == StartType.Fixed
? GetStartTimeAfter(nextState with { InFlood = false }, peekScheduleItem)
: DateTimeOffset.MaxValue;

Loading…
Cancel
Save