{"version":3,"file":"externalMediaPlaybackTransport.mjs","names":[],"sources":["../../../../src/hooks/playback/internal/externalMediaPlaybackTransport.ts"],"sourcesContent":["import {\n  timelineCommandFail,\n  timelineCommandInvalidInput,\n  timelineCommandOk,\n} from '@techsquidtv/canvas-timeline-core';\nimport type {\n  TimelineCommandResult,\n  ActiveLayerResult,\n  ActiveLayerSelector,\n  MaybePromise,\n  PlaybackOptions,\n  TimelineContentPlaybackStatus,\n  TimelineLayerSyncDetails,\n  TimelineMediaSyncReason,\n} from '@techsquidtv/canvas-timeline-core';\nimport { useTimelineEngine } from '#react/hooks/core/useTimelineEngine';\nimport { useTimelineSelector } from '#react/hooks/core/useTimelineSelector';\nimport { toMediaError } from '#react/hooks/playback/mediaError';\nimport { quantizeTimelineTimeToFrame } from '#react/hooks/playback/playbackFrameTime';\nimport {\n  fromSeconds,\n  fromTimecodeFrameNumber,\n  rationalEquals,\n  resolveTimecodeFrameRate,\n  toSeconds,\n} from '@techsquidtv/canvas-timeline-utils';\nimport type { RationalTime, TimecodeFrameRate } from '@techsquidtv/canvas-timeline-utils';\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\n/**\n * Options for coordinating timeline playback with an external media clock.\n *\n * @remarks\n *\n * Use this hook when your preview surface owns the media clock. The hook tells\n * {@link https://canvastimeline.com/packages/core/api/timeline-engine | TimelineEngine}\n * to play with an external clock, reads timeline seconds from `getClockTime`,\n * and calls `syncLayers` with an {@link ActiveLayerResult} whenever active\n * clips need to be rendered, sought, or paused.\n *\n * Higher-level adapters such as `useHTMLTimelineMedia` and\n * `useMediabunnyTimelineMedia` wrap this contract for common media stacks.\n *\n * @template LayerName - Named media layer keys inferred from `layers`, such as\n * `\"visuals\" | \"audio\"`.\n *\n * @see {@link TimelineLayerSyncDetails}\n * @see {@link useTimelineMediaSync}\n * @see {@link https://canvastimeline.com/demos/media-preview-sync | Mediabunny media sync demo}\n */\nexport interface UseTimelineMediaPlaybackOptions<LayerName extends string = string> {\n  /** Returns current timeline seconds from the external media clock. */\n  getClockTime: () => number;\n  /** Optional sequence frame rate used to quantize playback updates to project frames. */\n  frameRate?: TimecodeFrameRate;\n  /** Core playback range policy applied to the external clock; looping is configured by `loop`. */\n  playbackOptions?: Omit<PlaybackOptions, 'clock' | 'loop'>;\n  /** Stops the external media clock when timeline playback pauses or leaves active content. */\n  stopClock?: () => void;\n  /** Applies a validated playback-rate change to the external media clock. */\n  setClockRate?: (playbackRate: number) => void;\n  /** Named active layer selectors used by the external media surface. */\n  layers: Record<LayerName, ActiveLayerSelector>;\n  /** Synchronizes external rendering, audio, text, or effects for the active layers. */\n  syncLayers?: (details: TimelineLayerSyncDetails<LayerName>) => MaybePromise<void>;\n  /**\n   * Enables looping and realigns the external clock after Core returns to the range start.\n   * The callback is invoked once per loop transition until the clock re-enters range.\n   * Its presence and implementation are captured when each playback run begins.\n   */\n  loop?: (\n    timelineTime: RationalTime,\n    activeLayers: ActiveLayerResult<LayerName>\n  ) => MaybePromise<void>;\n  /** Receives high-level playback status changes. */\n  onStatus?: (status: TimelineContentPlaybackStatus) => void;\n  /** Receives external rendering or scheduling failures before playback is paused. */\n  onError?: (error: Error) => void;\n}\n\ninterface PlaybackFrameCursor {\n  frameNumber: number;\n  frameRate: number;\n}\n\ninterface LayerSynchronizationResult<LayerName extends string> {\n  activeLayers: ActiveLayerResult<LayerName>;\n  error?: Error;\n  superseded?: boolean;\n}\n\ninterface TimelineMediaPlaybackSynchronizationRunner {\n  run: <Result>(operation: () => Promise<Result>, superseded: () => Result) => Promise<Result>;\n}\n\nfunction getPlaybackFrameNumber(seconds: number, frameRate: TimecodeFrameRate) {\n  const resolvedFrameRate = resolveTimecodeFrameRate(frameRate);\n  const frameNumber = Math.max(0, Math.floor(Math.max(0, seconds) * resolvedFrameRate + 1e-9));\n  return { frameNumber, frameRate: resolvedFrameRate };\n}\n\nfunction createCompetingClockFailure(): TimelineCommandResult {\n  return timelineCommandFail(\n    'policy-rejected',\n    'Timeline playback is already controlled by another clock.'\n  );\n}\n\n/**\n * Result returned by `useTimelineMediaPlayback`.\n *\n * @remarks\n *\n * These commands operate on the timeline engine and resolve\n * {@link TimelineCommandResult} values so toolbar code can show disabled,\n * content-gap, invalid-input, or synchronization feedback without reading\n * private engine state. Playback and rate changes await serialized adapter\n * synchronization before resolving.\n */\nexport interface UseTimelineMediaPlaybackResult {\n  /** Whether the timeline engine is currently playing against the external clock. */\n  playing: boolean;\n  /** Current timeline playback speed multiplier. */\n  playbackRate: number;\n  /** Starts playback after active external layers finish synchronizing. */\n  play: () => Promise<TimelineCommandResult>;\n  /** Stops timeline playback and synchronizes external media into a paused state. */\n  pause: () => TimelineCommandResult;\n  /** Updates both clocks and resolves after active layers resynchronize. */\n  setPlaybackRate: (rate: number) => Promise<TimelineCommandResult>;\n}\n\n/**\n * Coordinates timeline playback with an external media clock.\n *\n * @remarks\n *\n * The hook is media-library agnostic. Apps provide a clock and a single layer\n * sync callback while the hook advances the TimelineEngine playhead and decides\n * when active layer clips need to be resynchronized.\n *\n * @param options - External media clock, active layer selectors, sync callback, and status callback.\n * @template LayerName - Named media layer keys inferred from `options.layers`,\n * such as `\"visuals\" | \"audio\"`.\n * @returns Timeline playback state and commands backed by the external media clock.\n *\n * @example\n * ```tsx\n * import { useMemo, useRef } from 'react';\n * import { useTimelineMediaPlayback } from '@techsquidtv/canvas-timeline-react/hooks';\n *\n * const previewLayers = {\n *   visuals: { trackKind: 'visual' },\n *   audio: { trackKind: 'audio' },\n * } as const;\n *\n * export function CustomClockTransport() {\n *   const clockSecondsRef = useRef(0);\n *   const layers = useMemo(() => previewLayers, []);\n *   const playback = useTimelineMediaPlayback({\n *     layers,\n *     getClockTime: () => clockSecondsRef.current,\n *     stopClock: () => {\n *       clockSecondsRef.current = 0;\n *     },\n *     syncLayers: ({ activeLayers, reason }) => {\n *       const visualClip = activeLayers.primary.visuals?.clip;\n *       console.info(reason, visualClip?.id ?? 'blank frame');\n *     },\n *   });\n *\n *   return (\n *     <button type=\"button\" onClick={() => playback.playing ? playback.pause() : playback.play()}>\n *       {playback.playing ? 'Pause' : 'Play'}\n *     </button>\n *   );\n * }\n * ```\n *\n * @see {@link ActiveLayerSelector}\n * @see {@link ActiveLayerResult}\n * @see {@link useTimelineMediaSync}\n * @see {@link https://canvastimeline.com/docs/react-hooks | React editor hooks}\n */\nexport function useTimelineMediaPlayback<LayerName extends string = string>(\n  options: UseTimelineMediaPlaybackOptions<LayerName>\n): UseTimelineMediaPlaybackResult {\n  return useTimelineMediaPlaybackInternal(options);\n}\n\nexport function useTimelineMediaPlaybackInternal<LayerName extends string = string>(\n  options: UseTimelineMediaPlaybackOptions<LayerName>,\n  delegatedSynchronization?: TimelineMediaPlaybackSynchronizationRunner\n): UseTimelineMediaPlaybackResult {\n  const engine = useTimelineEngine();\n  const state = useTimelineSelector((state) => ({\n    playbackRate: state.playbackRate,\n    playing: state.playing,\n  }));\n  const optionsRef = useRef(options);\n  const animationFrameRef = useRef<number | null>(null);\n  const pausingRef = useRef(false);\n  const playbackFrameCursorRef = useRef<PlaybackFrameCursor | null>(null);\n  const loopTransitionRef = useRef<object | null>(null);\n  const playbackGenerationRef = useRef(0);\n  const playbackStartGenerationRef = useRef<number | null>(null);\n  const ownsPlaybackRef = useRef(false);\n  const synchronizationQueueRef = useRef<Promise<void>>(Promise.resolve());\n\n  useEffect(() => {\n    optionsRef.current = options;\n  }, [options]);\n\n  const cancelTick = useCallback(() => {\n    if (animationFrameRef.current !== null) {\n      cancelAnimationFrame(animationFrameRef.current);\n      animationFrameRef.current = null;\n    }\n  }, []);\n\n  const hasCompetingClock = useCallback(\n    () => engine.getState().playing && !ownsPlaybackRef.current,\n    [engine]\n  );\n\n  const getActiveLayers = useCallback(\n    (time: RationalTime) => {\n      return engine.media.getActiveLayers({\n        time,\n        layers: optionsRef.current.layers,\n      });\n    },\n    [engine]\n  );\n\n  const runSynchronization = useCallback(\n    <Result>(synchronize: () => Promise<Result>, superseded: () => Result) => {\n      if (delegatedSynchronization !== undefined) {\n        return delegatedSynchronization.run(synchronize, superseded);\n      }\n\n      const pendingSynchronization = synchronizationQueueRef.current.then(synchronize, synchronize);\n      synchronizationQueueRef.current = pendingSynchronization.then(\n        () => undefined,\n        () => undefined\n      );\n      return pendingSynchronization;\n    },\n    [delegatedSynchronization]\n  );\n\n  const enqueueBestEffortSynchronization = useCallback(\n    (\n      syncLayers: UseTimelineMediaPlaybackOptions<LayerName>['syncLayers'],\n      details: TimelineLayerSyncDetails<LayerName>,\n      generation: number\n    ) => {\n      const synchronize = async () => {\n        if (playbackGenerationRef.current !== generation) {\n          return;\n        }\n        try {\n          await syncLayers?.(details);\n        } catch {\n          // Adapter cleanup should not throw during pause or unmount.\n        }\n      };\n      void runSynchronization(synchronize, () => undefined);\n    },\n    [runSynchronization]\n  );\n\n  const pause = useCallback(\n    (status: TimelineContentPlaybackStatus = 'paused', force = false) => {\n      if (\n        !force &&\n        status === 'paused' &&\n        animationFrameRef.current === null &&\n        playbackStartGenerationRef.current === null &&\n        !engine.getState().playing\n      ) {\n        return;\n      }\n      if (pausingRef.current) {\n        return;\n      }\n\n      pausingRef.current = true;\n      const ownedEnginePlayback = ownsPlaybackRef.current;\n      const generation = playbackGenerationRef.current + 1;\n      playbackGenerationRef.current = generation;\n      playbackStartGenerationRef.current = null;\n      ownsPlaybackRef.current = false;\n      cancelTick();\n      playbackFrameCursorRef.current = null;\n      loopTransitionRef.current = null;\n      try {\n        const timelineTime = engine.getTime();\n        const currentOptions = optionsRef.current;\n        currentOptions.stopClock?.();\n        enqueueBestEffortSynchronization(\n          currentOptions.syncLayers,\n          {\n            activeLayers: getActiveLayers(timelineTime),\n            timelineTime,\n            reason: status === 'content-gap' ? 'gap' : 'pause',\n          },\n          generation\n        );\n        if (ownedEnginePlayback && engine.getState().playing) {\n          engine.pause();\n        }\n        currentOptions.onStatus?.(status);\n      } finally {\n        pausingRef.current = false;\n      }\n    },\n    [cancelTick, engine, enqueueBestEffortSynchronization, getActiveLayers]\n  );\n\n  const handleAdapterFailure = useCallback(\n    (cause: unknown, generation: number) => {\n      const syncError = toMediaError(cause);\n      if (playbackGenerationRef.current !== generation) {\n        return syncError;\n      }\n      pause('paused', true);\n      optionsRef.current.onError?.(syncError);\n      return syncError;\n    },\n    [pause]\n  );\n\n  const synchronizeLayersNow = useCallback(\n    async (\n      timelineTime: RationalTime,\n      reason: TimelineMediaSyncReason,\n      generation: number,\n      activeLayers: ActiveLayerResult<LayerName>\n    ): Promise<LayerSynchronizationResult<LayerName>> => {\n      if (playbackGenerationRef.current !== generation) {\n        return { activeLayers, superseded: true };\n      }\n\n      try {\n        await optionsRef.current.syncLayers?.({\n          activeLayers,\n          timelineTime,\n          reason,\n        });\n      } catch (syncError) {\n        if (playbackGenerationRef.current !== generation) {\n          return { activeLayers, superseded: true };\n        }\n        return { activeLayers, error: handleAdapterFailure(syncError, generation) };\n      }\n\n      return playbackGenerationRef.current === generation\n        ? { activeLayers }\n        : { activeLayers, superseded: true };\n    },\n    [handleAdapterFailure]\n  );\n\n  const synchronizeLayers = useCallback(\n    (\n      timelineTime: RationalTime,\n      reason: TimelineMediaSyncReason,\n      generation: number\n    ): Promise<LayerSynchronizationResult<LayerName>> => {\n      const activeLayers = getActiveLayers(timelineTime);\n      return runSynchronization(\n        () => synchronizeLayersNow(timelineTime, reason, generation, activeLayers),\n        () => ({ activeLayers, superseded: true })\n      );\n    },\n    [getActiveLayers, runSynchronization, synchronizeLayersNow]\n  );\n\n  const pausePlayback = useCallback(() => {\n    if (hasCompetingClock() && playbackStartGenerationRef.current === null) {\n      return createCompetingClockFailure();\n    }\n    pause('paused');\n    return timelineCommandOk();\n  }, [hasCompetingClock, pause]);\n\n  const play = useCallback(async () => {\n    if (engine.getState().playing) {\n      return hasCompetingClock() ? createCompetingClockFailure() : timelineCommandOk();\n    }\n\n    const generation = playbackGenerationRef.current + 1;\n    playbackGenerationRef.current = generation;\n    playbackStartGenerationRef.current = generation;\n    const currentOptions = optionsRef.current;\n    const loop = currentOptions.loop;\n    loopTransitionRef.current = null;\n    const currentTime = engine.getTime();\n    const playbackOptions = {\n      ...currentOptions.playbackOptions,\n      loop: loop !== undefined,\n    };\n    const resolvedStartTime = engine.getPlaybackStartTime(playbackOptions);\n    const startTime = quantizeTimelineTimeToFrame(resolvedStartTime, currentOptions.frameRate);\n    if (!rationalEquals(currentTime, startTime)) {\n      engine.setTime(startTime);\n    }\n    playbackFrameCursorRef.current =\n      currentOptions.frameRate === undefined\n        ? null\n        : getPlaybackFrameNumber(toSeconds(startTime), currentOptions.frameRate);\n    const synchronization = await synchronizeLayers(startTime, 'play', generation);\n    const { activeLayers } = synchronization;\n    if (synchronization.superseded === true) {\n      return timelineCommandFail('policy-rejected', 'Playback start was superseded.');\n    }\n    if (synchronization.error !== undefined) {\n      return timelineCommandFail(\n        'sync-failed',\n        'External media synchronization failed.',\n        synchronization.error\n      );\n    }\n    if (!activeLayers.hasActiveClips) {\n      pause('content-gap');\n      return timelineCommandFail('content-gap');\n    }\n\n    if (engine.getState().playing) {\n      if (playbackStartGenerationRef.current === generation) {\n        playbackStartGenerationRef.current = null;\n      }\n      return hasCompetingClock() ? createCompetingClockFailure() : timelineCommandOk();\n    }\n\n    try {\n      optionsRef.current.setClockRate?.(engine.getPlaybackRate());\n    } catch (clockError) {\n      return timelineCommandFail(\n        'sync-failed',\n        'External media clock rate could not be updated.',\n        handleAdapterFailure(clockError, generation)\n      );\n    }\n\n    const started = engine.play({ ...playbackOptions, clock: 'external' });\n    if (!started) {\n      if (playbackStartGenerationRef.current === generation) {\n        playbackStartGenerationRef.current = null;\n      }\n      return timelineCommandFail('unsupported');\n    }\n\n    if (playbackStartGenerationRef.current === generation) {\n      playbackStartGenerationRef.current = null;\n    }\n    ownsPlaybackRef.current = true;\n    optionsRef.current.onStatus?.('playing');\n\n    const tick = async () => {\n      if (playbackGenerationRef.current !== generation) {\n        return;\n      }\n      if (!engine.getState().playing) {\n        pause('paused');\n        return;\n      }\n\n      const currentOptions = optionsRef.current;\n      const clockSeconds = currentOptions.getClockTime();\n      let timelineTime: RationalTime;\n\n      if (currentOptions.frameRate === undefined) {\n        playbackFrameCursorRef.current = null;\n        timelineTime = fromSeconds(clockSeconds, engine.getTime().r);\n      } else {\n        const nextCursor = getPlaybackFrameNumber(clockSeconds, currentOptions.frameRate);\n        const currentCursor = playbackFrameCursorRef.current;\n        if (\n          currentCursor?.frameNumber === nextCursor.frameNumber &&\n          currentCursor.frameRate === nextCursor.frameRate\n        ) {\n          animationFrameRef.current = requestAnimationFrame(() => {\n            void tick();\n          });\n          return;\n        }\n\n        playbackFrameCursorRef.current = nextCursor;\n        timelineTime = fromTimecodeFrameNumber(\n          nextCursor.frameNumber,\n          currentOptions.frameRate,\n          engine.getTime().r\n        );\n      }\n\n      const update = engine.updateExternalPlaybackTime(timelineTime);\n      if (update.action === 'pause') {\n        pause('paused');\n        return;\n      }\n      const nextActiveLayers = getActiveLayers(update.time);\n      if (update.action !== 'loop') {\n        loopTransitionRef.current = null;\n      } else if (loopTransitionRef.current === null) {\n        const transition = {};\n        loopTransitionRef.current = transition;\n        try {\n          await loop?.(update.time, nextActiveLayers);\n        } catch (loopError) {\n          if (loopTransitionRef.current === transition) {\n            handleAdapterFailure(loopError, generation);\n          }\n          return;\n        }\n      }\n      if (playbackGenerationRef.current !== generation) {\n        return;\n      }\n\n      const synchronization = await synchronizeLayers(update.time, 'tick', generation);\n      if (synchronization.superseded === true || synchronization.error !== undefined) {\n        return;\n      }\n      if (!synchronization.activeLayers.hasActiveClips) {\n        pause('content-gap');\n        return;\n      }\n\n      if (playbackGenerationRef.current === generation) {\n        animationFrameRef.current = requestAnimationFrame(() => {\n          void tick();\n        });\n      }\n    };\n\n    animationFrameRef.current = requestAnimationFrame(() => {\n      void tick();\n    });\n    return timelineCommandOk();\n  }, [engine, getActiveLayers, handleAdapterFailure, hasCompetingClock, pause, synchronizeLayers]);\n\n  const setPlaybackRate = useCallback(\n    (rate: number) =>\n      runSynchronization(\n        async () => {\n          if (hasCompetingClock()) {\n            return createCompetingClockFailure();\n          }\n\n          const previousRate = engine.getPlaybackRate();\n          try {\n            engine.setPlaybackRate(rate);\n          } catch (rateError) {\n            return timelineCommandInvalidInput(\n              'Playback rate must be a positive finite number.',\n              rateError\n            );\n          }\n\n          try {\n            optionsRef.current.setClockRate?.(rate);\n          } catch (clockError) {\n            engine.setPlaybackRate(previousRate);\n            try {\n              optionsRef.current.setClockRate?.(previousRate);\n            } catch {\n              // Preserve the original external-clock failure in the command result.\n            }\n            return timelineCommandFail(\n              'sync-failed',\n              'External media clock rate could not be updated.',\n              toMediaError(clockError)\n            );\n          }\n\n          const timelineTime = engine.getTime();\n          const synchronization = await synchronizeLayersNow(\n            timelineTime,\n            'rate',\n            playbackGenerationRef.current,\n            getActiveLayers(timelineTime)\n          );\n          if (synchronization.superseded === true) {\n            return timelineCommandFail('policy-rejected', 'Playback rate change was superseded.');\n          }\n          if (synchronization.error !== undefined) {\n            return timelineCommandFail(\n              'sync-failed',\n              'External media synchronization failed.',\n              synchronization.error\n            );\n          }\n          return timelineCommandOk();\n        },\n        () => timelineCommandFail('policy-rejected', 'Playback rate change was superseded.')\n      ),\n    [engine, getActiveLayers, hasCompetingClock, runSynchronization, synchronizeLayersNow]\n  );\n\n  useEffect(\n    () =>\n      engine.on('playback:state', (playing) => {\n        if (!playing) {\n          pause('paused');\n        }\n      }),\n    [engine, pause]\n  );\n\n  useEffect(\n    () => () => {\n      const ownedEnginePlayback = ownsPlaybackRef.current;\n      const ownedMediaWork = ownedEnginePlayback || playbackStartGenerationRef.current !== null;\n      const generation = playbackGenerationRef.current + 1;\n      playbackGenerationRef.current = generation;\n      playbackStartGenerationRef.current = null;\n      ownsPlaybackRef.current = false;\n      cancelTick();\n      if (!ownedMediaWork) {\n        return;\n      }\n      const timelineTime = engine.getTime();\n      const currentOptions = optionsRef.current;\n      currentOptions.stopClock?.();\n      enqueueBestEffortSynchronization(\n        currentOptions.syncLayers,\n        {\n          activeLayers: getActiveLayers(timelineTime),\n          timelineTime,\n          reason: 'pause',\n        },\n        generation\n      );\n      if (ownedEnginePlayback && engine.getState().playing) {\n        engine.pause();\n      }\n    },\n    [cancelTick, engine, enqueueBestEffortSynchronization, getActiveLayers]\n  );\n\n  return useMemo(\n    () => ({\n      playing: state.playing ?? false,\n      playbackRate: state.playbackRate ?? 1,\n      play,\n      pause: pausePlayback,\n      setPlaybackRate,\n    }),\n    [pausePlayback, play, setPlaybackRate, state.playing, state.playbackRate]\n  );\n}\n"],"mappings":"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"}