Skip to content

Commit 6a75378

Browse files
committed
feat: calculate lookahead offsets when setting a take point in the middle of a piece
1 parent e0d984e commit 6a75378

File tree

3 files changed

+109
-52
lines changed

3 files changed

+109
-52
lines changed

packages/job-worker/src/playout/lookahead/findForLayer.ts

Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,54 +11,66 @@ export interface LookaheadResult {
1111
future: Array<LookaheadTimelineObject>
1212
}
1313

14+
export interface PartInstanceAndPieceInstancesInfos {
15+
previous?: PartInstanceAndPieceInstances
16+
current?: PartInstanceAndPieceInstances
17+
next?: PartInstanceAndPieceInstances
18+
}
19+
1420
export function findLookaheadForLayer(
1521
context: JobContext,
16-
currentPartInstanceId: PartInstanceId | null,
17-
partInstancesInfo: PartInstanceAndPieceInstances[],
18-
previousPartInstanceInfo: PartInstanceAndPieceInstances | undefined,
22+
partInstancesInfo: PartInstanceAndPieceInstancesInfos,
1923
orderedPartInfos: Array<PartAndPieces>,
2024
layer: string,
2125
lookaheadTargetFutureObjects: number,
22-
lookaheadMaxSearchDistance: number
26+
lookaheadMaxSearchDistance: number,
27+
nextTimeOffset?: number | null
2328
): LookaheadResult {
2429
const span = context.startSpan(`findLookaheadForlayer.${layer}`)
30+
const currentPartId = partInstancesInfo.current?.part._id ?? null
2531
const res: LookaheadResult = {
2632
timed: [],
2733
future: [],
2834
}
2935

3036
// Track the previous info for checking how the timeline will be built
3137
let previousPart: ReadonlyDeep<DBPart> | undefined
32-
if (previousPartInstanceInfo) {
33-
previousPart = previousPartInstanceInfo.part.part
38+
if (partInstancesInfo.previous?.part.part) {
39+
previousPart = partInstancesInfo.previous.part.part
3440
}
3541

3642
// Generate timed/future objects for the partInstances
37-
for (const partInstanceInfo of partInstancesInfo) {
38-
if (!partInstanceInfo.onTimeline && lookaheadMaxSearchDistance <= 0) break
43+
if (partInstancesInfo.current) {
44+
const { objs: currentObjs, partInfo: currentPartInfo } = generatePartInstanceLookaheads(
45+
context,
46+
partInstancesInfo.current,
47+
partInstancesInfo.current.part._id,
48+
layer,
49+
previousPart
50+
)
3951

40-
const partInfo: PartAndPieces = {
41-
part: partInstanceInfo.part.part,
42-
usesInTransition: partInstanceInfo.calculatedTimings.inTransitionStart !== null,
43-
pieces: sortPieceInstancesByStart(partInstanceInfo.allPieces, partInstanceInfo.nowInPart),
52+
if (partInstancesInfo.current.onTimeline) {
53+
res.timed.push(...currentObjs)
4454
}
55+
previousPart = currentPartInfo.part
56+
}
4557

46-
const objs = findLookaheadObjectsForPart(
58+
// for Lookaheads in the next part we need to take the nextTimeOffset into account.
59+
// TODO: Check if having two pieces after eachother on the same layer is handled correctly
60+
if (partInstancesInfo.next) {
61+
const { objs: nextObjs, partInfo: nextPartInfo } = generatePartInstanceLookaheads(
4762
context,
48-
currentPartInstanceId,
63+
partInstancesInfo.next,
64+
currentPartId,
4965
layer,
5066
previousPart,
51-
partInfo,
52-
partInstanceInfo.part._id
67+
nextTimeOffset
5368
)
5469

55-
if (partInstanceInfo.onTimeline) {
56-
res.timed.push(...objs)
57-
} else {
58-
res.future.push(...objs)
70+
if (partInstancesInfo.next?.onTimeline) {
71+
res.future.push(...nextObjs)
5972
}
60-
61-
previousPart = partInfo.part
73+
previousPart = nextPartInfo.part
6274
}
6375

6476
if (lookaheadMaxSearchDistance > 1 && lookaheadTargetFutureObjects > 0) {
@@ -69,14 +81,7 @@ export function findLookaheadForLayer(
6981
}
7082

7183
if (partInfo.pieces.length > 0 && isPartPlayable(partInfo.part)) {
72-
const objs = findLookaheadObjectsForPart(
73-
context,
74-
currentPartInstanceId,
75-
layer,
76-
previousPart,
77-
partInfo,
78-
null
79-
)
84+
const objs = findLookaheadObjectsForPart(context, currentPartId, layer, previousPart, partInfo, null)
8085
res.future.push(...objs)
8186
previousPart = partInfo.part
8287
}
@@ -86,3 +91,28 @@ export function findLookaheadForLayer(
8691
if (span) span.end()
8792
return res
8893
}
94+
function generatePartInstanceLookaheads(
95+
context: JobContext,
96+
partInstanceInfo: PartInstanceAndPieceInstances,
97+
currentPartInstanceId: PartInstanceId | null,
98+
layer: string,
99+
previousPart: ReadonlyDeep<DBPart> | undefined,
100+
nextTimeOffset?: number | null
101+
): { objs: LookaheadTimelineObject[]; partInfo: PartAndPieces } {
102+
const partInfo: PartAndPieces = {
103+
part: partInstanceInfo.part.part,
104+
usesInTransition: partInstanceInfo.calculatedTimings.inTransitionStart !== null,
105+
pieces: sortPieceInstancesByStart(partInstanceInfo.allPieces, partInstanceInfo.nowInPart),
106+
}
107+
108+
const objs = findLookaheadObjectsForPart(
109+
context,
110+
currentPartInstanceId,
111+
layer,
112+
previousPart,
113+
partInfo,
114+
partInstanceInfo.part._id,
115+
nextTimeOffset ?? undefined
116+
)
117+
return { objs, partInfo }
118+
}

packages/job-worker/src/playout/lookahead/findObjects.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ export function findLookaheadObjectsForPart(
9292
layer: string,
9393
previousPart: ReadonlyDeep<DBPart> | undefined,
9494
partInfo: PartAndPieces,
95-
partInstanceId: PartInstanceId | null
95+
partInstanceId: PartInstanceId | null,
96+
nextTimeOffset?: number
9697
): Array<LookaheadTimelineObject> {
9798
// Sanity check, if no part to search, then abort
9899
if (!partInfo || partInfo.pieces.length === 0) {
@@ -105,6 +106,31 @@ export function findLookaheadObjectsForPart(
105106

106107
const obj = getObjectMapForPiece(rawPiece).get(layer)
107108
if (obj) {
109+
// TODO: forcing these types feels wrong, do we even need to take the object's enable into account?
110+
const objEnable: typeof rawPiece.piece.enable = Array.isArray(obj.enable)
111+
? (obj.enable[0] as typeof rawPiece.piece.enable)
112+
: (obj.enable as typeof rawPiece.piece.enable)
113+
let lookaheadOffset: number | undefined
114+
115+
if (nextTimeOffset) {
116+
const pieceStart = rawPiece.piece.enable.start === 'now' ? 0 : rawPiece.piece.enable.start
117+
const objStart = objEnable.start === 'now' ? 0 : objEnable.start
118+
119+
const offset = nextTimeOffset - pieceStart - objStart
120+
121+
lookaheadOffset = offset > 0 ? offset : undefined
122+
} else {
123+
lookaheadOffset = undefined
124+
}
125+
// TODO: remove console log before PR
126+
// console.log(
127+
// `--------------------------LOOK HERE---------------------------2\n${JSON.stringify(
128+
// { ...obj, lookaheadOffset },
129+
// null,
130+
// 2
131+
// )}`
132+
// )
133+
console.log('lookaheadOffset: ' + nextTimeOffset)
108134
allObjs.push(
109135
literal<LookaheadTimelineObject>({
110136
metaData: undefined,
@@ -113,6 +139,7 @@ export function findLookaheadObjectsForPart(
113139
pieceInstanceId: getBestPieceInstanceId(rawPiece),
114140
infinitePieceInstanceId: rawPiece.infinite?.infiniteInstanceId,
115141
partInstanceId: partInstanceId ?? protectString(unprotectString(partInfo.part._id)),
142+
lookaheadOffset,
116143
})
117144
)
118145
}

packages/job-worker/src/playout/lookahead/index.ts

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getOrderedPartsAfterPlayhead, PartAndPieces, PartInstanceAndPieceInstances } from './util.js'
2-
import { findLookaheadForLayer, LookaheadResult } from './findForLayer.js'
2+
import { findLookaheadForLayer, LookaheadResult, PartInstanceAndPieceInstancesInfos } from './findForLayer.js'
33
import { PlayoutModel } from '../model/PlayoutModel.js'
44
import { sortPieceInstancesByStart } from '../pieces.js'
55
import { MappingExt } from '@sofie-automation/corelib/dist/dataModel/Studio'
@@ -90,37 +90,38 @@ export async function getLookeaheadObjects(
9090
},
9191
})
9292

93-
const partInstancesInfo: PartInstanceAndPieceInstances[] = _.compact([
94-
partInstancesInfo0.current
93+
// Track the previous info for checking how the timeline will be built
94+
let previousPartInfo: PartInstanceAndPieceInstances | undefined
95+
if (partInstancesInfo0.previous) {
96+
previousPartInfo = removeInfiniteContinuations({
97+
part: partInstancesInfo0.previous.partInstance,
98+
onTimeline: true,
99+
nowInPart: partInstancesInfo0.previous.nowInPart,
100+
allPieces: getPrunedEndedPieceInstances(partInstancesInfo0.previous),
101+
calculatedTimings: partInstancesInfo0.previous.calculatedTimings,
102+
})
103+
}
104+
105+
const partInstancesInfo: PartInstanceAndPieceInstancesInfos = {
106+
previous: previousPartInfo,
107+
current: partInstancesInfo0.current
95108
? removeInfiniteContinuations({
96109
part: partInstancesInfo0.current.partInstance,
97110
onTimeline: true,
98111
nowInPart: partInstancesInfo0.current.nowInPart,
99112
allPieces: getPrunedEndedPieceInstances(partInstancesInfo0.current),
100113
calculatedTimings: partInstancesInfo0.current.calculatedTimings,
101-
})
114+
})
102115
: undefined,
103-
partInstancesInfo0.next
116+
next: partInstancesInfo0.next
104117
? removeInfiniteContinuations({
105118
part: partInstancesInfo0.next.partInstance,
106119
onTimeline: !!partInstancesInfo0.current?.partInstance?.part?.autoNext, //TODO -QL
107120
nowInPart: partInstancesInfo0.next.nowInPart,
108121
allPieces: partInstancesInfo0.next.pieceInstances,
109122
calculatedTimings: partInstancesInfo0.next.calculatedTimings,
110-
})
123+
})
111124
: undefined,
112-
])
113-
114-
// Track the previous info for checking how the timeline will be built
115-
let previousPartInfo: PartInstanceAndPieceInstances | undefined
116-
if (partInstancesInfo0.previous) {
117-
previousPartInfo = removeInfiniteContinuations({
118-
part: partInstancesInfo0.previous.partInstance,
119-
onTimeline: true,
120-
nowInPart: partInstancesInfo0.previous.nowInPart,
121-
allPieces: getPrunedEndedPieceInstances(partInstancesInfo0.previous),
122-
calculatedTimings: partInstancesInfo0.previous.calculatedTimings,
123-
})
124125
}
125126

126127
// TODO: Do we need to use processAndPrunePieceInstanceTimings on these pieces? In theory yes, but that gets messy and expensive.
@@ -164,13 +165,12 @@ export async function getLookeaheadObjects(
164165

165166
const lookaheadObjs = findLookaheadForLayer(
166167
context,
167-
playoutModel.playlist.currentPartInfo?.partInstanceId ?? null,
168168
partInstancesInfo,
169-
previousPartInfo,
170169
orderedPartInfos,
171170
layerId,
172171
lookaheadTargetObjects,
173-
lookaheadMaxSearchDistance
172+
lookaheadMaxSearchDistance,
173+
playoutModel.playlist.nextTimeOffset
174174
)
175175

176176
timelineObjs.push(...processResult(lookaheadObjs, mapping.lookahead))

0 commit comments

Comments
 (0)