MCPcopy Create free account
hub / github.com/heygen-com/hyperframes / syncRuntimeMedia

Function syncRuntimeMedia

packages/core/src/runtime/media.ts:132–365  ·  view source on GitHub ↗
(params: {
  clips: RuntimeMediaClip[];
  timeSeconds: number;
  playing: boolean;
  playbackRate: number;
  /** Force-mute every element (parent-frame proxy owns all audio). Asserted per
   *  tick so sub-composition media added mid-playback inherits the silence. */
  outputMuted?: boolean;
  /**
   * User's explicit mute preference (set via `onSetMuted`). Symmetric to
   * `outputMuted` — also asserted per tick — so a sub-composition that
   * activates after the user mutes doesn't briefly play at author volume
   * before the next bridge message lands.
   */
  userMuted?: boolean;
  /**
   * User's volume preference (0–1, set via `onSetVolume`). Multiplied with the
   * per-clip author volume so `data-volume="0.5"` at user volume 0.8 yields 0.4.
   */
  userVolume?: number;
  /**
   * Invoked at most once when a media element's `play()` promise rejects with
   * `NotAllowedError`. The caller is expected to latch and post a single
   * outbound message; further invocations are suppressed by the caller.
   */
  onAutoplayBlocked?: () => void;
  onElementVolume?: (el: HTMLMediaElement, volume: number) => void;
  /** Is THIS element owned by the Web Audio transport? Owned → mute it (transport
   *  plays it); not owned → leave audible (HTMLMedia fallback). Per-element, not a
   *  global flag, so a not-yet-claimed track isn't muted by other tracks. */
  isWebAudioOwned?: (el: HTMLMediaElement) => boolean;
  forceSync?: boolean;
})

Source from the content-addressed store, hash-verified

130
131// fallow-ignore-next-line complexity
132export function syncRuntimeMedia(params: {
133 clips: RuntimeMediaClip[];
134 timeSeconds: number;
135 playing: boolean;
136 playbackRate: number;
137 /** Force-mute every element (parent-frame proxy owns all audio). Asserted per
138 * tick so sub-composition media added mid-playback inherits the silence. */
139 outputMuted?: boolean;
140 /**
141 * User's explicit mute preference (set via `onSetMuted`). Symmetric to
142 * `outputMuted` — also asserted per tick — so a sub-composition that
143 * activates after the user mutes doesn't briefly play at author volume
144 * before the next bridge message lands.
145 */
146 userMuted?: boolean;
147 /**
148 * User's volume preference (0–1, set via `onSetVolume`). Multiplied with the
149 * per-clip author volume so `data-volume="0.5"` at user volume 0.8 yields 0.4.
150 */
151 userVolume?: number;
152 /**
153 * Invoked at most once when a media element's `play()` promise rejects with
154 * `NotAllowedError`. The caller is expected to latch and post a single
155 * outbound message; further invocations are suppressed by the caller.
156 */
157 onAutoplayBlocked?: () => void;
158 onElementVolume?: (el: HTMLMediaElement, volume: number) => void;
159 /** Is THIS element owned by the Web Audio transport? Owned → mute it (transport
160 * plays it); not owned → leave audible (HTMLMedia fallback). Per-element, not a
161 * global flag, so a not-yet-claimed track isn't muted by other tracks. */
162 isWebAudioOwned?: (el: HTMLMediaElement) => boolean;
163 forceSync?: boolean;
164}): void {
165 const forceMuteAll = !!(params.outputMuted || params.userMuted);
166 for (const clip of params.clips) {
167 const { el } = clip;
168 if (!el.isConnected) continue;
169 let relTime = (params.timeSeconds - clip.start) * clip.playbackRate + clip.mediaStart;
170 // An ended non-loop element has played its file to natural completion.
171 // Don't restart it — if the authored duration extends past the file's
172 // actual length, the element sits silently until the composition ends.
173 // (el.ended resets to false when the user scrubs back, so seeks work.)
174 const isActive =
175 params.timeSeconds >= clip.start &&
176 params.timeSeconds <= clip.end &&
177 relTime >= 0 &&
178 (!el.ended || clip.loop);
179 if (isActive) {
180 // Loop wrapping: when media reaches end, restart from mediaStart
181 if (clip.loop && clip.sourceDuration != null && clip.sourceDuration > 0) {
182 const loopLength = clip.sourceDuration - clip.mediaStart;
183 if (loopLength > 0 && relTime >= clip.sourceDuration) {
184 relTime = clip.mediaStart + ((relTime - clip.mediaStart) % loopLength);
185 }
186 }
187 const userVol = clampVolume(params.userVolume ?? 1);
188 const fallbackAuthorVolume = clampVolume(clip.volume ?? 1);
189 const previousRuntimeVolume = lastRuntimeAppliedVolume.get(el);

Callers 2

syncMediaForCurrentStateFunction · 0.90
media.test.tsFile · 0.90

Calls 12

swallowFunction · 0.90
interpolateVolumeGainFunction · 0.85
isUnplayableFunction · 0.85
markPlayRequestedFunction · 0.85
getMethod · 0.80
absMethod · 0.80
addMethod · 0.80
deleteMethod · 0.80
clampVolumeFunction · 0.70
setMethod · 0.65
playMethod · 0.65
pauseMethod · 0.65

Tested by

no test coverage detected