Skip to content

Commit f9af954

Browse files
authored
Merge pull request Sofie-Automation#1441 from nrkno/fix/preroll-fix
Fix: Account for Part when calculating piece times (SOFIE-3922)
2 parents fd189ca + 014a7b4 commit f9af954

File tree

8 files changed

+47
-36
lines changed

8 files changed

+47
-36
lines changed

packages/corelib/src/playout/processAndPrune.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,7 @@ function getPieceStartTimeAsReference(newPieceStart: number | 'now'): number | R
1515
}
1616

1717
function getPieceStartTimeWithinPart(p: ReadonlyDeep<PieceInstance>): 'now' | number {
18-
// If the piece is dynamically inserted, then its preroll should be factored into its start time, but not for any infinite continuations
19-
const isStartOfAdlib =
20-
!!p.dynamicallyInserted && !(p.infinite?.fromPreviousPart || p.infinite?.fromPreviousPlayhead)
21-
22-
if (isStartOfAdlib && p.piece.enable.start !== 'now') {
23-
return p.piece.enable.start + (p.piece.prerollDuration ?? 0)
24-
} else {
25-
return p.piece.enable.start
26-
}
18+
return p.piece.enable.start
2719
}
2820

2921
function isClear(piece?: ReadonlyDeep<PieceInstance>): boolean {

packages/job-worker/src/playout/__tests__/timeline.test.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,8 +1285,9 @@ describe('Timeline', () => {
12851285
})
12861286

12871287
const pieceOffset = 12560
1288+
12881289
// Simulate the piece timing confirmation from playout-gateway
1289-
await doSimulatePiecePlaybackTimings(playlistId, pieceOffset, 2)
1290+
await doSimulatePiecePlaybackTimings(playlistId, pieceOffset, 2) // This pieceOffset includes the partPreroll
12901291

12911292
// Now we have a concrete time
12921293
await checkTimings({
@@ -1456,16 +1457,14 @@ describe('Timeline', () => {
14561457
// Simulate the piece timing confirmation from playout-gateway
14571458
await doSimulatePiecePlaybackTimings(playlistId, pieceOffset, 2)
14581459

1459-
const pieceOffsetWithPreroll = pieceOffset + 340
1460-
14611460
// Now we have a concrete time
14621461
await checkTimings({
14631462
previousPart: null,
14641463
currentPieces: {
14651464
piece000: {
14661465
controlObj: {
14671466
start: 500, // This one gave the preroll
1468-
end: pieceOffsetWithPreroll,
1467+
end: pieceOffset,
14691468
},
14701469
childGroup: {
14711470
preroll: 500,
@@ -1484,7 +1483,7 @@ describe('Timeline', () => {
14841483
[adlibbedPieceId]: {
14851484
// Our adlibbed piece
14861485
controlObj: {
1487-
start: pieceOffsetWithPreroll,
1486+
start: pieceOffset,
14881487
},
14891488
childGroup: {
14901489
preroll: 340,

packages/job-worker/src/playout/timeline/__tests__/pieceGroup.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ describe('Pieces', () => {
539539
classes: [],
540540
enable: {
541541
...enable,
542-
end: 400,
542+
end: 600,
543543
},
544544
})
545545
})

packages/job-worker/src/playout/timeline/multi-gateway.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,14 +225,15 @@ function deNowifyCurrentPieces(
225225
const objectsNotDeNowified: TimelineObjRundown[] = []
226226
// The relative time for 'now' to be resolved to, inside of the part group
227227
const nowInPart = targetNowTime - currentPartGroupStartTime
228+
const nowInPartWithoutPreroll = nowInPart - (currentPartInstance.partInstance.partPlayoutTimings?.toPartDelay ?? 0)
228229

229230
// Ensure any pieces in the currentPartInstance have their now replaced
230231
for (const pieceInstance of currentPartInstance.pieceInstances) {
231232
if (pieceInstance.pieceInstance.piece.enable.start === 'now') {
232233
pieceInstance.updatePieceProps({
233234
enable: {
234235
...pieceInstance.pieceInstance.piece.enable,
235-
start: nowInPart,
236+
start: nowInPartWithoutPreroll,
236237
},
237238
})
238239
}

packages/job-worker/src/playout/timeline/part.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export function transformPartIntoTimeline(
6666
nowInParentGroup,
6767
pieceInstance,
6868
pieceEnable,
69-
0,
69+
pieceInstance.dynamicallyInserted ? 0 : partTimings.toPartDelay,
7070
pieceGroupFirstObjClasses,
7171
isInHold,
7272
false

packages/job-worker/src/playout/timeline/piece.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,8 @@ export function getPieceEnableInsidePart(
9595
): TSR.Timeline.TimelineEnable {
9696
const pieceEnable: TSR.Timeline.TimelineEnable = { ...pieceInstance.piece.enable }
9797
if (typeof pieceEnable.start === 'number') {
98-
if (pieceInstance.dynamicallyInserted) {
99-
// timed adlibbed pieces needs to factor in their preroll
100-
pieceEnable.start += pieceInstance.piece.prerollDuration || 0
101-
} else {
102-
// timed planned pieces should be offset based on the preroll of the part
103-
pieceEnable.start += partTimings.toPartDelay
104-
}
98+
// pieces should be offset based on the preroll of the part
99+
pieceEnable.start += partTimings.toPartDelay
105100
}
106101

107102
// If the part has an end time, we can consider post-roll

packages/job-worker/src/playout/timeline/pieceGroup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export function createPieceGroupAndCap(
136136
let resolvedEndCap: number | string | undefined
137137
// If the start has been adjusted, the end needs to be updated to compensate
138138
if (typeof pieceInstance.resolvedEndCap === 'number') {
139-
resolvedEndCap = pieceInstance.resolvedEndCap - (pieceStartOffset ?? 0)
139+
resolvedEndCap = pieceInstance.resolvedEndCap + (pieceStartOffset ?? 0)
140140
} else if (pieceInstance.resolvedEndCap) {
141141
// TODO - there could already be a piece with a cap of 'now' that we could use as our end time
142142
// As the cap is for 'now', rather than try to get tsr to understand `end: 'now'`, we can create a 'now' object to tranlate it

packages/job-worker/src/playout/timings/timelineTriggerTime.ts

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { PieceTimelineMetadata } from '../timeline/pieceGroup'
1313
import { deserializeTimelineBlob } from '@sofie-automation/corelib/dist/dataModel/Timeline'
1414
import { ReadonlyDeep } from 'type-fest'
1515
import { AnyBulkWriteOperation } from 'mongodb'
16+
import { DBPartInstance } from '@sofie-automation/corelib/dist/dataModel/PartInstance'
1617

1718
/**
1819
* Called from Playout-gateway when the trigger-time of a timeline object has updated
@@ -40,20 +41,38 @@ export async function handleTimelineTriggerTime(context: JobContext, data: OnTim
4041
(id): id is PartInstanceId => id !== null
4142
)
4243

43-
// We only need the PieceInstances, so load just them
44-
const pieceInstances = await context.directCollections.PieceInstances.findFetch({
45-
rundownId: { $in: rundownIDs },
46-
partInstanceId: {
47-
$in: partInstanceIDs,
48-
},
49-
})
44+
// We only need the PieceInstances and PartInstances, so load just them
45+
const [pieceInstances, partInstances] = await Promise.all([
46+
context.directCollections.PieceInstances.findFetch({
47+
rundownId: { $in: rundownIDs },
48+
partInstanceId: {
49+
$in: partInstanceIDs,
50+
},
51+
}),
52+
context.directCollections.PartInstances.findFetch(
53+
{
54+
rundownId: { $in: rundownIDs },
55+
_id: {
56+
$in: partInstanceIDs,
57+
},
58+
},
59+
{
60+
projection: {
61+
_id: 1,
62+
partPlayoutTimings: 1,
63+
},
64+
}
65+
) as Promise<Array<Pick<DBPartInstance, '_id' | 'partPlayoutTimings'>>>,
66+
])
5067
const pieceInstancesMap = normalizeArrayToMap(pieceInstances, '_id')
68+
const partInstanceMap = normalizeArrayToMap(partInstances, '_id')
5169

5270
// Take ownership of the playlist in the db, so that we can mutate the timeline and piece instances
5371
const changes = timelineTriggerTimeInner(
5472
context,
5573
studioCache,
5674
data.results,
75+
partInstanceMap,
5776
pieceInstancesMap,
5877
activePlaylist
5978
)
@@ -62,7 +81,7 @@ export async function handleTimelineTriggerTime(context: JobContext, data: OnTim
6281
})
6382
} else {
6483
// No playlist is active. no extra lock needed
65-
timelineTriggerTimeInner(context, studioCache, data.results, undefined, undefined)
84+
timelineTriggerTimeInner(context, studioCache, data.results, undefined, undefined, undefined)
6685
}
6786
})
6887
}
@@ -107,6 +126,7 @@ function timelineTriggerTimeInner(
107126
context: JobContext,
108127
studioPlayoutModel: StudioPlayoutModel,
109128
results: OnTimelineTriggerTimeProps['results'],
129+
partInstances: Map<PartInstanceId, Pick<DBPartInstance, '_id' | 'partPlayoutTimings'>> | undefined,
110130
pieceInstances: Map<PieceInstanceId, PieceInstance> | undefined,
111131
activePlaylist: ReadonlyDeep<DBRundownPlaylist> | undefined
112132
) {
@@ -142,7 +162,7 @@ function timelineTriggerTimeInner(
142162

143163
const objPieceInstanceId = (obj.metaData as Partial<PieceTimelineMetadata> | undefined)
144164
?.triggerPieceInstanceId
145-
if (objPieceInstanceId && activePlaylist && pieceInstances) {
165+
if (objPieceInstanceId && activePlaylist && pieceInstances && partInstances) {
146166
logger.debug('Update PieceInstance: ', {
147167
pieceId: objPieceInstanceId,
148168
time: new Date(o.time).toTimeString(),
@@ -154,8 +174,12 @@ function timelineTriggerTimeInner(
154174
pieceInstance.dynamicallyInserted !== undefined &&
155175
pieceInstance.piece.enable.start === 'now'
156176
) {
157-
pieceInstance.piece.enable.start = o.time
158-
changes.setStartTime.set(pieceInstance._id, o.time)
177+
const partPrerollDuration =
178+
partInstances.get(pieceInstance.partInstanceId)?.partPlayoutTimings?.toPartDelay ?? 0
179+
180+
const adjustedStartTime = o.time - partPrerollDuration
181+
pieceInstance.piece.enable.start = adjustedStartTime
182+
changes.setStartTime.set(pieceInstance._id, adjustedStartTime)
159183

160184
const takeTime = pieceInstance.dynamicallyInserted
161185
lastTakeTime = lastTakeTime === undefined ? takeTime : Math.max(lastTakeTime, takeTime)

0 commit comments

Comments
 (0)