diff --git a/src/tv2-common/videoSwitchers/Atem.ts b/src/tv2-common/videoSwitchers/Atem.ts index 59802b3d..f8531adf 100644 --- a/src/tv2-common/videoSwitchers/Atem.ts +++ b/src/tv2-common/videoSwitchers/Atem.ts @@ -40,7 +40,7 @@ export class Atem extends VideoSwitcherBase { public getMixEffectTimelineObject(props: MixEffectProps): TSR.TimelineObjAtemME & TimelineBlueprintExt { const { content } = props const me: TSR.TimelineObjAtemME['content']['me'] = - content.input && content.transition + content.input && content.transition && content.transition !== TransitionStyle.CUT ? { input: this.getInputNumber(content.input), transition: this.getTransition(content.transition), diff --git a/src/tv2-common/videoSwitchers/VideoSwitcher.ts b/src/tv2-common/videoSwitchers/VideoSwitcher.ts index cab4fdb7..6122db6b 100644 --- a/src/tv2-common/videoSwitchers/VideoSwitcher.ts +++ b/src/tv2-common/videoSwitchers/VideoSwitcher.ts @@ -4,6 +4,7 @@ import { AuxProps, DskProps, DveProps, + getTimeFromFrames, MixEffectProps, OnAirMixEffectProps, SpecialInput, @@ -53,6 +54,25 @@ export abstract class VideoSwitcherBase implements VideoSwitcher { layer: this.uniformConfig.switcherLLayers.primaryMixEffect }) ) + + // In order to set the preview we need to create another TimelineObject after the transition is over that sets program and preview. + if (properties.content.transitionDuration) { + // We need a slight delay before we tell the VideoMixer to set the next source, else we risk that the VideoMixer sets the next source in program. + const previewTimelineObjectDelay: number = 300 + result.push( + this.getMixEffectTimelineObject({ + ...properties, + id: primaryId + Math.floor(Math.random() * 10000), + layer: this.uniformConfig.switcherLLayers.primaryMixEffect, + enable: { start: getTimeFromFrames(properties.content.transitionDuration) + previewTimelineObjectDelay }, + content: { + input: properties.content.input, + transition: undefined + } + }) + ) + } + if (this.uniformConfig.switcherLLayers.primaryMixEffectClone) { result.push( this.getMixEffectTimelineObject({ diff --git a/src/tv2-common/videoSwitchers/__tests__/Atem.spec.ts b/src/tv2-common/videoSwitchers/__tests__/Atem.spec.ts index 92331b2b..1eea66cd 100644 --- a/src/tv2-common/videoSwitchers/__tests__/Atem.spec.ts +++ b/src/tv2-common/videoSwitchers/__tests__/Atem.spec.ts @@ -80,7 +80,7 @@ describe('ATEM', () => { }) }) - test('sets input when CUT transition provided', () => { + test('sets programInput when CUT transition provided', () => { const atem = createTestee() const timelineObject = atem.getMixEffectTimelineObject({ layer: SwitcherMixEffectLLayer.PROGRAM, @@ -92,8 +92,7 @@ describe('ATEM', () => { expect(timelineObject).toMatchObject({ content: { me: { - input: 5, - transition: TSR.AtemTransitionStyle.CUT + programInput: 5 } } }) diff --git a/src/tv2_afvd_showstyle/__tests__/transitions.spec.ts b/src/tv2_afvd_showstyle/__tests__/transitions.spec.ts index cadac354..03a9ac14 100644 --- a/src/tv2_afvd_showstyle/__tests__/transitions.spec.ts +++ b/src/tv2_afvd_showstyle/__tests__/transitions.spec.ts @@ -125,7 +125,7 @@ function testNotes(context: SegmentUserContextMock) { } describe('Primary Cue Transitions Without Config', () => { - it('Cuts by default for KAM', async () => { + it('Transition is undefined by default for KAM', async () => { const ingestSegment = _.clone(templateSegment) ingestSegment.payload.iNewsStory.body = '\r\n
' @@ -138,7 +138,7 @@ describe('Primary Cue Transitions Without Config', () => { const piece = getPieceOnLayerFromPart(segment, SourceLayer.PgmCam) const atemCutObj = getATEMMEObj(piece) - expect(atemCutObj.content.me.transition).toBe(TSR.AtemTransitionStyle.CUT) + expect(atemCutObj.content.me.transition).toBe(undefined) }) it('Adds effekt to KAM', async () => { @@ -156,7 +156,7 @@ describe('Primary Cue Transitions Without Config', () => { checkPartExistsWithProperties(segment, getTransitionProperties(MOCK_EFFEKT_2)) - expect(atemCutObj.content.me.transition).toBe(TSR.AtemTransitionStyle.CUT) + expect(atemCutObj.content.me.transition).toBe(undefined) }) it('Adds mix to KAM', async () => { @@ -183,7 +183,7 @@ describe('Primary Cue Transitions Without Config', () => { expect(atemCutObj.content.me.transitionSettings?.mix?.rate).toBe(11) }) - it('Cuts by default for EVS1', async () => { + it('Transition is undefined by default for EVS1', async () => { const ingestSegment = _.clone(templateSegment) ingestSegment.payload.iNewsStory.body = '\r\n
' @@ -196,7 +196,7 @@ describe('Primary Cue Transitions Without Config', () => { const piece = getPieceOnLayerFromPart(segment, SourceLayer.PgmLocal) const atemCutObj = getATEMMEObj(piece) - expect(atemCutObj.content.me.transition).toBe(TSR.AtemTransitionStyle.CUT) + expect(atemCutObj.content.me.transition).toBe(undefined) }) it('Adds effekt to EVS1', async () => { @@ -213,7 +213,7 @@ describe('Primary Cue Transitions Without Config', () => { const atemCutObj = getATEMMEObj(piece) checkPartExistsWithProperties(segment, getTransitionProperties(MOCK_EFFEKT_1)) - expect(atemCutObj.content.me.transition).toBe(TSR.AtemTransitionStyle.CUT) + expect(atemCutObj.content.me.transition).toBe(undefined) }) it('Adds mix to EVS1', async () => { @@ -240,7 +240,7 @@ describe('Primary Cue Transitions Without Config', () => { expect(atemCutObj.content.me.transitionSettings?.mix?.rate).toBe(15) }) - it('Cuts by default for EVS1VO', async () => { + it('Transition is undefined by default for EVS1VO', async () => { const ingestSegment = _.clone(templateSegment) ingestSegment.payload.iNewsStory.body = '\r\n
' @@ -253,7 +253,7 @@ describe('Primary Cue Transitions Without Config', () => { const piece = getPieceOnLayerFromPart(segment, SourceLayer.PgmLocal) const atemCutObj = getATEMMEObj(piece) - expect(atemCutObj.content.me.transition).toBe(TSR.AtemTransitionStyle.CUT) + expect(atemCutObj.content.me.transition).toBe(undefined) }) it('Adds effekt to EVS1VO', async () => { @@ -270,7 +270,7 @@ describe('Primary Cue Transitions Without Config', () => { const atemCutObj = getATEMMEObj(piece) checkPartExistsWithProperties(segment, getTransitionProperties(MOCK_EFFEKT_1)) - expect(atemCutObj.content.me.transition).toBe(TSR.AtemTransitionStyle.CUT) + expect(atemCutObj.content.me.transition).toBe(undefined) }) it('Adds mix to EVS1VO', async () => { @@ -297,7 +297,7 @@ describe('Primary Cue Transitions Without Config', () => { expect(atemCutObj.content.me.transitionSettings?.mix?.rate).toBe(25) }) - it('Cuts by default for SERVER', async () => { + it('Transition is undefined by default for SERVER', async () => { const ingestSegment = _.clone(templateSegment) ingestSegment.payload.iNewsStory.body = '\r\n
' @@ -310,7 +310,7 @@ describe('Primary Cue Transitions Without Config', () => { const piece = getPieceOnLayerFromPart(segment, SourceLayer.PgmServer) const atemCutObj = getATEMMEObj(piece) - expect(atemCutObj.content.me.transition).toBe(TSR.AtemTransitionStyle.CUT) + expect(atemCutObj.content.me.transition).toBe(undefined) }) it('Adds effekt to SERVER', async () => { @@ -327,7 +327,7 @@ describe('Primary Cue Transitions Without Config', () => { const atemCutObj = getATEMMEObj(piece) checkPartExistsWithProperties(segment, getTransitionProperties(MOCK_EFFEKT_2)) - expect(atemCutObj.content.me.transition).toBe(TSR.AtemTransitionStyle.CUT) + expect(atemCutObj.content.me.transition).toBe(undefined) }) it('Adds mix to SERVER', async () => { @@ -354,7 +354,7 @@ describe('Primary Cue Transitions Without Config', () => { expect(atemCutObj.content.me.transitionSettings?.mix?.rate).toBe(20) }) - it('Cuts by default for VO', async () => { + it('Transition is undefined by default for VO', async () => { const ingestSegment = _.clone(templateSegment) ingestSegment.payload.iNewsStory.body = '\r\n
' @@ -367,7 +367,7 @@ describe('Primary Cue Transitions Without Config', () => { const piece = getPieceOnLayerFromPart(segment, SourceLayer.PgmVoiceOver) const atemCutObj = getATEMMEObj(piece) - expect(atemCutObj.content.me.transition).toBe(TSR.AtemTransitionStyle.CUT) + expect(atemCutObj.content.me.transition).toBe(undefined) }) it('Adds effekt to VO', async () => { @@ -384,7 +384,7 @@ describe('Primary Cue Transitions Without Config', () => { const atemCutObj = getATEMMEObj(piece) checkPartExistsWithProperties(segment, getTransitionProperties(MOCK_EFFEKT_1)) - expect(atemCutObj.content.me.transition).toBe(TSR.AtemTransitionStyle.CUT) + expect(atemCutObj.content.me.transition).toBe(undefined) }) it('Adds mix to VO', async () => { diff --git a/src/tv2_offtube_showstyle/__tests__/actions.spec.ts b/src/tv2_offtube_showstyle/__tests__/actions.spec.ts index 1d8b52fd..bd4d9065 100644 --- a/src/tv2_offtube_showstyle/__tests__/actions.spec.ts +++ b/src/tv2_offtube_showstyle/__tests__/actions.spec.ts @@ -409,10 +409,10 @@ function getATEMMEObj(piece: IBlueprintPieceInstance): TSR.TimelineObjAtemME { return atemObj! } -function expectATEMToCut(piece: IBlueprintPieceInstance) { +function expectUndefinedTransition(piece: IBlueprintPieceInstance): void { const atemObj = getATEMMEObj(piece) - expect(atemObj.content.me.transition).toBe(TSR.AtemTransitionStyle.CUT) + expect(atemObj.content.me.transition).toBe(undefined) } function expectATEMToMixOver(piece: IBlueprintPieceInstance, frames: number) { @@ -956,7 +956,7 @@ describe('Combination Actions', () => { validateNextPartExistsWithDuration(context, 0) validateCameraPiece(camPiece) - expectATEMToCut(camPiece!) + expectUndefinedTransition(camPiece!) await executeActionOfftube(context, AdlibActionType.TAKE_WITH_TRANSITION, setMIX20AsTransition) @@ -999,7 +999,7 @@ describe('Combination Actions', () => { validateNextPartExistsWithDuration(context, 0) validateCameraPiece(camPiece) - expectATEMToCut(camPiece!) + expectUndefinedTransition(camPiece!) await executeActionOfftube(context, AdlibActionType.TAKE_WITH_TRANSITION, setMIX20AsTransition) @@ -1042,7 +1042,7 @@ describe('Combination Actions', () => { validateNextPartExistsWithDuration(context, 0) validateCameraPiece(camPiece) - expectATEMToCut(camPiece!) + expectUndefinedTransition(camPiece!) await executeActionOfftube(context, AdlibActionType.TAKE_WITH_TRANSITION, setMIX20AsTransition) @@ -1085,7 +1085,7 @@ describe('Combination Actions', () => { validateNextPartExistsWithDuration(context, 0) validateCameraPiece(camPiece) - expectATEMToCut(camPiece!) + expectUndefinedTransition(camPiece!) await executeActionOfftube(context, AdlibActionType.TAKE_WITH_TRANSITION, setMIX20AsTransition)