From 47dfeb8e16af8effcfe6f2325503502cfe0e86fe Mon Sep 17 00:00:00 2001 From: Rasmus Lindved Date: Tue, 7 Nov 2023 11:51:59 +0100 Subject: [PATCH] SOF-1694 Dve Piece now includes metadata needed for inserting Sources to inputs by Actions --- src/tv2-common/content/dve.ts | 62 ++++++++++++++++--- src/tv2_afvd_showstyle/helpers/content/dve.ts | 3 +- src/tv2_afvd_showstyle/helpers/pieces/dve.ts | 1 + 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/tv2-common/content/dve.ts b/src/tv2-common/content/dve.ts index 447fda0f..e83aa2bf 100644 --- a/src/tv2-common/content/dve.ts +++ b/src/tv2-common/content/dve.ts @@ -105,6 +105,13 @@ export interface DVEPieceMetaData extends PieceMetaData { userData: ActionSelectDVE mediaPlayerSessions?: string[] // TODO: Should probably move to a ServerPieceMetaData serverPlaybackTiming?: Array<{ start?: number; end?: number }> + dve?: DvePieceActionMetadata +} + +// This is used by the "new" Blueprint in order to put sources into a planned DVE. +export interface DvePieceActionMetadata { + boxes: BoxConfig[] + audioTimelineObjectsForBoxes: { [inputIndex: number]: TSR.TSRTimelineObj[] } } export interface DVEOptions { @@ -125,7 +132,7 @@ export function MakeContentDVEBase< parsedCue: CueDefinitionDVE, dveConfig: DVEConfigInput | undefined, dveGeneratorOptions: DVEOptions -): { content: WithTimeline; valid: boolean } { +): { content: WithTimeline; valid: boolean; dvePieceActionMetadata?: DvePieceActionMetadata } { if (!dveConfig) { context.core.notifyUserWarning(`DVE ${parsedCue.template} is not configured`) return { @@ -162,7 +169,7 @@ export function MakeContentDVE2< sources: DVESources | undefined, dveGeneratorOptions: DVEOptions, mediaPlayerSessionId?: string -): { content: WithTimeline; valid: boolean } { +): { content: WithTimeline; valid: boolean; dvePieceActionMetadata?: DvePieceActionMetadata } { let template: DVEConfig try { template = JSON.parse(dveConfig.DVEJSON) as DVEConfig @@ -194,13 +201,15 @@ export function MakeContentDVE2< let valid = true let hasServer = false - boxAssigments.forEach((mappingFrom, num) => { - const box = boxes[num] + const audioTimelineObjectsForBoxes: { [inputIndex: number]: TSR.TSRTimelineObj[] } = {} + + boxAssigments.forEach((mappingFrom, index) => { + const box: BoxConfig = boxes[index] if (mappingFrom === undefined) { if (sources) { // If it is intentional there are no sources, then ignore // TODO - should this warn? - context.core.notifyUserWarning(`Missing source type for DVE box: ${num + 1}`) + context.core.notifyUserWarning(`Missing source type for DVE box: ${index + 1}`) setBoxToBlack(box, boxSources) valid = false } @@ -226,9 +235,12 @@ export function MakeContentDVE2< setBoxSource(box, boxSources, sourceInfoCam) cameraSources.push(box.source) - dveTimeline.push( - ...GetSisyfosTimelineObjForCamera(context.config, sourceInfoCam, mappingFrom.minusMic, audioEnable) + + const audioTimelineObjectsForCamera: TSR.TSRTimelineObj[] = addRandomIdIfMissing( + GetSisyfosTimelineObjForCamera(context.config, sourceInfoCam, mappingFrom.minusMic, audioEnable) ) + audioTimelineObjectsForBoxes[index] = audioTimelineObjectsForCamera + dveTimeline.push(...audioTimelineObjectsForCamera) break case SourceType.REMOTE: const sourceInfoLive = findSourceInfo(context.config.sources, mappingFrom) @@ -240,7 +252,12 @@ export function MakeContentDVE2< } setBoxSource(box, boxSources, sourceInfoLive) - dveTimeline.push(...GetSisyfosTimelineObjForRemote(context.config, sourceInfoLive, audioEnable)) + + const audioTimelineObjectsForRemote: TSR.TSRTimelineObj[] = addRandomIdIfMissing( + GetSisyfosTimelineObjForRemote(context.config, sourceInfoLive, audioEnable) + ) + audioTimelineObjectsForBoxes[index] = audioTimelineObjectsForRemote + dveTimeline.push(...audioTimelineObjectsForRemote) break case SourceType.REPLAY: const sourceInfoReplay = findSourceInfo(context.config.sources, mappingFrom) @@ -252,7 +269,12 @@ export function MakeContentDVE2< } setBoxSource(box, boxSources, sourceInfoReplay) - dveTimeline.push(...GetSisyfosTimelineObjForReplay(context.config, sourceInfoReplay, mappingFrom.vo)) + + const audioTimelineObjectsForReplay: TSR.TSRTimelineObj[] = addRandomIdIfMissing( + GetSisyfosTimelineObjForReplay(context.config, sourceInfoReplay, mappingFrom.vo) + ) + audioTimelineObjectsForBoxes[index] = audioTimelineObjectsForReplay + dveTimeline.push(...audioTimelineObjectsForReplay) break case SourceType.GRAFIK: if (mappingFrom.name === 'FULL') { @@ -260,7 +282,12 @@ export function MakeContentDVE2< sourceLayerType: SourceLayerType.GRAPHICS, port: findDskFullGfx(context.config).Fill }) - dveTimeline.push(...GetSisyfosTimelineObjForFull(context.config)) + + const audioTimelineObjectsForFull: TSR.TSRTimelineObj[] = addRandomIdIfMissing( + GetSisyfosTimelineObjForFull(context.config) + ) + audioTimelineObjectsForBoxes[index] = audioTimelineObjectsForFull + dveTimeline.push(...audioTimelineObjectsForFull) } else { context.core.notifyUserWarning(`Unsupported engine for DVE: ${mappingFrom.name}`) setBoxToBlack(box, boxSources) @@ -277,6 +304,11 @@ export function MakeContentDVE2< } }) + const dvePieceActionMetadata: DvePieceActionMetadata = { + boxes, + audioTimelineObjectsForBoxes + } + const graphicsTemplate = getDveGraphicsTemplate(dveConfig, context.core.notifyUserWarning) const graphicsTemplateStyle = getDveGraphicsTemplateStyle(graphicsTemplate) const locatorType = getDveLocatorType(graphicsTemplate) @@ -297,6 +329,7 @@ export function MakeContentDVE2< } return { + dvePieceActionMetadata, valid, content: literal>({ boxSourceConfiguration: boxSources, @@ -375,6 +408,15 @@ export function MakeContentDVE2< } } +function addRandomIdIfMissing(timelineObjects: TSR.TSRTimelineObj[]): TSR.TSRTimelineObj[] { + return timelineObjects.map((timelineObject) => { + if (timelineObject.id === '') { + timelineObject.id = `${Math.floor(Math.random() * Date.now())}` + } + return timelineObject + }) +} + function getDveGraphicsTemplate(dveConfigInput: DVEConfigInput, notifyUserWarning: (message: string) => void): object { try { const dveGraphicsTemplate = JSON.parse(dveConfigInput.DVEGraphicsTemplateJSON) diff --git a/src/tv2_afvd_showstyle/helpers/content/dve.ts b/src/tv2_afvd_showstyle/helpers/content/dve.ts index ea8efc0a..67f42f98 100644 --- a/src/tv2_afvd_showstyle/helpers/content/dve.ts +++ b/src/tv2_afvd_showstyle/helpers/content/dve.ts @@ -3,6 +3,7 @@ import { CueDefinitionDVE, DVEConfigInput, DVEOptions, + DvePieceActionMetadata, MakeContentDVEBase, PartDefinition, ShowStyleContext @@ -33,6 +34,6 @@ export function MakeContentDVE( partDefinition: PartDefinition, parsedCue: CueDefinitionDVE, dveConfig: DVEConfigInput | undefined -): { content: WithTimeline; valid: boolean } { +): { content: WithTimeline; valid: boolean; dvePieceActionMetadata?: DvePieceActionMetadata } { return MakeContentDVEBase(context, partDefinition, parsedCue, dveConfig, AFVD_DVE_GENERATOR_OPTIONS) } diff --git a/src/tv2_afvd_showstyle/helpers/pieces/dve.ts b/src/tv2_afvd_showstyle/helpers/pieces/dve.ts index 84088fd6..135ad747 100644 --- a/src/tv2_afvd_showstyle/helpers/pieces/dve.ts +++ b/src/tv2_afvd_showstyle/helpers/pieces/dve.ts @@ -90,6 +90,7 @@ export function EvaluateDVE( content: content.content, prerollDuration: Number(context.config.studio.CasparPrerollDuration) || 0, metaData: { + dve: content.dvePieceActionMetadata, type: Tv2PieceType.SPLIT_SCREEN, outputLayer: Tv2OutputLayer.PROGRAM, mediaPlayerSessions: [partDefinition.segmentExternalId],