Skip to content

Commit 93beb68

Browse files
committed
wip: draft for freeze()-continue()-jumpTo()
1 parent 256bff4 commit 93beb68

File tree

4 files changed

+41
-20
lines changed

4 files changed

+41
-20
lines changed

packages/timeline-state-resolver/src/conductor.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,15 @@ export class Conductor extends EventEmitter<ConductorEvents> {
307307
return realTime - this._totalFrozenDuration
308308
}
309309

310+
public getNowWithFreezeOffset(): number {
311+
// This is the time that is used for resolving the timeline
312+
// It takes into account the frozen state and the accumulated frozen time
313+
// this time should replace Date.Now() multiple places in the code
314+
// Could be placed in a NowHandler() (as the rough example in server/nowHandler.ts)
315+
const realTime = this._options.getCurrentTime?.() ?? Date.now()
316+
return realTime - this._totalFrozenDuration + (this._isFrozen ? this._freezeStartRealTime ?? 0 : 0)
317+
}
318+
310319
// The Freeze/Continue/Seek is meant for rehearsal purposes
311320
// as a lot of equipment could give artifacts in production.
312321
public freeze(): void {
@@ -325,8 +334,6 @@ export class Conductor extends EventEmitter<ConductorEvents> {
325334
this.emit('error', 'Error freezing devices:', error)
326335
})
327336

328-
// Stop timeline resolution
329-
this._stopTriggerResolveTimeline()
330337

331338
this.emit('info', `Timeline frozen at t=${this._freezeTimelineTime}`)
332339
}
@@ -359,7 +366,8 @@ export class Conductor extends EventEmitter<ConductorEvents> {
359366
this._freezeTimelineTime = undefined
360367
}
361368

362-
public seek(seconds: number): void {
369+
public jumpto(seconds: number): void {
370+
this.freeze()
363371
const seekAmount = seconds * 1000
364372

365373
// Update the frozen timeline time if we're currently frozen
@@ -372,14 +380,16 @@ export class Conductor extends EventEmitter<ConductorEvents> {
372380

373381
this._mapAllConnections(true, async (device) => {
374382
// This is to support a visual indication (black)
383+
// And a fade to black in audio devices
375384
// But also to hide any artifacts while seeking
385+
// We need to decide the time to dip under recalculation
386+
// so that the devices can prepare for the new state
387+
// but also not to slow so it feels sluggish
376388
await device.device.dipUnderRecalculation?.()
377389
}).catch((error) => {
378390
this.emit('error', 'Error dip to black on devices:', error)
379391
})
380-
381-
// trigger immediate:
382-
this.resetResolver()
392+
this.continue()
383393
}
384394

385395
/**
@@ -555,14 +565,6 @@ export class Conductor extends EventEmitter<ConductorEvents> {
555565
}
556566
}
557567

558-
/**
559-
* To implement a Freeze a stop any planned triggers
560-
*/
561-
private _stopTriggerResolveTimeline() {
562-
clearTimeout(this._resolveTimelineTrigger)
563-
delete this._resolveTimelineTrigger
564-
}
565-
566568
/**
567569
* Resolves the timeline for the next resolve-time, generates the commands and passes on the commands.
568570
*/
@@ -572,7 +574,10 @@ export class Conductor extends EventEmitter<ConductorEvents> {
572574
.add(async () => {
573575
return this._resolveTimelineInner()
574576
.then((nextResolveTime) => {
575-
this._nextResolveTime = nextResolveTime ?? 0
577+
if (!this._isFrozen) {
578+
// If we're not frozen, we can trigger the next resolve:
579+
this._nextResolveTime = nextResolveTime ?? 0
580+
}
576581
})
577582
.catch((e) => {
578583
this.emit('error', 'Caught error in _resolveTimelineInner' + e)
@@ -592,7 +597,7 @@ export class Conductor extends EventEmitter<ConductorEvents> {
592597

593598
let nextResolveTime = 0
594599
let timeUntilNextResolve = LOOKAHEADTIME
595-
const startTime = Date.now()
600+
const startTime = this.getNowWithFreezeOffset()
596601

597602
const statMeasureStart: number = this._statMeasureStart
598603
let statTimeStateHandled = -1
@@ -764,7 +769,7 @@ export class Conductor extends EventEmitter<ConductorEvents> {
764769
}
765770
)
766771

767-
statTimeStateHandled = Date.now()
772+
statTimeStateHandled = this.getNowWithFreezeOffset()
768773

769774
// Now that we've handled this point in time, it's time to determine what the next point in time is:
770775
const nextEventTime: number | undefined = tlState.nextEvents[0]?.time
@@ -870,7 +875,7 @@ export class Conductor extends EventEmitter<ConductorEvents> {
870875
timelineSizeOld: this._timeline.length,
871876
timelineResolved: statTimeTimelineResolved,
872877
stateHandled: statTimeStateHandled,
873-
done: Date.now(),
878+
done: this.getNowWithFreezeOffset(),
874879
estimatedResolveTime: estimatedResolveTime,
875880
})
876881

packages/timeline-state-resolver/src/integrations/casparCG/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,8 @@ export class CasparCGDevice extends DeviceWithState<State, DeviceOptionsCasparCG
302302
}
303303

304304
async freeze(): Promise<void> {
305+
// This time should be the central time from
306+
// Conductor, or a nowHandler.ts file
305307
this._frozenTime = this.getCurrentTime()
306308

307309
// Pause all media playback

packages/timeline-state-resolver/src/service/device.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@ export abstract class Device<DeviceOptions, DeviceState, Command extends Command
8888
async freeze?(): Promise<void>
8989

9090
/** Continue from frozen state, accounting for the time that passed while frozen */
91-
async continue?(frozenDuration: number): Promise<void>
91+
async continue?(): Promise<void>
9292

9393
/**
9494
* This should be called to make a dip (black or silence) in devices while recalculating
9595
* a new state/position
9696
**/
97-
async dipUnderRecalculation?(): Promise<void>
97+
async dipUnderRecalculation?(dipTime: number): Promise<void>
9898

9999
/** Whether this device supports freeze/continue functionality */
100100
get supportsFreezing(): boolean {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export function dateNow(): number {
2+
return (someExternalClock() ?? Date.now())
3+
}
4+
5+
export function dateNowWithFreezeOffset(): number {
6+
return (someExternalClock || Date.now()) + freezeOffset
7+
}
8+
9+
export function setFreezeOffset(offset: number): void {
10+
freezeOffset = offset
11+
}
12+
export function resetFreezeOffset(): void {
13+
freezeOffset = 0
14+
}

0 commit comments

Comments
 (0)