{"version":3,"file":"av-cliper.umd.cjs","sources":["../src/dom-utils.ts","../src/av-utils.ts","../src/clips/iclip.ts","../src/mp4-utils/mp4box-utils.ts","../src/clips/mp4-clip.ts","../src/clips/img-clip.ts","../src/clips/audio-clip.ts","../src/clips/media-stream-clip.ts","../src/clips/embed-subtitles-clip.ts","../src/mp4-utils/sample-transform.ts","../src/mp4-utils/index.ts","../src/chromakey.ts","../src/sprite/rect.ts","../src/sprite/base-sprite.ts","../src/sprite/offscreen-sprite.ts","../src/sprite/visible-sprite.ts","../src/combinator.ts"],"sourcesContent":["// 在主线程中执行的 工具函数\n\n/**\n * 创建一个新的 HTML 元素\n * @param tagName - 要创建的元素的标签名\n * @returns 新创建的 HTML 元素\n */\nexport function createEl(tagName: string): HTMLElement {\n  return document.createElement(tagName);\n}\n\nfunction arrayBufferToBase64(buffer: ArrayBuffer) {\n  var binary = '';\n  var bytes = new Uint8Array(buffer);\n  var len = bytes.byteLength;\n  for (let i = 0; i < len; i++) {\n    binary += String.fromCharCode(bytes[i]);\n  }\n  return window.btoa(binary);\n}\n\n/**\n * 将文本渲染为图片\n * @param txt - 要渲染的文本\n * @param cssText - 应用于文本的 CSS 样式\n * @returns 渲染后的图片元素\n */\nexport async function renderTxt2Img(\n  txt: string,\n  cssText: string,\n  opts: {\n    font?: { name: string; url: string };\n    onCreated?: (el: HTMLElement) => void;\n  } = {},\n): Promise<HTMLImageElement> {\n  const div = createEl('pre');\n  div.style.cssText = `margin: 0; ${cssText}; position: fixed;`;\n  div.textContent = txt;\n  document.body.appendChild(div);\n  opts.onCreated?.(div);\n\n  const { width, height } = div.getBoundingClientRect();\n  // 计算出 rect，立即从dom移除\n  div.remove();\n\n  const img = new Image();\n  img.width = width;\n  img.height = height;\n  const fontFaceStr =\n    opts.font == null\n      ? ''\n      : `\n    @font-face {\n      font-family: '${opts.font.name}';\n      src: url('data:font/woff2;base64,${arrayBufferToBase64(await (await fetch(opts.font.url)).arrayBuffer())}') format('woff2');\n    }\n  `;\n  const svgStr = `\n    <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\">\n      <style>\n        ${fontFaceStr}\n      </style>\n      <foreignObject width=\"100%\" height=\"100%\">\n        <div xmlns=\"http://www.w3.org/1999/xhtml\">${div.outerHTML}</div>\n      </foreignObject>\n    </svg>\n  `\n    .replace(/\\t/g, '')\n    .replace(/#/g, '%23');\n\n  img.src = `data:image/svg+xml;charset=utf-8,${svgStr}`;\n\n  await new Promise((resolve) => {\n    img.onload = resolve;\n  });\n  return img;\n}\n\n/**\n * 将文本渲染为 {@link ImageBitmap}，用来创建 {@link ImgClip}\n * @param txt - 要渲染的文本\n * @param cssText - 应用于文本的 CSS 样式\n * @param opts - 选项\n * @param opts.font -  自定义字体\n * @param opts.onCreated - 创建完成后的回调\n *\n * @example\n * new ImgClip(\n *   await renderTxt2ImgBitmap(\n *     '水印',\n *    `font-size:40px; color: white; text-shadow: 2px 2px 6px red; font-family: CustomFont;`,\n *    {\n *      font: {\n *        name: 'CustomFont',\n *        url: '/CustomFont.ttf',\n *      },\n *    },\n *   )\n * )\n */\nexport async function renderTxt2ImgBitmap(\n  txt: string,\n  cssText: string,\n  opts: {\n    font?: { name: string; url: string };\n    onCreated?: (el: HTMLElement) => void;\n  } = {},\n): Promise<ImageBitmap> {\n  const imgEl = await renderTxt2Img(txt, cssText, opts);\n  const cvs = new OffscreenCanvas(imgEl.width, imgEl.height);\n  const ctx = cvs.getContext('2d');\n  ctx?.drawImage(imgEl, 0, 0, imgEl.width, imgEl.height);\n  return await createImageBitmap(cvs);\n}\n","// 能同时在 worker 和主线程中运行的工具函数\n\nimport { workerTimer } from '@webav/internal-utils';\nimport * as waveResampler from 'wave-resampler';\n\nimport { Log } from '@webav/internal-utils';\n\nif (import.meta.env?.DEV) {\n  Log.setLogLevel(Log.debug);\n}\n\nif (import.meta.env?.MODE === 'test') {\n  Log.setLogLevel(Log.warn);\n}\n\n/**\n * 合并（串联）多个 Float32Array，通常用于合并 PCM 数据\n */\nexport function concatFloat32Array(bufs: Float32Array[]): Float32Array {\n  const rs = new Float32Array(\n    bufs.map((buf) => buf.length).reduce((a, b) => a + b),\n  );\n\n  let offset = 0;\n  for (const buf of bufs) {\n    rs.set(buf, offset);\n    offset += buf.length;\n  }\n\n  return rs;\n}\n\n/**\n * 将小片段的 PCM 合并成一个大片段\n * @param fragments 小片段 PCM，子元素是不同声道的原始 PCM 数据\n */\nexport function concatPCMFragments(\n  fragments: Float32Array[][],\n): Float32Array[] {\n  // fragments: [[chan0, chan1], [chan0, chan1]...]\n  // chanListPCM: [[chan0, chan0...], [chan1, chan1...]]\n  const chanListPCM: Float32Array[][] = [];\n  for (let i = 0; i < fragments.length; i += 1) {\n    for (let j = 0; j < fragments[i].length; j += 1) {\n      if (chanListPCM[j] == null) chanListPCM[j] = [];\n      chanListPCM[j].push(fragments[i][j]);\n    }\n  }\n  // [bigChan0, bigChan1]\n  return chanListPCM.map(concatFloat32Array);\n}\n\n/**\n * 从 AudioData 中提取 PCM 数据的工具函数\n */\nexport function extractPCM4AudioData(ad: AudioData): Float32Array[] {\n  if (ad.format === 'f32-planar') {\n    const rs = [];\n    for (let idx = 0; idx < ad.numberOfChannels; idx += 1) {\n      const chanBufSize = ad.allocationSize({ planeIndex: idx });\n      const chanBuf = new ArrayBuffer(chanBufSize);\n      ad.copyTo(chanBuf, { planeIndex: idx });\n      rs.push(new Float32Array(chanBuf));\n    }\n    return rs;\n  } else if (ad.format === 'f32') {\n    const buf = new ArrayBuffer(ad.allocationSize({ planeIndex: 0 }));\n    ad.copyTo(buf, { planeIndex: 0 });\n    return convertF32ToPlanar(new Float32Array(buf), ad.numberOfChannels);\n  } else if (ad.format === 's16') {\n    const buf = new ArrayBuffer(ad.allocationSize({ planeIndex: 0 }));\n    ad.copyTo(buf, { planeIndex: 0 });\n    return convertS16ToF32Planar(new Int16Array(buf), ad.numberOfChannels);\n  }\n  throw Error('Unsupported audio data format');\n}\n\n/**\n * Convert s16 PCM to f32-planar\n * @param  pcmS16Data - The s16 PCM data.\n * @param  numChannels - Number of audio channels.\n * @returns An array of Float32Array, each containing the audio data for one channel.\n */\nfunction convertS16ToF32Planar(pcmS16Data: Int16Array, numChannels: number) {\n  const numSamples = pcmS16Data.length / numChannels;\n  const planarData = Array.from(\n    { length: numChannels },\n    () => new Float32Array(numSamples),\n  );\n\n  for (let i = 0; i < numSamples; i++) {\n    for (let channel = 0; channel < numChannels; channel++) {\n      const sample = pcmS16Data[i * numChannels + channel];\n      planarData[channel][i] = sample / 32768; // Normalize to range [-1.0, 1.0]\n    }\n  }\n\n  return planarData;\n}\n\nfunction convertF32ToPlanar(pcmF32Data: Float32Array, numChannels: number) {\n  const numSamples = pcmF32Data.length / numChannels;\n  const planarData = Array.from(\n    { length: numChannels },\n    () => new Float32Array(numSamples),\n  );\n\n  for (let i = 0; i < numSamples; i++) {\n    for (let channel = 0; channel < numChannels; channel++) {\n      planarData[channel][i] = pcmF32Data[i * numChannels + channel];\n    }\n  }\n\n  return planarData;\n}\n\n/**\n * 从 AudioBuffer 中提取 PCM\n */\nexport function extractPCM4AudioBuffer(ab: AudioBuffer): Float32Array[] {\n  return Array(ab.numberOfChannels)\n    .fill(0)\n    .map((_, idx) => {\n      return ab.getChannelData(idx);\n    });\n}\n\n/**\n * 调整音频数据的音量\n * @param ad - 要调整的音频对象\n * @param volume - 音量调整系数（0.0 - 1.0）\n * @returns 调整音量后的新音频数据\n */\nexport function adjustAudioDataVolume(ad: AudioData, volume: number) {\n  const data = new Float32Array(\n    concatFloat32Array(extractPCM4AudioData(ad)),\n  ).map((v) => v * volume);\n  const newAd = new AudioData({\n    sampleRate: ad.sampleRate,\n    numberOfChannels: ad.numberOfChannels,\n    timestamp: ad.timestamp,\n    format: ad.format!,\n    numberOfFrames: ad.numberOfFrames,\n    data,\n  });\n  ad.close();\n  return newAd;\n}\n\n/**\n * 解码图像流，返回一个视频帧数组。\n *\n * @param stream - 包含图像数据的可读流。\n * @param type - 图像的 MIME 类型，例如 'image/jpeg'。\n *\n * @returns 返回一个 Promise，该 Promise 在解码完成后解析为 {@link VideoFrame} 数组。\n *\n * @see [解码动图](https://webav-tech.github.io/WebAV/demo/1_3-decode-image)\n *\n * @example\n *\n * const frames = await decodeImg(\n *   (await fetch('<gif url>')).body!,\n *   `image/gif`,\n * );\n */\nexport async function decodeImg(\n  stream: ReadableStream<Uint8Array>,\n  type: string,\n): Promise<VideoFrame[]> {\n  const init = {\n    type,\n    data: stream,\n  };\n  const imageDecoder = new ImageDecoder(init);\n\n  await Promise.all([imageDecoder.completed, imageDecoder.tracks.ready]);\n\n  let frameCnt = imageDecoder.tracks.selectedTrack?.frameCount ?? 1;\n\n  const rs: VideoFrame[] = [];\n  for (let i = 0; i < frameCnt; i += 1) {\n    rs.push((await imageDecoder.decode({ frameIndex: i })).image);\n  }\n  return rs;\n}\n\n/**\n * 混合双通道音轨的 PCM 数据，并将多声道并排成一个 Float32Array 输出\n * @param audios - 一个二维数组，每个元素是一个 Float32Array 数组，代表一个音频流的 PCM 数据。\n * 每个 Float32Array 数组的第一个元素是左声道数据，第二个元素（如果有）是右声道数据。\n * 如果只有左声道数据，则右声道将复用左声道数据。\n *\n * @returns 返回一个 Float32Array，返回结果是将这个一个音轨的左右声道并排成 Float32Array。\n *\n * @example\n *\n * const audios = [\n *   [new Float32Array([0.1, 0.2, 0.3]), new Float32Array([0.4, 0.5, 0.6])],\n *   [new Float32Array([0.7, 0.8, 0.9])],\n * ];\n * const mixed = mixinPCM(audios);\n */\nexport function mixinPCM(audios: Float32Array[][]): Float32Array {\n  const maxLen = Math.max(...audios.map((a) => a[0]?.length ?? 0));\n  const data = new Float32Array(maxLen * 2);\n\n  for (let bufIdx = 0; bufIdx < maxLen; bufIdx++) {\n    let chan0 = 0;\n    let chan1 = 0;\n    for (let trackIdx = 0; trackIdx < audios.length; trackIdx++) {\n      const _c0 = audios[trackIdx][0]?.[bufIdx] ?? 0;\n      // 如果是单声道 PCM，第二声道复用第一声道数据\n      const _c1 = audios[trackIdx][1]?.[bufIdx] ?? _c0;\n      chan0 += _c0;\n      chan1 += _c1;\n    }\n    data[bufIdx] = chan0;\n    data[bufIdx + maxLen] = chan1;\n  }\n\n  return data;\n}\n\n/**\n * 对 PCM 音频数据进行重采样。\n *\n * @param pcmData - 一个 Float32Array 数组，每个元素代表一个声道的 PCM 数据。\n * @param curRate - 当前的采样率。\n * @param target - 目标参数对象。\n * @param target.rate - 目标采样率。\n * @param target.chanCount - 目标声道数。\n *\n * @returns 返回一个 Promise，该 Promise 在重采样完成后解析为一个 Float32Array 数组，每个元素代表一个声道的 PCM 数据。\n *\n * @example\n *\n * const pcmData = [new Float32Array([0.1, 0.2, 0.3]), new Float32Array([0.4, 0.5, 0.6])];\n * const curRate = 44100;\n * const target = { rate: 48000, chanCount: 2 };\n * const resampled = await audioResample(pcmData, curRate, target);\n */\nexport async function audioResample(\n  pcmData: Float32Array[],\n  curRate: number,\n  target: {\n    rate: number;\n    chanCount: number;\n  },\n): Promise<Float32Array[]> {\n  const chanCnt = pcmData.length;\n  const emptyPCM = Array(target.chanCount)\n    .fill(0)\n    .map(() => new Float32Array(0));\n  if (chanCnt === 0) return emptyPCM;\n\n  const len = Math.max(...pcmData.map((c) => c.length));\n  if (len === 0) return emptyPCM;\n\n  // The Worker scope does not have access to OfflineAudioContext\n  if (globalThis.OfflineAudioContext == null) {\n    return pcmData.map(\n      (p) =>\n        new Float32Array(\n          waveResampler.resample(p, curRate, target.rate, {\n            method: 'sinc',\n            LPF: false,\n          }),\n        ),\n    );\n  }\n\n  const ctx = new globalThis.OfflineAudioContext(\n    target.chanCount,\n    (len * target.rate) / curRate,\n    target.rate,\n  );\n  const abSource = ctx.createBufferSource();\n  const ab = ctx.createBuffer(chanCnt, len, curRate);\n  pcmData.forEach((d, idx) => ab.copyToChannel(d, idx));\n\n  abSource.buffer = ab;\n  abSource.connect(ctx.destination);\n  abSource.start();\n\n  return extractPCM4AudioBuffer(await ctx.startRendering());\n}\n\n/**\n * 使当前执行环境暂停一段时间。\n * @param time - 暂停的时间，单位为毫秒。\n * @example\n * await sleep(1000);  // 暂停 1 秒\n */\nexport function sleep(time: number): Promise<void> {\n  return new Promise((resolve) => {\n    const stop = workerTimer(() => {\n      stop();\n      resolve();\n    }, time);\n  });\n}\n\n/**\n * 从给定的 Float32Array 中提取一个环形切片，超出边界从 0 开始循环\n *\n * 主要用于截取 PCM 实现循环播放\n *\n * @param data - 输入的 Float32Array。\n * @param start - 切片的开始索引。\n * @param end - 切片的结束索引。\n * @returns - 返回一个新的 Float32Array，包含从 start 到 end 的数据。\n *\n * @example\n * const data = new Float32Array([0, 1, 2, 3, 4, 5]);\n * ringSliceFloat32Array(data, 4, 6); // => Float32Array [4, 5, 0]\n */\nexport function ringSliceFloat32Array(\n  data: Float32Array,\n  start: number,\n  end: number,\n): Float32Array {\n  const cnt = end - start;\n  const rs = new Float32Array(cnt);\n  let i = 0;\n  while (i < cnt) {\n    rs[i] = data[(start + i) % data.length];\n    i += 1;\n  }\n  return rs;\n}\n\n/**\n * 改变 PCM 数据的播放速率，1 表示正常播放，0.5 表示播放速率减半，2 表示播放速率加倍\n */\nexport function changePCMPlaybackRate(\n  pcmData: Float32Array,\n  playbackRate: number,\n) {\n  // 计算新的采样率\n  const newLength = Math.floor(pcmData.length / playbackRate);\n  const newPcmData = new Float32Array(newLength);\n\n  // 线性插值\n  for (let i = 0; i < newLength; i++) {\n    // 原始数据中的位置\n    const originalIndex = i * playbackRate;\n    const intIndex = Math.floor(originalIndex);\n    const frac = originalIndex - intIndex;\n\n    // 边界检查\n    if (intIndex + 1 < pcmData.length) {\n      newPcmData[i] =\n        pcmData[intIndex] * (1 - frac) + pcmData[intIndex + 1] * frac;\n    } else {\n      newPcmData[i] = pcmData[intIndex]; // 最后一个样本\n    }\n  }\n\n  return newPcmData;\n}\n","interface IClipMeta {\n  width: number;\n  height: number;\n  duration: number;\n}\n\n/**\n * 所有素材需要实现的接口\n *\n * 素材（Clip）是不同数据类型的抽象，给其他模块提供数据\n *\n * WebAV 内置了 {@link MP4Clip}, {@link AudioClip}, {@link ImgClip}, {@link MediaStreamClip} 等常用素材，用于给 {@link Combinator} {@link AVCanvas} 提供数据\n *\n * 你只需实现该接口即可自定义素材，拥有最大的灵活度来生成视频内容，比如动画、转场效果等\n * @see [自定义素材](https://webav-tech.github.io/WebAV/demo/2_6-custom-clip)\n *\n */\nexport interface IClip {\n  /**\n   * 从素材中提取指定时间数据\n   * @param time 时间，单位 微秒\n   */\n  tick: (time: number) => Promise<{\n    video?: VideoFrame | ImageBitmap | null;\n    audio?: Float32Array[];\n    state: 'done' | 'success';\n  }>;\n\n  /**\n   * 当素材准备完成，ready 会切换到 resolved 状态\n   */\n  readonly ready: Promise<IClipMeta>;\n\n  /**\n   * 数据元数据\n   */\n  readonly meta: IClipMeta;\n\n  /**\n   * clone，返回一个新素材\n   */\n  clone: () => Promise<this>;\n\n  /**\n   * 按指定时间切割，返回该时刻前后两个新素材，常用于剪辑场景按时间分割素材\n   *\n   * 该方法不会破坏原素材的数据\n   *\n   * @param time 时间，微秒\n   * @returns\n   */\n  split?: (time: number) => Promise<[this, this]>;\n\n  /**\n   * 销毁实例，释放资源\n   */\n  destroy: () => void;\n}\n\n/**\n * 默认的音频设置，⚠️ 不要变更它的值 ⚠️\n */\nexport const DEFAULT_AUDIO_CONF = {\n  sampleRate: 48000,\n  channelCount: 2,\n  codec: 'mp4a.40.2',\n} as const;\n","import mp4box, {\n  AudioTrackOpts,\n  ESDSBoxParser,\n  MP4ABoxParser,\n  MP4ArrayBuffer,\n  MP4File,\n  MP4Info,\n  MP4Sample,\n  TrakBoxParser,\n  VideoTrackOpts,\n} from '@webav/mp4box.js';\nimport { file } from 'opfs-tools';\nimport { DEFAULT_AUDIO_CONF } from '../clips';\n\nexport function extractFileConfig(file: MP4File, info: MP4Info) {\n  const vTrack = info.videoTracks[0];\n  const rs: {\n    videoTrackConf?: VideoTrackOpts;\n    videoDecoderConf?: Parameters<VideoDecoder['configure']>[0];\n    audioTrackConf?: AudioTrackOpts;\n    audioDecoderConf?: Parameters<AudioDecoder['configure']>[0];\n  } = {};\n  if (vTrack != null) {\n    const videoDesc = parseVideoCodecDesc(file.getTrackById(vTrack.id))?.buffer;\n    const { descKey, type } = vTrack.codec.startsWith('avc1')\n      ? { descKey: 'avcDecoderConfigRecord', type: 'avc1' }\n      : vTrack.codec.startsWith('hvc1')\n        ? { descKey: 'hevcDecoderConfigRecord', type: 'hvc1' }\n        : { descKey: '', type: '' };\n    if (descKey !== '') {\n      rs.videoTrackConf = {\n        timescale: vTrack.timescale,\n        duration: vTrack.duration,\n        width: vTrack.video.width,\n        height: vTrack.video.height,\n        brands: info.brands,\n        type,\n        [descKey]: videoDesc,\n      };\n    }\n\n    rs.videoDecoderConf = {\n      codec: vTrack.codec,\n      codedHeight: vTrack.video.height,\n      codedWidth: vTrack.video.width,\n      description: videoDesc as ArrayBuffer,\n    };\n  }\n\n  const aTrack = info.audioTracks[0];\n  if (aTrack != null) {\n    const esdsBox = getESDSBoxFromMP4File(file);\n    rs.audioTrackConf = {\n      timescale: aTrack.timescale,\n      samplerate: aTrack.audio.sample_rate,\n      channel_count: aTrack.audio.channel_count,\n      hdlr: 'soun',\n      type: aTrack.codec.startsWith('mp4a') ? 'mp4a' : aTrack.codec,\n      description: getESDSBoxFromMP4File(file),\n    };\n    rs.audioDecoderConf = {\n      codec: aTrack.codec.startsWith('mp4a')\n        ? DEFAULT_AUDIO_CONF.codec\n        : aTrack.codec,\n      numberOfChannels: aTrack.audio.channel_count,\n      sampleRate: aTrack.audio.sample_rate,\n      ...(esdsBox == null ? {} : parseAudioInfo4ESDSBox(esdsBox)),\n    };\n  }\n  return rs;\n}\n\n// track is H.264, H.265 or VPX.\nfunction parseVideoCodecDesc(track: TrakBoxParser): Uint8Array | undefined {\n  for (const entry of track.mdia.minf.stbl.stsd.entries) {\n    // @ts-expect-error\n    const box = entry.avcC ?? entry.hvcC ?? entry.av1C ?? entry.vpcC;\n    if (box != null) {\n      const stream = new mp4box.DataStream(\n        undefined,\n        0,\n        mp4box.DataStream.BIG_ENDIAN,\n      );\n      box.write(stream);\n      return new Uint8Array(stream.buffer.slice(8)); // Remove the box header.\n    }\n  }\n  return undefined;\n}\n\nfunction getESDSBoxFromMP4File(file: MP4File, codec = 'mp4a') {\n  const mp4aBox = file.moov?.traks\n    .map((t) => t.mdia.minf.stbl.stsd.entries)\n    .flat()\n    .find(({ type }) => type === codec) as MP4ABoxParser;\n\n  return mp4aBox?.esds;\n}\n\n// 解决封装层音频信息标识错误，导致解码异常\nfunction parseAudioInfo4ESDSBox(esds: ESDSBoxParser) {\n  const decoderConf = esds.esd.descs[0]?.descs[0];\n  if (decoderConf == null) return {};\n\n  const [byte1, byte2] = decoderConf.data;\n  // sampleRate 是第一字节后 3bit + 第二字节前 1bit\n  const sampleRateIdx = ((byte1 & 0x07) << 1) + (byte2 >> 7);\n  // numberOfChannels 是第二字节 [2, 5] 4bit\n  const numberOfChannels = (byte2 & 0x7f) >> 3;\n  const sampleRateEnum = [\n    96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025,\n    8000, 7350,\n  ] as const;\n  return {\n    sampleRate: sampleRateEnum[sampleRateIdx],\n    numberOfChannels,\n  };\n}\n\n/**\n * 快速解析 mp4 文件，如果是非 fMP4 格式，会优先解析 moov box（略过 mdat）避免占用过多内存\n */\nexport async function quickParseMP4File(\n  reader: Awaited<ReturnType<ReturnType<typeof file>['createReader']>>,\n  onReady: (data: { mp4boxFile: MP4File; info: MP4Info }) => void,\n  onSamples: (\n    id: number,\n    sampleType: 'video' | 'audio',\n    samples: MP4Sample[],\n  ) => void,\n) {\n  const mp4boxFile = mp4box.createFile(false);\n  mp4boxFile.onReady = (info) => {\n    onReady({ mp4boxFile, info });\n    const vTrackId = info.videoTracks[0]?.id;\n    if (vTrackId != null)\n      mp4boxFile.setExtractionOptions(vTrackId, 'video', { nbSamples: 100 });\n\n    const aTrackId = info.audioTracks[0]?.id;\n    if (aTrackId != null)\n      mp4boxFile.setExtractionOptions(aTrackId, 'audio', { nbSamples: 100 });\n\n    mp4boxFile.start();\n  };\n  mp4boxFile.onSamples = onSamples;\n\n  await parse();\n\n  async function parse() {\n    let cursor = 0;\n    const maxReadSize = 30 * 1024 * 1024;\n    while (true) {\n      const data = (await reader.read(maxReadSize, {\n        at: cursor,\n      })) as MP4ArrayBuffer;\n      if (data.byteLength === 0) break;\n      data.fileStart = cursor;\n      const nextPos = mp4boxFile.appendBuffer(data);\n      if (nextPos == null) break;\n      cursor = nextPos;\n    }\n\n    mp4boxFile.stop();\n  }\n}\n","import { Log } from '@webav/internal-utils';\nimport { MP4Info, MP4Sample } from '@webav/mp4box.js';\nimport { file, tmpfile, write } from 'opfs-tools';\nimport { audioResample, extractPCM4AudioData, sleep } from '../av-utils';\nimport {\n  extractFileConfig,\n  quickParseMP4File,\n} from '../mp4-utils/mp4box-utils';\nimport { DEFAULT_AUDIO_CONF, IClip } from './iclip';\n\nlet CLIP_ID = 0;\n\ntype OPFSToolFile = ReturnType<typeof file>;\nfunction isOTFile(obj: any): obj is OPFSToolFile {\n  return obj.kind === 'file' && obj.createReader instanceof Function;\n}\n\n// 用于内部创建 MP4Clip 实例\ntype MPClipCloneArgs = Awaited<ReturnType<typeof mp4FileToSamples>> & {\n  localFile: OPFSToolFile;\n};\n\ninterface MP4DecoderConf {\n  video: VideoDecoderConfig | null;\n  audio: AudioDecoderConfig | null;\n}\n\ninterface MP4ClipOpts {\n  audio?: boolean | { volume: number };\n  /**\n   * 不安全，随时可能废弃\n   */\n  __unsafe_hardwareAcceleration__?: HardwarePreference;\n}\n\ntype ExtMP4Sample = Omit<MP4Sample, 'data'> & {\n  is_idr: boolean;\n  deleted?: boolean;\n  data: null | Uint8Array;\n};\n\ntype LocalFileReader = Awaited<ReturnType<OPFSToolFile['createReader']>>;\n\ntype ThumbnailOpts = {\n  start: number;\n  end: number;\n  step: number;\n};\n\n/**\n * MP4 素材，解析 MP4 文件，使用 {@link MP4Clip.tick} 按需解码指定时间的图像帧\n *\n * 可用于实现视频抽帧、生成缩略图、视频编辑等功能\n *\n * @example\n * new MP4Clip((await fetch('<mp4 url>')).body)\n * new MP4Clip(mp4File.stream())\n *\n * @see {@link Combinator}\n * @see [AVCanvas](../../av-canvas/classes/AVCanvas.html)\n *\n * @see [解码播放视频](https://webav-tech.github.io/WebAV/demo/1_1-decode-video)\n */\nexport class MP4Clip implements IClip {\n  #insId = CLIP_ID++;\n\n  #log = Log.create(`MP4Clip id:${this.#insId},`);\n\n  ready: IClip['ready'];\n\n  #destroyed = false;\n\n  #meta = {\n    // 微秒\n    duration: 0,\n    width: 0,\n    height: 0,\n    audioSampleRate: 0,\n    audioChanCount: 0,\n  };\n\n  get meta() {\n    return { ...this.#meta };\n  }\n\n  #localFile: OPFSToolFile;\n\n  #headerBoxPos: Array<{ start: number; size: number }> = [];\n  /**\n   * 提供视频头（box: ftyp, moov）的二进制数据\n   * 使用任意 mp4 demxer 解析即可获得详细的视频信息\n   * 单元测试包含使用 mp4box.js 解析示例代码\n   */\n  async getFileHeaderBinData() {\n    await this.ready;\n    const oFile = await this.#localFile.getOriginFile();\n    if (oFile == null) throw Error('MP4Clip localFile is not origin file');\n\n    return await new Blob(\n      this.#headerBoxPos.map(({ start, size }) =>\n        oFile.slice(start, start + size),\n      ),\n    ).arrayBuffer();\n  }\n\n  #volume = 1;\n\n  #videoSamples: ExtMP4Sample[] = [];\n\n  #audioSamples: ExtMP4Sample[] = [];\n\n  #videoFrameFinder: VideoFrameFinder | null = null;\n  #audioFrameFinder: AudioFrameFinder | null = null;\n\n  #decoderConf: {\n    video: VideoDecoderConfig | null;\n    audio: AudioDecoderConfig | null;\n  } = {\n    video: null,\n    audio: null,\n  };\n\n  #opts: MP4ClipOpts = { audio: true };\n\n  constructor(\n    source: OPFSToolFile | ReadableStream<Uint8Array> | MPClipCloneArgs,\n    opts: MP4ClipOpts = {},\n  ) {\n    if (\n      !(source instanceof ReadableStream) &&\n      !isOTFile(source) &&\n      !Array.isArray(source.videoSamples)\n    ) {\n      throw Error('Illegal argument');\n    }\n\n    this.#opts = { audio: true, ...opts };\n    this.#volume =\n      typeof opts.audio === 'object' && 'volume' in opts.audio\n        ? opts.audio.volume\n        : 1;\n\n    const initByStream = async (s: ReadableStream) => {\n      await write(this.#localFile, s);\n      return this.#localFile;\n    };\n\n    this.#localFile = isOTFile(source)\n      ? source\n      : 'localFile' in source\n        ? source.localFile // from clone\n        : tmpfile();\n\n    this.ready = (\n      source instanceof ReadableStream\n        ? initByStream(source).then((otFile) =>\n            mp4FileToSamples(otFile, this.#opts),\n          )\n        : isOTFile(source)\n          ? mp4FileToSamples(source, this.#opts)\n          : Promise.resolve(source)\n    ).then(\n      async ({ videoSamples, audioSamples, decoderConf, headerBoxPos }) => {\n        this.#videoSamples = videoSamples;\n        this.#audioSamples = audioSamples;\n        this.#decoderConf = decoderConf;\n        this.#headerBoxPos = headerBoxPos;\n\n        const { videoFrameFinder, audioFrameFinder } = genDecoder(\n          {\n            video:\n              decoderConf.video == null\n                ? null\n                : {\n                    ...decoderConf.video,\n                    hardwareAcceleration:\n                      this.#opts.__unsafe_hardwareAcceleration__,\n                  },\n            audio: decoderConf.audio,\n          },\n          await this.#localFile.createReader(),\n          videoSamples,\n          audioSamples,\n          this.#opts.audio !== false ? this.#volume : 0,\n        );\n        this.#videoFrameFinder = videoFrameFinder;\n        this.#audioFrameFinder = audioFrameFinder;\n\n        this.#meta = genMeta(decoderConf, videoSamples, audioSamples);\n        this.#log.info('MP4Clip meta:', this.#meta);\n        return { ...this.#meta };\n      },\n    );\n  }\n\n  /**\n   * 拦截 {@link MP4Clip.tick} 方法返回的数据，用于对图像、音频数据二次处理\n   * @param time 调用 tick 的时间\n   * @param tickRet tick 返回的数据\n   *\n   * @see [移除视频绿幕背景](https://webav-tech.github.io/WebAV/demo/3_2-chromakey-video)\n   */\n  tickInterceptor: <T extends Awaited<ReturnType<MP4Clip['tick']>>>(\n    time: number,\n    tickRet: T,\n  ) => Promise<T> = async (_, tickRet) => tickRet;\n\n  /**\n   * 获取素材指定时刻的图像帧、音频数据\n   * @param time 微秒\n   */\n  async tick(time: number): Promise<{\n    video?: VideoFrame;\n    audio: Float32Array[];\n    state: 'success' | 'done';\n  }> {\n    if (time >= this.#meta.duration) {\n      return await this.tickInterceptor(time, {\n        audio: (await this.#audioFrameFinder?.find(time)) ?? [],\n        state: 'done',\n      });\n    }\n\n    const [audio, video] = await Promise.all([\n      this.#audioFrameFinder?.find(time) ?? [],\n      this.#videoFrameFinder?.find(time),\n    ]);\n\n    if (video == null) {\n      return await this.tickInterceptor(time, {\n        audio,\n        state: 'success',\n      });\n    }\n\n    return await this.tickInterceptor(time, {\n      video,\n      audio,\n      state: 'success',\n    });\n  }\n\n  #thumbAborter = new AbortController();\n  /**\n   * 生成缩略图，默认每个关键帧生成一个 100px 宽度的缩略图。\n   *\n   * @param imgWidth 缩略图宽度，默认 100\n   * @param opts Partial<ThumbnailOpts>\n   * @returns Promise<Array<{ ts: number; img: Blob }>>\n   */\n  async thumbnails(\n    imgWidth = 100,\n    opts?: Partial<ThumbnailOpts>,\n  ): Promise<Array<{ ts: number; img: Blob }>> {\n    this.#thumbAborter.abort();\n    this.#thumbAborter = new AbortController();\n    const aborterSignal = this.#thumbAborter.signal;\n\n    await this.ready;\n    const abortMsg = 'generate thumbnails aborted';\n    if (aborterSignal.aborted) throw Error(abortMsg);\n\n    const { width, height } = this.#meta;\n    const convtr = createVF2BlobConvtr(\n      imgWidth,\n      Math.round(height * (imgWidth / width)),\n      { quality: 0.1, type: 'image/png' },\n    );\n\n    return new Promise<Array<{ ts: number; img: Blob }>>(\n      async (resolve, reject) => {\n        let pngPromises: Array<{ ts: number; img: Promise<Blob> }> = [];\n        const vc = this.#decoderConf.video;\n        if (vc == null || this.#videoSamples.length === 0) {\n          resolver();\n          return;\n        }\n        aborterSignal.addEventListener('abort', () => {\n          reject(Error(abortMsg));\n        });\n\n        async function resolver() {\n          if (aborterSignal.aborted) return;\n          resolve(\n            await Promise.all(\n              pngPromises.map(async (it) => ({\n                ts: it.ts,\n                img: await it.img,\n              })),\n            ),\n          );\n        }\n\n        function pushPngPromise(vf: VideoFrame) {\n          pngPromises.push({\n            ts: vf.timestamp,\n            img: convtr(vf),\n          });\n        }\n\n        const { start = 0, end = this.#meta.duration, step } = opts ?? {};\n        if (step) {\n          let cur = start;\n          // 创建一个新的 VideoFrameFinder 实例，避免与 tick 方法共用而导致冲突\n          const videoFrameFinder = new VideoFrameFinder(\n            await this.#localFile.createReader(),\n            this.#videoSamples,\n            {\n              ...vc,\n              hardwareAcceleration: this.#opts.__unsafe_hardwareAcceleration__,\n            },\n          );\n          while (cur <= end && !aborterSignal.aborted) {\n            const vf = await videoFrameFinder.find(cur);\n            if (vf) pushPngPromise(vf);\n            cur += step;\n          }\n          videoFrameFinder.destroy();\n          resolver();\n        } else {\n          await thumbnailByKeyFrame(\n            this.#videoSamples,\n            this.#localFile,\n            vc,\n            aborterSignal,\n            { start, end },\n            (vf, done) => {\n              if (vf != null) pushPngPromise(vf);\n              if (done) resolver();\n            },\n          );\n        }\n      },\n    );\n  }\n\n  async split(time: number) {\n    await this.ready;\n\n    if (time <= 0 || time >= this.#meta.duration)\n      throw Error('\"time\" out of bounds');\n\n    const [preVideoSlice, postVideoSlice] = splitVideoSampleByTime(\n      this.#videoSamples,\n      time,\n    );\n    const [preAudioSlice, postAudioSlice] = splitAudioSampleByTime(\n      this.#audioSamples,\n      time,\n    );\n    const preClip = new MP4Clip(\n      {\n        localFile: this.#localFile,\n        videoSamples: preVideoSlice ?? [],\n        audioSamples: preAudioSlice ?? [],\n        decoderConf: this.#decoderConf,\n        headerBoxPos: this.#headerBoxPos,\n      },\n      this.#opts,\n    );\n    const postClip = new MP4Clip(\n      {\n        localFile: this.#localFile,\n        videoSamples: postVideoSlice ?? [],\n        audioSamples: postAudioSlice ?? [],\n        decoderConf: this.#decoderConf,\n        headerBoxPos: this.#headerBoxPos,\n      },\n      this.#opts,\n    );\n    await Promise.all([preClip.ready, postClip.ready]);\n\n    return [preClip, postClip] as [this, this];\n  }\n\n  async clone() {\n    await this.ready;\n    const clip = new MP4Clip(\n      {\n        localFile: this.#localFile,\n        videoSamples: [...this.#videoSamples],\n        audioSamples: [...this.#audioSamples],\n        decoderConf: this.#decoderConf,\n        headerBoxPos: this.#headerBoxPos,\n      },\n      this.#opts,\n    );\n    await clip.ready;\n    clip.tickInterceptor = this.tickInterceptor;\n    return clip as this;\n  }\n\n  /**\n   * 拆分 MP4Clip 为仅包含视频轨道和音频轨道的 MP4Clip\n   * @returns Mp4CLip[]\n   */\n  async splitTrack() {\n    await this.ready;\n    const clips: MP4Clip[] = [];\n    if (this.#videoSamples.length > 0) {\n      const videoClip = new MP4Clip(\n        {\n          localFile: this.#localFile,\n          videoSamples: [...this.#videoSamples],\n          audioSamples: [],\n          decoderConf: {\n            video: this.#decoderConf.video,\n            audio: null,\n          },\n          headerBoxPos: this.#headerBoxPos,\n        },\n        this.#opts,\n      );\n      await videoClip.ready;\n      videoClip.tickInterceptor = this.tickInterceptor;\n      clips.push(videoClip);\n    }\n    if (this.#audioSamples.length > 0) {\n      const audioClip = new MP4Clip(\n        {\n          localFile: this.#localFile,\n          videoSamples: [],\n          audioSamples: [...this.#audioSamples],\n          decoderConf: {\n            audio: this.#decoderConf.audio,\n            video: null,\n          },\n          headerBoxPos: this.#headerBoxPos,\n        },\n        this.#opts,\n      );\n      await audioClip.ready;\n      audioClip.tickInterceptor = this.tickInterceptor;\n      clips.push(audioClip);\n    }\n\n    return clips;\n  }\n\n  destroy(): void {\n    if (this.#destroyed) return;\n    this.#log.info('MP4Clip destroy');\n    this.#destroyed = true;\n\n    this.#videoFrameFinder?.destroy();\n    this.#audioFrameFinder?.destroy();\n  }\n}\n\nfunction genMeta(\n  decoderConf: MP4DecoderConf,\n  videoSamples: ExtMP4Sample[],\n  audioSamples: ExtMP4Sample[],\n) {\n  const meta = {\n    duration: 0,\n    width: 0,\n    height: 0,\n    audioSampleRate: 0,\n    audioChanCount: 0,\n  };\n  if (decoderConf.video != null && videoSamples.length > 0) {\n    meta.width = decoderConf.video.codedWidth ?? 0;\n    meta.height = decoderConf.video.codedHeight ?? 0;\n  }\n  if (decoderConf.audio != null && audioSamples.length > 0) {\n    meta.audioSampleRate = DEFAULT_AUDIO_CONF.sampleRate;\n    meta.audioChanCount = DEFAULT_AUDIO_CONF.channelCount;\n  }\n\n  let vDuration = 0;\n  let aDuration = 0;\n  if (videoSamples.length > 0) {\n    for (let i = videoSamples.length - 1; i >= 0; i--) {\n      const s = videoSamples[i];\n      if (s.deleted) continue;\n      vDuration = s.cts + s.duration;\n      break;\n    }\n  }\n  if (audioSamples.length > 0) {\n    const lastSampele = audioSamples.at(-1)!;\n    aDuration = lastSampele.cts + lastSampele.duration;\n  }\n  meta.duration = Math.max(vDuration, aDuration);\n\n  return meta;\n}\n\nfunction genDecoder(\n  decoderConf: MP4DecoderConf,\n  localFileReader: LocalFileReader,\n  videoSamples: ExtMP4Sample[],\n  audioSamples: ExtMP4Sample[],\n  volume: number,\n) {\n  return {\n    audioFrameFinder:\n      volume === 0 || decoderConf.audio == null || audioSamples.length === 0\n        ? null\n        : new AudioFrameFinder(\n            localFileReader,\n            audioSamples,\n            decoderConf.audio,\n            {\n              volume,\n              targetSampleRate: DEFAULT_AUDIO_CONF.sampleRate,\n            },\n          ),\n    videoFrameFinder:\n      decoderConf.video == null || videoSamples.length === 0\n        ? null\n        : new VideoFrameFinder(\n            localFileReader,\n            videoSamples,\n            decoderConf.video,\n          ),\n  };\n}\n\nasync function mp4FileToSamples(otFile: OPFSToolFile, opts: MP4ClipOpts = {}) {\n  let mp4Info: MP4Info | null = null;\n  const decoderConf: MP4DecoderConf = { video: null, audio: null };\n  let videoSamples: ExtMP4Sample[] = [];\n  let audioSamples: ExtMP4Sample[] = [];\n  let headerBoxPos: Array<{ start: number; size: number }> = [];\n\n  let videoDeltaTS = -1;\n  let audioDeltaTS = -1;\n  let findedFirstSync = false;\n  const reader = await otFile.createReader();\n  await quickParseMP4File(\n    reader,\n    (data) => {\n      mp4Info = data.info;\n      const ftyp = data.mp4boxFile.ftyp!;\n      headerBoxPos.push({ start: ftyp.start, size: ftyp.size });\n      const moov = data.mp4boxFile.moov!;\n      headerBoxPos.push({ start: moov.start, size: moov.size });\n\n      let { videoDecoderConf: vc, audioDecoderConf: ac } = extractFileConfig(\n        data.mp4boxFile,\n        data.info,\n      );\n      decoderConf.video = vc ?? null;\n      decoderConf.audio = ac ?? null;\n      if (vc == null && ac == null) {\n        Log.error('MP4Clip no video and audio track');\n      }\n      Log.info(\n        'mp4BoxFile moov ready',\n        {\n          ...data.info,\n          tracks: null,\n          videoTracks: null,\n          audioTracks: null,\n        },\n        decoderConf,\n      );\n    },\n    (_, type, samples) => {\n      if (type === 'video') {\n        if (videoDeltaTS === -1) videoDeltaTS = samples[0].dts;\n        for (let i = 0; i < samples.length; i++) {\n          const s = samples[i];\n          if (!findedFirstSync && s.is_sync) {\n            findedFirstSync = true;\n            videoSamples.push(\n              normalizeTimescale(s, videoDeltaTS, 'video', true),\n            );\n          } else {\n            videoSamples.push(\n              normalizeTimescale(s, videoDeltaTS, 'video', false),\n            );\n          }\n        }\n      } else if (type === 'audio' && opts.audio) {\n        if (audioDeltaTS === -1) audioDeltaTS = samples[0].dts;\n        for (const s of samples) {\n          audioSamples.push(normalizeTimescale(s, audioDeltaTS, 'audio'));\n        }\n      }\n    },\n  );\n  await reader.close();\n\n  const lastSampele = videoSamples.at(-1) ?? audioSamples.at(-1);\n  if (mp4Info == null) {\n    throw Error('MP4Clip stream is done, but not emit ready');\n  } else if (lastSampele == null) {\n    throw Error('MP4Clip stream not contain any sample');\n  }\n  // 修复首帧黑帧\n  fixFirstBlackFrame(videoSamples);\n  Log.info('mp4 stream parsed');\n  return {\n    videoSamples,\n    audioSamples,\n    decoderConf,\n    headerBoxPos,\n  };\n}\n\nfunction normalizeTimescale(\n  s: MP4Sample,\n  delta = 0,\n  sampleType: 'video' | 'audio',\n  isFirstSync?: boolean,\n) {\n  // todo: perf 丢弃多余字段，小尺寸对象性能更好\n  let offset = s.offset;\n  const isVideoSync = sampleType === 'video' && s.is_sync;\n  const idrOffset = isVideoSync\n    ? idrNALUOffset(s.data, s.description.type, offset)\n    : -1;\n\n  // 默认信任第一个关键帧 是 IDR 帧，兼容某些异常标注的视频文件\n  let is_idr = isFirstSync === true && isVideoSync;\n  let size = s.size;\n  if (idrOffset >= 0) {\n    // 当 IDR 帧前面包含非图像帧数据（如 SEI），可能导致解码失败\n    // 所以此处通过控制 offset、size 字段 跳过非图像帧数据\n    offset = idrOffset;\n    size -= idrOffset - offset;\n    // 非第一个关键帧，如果根据 naluType 判定是 IDR 帧，则设置 is_idr\n    is_idr = true;\n  }\n\n  return {\n    ...s,\n    is_idr,\n    offset,\n    size,\n    cts: ((s.cts - delta) / s.timescale) * 1e6,\n    dts: ((s.dts - delta) / s.timescale) * 1e6,\n    duration: (s.duration / s.timescale) * 1e6,\n    timescale: 1e6,\n    // 音频数据量可控，直接保存在内存中\n    data: sampleType === 'video' ? null : s.data,\n  };\n}\n\nclass VideoFrameFinder {\n  #dec: VideoDecoder | null = null;\n  constructor(\n    public localFileReader: LocalFileReader,\n    public samples: ExtMP4Sample[],\n    public conf: VideoDecoderConfig,\n  ) {}\n\n  #ts = 0;\n  #curAborter = { abort: false, st: performance.now() };\n  find = async (time: number): Promise<VideoFrame | null> => {\n    if (\n      this.#dec == null ||\n      this.#dec.state === 'closed' ||\n      time <= this.#ts ||\n      time - this.#ts > 3e6\n    ) {\n      this.#reset(time);\n    }\n\n    this.#curAborter.abort = true;\n    this.#ts = time;\n\n    this.#curAborter = { abort: false, st: performance.now() };\n    const vf = await this.#parseFrame(time, this.#dec, this.#curAborter);\n    this.#sleepCnt = 0;\n    return vf;\n  };\n\n  // fix VideoFrame duration is null\n  #lastVfDur = 0;\n\n  #downgradeSoftDecode = false;\n  #videoDecCusorIdx = 0;\n  #videoFrames: VideoFrame[] = [];\n  #outputFrameCnt = 0;\n  #inputChunkCnt = 0;\n  #sleepCnt = 0;\n  #predecodeErr = false;\n  #parseFrame = async (\n    time: number,\n    dec: VideoDecoder | null,\n    aborter: { abort: boolean; st: number },\n  ): Promise<VideoFrame | null> => {\n    if (dec == null || dec.state === 'closed' || aborter.abort) return null;\n\n    if (this.#videoFrames.length > 0) {\n      const vf = this.#videoFrames[0];\n      if (time < vf.timestamp) return null;\n      // 弹出第一帧\n      this.#videoFrames.shift();\n      // 第一帧过期，找下一帧\n      if (time > vf.timestamp + (vf.duration ?? 0)) {\n        vf.close();\n        return await this.#parseFrame(time, dec, aborter);\n      }\n\n      if (!this.#predecodeErr && this.#videoFrames.length < 10) {\n        // 预解码 避免等待\n        this.#startDecode(dec).catch((err) => {\n          this.#predecodeErr = true;\n          this.#reset(time);\n          throw err;\n        });\n      }\n      // 符合期望\n      return vf;\n    }\n\n    // 缺少帧数据\n    if (\n      this.#decoding ||\n      (this.#outputFrameCnt < this.#inputChunkCnt && dec.decodeQueueSize > 0)\n    ) {\n      if (performance.now() - aborter.st > 6e3) {\n        throw Error(\n          `MP4Clip.tick video timeout, ${JSON.stringify(this.#getState())}`,\n        );\n      }\n      // 解码中，等待，然后重试\n      this.#sleepCnt += 1;\n      await sleep(15);\n    } else if (this.#videoDecCusorIdx >= this.samples.length) {\n      // decode completed\n      return null;\n    } else {\n      try {\n        await this.#startDecode(dec);\n      } catch (err) {\n        this.#reset(time);\n        throw err;\n      }\n    }\n    return await this.#parseFrame(time, dec, aborter);\n  };\n\n  #decoding = false;\n  #startDecode = async (dec: VideoDecoder) => {\n    if (this.#decoding || dec.decodeQueueSize > 600) return;\n\n    // 启动解码任务，然后重试\n    let endIdx = this.#videoDecCusorIdx + 1;\n    if (endIdx > this.samples.length) return;\n\n    this.#decoding = true;\n    // 该 GoP 时间区间有时间匹配，且未被删除的帧\n    let hasValidFrame = false;\n    for (; endIdx < this.samples.length; endIdx++) {\n      const s = this.samples[endIdx];\n      if (!hasValidFrame && !s.deleted) {\n        hasValidFrame = true;\n      }\n      // 找一个 GoP，所以是下一个 IDR 帧结束\n      if (s.is_idr) break;\n    }\n\n    if (hasValidFrame) {\n      const samples = this.samples.slice(this.#videoDecCusorIdx, endIdx);\n      if (samples[0]?.is_idr !== true) {\n        Log.warn('First sample not idr frame');\n      } else {\n        const readStarTime = performance.now();\n        const chunks = await videosamples2Chunks(samples, this.localFileReader);\n\n        const readCost = performance.now() - readStarTime;\n        if (readCost > 1000) {\n          const first = samples[0];\n          const last = samples.at(-1)!;\n          const rangSize = last.offset + last.size - first.offset;\n          Log.warn(\n            `Read video samples time cost: ${Math.round(readCost)}ms, file chunk size: ${rangSize}`,\n          );\n        }\n        // Wait for the previous asynchronous operation to complete, at which point the task may have already been terminated\n        if (dec.state === 'closed') return;\n\n        this.#lastVfDur = chunks[0]?.duration ?? 0;\n        decodeGoP(dec, chunks, {\n          onDecodingError: (err) => {\n            if (this.#downgradeSoftDecode) {\n              throw err;\n            } else if (this.#outputFrameCnt === 0) {\n              this.#downgradeSoftDecode = true;\n              Log.warn('Downgrade to software decode');\n              this.#reset();\n            }\n          },\n        });\n\n        this.#inputChunkCnt += chunks.length;\n      }\n    }\n    this.#videoDecCusorIdx = endIdx;\n    this.#decoding = false;\n  };\n\n  #reset = (time?: number) => {\n    this.#decoding = false;\n    this.#videoFrames.forEach((f) => f.close());\n    this.#videoFrames = [];\n    if (time == null || time === 0) {\n      this.#videoDecCusorIdx = 0;\n    } else {\n      let keyIdx = 0;\n      for (let i = 0; i < this.samples.length; i++) {\n        const s = this.samples[i];\n        if (s.is_idr) keyIdx = i;\n        if (s.cts < time) continue;\n        this.#videoDecCusorIdx = keyIdx;\n        break;\n      }\n    }\n    this.#inputChunkCnt = 0;\n    this.#outputFrameCnt = 0;\n    if (this.#dec?.state !== 'closed') this.#dec?.close();\n    const encoderConf = {\n      ...this.conf,\n      ...(this.#downgradeSoftDecode\n        ? { hardwareAcceleration: 'prefer-software' }\n        : {}),\n    } as VideoDecoderConfig;\n    this.#dec = new VideoDecoder({\n      output: (vf) => {\n        this.#outputFrameCnt += 1;\n        if (vf.timestamp === -1) {\n          vf.close();\n          return;\n        }\n        let rsVf = vf;\n        if (vf.duration == null) {\n          rsVf = new VideoFrame(vf, {\n            duration: this.#lastVfDur,\n          });\n          vf.close();\n        }\n        this.#videoFrames.push(rsVf);\n      },\n      error: (err) => {\n        if (err.message.includes('Codec reclaimed due to inactivity')) {\n          // todo:  因无活动被自动关闭的解码器，是否需要自动重启？\n          this.#dec = null;\n          Log.warn(err.message);\n          return;\n        }\n\n        const errMsg = `VideoFinder VideoDecoder err: ${err.message}, config: ${JSON.stringify(encoderConf)}, state: ${JSON.stringify(this.#getState())}`;\n        Log.error(errMsg);\n        throw Error(errMsg);\n      },\n    });\n    this.#dec.configure(encoderConf);\n  };\n\n  #getState = () => ({\n    time: this.#ts,\n    decState: this.#dec?.state,\n    decQSize: this.#dec?.decodeQueueSize,\n    decCusorIdx: this.#videoDecCusorIdx,\n    sampleLen: this.samples.length,\n    inputCnt: this.#inputChunkCnt,\n    outputCnt: this.#outputFrameCnt,\n    cacheFrameLen: this.#videoFrames.length,\n    softDeocde: this.#downgradeSoftDecode,\n    clipIdCnt: CLIP_ID,\n    sleepCnt: this.#sleepCnt,\n    memInfo: memoryUsageInfo(),\n  });\n\n  destroy = () => {\n    if (this.#dec?.state !== 'closed') this.#dec?.close();\n    this.#dec = null;\n    this.#curAborter.abort = true;\n    this.#videoFrames.forEach((f) => f.close());\n    this.#videoFrames = [];\n    this.localFileReader.close();\n  };\n}\n\nfunction findIndexOfSamples(time: number, samples: ExtMP4Sample[]) {\n  for (let i = 0; i < samples.length; i++) {\n    const s = samples[i];\n    if (time >= s.cts && time < s.cts + s.duration) {\n      return i;\n    }\n    if (s.cts > time) break;\n  }\n  return 0;\n}\n\nclass AudioFrameFinder {\n  #volume = 1;\n  #sampleRate;\n  constructor(\n    public localFileReader: LocalFileReader,\n    public samples: ExtMP4Sample[],\n    public conf: AudioDecoderConfig,\n    opts: { volume: number; targetSampleRate: number },\n  ) {\n    this.#volume = opts.volume;\n    this.#sampleRate = opts.targetSampleRate;\n  }\n\n  #dec: ReturnType<typeof createAudioChunksDecoder> | null = null;\n  #curAborter = { abort: false, st: performance.now() };\n  find = async (time: number): Promise<Float32Array[]> => {\n    const needResetTime = time <= this.#ts || time - this.#ts > 0.1e6;\n    if (this.#dec == null || this.#dec.state === 'closed' || needResetTime) {\n      this.#reset();\n    }\n\n    if (needResetTime) {\n      // 前后获取音频数据差异不能超过 100ms(经验值)，否则视为 seek 操作，重置解码器\n      // seek 操作，重置时间\n      this.#ts = time;\n      this.#decCusorIdx = findIndexOfSamples(time, this.samples);\n    }\n\n    this.#curAborter.abort = true;\n    const deltaTime = time - this.#ts;\n    this.#ts = time;\n\n    this.#curAborter = { abort: false, st: performance.now() };\n\n    const pcmData = await this.#parseFrame(\n      Math.ceil(deltaTime * (this.#sampleRate / 1e6)),\n      this.#dec,\n      this.#curAborter,\n    );\n    this.#sleepCnt = 0;\n    return pcmData;\n  };\n\n  #ts = 0;\n  #decCusorIdx = 0;\n  #pcmData: {\n    frameCnt: number;\n    data: [Float32Array, Float32Array][];\n  } = {\n    frameCnt: 0,\n    data: [],\n  };\n  #sleepCnt = 0;\n  #parseFrame = async (\n    emitFrameCnt: number,\n    dec: ReturnType<typeof createAudioChunksDecoder> | null = null,\n    aborter: { abort: boolean; st: number },\n  ): Promise<Float32Array[]> => {\n    if (\n      dec == null ||\n      aborter.abort ||\n      dec.state === 'closed' ||\n      emitFrameCnt === 0\n    ) {\n      return [];\n    }\n\n    // 数据满足需要\n    const ramainFrameCnt = this.#pcmData.frameCnt - emitFrameCnt;\n    if (ramainFrameCnt > 0) {\n      // 剩余音频数据小于 100ms，预先解码\n      if (ramainFrameCnt < DEFAULT_AUDIO_CONF.sampleRate / 10) {\n        this.#startDecode(dec);\n      }\n      return emitAudioFrames(this.#pcmData, emitFrameCnt);\n    }\n\n    if (dec.decoding) {\n      if (performance.now() - aborter.st > 3e3) {\n        aborter.abort = true;\n        throw Error(\n          `MP4Clip.tick audio timeout, ${JSON.stringify(this.#getState())}`,\n        );\n      }\n      // 解码中，等待\n      this.#sleepCnt += 1;\n      await sleep(15);\n    } else if (this.#decCusorIdx >= this.samples.length - 1) {\n      // 最后片段，返回剩余数据\n      return emitAudioFrames(this.#pcmData, this.#pcmData.frameCnt);\n    } else {\n      this.#startDecode(dec);\n    }\n    return this.#parseFrame(emitFrameCnt, dec, aborter);\n  };\n\n  #startDecode = (dec: ReturnType<typeof createAudioChunksDecoder>) => {\n    const onceDecodeCnt = 10;\n    if (dec.decodeQueueSize > onceDecodeCnt) return;\n    // 启动解码任务\n    const samples = [];\n    let i = this.#decCusorIdx;\n    while (i < this.samples.length) {\n      const s = this.samples[i];\n      i += 1;\n      if (s.deleted) continue;\n      samples.push(s);\n      if (samples.length >= onceDecodeCnt) break;\n    }\n    this.#decCusorIdx = i;\n\n    dec.decode(\n      samples.map(\n        (s) =>\n          new EncodedAudioChunk({\n            type: 'key',\n            timestamp: s.cts,\n            duration: s.duration,\n            data: s.data!,\n          }),\n      ),\n    );\n  };\n\n  #reset = () => {\n    this.#ts = 0;\n    this.#decCusorIdx = 0;\n    this.#pcmData = {\n      frameCnt: 0,\n      data: [],\n    };\n    this.#dec?.close();\n    this.#dec = createAudioChunksDecoder(\n      this.conf,\n      {\n        resampleRate: DEFAULT_AUDIO_CONF.sampleRate,\n        volume: this.#volume,\n      },\n      (pcmArr) => {\n        this.#pcmData.data.push(pcmArr as [Float32Array, Float32Array]);\n        this.#pcmData.frameCnt += pcmArr[0].length;\n      },\n    );\n  };\n\n  #getState = () => ({\n    time: this.#ts,\n    decState: this.#dec?.state,\n    decQSize: this.#dec?.decodeQueueSize,\n    decCusorIdx: this.#decCusorIdx,\n    sampleLen: this.samples.length,\n    pcmLen: this.#pcmData.frameCnt,\n    clipIdCnt: CLIP_ID,\n    sleepCnt: this.#sleepCnt,\n    memInfo: memoryUsageInfo(),\n  });\n\n  destroy = () => {\n    this.#dec = null;\n    this.#curAborter.abort = true;\n    this.#pcmData = {\n      frameCnt: 0,\n      data: [],\n    };\n    this.localFileReader.close();\n  };\n}\n\nfunction createAudioChunksDecoder(\n  decoderConf: AudioDecoderConfig,\n  opts: { resampleRate: number; volume: number },\n  outputCb: (pcm: Float32Array[]) => void,\n) {\n  let inputCnt = 0;\n  let outputCnt = 0;\n  const outputHandler = (pcmArr: Float32Array[]) => {\n    outputCnt += 1;\n    if (pcmArr.length === 0) return;\n    // 音量调节\n    if (opts.volume !== 1) {\n      for (const pcm of pcmArr)\n        for (let i = 0; i < pcm.length; i++) pcm[i] *= opts.volume;\n    }\n\n    // 补齐双声道\n    if (pcmArr.length === 1) pcmArr = [pcmArr[0], pcmArr[0]];\n\n    outputCb(pcmArr);\n  };\n  const resampleQ = createPromiseQueue<Float32Array[]>(outputHandler);\n\n  const needResample = opts.resampleRate !== decoderConf.sampleRate;\n  let adec = new AudioDecoder({\n    output: (ad) => {\n      const pcm = extractPCM4AudioData(ad);\n      if (needResample) {\n        resampleQ(() =>\n          audioResample(pcm, ad.sampleRate, {\n            rate: opts.resampleRate,\n            chanCount: ad.numberOfChannels,\n          }),\n        );\n      } else {\n        outputHandler(pcm);\n      }\n      ad.close();\n    },\n    error: (err) => {\n      if (err.message.includes('Codec reclaimed due to inactivity')) {\n        return;\n      }\n      handleDecodeError('MP4Clip AudioDecoder err', err as Error);\n    },\n  });\n  adec.configure(decoderConf);\n\n  function handleDecodeError(prefixStr: string, err: Error) {\n    const errMsg = `${prefixStr}: ${(err as Error).message}, state: ${JSON.stringify(\n      {\n        qSize: adec.decodeQueueSize,\n        state: adec.state,\n        inputCnt,\n        outputCnt,\n      },\n    )}`;\n    Log.error(errMsg);\n    throw Error(errMsg);\n  }\n\n  return {\n    decode(chunks: EncodedAudioChunk[]) {\n      inputCnt += chunks.length;\n      try {\n        for (const chunk of chunks) adec.decode(chunk);\n      } catch (err) {\n        handleDecodeError('decode audio chunk error', err as Error);\n      }\n    },\n    close() {\n      if (adec.state !== 'closed') adec.close();\n    },\n    get decoding() {\n      return inputCnt > outputCnt && adec.decodeQueueSize > 0;\n    },\n    get state() {\n      return adec.state;\n    },\n    get decodeQueueSize() {\n      return adec.decodeQueueSize;\n    },\n  };\n}\n\n// 并行执行任务，但按顺序emit结果\nfunction createPromiseQueue<T extends any>(onResult: (data: T) => void) {\n  const rsCache: T[] = [];\n  let waitingIdx = 0;\n\n  function updateRs(rs: T, emitIdx: number) {\n    rsCache[emitIdx] = rs;\n    emitRs();\n  }\n\n  function emitRs() {\n    const rs = rsCache[waitingIdx];\n    if (rs == null) return;\n    onResult(rs);\n\n    waitingIdx += 1;\n    emitRs();\n  }\n\n  let addIdx = 0;\n  return (task: () => Promise<T>) => {\n    const emitIdx = addIdx;\n    addIdx += 1;\n    task()\n      .then((rs) => updateRs(rs, emitIdx))\n      .catch((err) => updateRs(err, emitIdx));\n  };\n}\n\nfunction emitAudioFrames(\n  pcmData: { frameCnt: number; data: [Float32Array, Float32Array][] },\n  emitCnt: number,\n) {\n  // todo: perf 重复利用内存空间\n  const audio = [new Float32Array(emitCnt), new Float32Array(emitCnt)];\n  let offset = 0;\n  let i = 0;\n  for (; i < pcmData.data.length; ) {\n    const [chan0, chan1] = pcmData.data[i];\n    if (offset + chan0.length > emitCnt) {\n      const gapCnt = emitCnt - offset;\n      audio[0].set(chan0.subarray(0, gapCnt), offset);\n      audio[1].set(chan1.subarray(0, gapCnt), offset);\n      pcmData.data[i][0] = chan0.subarray(gapCnt, chan0.length);\n      pcmData.data[i][1] = chan1.subarray(gapCnt, chan1.length);\n      break;\n    } else {\n      audio[0].set(chan0, offset);\n      audio[1].set(chan1, offset);\n      offset += chan0.length;\n      i++;\n    }\n  }\n  pcmData.data = pcmData.data.slice(i);\n  pcmData.frameCnt -= emitCnt;\n  return audio;\n}\n\nasync function videosamples2Chunks(\n  samples: ExtMP4Sample[],\n  reader: Awaited<ReturnType<OPFSToolFile['createReader']>>,\n): Promise<EncodedVideoChunk[]> {\n  const first = samples[0];\n  const last = samples.at(-1);\n  if (last == null) return [];\n\n  const rangSize = last.offset + last.size - first.offset;\n  if (rangSize < 30e6) {\n    // 单次读取数据小于 30M，就一次性读取数据，降低 IO 频次\n    const data = new Uint8Array(\n      await reader.read(rangSize, { at: first.offset }),\n    );\n    return samples.map((s) => {\n      const offset = s.offset - first.offset;\n      return new EncodedVideoChunk({\n        type: s.is_sync ? 'key' : 'delta',\n        timestamp: s.cts,\n        duration: s.duration,\n        data: data.subarray(offset, offset + s.size),\n      });\n    });\n  }\n\n  return await Promise.all(\n    samples.map(async (s) => {\n      return new EncodedVideoChunk({\n        type: s.is_sync ? 'key' : 'delta',\n        timestamp: s.cts,\n        duration: s.duration,\n        data: await reader.read(s.size, {\n          at: s.offset,\n        }),\n      });\n    }),\n  );\n}\n\nfunction createVF2BlobConvtr(\n  width: number,\n  height: number,\n  opts?: ImageEncodeOptions,\n) {\n  const cvs = new OffscreenCanvas(width, height);\n  const ctx = cvs.getContext('2d')!;\n\n  return async (vf: VideoFrame) => {\n    ctx.drawImage(vf, 0, 0, width, height);\n    vf.close();\n    const blob = await cvs.convertToBlob(opts);\n    return blob;\n  };\n}\n\nfunction splitVideoSampleByTime(videoSamples: ExtMP4Sample[], time: number) {\n  if (videoSamples.length === 0) return [];\n  let gopStartIdx = 0;\n  let gopEndIdx = 0;\n  let hitIdx = -1;\n  for (let i = 0; i < videoSamples.length; i++) {\n    const s = videoSamples[i];\n    if (hitIdx === -1 && time < s.cts) hitIdx = i - 1;\n    if (s.is_idr) {\n      if (hitIdx === -1) {\n        gopStartIdx = i;\n      } else {\n        gopEndIdx = i;\n        break;\n      }\n    }\n  }\n\n  const hitSample = videoSamples[hitIdx];\n  if (hitSample == null) throw Error('Not found video sample by time');\n\n  const preSlice = videoSamples\n    .slice(0, gopEndIdx === 0 ? videoSamples.length : gopEndIdx)\n    .map((s) => ({ ...s }));\n  for (let i = gopStartIdx; i < preSlice.length; i++) {\n    const s = preSlice[i];\n    if (time < s.cts) {\n      s.deleted = true;\n      s.cts = -1;\n    }\n  }\n  fixFirstBlackFrame(preSlice);\n\n  const postSlice = videoSamples\n    .slice(hitSample.is_idr ? hitIdx : gopStartIdx)\n    .map((s) => ({ ...s, cts: s.cts - time }));\n\n  for (const s of postSlice) {\n    if (s.cts < 0) {\n      s.deleted = true;\n      s.cts = -1;\n    }\n  }\n  fixFirstBlackFrame(postSlice);\n\n  return [preSlice, postSlice];\n}\n\nfunction splitAudioSampleByTime(audioSamples: ExtMP4Sample[], time: number) {\n  if (audioSamples.length === 0) return [];\n  let hitIdx = -1;\n  for (let i = 0; i < audioSamples.length; i++) {\n    const s = audioSamples[i];\n    if (time > s.cts) continue;\n    hitIdx = i;\n    break;\n  }\n  if (hitIdx === -1) throw Error('Not found audio sample by time');\n  const preSlice = audioSamples.slice(0, hitIdx).map((s) => ({ ...s }));\n  const postSlice = audioSamples\n    .slice(hitIdx)\n    .map((s) => ({ ...s, cts: s.cts - time }));\n  return [preSlice, postSlice];\n}\n\n// 兼容解码错误\nfunction decodeGoP(\n  dec: VideoDecoder,\n  chunks: EncodedVideoChunk[],\n  opts: {\n    onDecodingError?: (err: Error) => void;\n  },\n) {\n  if (dec.state !== 'configured') return;\n  for (let i = 0; i < chunks.length; i++) dec.decode(chunks[i]);\n\n  // todo：flush 之后下一帧必须是 IDR 帧，是否可以根据情况再决定调用 flush？\n  // windows 某些设备 flush 可能不会被 resolved，所以不能 await flush\n  dec.flush().catch((err) => {\n    if (!(err instanceof Error)) throw err;\n    if (\n      err.message.includes('Decoding error') &&\n      opts.onDecodingError != null\n    ) {\n      opts.onDecodingError(err);\n      return;\n    }\n    // reset 中断解码器，预期会抛出 AbortedError\n    if (!err.message.includes('Aborted due to close')) {\n      throw err;\n    }\n  });\n}\n\nfunction idrNALUOffset(\n  u8Arr: Uint8Array,\n  type: MP4Sample['description']['type'],\n  startOffset: number,\n) {\n  if (type !== 'avc1' && type !== 'hvc1') return 0;\n\n  const dv = new DataView(u8Arr.buffer);\n  let i = startOffset;\n  for (; i < u8Arr.byteLength - 4; ) {\n    if (type === 'avc1' && (dv.getUint8(i + 4) & 0x1f) === 5) {\n      return i;\n    } else if (type === 'hvc1') {\n      const nalUnitType = (dv.getUint8(i + 4) >> 1) & 0x3f;\n      if (nalUnitType === 19 || nalUnitType === 20) return i;\n    }\n    // 跳至下一个 NALU 继续检查\n    i += dv.getUint32(i) + 4;\n  }\n  return -1;\n}\n\nasync function thumbnailByKeyFrame(\n  samples: ExtMP4Sample[],\n  localFile: OPFSToolFile,\n  decConf: VideoDecoderConfig,\n  abortSingl: AbortSignal,\n  time: { start: number; end: number },\n  onOutput: (vf: VideoFrame | null, done: boolean) => void,\n) {\n  const fileReader = await localFile.createReader();\n\n  const chunks = await videosamples2Chunks(\n    samples.filter(\n      (s) =>\n        !s.deleted && s.is_sync && s.cts >= time.start && s.cts <= time.end,\n    ),\n    fileReader,\n  );\n  if (chunks.length === 0 || abortSingl.aborted) return;\n\n  let outputCnt = 0;\n  decodeGoP(createVideoDec(), chunks, {\n    onDecodingError: (err) => {\n      Log.warn('thumbnailsByKeyFrame', err);\n      // 尝试降级一次\n      if (outputCnt === 0) {\n        decodeGoP(createVideoDec(true), chunks, {\n          onDecodingError: (err) => {\n            fileReader.close();\n            Log.error('thumbnailsByKeyFrame retry soft deocde', err);\n          },\n        });\n      } else {\n        onOutput(null, true);\n        fileReader.close();\n      }\n    },\n  });\n\n  function createVideoDec(downgrade = false) {\n    const encoderConf = {\n      ...decConf,\n      ...(downgrade ? { hardwareAcceleration: 'prefer-software' } : {}),\n    } as VideoDecoderConfig;\n    const dec = new VideoDecoder({\n      output: (vf) => {\n        outputCnt += 1;\n        const done = outputCnt === chunks.length;\n        onOutput(vf, done);\n        if (done) {\n          fileReader.close();\n          if (dec.state !== 'closed') dec.close();\n        }\n      },\n      error: (err) => {\n        const errMsg = `thumbnails decoder error: ${err.message}, config: ${JSON.stringify(encoderConf)}, state: ${JSON.stringify(\n          {\n            qSize: dec.decodeQueueSize,\n            state: dec.state,\n            outputCnt,\n            inputCnt: chunks.length,\n          },\n        )}`;\n        Log.error(errMsg);\n        throw Error(errMsg);\n      },\n    });\n    abortSingl.addEventListener('abort', () => {\n      fileReader.close();\n      if (dec.state !== 'closed') dec.close();\n    });\n    dec.configure(encoderConf);\n    return dec;\n  }\n}\n\n// 如果第一帧出现的时间偏移较大，会导致第一帧为黑帧，这里尝试自动消除第一帧前的黑帧\nfunction fixFirstBlackFrame(samples: ExtMP4Sample[]) {\n  let iframeCnt = 0;\n  let minCtsSample: ExtMP4Sample | null = null;\n  // cts 最小表示视频的第一帧\n  for (const s of samples) {\n    if (s.deleted) continue;\n    // 最多检测两个 I 帧之间的帧\n    if (s.is_sync) iframeCnt += 1;\n    if (iframeCnt >= 2) break;\n\n    if (minCtsSample == null || s.cts < minCtsSample.cts) {\n      minCtsSample = s;\n    }\n  }\n  // 200ms 是经验值，自动消除 200ms 内的黑帧，超过则不处理\n  if (minCtsSample != null && minCtsSample.cts < 200e3) {\n    minCtsSample.duration += minCtsSample.cts;\n    minCtsSample.cts = 0;\n  }\n}\n\nfunction memoryUsageInfo() {\n  try {\n    // @ts-ignore\n    const mem = performance.memory;\n    return {\n      jsHeapSizeLimit: mem.jsHeapSizeLimit,\n      totalJSHeapSize: mem.totalJSHeapSize,\n      usedJSHeapSize: mem.usedJSHeapSize,\n      percentUsed: (mem.usedJSHeapSize / mem.jsHeapSizeLimit).toFixed(3),\n      percentTotal: (mem.totalJSHeapSize / mem.jsHeapSizeLimit).toFixed(3),\n    };\n  } catch (err) {\n    return {};\n  }\n}\n\nif (import.meta.vitest) {\n  const { it, expect } = import.meta.vitest;\n\n  it('normalizeTimescale should compatible with anomalous data', () => {\n    // 视频容器数据异常时，直接使用容器的信息\n    // 找不到 IDR 帧时，直接信任容器的 offset、size\n    const s: MP4Sample = {\n      offset: 48,\n      size: 1000,\n      cts: 0,\n      dts: 0,\n      duration: 1e6,\n      timescale: 1000,\n      is_sync: true,\n      deleted: false,\n      // 异常的数据，offset = 48 时，并非 idr 帧，且 nalu 长度取值错误\n      data: new Uint8Array([\n        0, 0, 0, 21, 103, 100, 0, 40, 172, 43, 32, 15, 0, 68, 216, 8, 128, 0, 1,\n        244, 0, 0, 93, 192, 66, 0, 0, 0, 4, 104, 235, 143, 44, 0, 1, 134, 130,\n        101, 184, 4, 127, 135, 242, 7, 227, 27, 39, 154, 66, 5, 153, 27, 242,\n        78, 0, 168, 62, 16, 51, 168, 150, 136, 69, 7, 149, 83, 39, 27, 153, 130,\n        214, 70, 144, 194, 191, 167, 74, 7, 11, 97, 224, 27, 193, 2, 122, 234,\n        71, 95, 7, 192, 199, 28, 181, 215, 12, 201, 182, 43, 105, 91,\n      ]),\n      track_id: 1,\n      // @ts-ignore\n      description: { type: 'avc1' },\n      is_rap: false,\n    };\n    const normalized = normalizeTimescale(s, 0, 'video', true);\n    expect(normalized.offset).toBe(48);\n    expect(normalized.size).toBe(1000);\n    expect(normalized.is_sync).toBe(normalized.is_idr);\n  });\n}\n","import { Log } from '@webav/internal-utils';\nimport { decodeImg } from '../av-utils';\nimport { IClip } from './iclip';\n\ntype AnimateImgType = 'avif' | 'webp' | 'png' | 'gif';\n\n/**\n * 图像素材，支持动图\n *\n * 普通文字可通过 {@link renderTxt2ImgBitmap} 转换成图片素材\n *\n * @example\n * new ImgClip((await fetch('<img url>')).body);\n *\n * @example\n * new ImgClip(\n *   await renderTxt2ImgBitmap(\n *     '水印',\n *    `font-size:40px; color: white; text-shadow: 2px 2px 6px red;`,\n *   )\n * )\n *\n * @see [视频合成](https://webav-tech.github.io/WebAV/demo/2_1-concat-video)\n */\nexport class ImgClip implements IClip {\n  ready: IClip['ready'];\n\n  #meta = {\n    // 微秒\n    duration: 0,\n    width: 0,\n    height: 0,\n  };\n\n  /**\n   * ⚠️ 静态图片的 duration 为 Infinity\n   *\n   * 使用 Sprite 包装时需要将它的 duration 设置为有限数\n   *\n   */\n  get meta() {\n    return { ...this.#meta };\n  }\n\n  #img: ImageBitmap | null = null;\n\n  #frames: VideoFrame[] = [];\n\n  /**\n   * 静态图片可使用流、ImageBitmap 初始化\n   *\n   * 动图需要使用 VideoFrame[] 或提供图片类型\n   */\n  constructor(\n    dataSource:\n      | ReadableStream\n      | ImageBitmap\n      | VideoFrame[]\n      | { type: `image/${AnimateImgType}`; stream: ReadableStream },\n  ) {\n    const initWithImgBitmap = (imgBitmap: ImageBitmap) => {\n      this.#img = imgBitmap;\n      this.#meta.width = imgBitmap.width;\n      this.#meta.height = imgBitmap.height;\n      this.#meta.duration = Infinity;\n      return { ...this.#meta };\n    };\n\n    if (dataSource instanceof ReadableStream) {\n      this.ready = new Response(dataSource)\n        .blob()\n        .then((data) => createImageBitmap(data))\n        .then(initWithImgBitmap);\n    } else if (dataSource instanceof ImageBitmap) {\n      this.ready = Promise.resolve(initWithImgBitmap(dataSource));\n    } else if (\n      Array.isArray(dataSource) &&\n      dataSource.every((it) => it instanceof VideoFrame)\n    ) {\n      this.#frames = dataSource;\n      const frame = this.#frames[0];\n      if (frame == null) throw Error('The frame count must be greater than 0');\n      this.#meta = {\n        width: frame.displayWidth,\n        height: frame.displayHeight,\n        duration: this.#frames.reduce(\n          (acc, cur) => acc + (cur.duration ?? 0),\n          0,\n        ),\n      };\n      this.ready = Promise.resolve({ ...this.#meta, duration: Infinity });\n    } else if ('type' in dataSource) {\n      this.ready = this.#initAnimateImg(\n        dataSource.stream,\n        dataSource.type,\n      ).then(() => ({\n        width: this.#meta.width,\n        height: this.#meta.height,\n        duration: Infinity,\n      }));\n    } else {\n      throw Error('Illegal arguments');\n    }\n  }\n\n  async #initAnimateImg(\n    stream: ReadableStream,\n    type: `image/${AnimateImgType}`,\n  ) {\n    this.#frames = await decodeImg(stream, type);\n    const firstVf = this.#frames[0];\n    if (firstVf == null) throw Error('No frame available in gif');\n\n    this.#meta = {\n      duration: this.#frames.reduce((acc, cur) => acc + (cur.duration ?? 0), 0),\n      width: firstVf.codedWidth,\n      height: firstVf.codedHeight,\n    };\n    Log.info('ImgClip ready:', this.#meta);\n  }\n\n  tickInterceptor: <T extends Awaited<ReturnType<ImgClip['tick']>>>(\n    time: number,\n    tickRet: T,\n  ) => Promise<T> = async (_, tickRet) => tickRet;\n\n  async tick(time: number): Promise<{\n    video: ImageBitmap | VideoFrame;\n    state: 'success';\n  }> {\n    if (this.#img != null) {\n      return await this.tickInterceptor(time, {\n        video: await createImageBitmap(this.#img),\n        state: 'success',\n      });\n    }\n    const tt = time % this.#meta.duration;\n    return await this.tickInterceptor(time, {\n      video: (\n        this.#frames.find(\n          (f) => tt >= f.timestamp && tt <= f.timestamp + (f.duration ?? 0),\n        ) ?? this.#frames[0]\n      ).clone(),\n      state: 'success',\n    });\n  }\n\n  async split(time: number) {\n    await this.ready;\n    if (this.#img != null) {\n      return [\n        new ImgClip(await createImageBitmap(this.#img)),\n        new ImgClip(await createImageBitmap(this.#img)),\n      ] as [this, this];\n    }\n    let hitIdx = -1;\n    for (let i = 0; i < this.#frames.length; i++) {\n      const vf = this.#frames[i];\n      if (time > vf.timestamp) continue;\n      hitIdx = i;\n      break;\n    }\n    if (hitIdx === -1) throw Error('Not found frame by time');\n    const preSlice = this.#frames\n      .slice(0, hitIdx)\n      .map((vf) => new VideoFrame(vf));\n    const postSlice = this.#frames.slice(hitIdx).map(\n      (vf) =>\n        new VideoFrame(vf, {\n          timestamp: vf.timestamp - time,\n        }),\n    );\n    return [new ImgClip(preSlice), new ImgClip(postSlice)] as [this, this];\n  }\n\n  async clone() {\n    await this.ready;\n    const data =\n      this.#img == null\n        ? this.#frames.map((vf) => vf.clone())\n        : await createImageBitmap(this.#img);\n    const newClip = new ImgClip(data) as this;\n    newClip.tickInterceptor = this.tickInterceptor;\n    return newClip;\n  }\n\n  destroy(): void {\n    Log.info('ImgClip destroy');\n    this.#img?.close();\n    this.#frames.forEach((f) => f.close());\n  }\n}\n","import { Log } from '@webav/internal-utils';\nimport {\n  concatPCMFragments,\n  extractPCM4AudioBuffer,\n  ringSliceFloat32Array,\n} from '../av-utils';\nimport { DEFAULT_AUDIO_CONF, IClip } from './iclip';\n\ninterface IAudioClipOpts {\n  loop?: boolean;\n  volume?: number;\n}\n\n/**\n * 音频素材，为创建、编辑音视频功能提供音频数据\n *\n * @example\n * new AudioClip((await fetch('<mp3 url>')).body, {\n *   loop: true,\n * }),\n */\nexport class AudioClip implements IClip {\n  static ctx: AudioContext | null = null;\n\n  ready: IClip['ready'];\n\n  #meta = {\n    // 微秒\n    duration: 0,\n    width: 0,\n    height: 0,\n  };\n\n  /**\n   * 音频元信息\n   *\n   * ⚠️ 注意，这里是转换后（标准化）的元信息，非原始音频元信息\n   */\n  get meta() {\n    return {\n      ...this.#meta,\n      sampleRate: DEFAULT_AUDIO_CONF.sampleRate,\n      chanCount: 2,\n    };\n  }\n\n  // 使用类型断言来避免 ArrayBufferLike 和 ArrayBuffer 的类型兼容性问题\n  #chan0Buf: Float32Array = new Float32Array();\n  #chan1Buf: Float32Array = new Float32Array();\n  /**\n   * 获取音频素材完整的 PCM 数据\n   */\n  getPCMData(): Float32Array[] {\n    return [this.#chan0Buf, this.#chan1Buf];\n  }\n\n  #opts;\n\n  /**\n   *\n   * @param dataSource 音频文件流\n   * @param opts 音频配置，控制音量、是否循环\n   */\n  constructor(\n    dataSource: ReadableStream<Uint8Array> | Float32Array[],\n    opts: IAudioClipOpts = {},\n  ) {\n    this.#opts = {\n      loop: false,\n      volume: 1,\n      ...opts,\n    };\n\n    this.ready = this.#init(dataSource).then(() => ({\n      // audio 没有宽高，无需绘制\n      width: 0,\n      height: 0,\n      duration: opts.loop ? Infinity : this.#meta.duration,\n    }));\n  }\n\n  async #init(\n    dataSource: ReadableStream<Uint8Array> | Float32Array[],\n  ): Promise<void> {\n    if (AudioClip.ctx == null) {\n      AudioClip.ctx = new AudioContext({\n        sampleRate: DEFAULT_AUDIO_CONF.sampleRate,\n      });\n    }\n\n    const tStart = performance.now();\n    const pcm =\n      dataSource instanceof ReadableStream\n        ? await parseStream2PCM(dataSource, AudioClip.ctx)\n        : dataSource;\n\n    Log.info('Audio clip decoded complete:', performance.now() - tStart);\n\n    const volume = this.#opts.volume;\n    if (volume !== 1) {\n      for (const chan of pcm)\n        for (let i = 0; i < chan.length; i += 1) chan[i] *= volume;\n    }\n\n    this.#meta.duration = (pcm[0].length / DEFAULT_AUDIO_CONF.sampleRate) * 1e6;\n\n    this.#chan0Buf = pcm[0];\n    // 单声道 转 立体声\n    this.#chan1Buf = pcm[1] ?? this.#chan0Buf;\n\n    Log.info(\n      'Audio clip convert to AudioData, time:',\n      performance.now() - tStart,\n    );\n  }\n\n  /**\n   * 拦截 {@link AudioClip.tick} 方法返回的数据，用于对音频数据二次处理\n   * @param time 调用 tick 的时间\n   * @param tickRet tick 返回的数据\n   *\n   * @see [移除视频绿幕背景](https://webav-tech.github.io/WebAV/demo/3_2-chromakey-video)\n   */\n  tickInterceptor: <T extends Awaited<ReturnType<AudioClip['tick']>>>(\n    time: number,\n    tickRet: T,\n  ) => Promise<T> = async (_, tickRet) => tickRet;\n\n  // 微秒\n  #ts = 0;\n  #frameOffset = 0;\n  /**\n   * 返回上次与当前时刻差对应的音频 PCM 数据；\n   *\n   * 若差值超过 3s 或当前时间小于上次时间，则重置状态\n   * @example\n   * tick(0) // => []\n   * tick(1e6) // => [leftChanPCM(1s), rightChanPCM(1s)]\n   *\n   */\n  async tick(time: number): Promise<{\n    audio: Float32Array[];\n    state: 'success' | 'done';\n  }> {\n    if (!this.#opts.loop && time >= this.#meta.duration) {\n      // 待观察：如果time跨度较大，返回done，理论上会丢失一些音频帧\n      return await this.tickInterceptor(time, { audio: [], state: 'done' });\n    }\n\n    const deltaTime = time - this.#ts;\n\n    // reset\n    if (time < this.#ts || deltaTime > 3e6) {\n      this.#ts = time;\n      this.#frameOffset = Math.ceil(\n        (this.#ts / 1e6) * DEFAULT_AUDIO_CONF.sampleRate,\n      );\n      return await this.tickInterceptor(time, {\n        audio: [new Float32Array(0), new Float32Array(0)],\n        state: 'success',\n      });\n    }\n\n    this.#ts = time;\n    const frameCnt = Math.ceil(\n      (deltaTime / 1e6) * DEFAULT_AUDIO_CONF.sampleRate,\n    );\n    const endIdx = this.#frameOffset + frameCnt;\n    const audio = this.#opts.loop\n      ? [\n          ringSliceFloat32Array(this.#chan0Buf, this.#frameOffset, endIdx),\n          ringSliceFloat32Array(this.#chan1Buf, this.#frameOffset, endIdx),\n        ]\n      : [\n          this.#chan0Buf.slice(this.#frameOffset, endIdx),\n          this.#chan1Buf.slice(this.#frameOffset, endIdx),\n        ];\n    this.#frameOffset = endIdx;\n\n    return await this.tickInterceptor(time, { audio, state: 'success' });\n  }\n\n  /**\n   * 按指定时间切割，返回前后两个音频素材\n   * @param time 时间，单位微秒\n   */\n  async split(time: number) {\n    await this.ready;\n    const frameCnt = Math.ceil((time / 1e6) * DEFAULT_AUDIO_CONF.sampleRate);\n    const preSlice = new AudioClip(\n      this.getPCMData().map((chan) => chan.slice(0, frameCnt)),\n      this.#opts,\n    );\n    const postSlice = new AudioClip(\n      this.getPCMData().map((chan) => chan.slice(frameCnt)),\n      this.#opts,\n    );\n    return [preSlice, postSlice] as [this, this];\n  }\n\n  async clone() {\n    await this.ready;\n    const clip = new AudioClip(this.getPCMData(), this.#opts) as this;\n    await clip.ready;\n    return clip;\n  }\n\n  /**\n   * 销毁实例，释放资源\n   */\n  destroy(): void {\n    this.#chan0Buf = new Float32Array(0);\n    this.#chan1Buf = new Float32Array(0);\n    Log.info('---- audioclip destroy ----');\n  }\n}\n\n/**\n * 拼接多个 AudioClip\n */\nexport async function concatAudioClip(\n  clips: AudioClip[],\n  opts?: IAudioClipOpts,\n) {\n  const bufs: Float32Array[][] = [];\n  for (const clip of clips) {\n    await clip.ready;\n    bufs.push(clip.getPCMData());\n  }\n  return new AudioClip(concatPCMFragments(bufs), opts);\n}\n\nasync function parseStream2PCM(\n  stream: ReadableStream<Uint8Array>,\n  ctx: AudioContext | OfflineAudioContext,\n): Promise<Float32Array[]> {\n  const buf = await new Response(stream).arrayBuffer();\n  return extractPCM4AudioBuffer(await ctx.decodeAudioData(buf));\n}\n","import { autoReadStream } from '@webav/internal-utils';\nimport { IClip } from './iclip';\n\n/**\n * 包装实时音视频流，仅用于 [AVCanvas](../../av-canvas/classes/AVCanvas.html)\n *\n * ⚠️ 不可用于 {@link Combinator} ，因为后台合成视频的速度是快于物理时间的，实时流无法提供非实时的数据\n *\n * @example\n * const spr = new VisibleSprite(\n *   new MediaStreamClip(\n *     await navigator.mediaDevices.getUserMedia({ video: true, audio: true, }),\n *   ),\n * );\n * await avCvs.addSprite(spr);\n */\nexport class MediaStreamClip implements IClip {\n  static ctx: AudioContext | null = null;\n\n  ready: IClip['ready'];\n\n  #meta = {\n    // 微秒\n    duration: 0,\n    width: 0,\n    height: 0,\n  };\n\n  get meta() {\n    return {\n      ...this.#meta,\n    };\n  }\n\n  #stopRenderCvs = () => {};\n\n  /**\n   * 实时流的音轨\n   */\n  readonly audioTrack: MediaStreamAudioTrack | null;\n\n  #cvs: OffscreenCanvas | null = null;\n\n  #ms: MediaStream;\n  constructor(ms: MediaStream) {\n    this.#ms = ms;\n    this.audioTrack = ms.getAudioTracks()[0] ?? null;\n    this.#meta.duration = Infinity;\n    const videoTrack = ms.getVideoTracks()[0];\n    if (videoTrack != null) {\n      videoTrack.contentHint = 'motion';\n      this.ready = new Promise((resolve) => {\n        this.#stopRenderCvs = renderVideoTrackToCvs(videoTrack, (cvs) => {\n          this.#meta.width = cvs.width;\n          this.#meta.height = cvs.height;\n          this.#cvs = cvs;\n          resolve(this.meta);\n        });\n      });\n    } else {\n      this.ready = Promise.resolve(this.meta);\n    }\n  }\n\n  async tick(): Promise<{\n    video: ImageBitmap | null;\n    audio: Float32Array[];\n    state: 'success';\n  }> {\n    return {\n      video: this.#cvs == null ? null : await createImageBitmap(this.#cvs),\n      audio: [],\n      state: 'success',\n    };\n  }\n\n  async split() {\n    return [await this.clone(), await this.clone()] as [this, this];\n  }\n\n  async clone() {\n    return new MediaStreamClip(this.#ms.clone()) as this;\n  }\n\n  destroy(): void {\n    this.#ms.getTracks().forEach((t) => t.stop());\n    this.#stopRenderCvs();\n  }\n}\n\nfunction renderVideoTrackToCvs(\n  track: MediaStreamVideoTrack,\n  onOffscreenCanvasReady: (cvs: OffscreenCanvas) => void,\n) {\n  let emitFF = false;\n  let cvsCtx: OffscreenCanvasRenderingContext2D;\n  return autoReadStream(\n    new MediaStreamTrackProcessor({\n      track,\n    }).readable,\n    {\n      onChunk: async (frame) => {\n        if (!emitFF) {\n          const { displayHeight, displayWidth } = frame;\n          const width = displayWidth ?? 0;\n          const height = displayHeight ?? 0;\n          const cvs = new OffscreenCanvas(width, height);\n          cvsCtx = cvs.getContext('2d')!;\n          onOffscreenCanvasReady(cvs);\n          emitFF = true;\n        }\n        cvsCtx.drawImage(frame, 0, 0);\n        frame.close();\n      },\n      onDone: async () => {},\n    },\n  );\n}\n","import { IClip } from './iclip';\n\ninterface IEmbedSubtitlesOpts {\n  color?: string;\n  textBgColor?: string | null;\n  type?: 'srt';\n  fontFamily?: string;\n  fontSize?: number;\n  letterSpacing?: string | null;\n  // 字幕偏离底部的距离\n  bottomOffset?: number;\n  strokeStyle?: string;\n  lineWidth?: number | null;\n  lineCap?: CanvasLineCap | null;\n  lineJoin?: CanvasLineJoin | null;\n  textShadow?: {\n    offsetX: number;\n    offsetY: number;\n    blur: number;\n    color: string;\n  };\n  videoWidth: number;\n  videoHeight: number;\n  fontWeight?: string | number;\n  fontStyle?: 'normal' | 'italic';\n}\n\ndeclare global {\n  interface OffscreenCanvasRenderingContext2D {\n    letterSpacing: string;\n  }\n}\n\ninterface SubtitleStruct {\n  start: number;\n  end: number;\n  text: string;\n}\n\n/**\n * 嵌入式字幕，将字幕（目前仅支持 SRT 格式）嵌入视频画面中\n *\n * @example\n * const es = new EmbedSubtitlesClip(srtSubtitleStr, {\n *   videoWidth: 1280,\n *   videoHeight: 720,\n *   fontFamily: 'Noto Sans SC',\n *   color: 'white',\n * });\n */\nexport class EmbedSubtitlesClip implements IClip {\n  ready: IClip['ready'];\n\n  #subtitles: SubtitleStruct[] = [];\n\n  #meta = {\n    width: 0,\n    height: 0,\n    duration: 0,\n  };\n\n  get meta() {\n    return { ...this.#meta };\n  }\n\n  #opts: Required<IEmbedSubtitlesOpts> = {\n    color: '#FFF',\n    textBgColor: null,\n    type: 'srt',\n    fontSize: 30,\n    letterSpacing: null,\n    bottomOffset: 30,\n    fontFamily: 'Noto Sans SC',\n    strokeStyle: '#000',\n    lineWidth: null,\n    lineCap: null,\n    lineJoin: null,\n    textShadow: {\n      offsetX: 2,\n      offsetY: 2,\n      blur: 4,\n      color: '#000',\n    },\n    videoWidth: 1280,\n    videoHeight: 720,\n    fontWeight: 'normal',\n    fontStyle: 'normal',\n  };\n\n  #cvs: OffscreenCanvas;\n  #ctx: OffscreenCanvasRenderingContext2D;\n\n  #lastVF: VideoFrame | null = null;\n\n  #lineHeight = 0;\n  #linePadding = 0;\n\n  constructor(content: string | SubtitleStruct[], opts: IEmbedSubtitlesOpts) {\n    this.#subtitles = Array.isArray(content)\n      ? content\n      : parseSrt(content).map(({ start, end, text }) => ({\n          start: start * 1e6,\n          end: end * 1e6,\n          text,\n        }));\n    if (this.#subtitles.length === 0) throw Error('No subtitles content');\n\n    this.#opts = Object.assign(this.#opts, opts);\n    // 如果需要绘制背景，则需要给文字添加边距\n    this.#linePadding =\n      opts.textBgColor == null ? 0 : (opts.fontSize ?? 50) * 0.2;\n\n    const {\n      fontSize,\n      fontFamily,\n      fontWeight,\n      fontStyle,\n      videoWidth,\n      videoHeight,\n      letterSpacing,\n    } = this.#opts;\n    this.#lineHeight = fontSize + this.#linePadding * 2;\n    this.#cvs = new OffscreenCanvas(videoWidth, videoHeight);\n    this.#ctx = this.#cvs.getContext('2d')!;\n    this.#ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;\n    this.#ctx.textAlign = 'center';\n    this.#ctx.textBaseline = 'top';\n    this.#ctx.letterSpacing = letterSpacing ?? '0px';\n\n    this.#meta = {\n      width: videoWidth,\n      height: videoHeight,\n      duration: this.#subtitles.at(-1)?.end ?? 0,\n    };\n    // 字幕的宽高 由视频画面内容决定\n    this.ready = Promise.resolve(this.meta);\n  }\n\n  #renderTxt(txt: string) {\n    const lines = txt\n      .split('\\n')\n      .reverse()\n      .map((t) => t.trim());\n\n    const { width, height } = this.#cvs;\n\n    const {\n      color,\n      fontSize,\n      textBgColor,\n      textShadow,\n      strokeStyle,\n      lineWidth,\n      lineCap,\n      lineJoin,\n      bottomOffset,\n    } = this.#opts;\n    const ctx = this.#ctx;\n\n    ctx.clearRect(0, 0, width, height);\n    ctx.globalAlpha = 0.6;\n    // 测试canvas背景\n    // ctx.fillStyle = 'red'\n    // ctx.fillRect(0, 0, this.#cvs.width, this.#cvs.height)\n\n    let bottomDistance = bottomOffset;\n    for (const lineStr of lines) {\n      const txtMeas = ctx.measureText(lineStr);\n      const centerX = width / 2;\n      if (textBgColor != null) {\n        ctx.shadowOffsetX = 0;\n        ctx.shadowOffsetY = 0;\n        ctx.shadowBlur = 0;\n        // 字幕背景\n        ctx.fillStyle = textBgColor;\n        ctx.globalAlpha = 0.5;\n        ctx.fillRect(\n          centerX - txtMeas.actualBoundingBoxLeft - this.#linePadding,\n          height - bottomDistance - this.#lineHeight,\n          txtMeas.width + this.#linePadding * 2,\n          this.#lineHeight,\n        );\n      } else {\n      }\n\n      ctx.shadowColor = textShadow.color;\n      ctx.shadowOffsetX = textShadow.offsetX;\n      ctx.shadowOffsetY = textShadow.offsetY;\n      ctx.shadowBlur = textShadow.blur;\n\n      ctx.globalAlpha = 1;\n\n      if (strokeStyle != null) {\n        ctx.lineWidth = lineWidth ?? fontSize / 6;\n        if (lineCap != null) ctx.lineCap = lineCap;\n        if (lineJoin != null) ctx.lineJoin = lineJoin;\n        ctx.strokeStyle = strokeStyle;\n        ctx.strokeText(\n          lineStr,\n          centerX,\n          height - bottomDistance - this.#lineHeight + this.#linePadding,\n        );\n      }\n\n      ctx.fillStyle = color;\n      ctx.fillText(\n        lineStr,\n        centerX,\n        height - bottomDistance - this.#lineHeight + this.#linePadding,\n      );\n\n      // 多行，底部偏移距离叠加\n      bottomDistance += this.#lineHeight + fontSize * 0.2;\n    }\n  }\n\n  /**\n   * @see {@link IClip.tick}\n   */\n  async tick(time: number): Promise<{\n    video?: VideoFrame;\n    state: 'done' | 'success';\n  }> {\n    if (\n      this.#lastVF != null &&\n      time >= this.#lastVF.timestamp &&\n      time <= this.#lastVF.timestamp + (this.#lastVF.duration ?? 0)\n    ) {\n      return { video: this.#lastVF.clone(), state: 'success' };\n    }\n\n    let i = 0;\n    for (; i < this.#subtitles.length; i += 1) {\n      if (time <= this.#subtitles[i].end) break;\n    }\n\n    const it = this.#subtitles[i] ?? this.#subtitles.at(-1);\n    if (time > it.end) return { state: 'done' };\n    if (time < it.start) {\n      // 此时无字幕内容，清空画布\n      this.#ctx.clearRect(0, 0, this.#cvs.width, this.#cvs.height);\n      const vf = new VideoFrame(this.#cvs, {\n        timestamp: time,\n        // 直到下个字幕出现的时机\n        duration: it.start - time,\n      });\n      this.#lastVF?.close();\n      this.#lastVF = vf;\n\n      return { video: vf.clone(), state: 'success' };\n    }\n\n    this.#renderTxt(it.text);\n\n    const vf = new VideoFrame(this.#cvs, {\n      timestamp: time,\n      duration: it.end - time,\n    });\n    this.#lastVF?.close();\n    this.#lastVF = vf;\n\n    return { video: vf.clone(), state: 'success' };\n  }\n\n  /**\n   * @see {@link IClip.split}\n   */\n  async split(time: number) {\n    await this.ready;\n    let hitIdx = -1;\n    for (let i = 0; i < this.#subtitles.length; i++) {\n      const sub = this.#subtitles[i];\n      if (time > sub.start) continue;\n      hitIdx = i;\n      break;\n    }\n    if (hitIdx === -1) throw Error('Not found subtitle by time');\n    const preSlice = this.#subtitles.slice(0, hitIdx).map((s) => ({ ...s }));\n    let preLastIt = preSlice.at(-1);\n    let postFirstIt = null;\n    // 切割时间命中字幕区间，需要将当前字幕元素拆成前后两份\n    if (preLastIt != null && preLastIt.end > time) {\n      postFirstIt = {\n        start: 0,\n        end: preLastIt.end - time,\n        text: preLastIt.text,\n      };\n\n      preLastIt.end = time;\n    }\n    const postSlice = this.#subtitles\n      .slice(hitIdx)\n      .map((s) => ({ ...s, start: s.start - time, end: s.end - time }));\n    if (postFirstIt != null) postSlice.unshift(postFirstIt);\n    return [\n      new EmbedSubtitlesClip(preSlice, this.#opts),\n      new EmbedSubtitlesClip(postSlice, this.#opts),\n    ] as [this, this];\n  }\n\n  /**\n   * @see {@link IClip.clone}\n   */\n  async clone() {\n    return new EmbedSubtitlesClip(this.#subtitles.slice(0), this.#opts) as this;\n  }\n\n  /**\n   * @see {@link IClip.destroy}\n   */\n  destroy() {\n    this.#lastVF?.close();\n  }\n}\n\n// SRT字幕格式 https://www.cnblogs.com/tocy/p/subtitle-format-srt.html\nfunction srtTimeToSeconds(time: string) {\n  const match = time.match(/(\\d{2}):(\\d{2}):(\\d{2}),(\\d{3})/);\n  if (match == null) throw Error(`time format error: ${time}`);\n\n  const hours = Number(match[1]);\n  const minutes = Number(match[2]);\n  const seconds = Number(match[3]);\n  const milliseconds = Number(match[4]);\n\n  return hours * 60 * 60 + minutes * 60 + seconds + milliseconds / 1000;\n}\n\nfunction parseSrt(srt: string) {\n  return (\n    srt\n      .split(/\\r|\\n/)\n      .map((s) => s.trim())\n      .filter((str) => str.length > 0)\n      // 匹配时间戳标记行，匹配失败的为字幕内容\n      .map((s) => ({\n        lineStr: s,\n        match: s.match(\n          /(\\d{2}:\\d{2}:\\d{2},\\d{3}) --> (\\d{2}:\\d{2}:\\d{2},\\d{3})/,\n        ),\n      }))\n      // 过滤掉时间上一行的数字标记\n      .filter(\n        ({ lineStr }, idx, source) =>\n          !(/^\\d+$/.test(lineStr) && source[idx + 1]?.match != null),\n      )\n      // 按时间标记行聚合，拼接字幕内容到 text 字段\n      .reduce(\n        (acc, { lineStr, match }) => {\n          if (match == null) {\n            const last = acc.at(-1);\n            if (last == null) return acc;\n\n            last.text += last.text.length === 0 ? lineStr : `\\n${lineStr}`;\n          } else {\n            acc.push({\n              start: srtTimeToSeconds(match[1]),\n              end: srtTimeToSeconds(match[2]),\n              text: '',\n            });\n          }\n\n          return acc;\n        },\n        [] as Array<{\n          start: number;\n          end: number;\n          text: string;\n        }>,\n      )\n  );\n}\n","import mp4box, {\n  MP4ArrayBuffer,\n  MP4File,\n  MP4Info,\n  MP4Sample,\n} from '@webav/mp4box.js';\n\n/**\n * 将原始字节流转换成 MP4Sample 流\n */\nexport class SampleTransform {\n  readable: ReadableStream<\n    | {\n        chunkType: 'ready';\n        data: { info: MP4Info; file: MP4File };\n      }\n    | {\n        chunkType: 'samples';\n        data: { id: number; type: 'video' | 'audio'; samples: MP4Sample[] };\n      }\n  >;\n\n  writable: WritableStream<Uint8Array>;\n\n  #inputBufOffset = 0;\n\n  constructor() {\n    const file = mp4box.createFile();\n    let streamCancelled = false;\n    this.readable = new ReadableStream(\n      {\n        start: (ctrl) => {\n          file.onReady = (info) => {\n            const vTrackId = info.videoTracks[0]?.id;\n            if (vTrackId != null)\n              file.setExtractionOptions(vTrackId, 'video', { nbSamples: 100 });\n\n            const aTrackId = info.audioTracks[0]?.id;\n            if (aTrackId != null)\n              file.setExtractionOptions(aTrackId, 'audio', { nbSamples: 100 });\n\n            ctrl.enqueue({ chunkType: 'ready', data: { info, file } });\n            file.start();\n          };\n\n          const releasedCnt: Record<number, number> = {};\n          file.onSamples = (id, type, samples) => {\n            ctrl.enqueue({\n              chunkType: 'samples',\n              data: { id, type, samples: samples.map((s) => ({ ...s })) },\n            });\n            releasedCnt[id] = (releasedCnt[id] ?? 0) + samples.length;\n            file.releaseUsedSamples(id, releasedCnt[id]);\n          };\n\n          file.onFlush = () => {\n            ctrl.close();\n          };\n        },\n        cancel: () => {\n          file.stop();\n          streamCancelled = true;\n        },\n      },\n      {\n        // 每条消息 100 个 samples\n        highWaterMark: 50,\n      },\n    );\n\n    this.writable = new WritableStream({\n      write: async (ui8Arr) => {\n        if (streamCancelled) {\n          this.writable.abort();\n          return;\n        }\n\n        const inputBuf = ui8Arr.buffer as MP4ArrayBuffer;\n        inputBuf.fileStart = this.#inputBufOffset;\n        this.#inputBufOffset += inputBuf.byteLength;\n        file.appendBuffer(inputBuf);\n      },\n      close: () => {\n        file.flush();\n        file.stop();\n        file.onFlush?.();\n      },\n    });\n  }\n}\n","import mp4box, {\n  MP4File,\n  MP4Sample,\n  SampleOpts,\n  TrakBoxParser,\n} from '@webav/mp4box.js';\nimport { autoReadStream, file2stream, Log } from '@webav/internal-utils';\nimport {\n  extractPCM4AudioData,\n  extractPCM4AudioBuffer,\n  mixinPCM,\n  ringSliceFloat32Array,\n  concatPCMFragments,\n} from '../av-utils';\nimport { DEFAULT_AUDIO_CONF } from '../clips';\nimport { SampleTransform } from './sample-transform';\nimport { extractFileConfig } from './mp4box-utils';\nimport { tmpfile, write } from 'opfs-tools';\n\nfunction fixMP4BoxFileDuration(\n  inMP4File: MP4File,\n): () => Promise<ReadableStream<Uint8Array> | null> {\n  let sendedBoxIdx = 0;\n  const boxes = inMP4File.boxes;\n  const tracks: Array<{ track: TrakBoxParser; id: number }> = [];\n  let totalDuration = 0;\n\n  async function write2TmpFile() {\n    const buf = box2Buf(boxes, sendedBoxIdx);\n    sendedBoxIdx = boxes.length;\n    // 释放引用，避免内存泄露\n    // todo: use unsafeReleaseMP4BoxFile\n    tracks.forEach(({ track, id }) => {\n      const s = track.samples.at(-1);\n      if (s != null)\n        totalDuration = Math.max(totalDuration, s.cts + s.duration);\n\n      inMP4File.releaseUsedSamples(id, track.samples.length);\n      track.samples = [];\n    });\n    inMP4File.mdats = [];\n    inMP4File.moofs = [];\n    if (buf != null) await tmpFileWriter?.write(buf);\n  }\n\n  let moovPrevBoxes: typeof boxes = [];\n  function moovBoxReady() {\n    if (moovPrevBoxes.length > 0) return true;\n\n    const moovIdx = boxes.findIndex((box) => box.type === 'moov');\n    if (moovIdx === -1) return false;\n\n    moovPrevBoxes = boxes.slice(0, moovIdx + 1);\n    sendedBoxIdx = moovIdx + 1;\n\n    if (tracks.length === 0) {\n      for (let i = 1; true; i += 1) {\n        const track = inMP4File.getTrackById(i);\n        if (track == null) break;\n        tracks.push({ track, id: i });\n      }\n    }\n\n    return true;\n  }\n\n  let timerId = 0;\n  // 把 moov 之外的 box 先写入临时文件，待更新 duration 之后再拼接临时文件\n  const postFile = tmpfile();\n  let tmpFileWriter: Awaited<\n    ReturnType<ReturnType<typeof tmpfile>['createWriter']>\n  > | null = null;\n\n  const initPromise = (async () => {\n    tmpFileWriter = await postFile.createWriter();\n\n    timerId = self.setInterval(() => {\n      if (!moovBoxReady()) return;\n      write2TmpFile();\n    }, 100);\n  })();\n\n  let stoped = false;\n  return async () => {\n    if (stoped) throw Error('File exported');\n    stoped = true;\n\n    await initPromise;\n    clearInterval(timerId);\n\n    if (!moovBoxReady() || tmpFileWriter == null) return null;\n    inMP4File.flush();\n    await write2TmpFile();\n    await tmpFileWriter?.close();\n\n    const moov = moovPrevBoxes.find((box) => box.type === 'moov') as\n      | typeof inMP4File.moov\n      | undefined;\n    if (moov == null) return null;\n\n    moov.mvhd.duration = totalDuration;\n\n    const rsFile = tmpfile();\n    const buf = box2Buf(moovPrevBoxes, 0)!;\n    await write(rsFile, buf);\n    await write(rsFile, postFile, { overwrite: false });\n\n    return await rsFile.stream();\n  };\n\n  function box2Buf(source: typeof boxes, startIdx: number): Uint8Array | null {\n    if (startIdx >= source.length) return null;\n\n    const ds = new mp4box.DataStream();\n    ds.endianness = mp4box.DataStream.BIG_ENDIAN;\n\n    for (let i = startIdx; i < source.length; i++) {\n      if (source[i] === null) continue;\n      source[i].write(ds);\n      delete source[i];\n    }\n    return new Uint8Array(ds.buffer);\n  }\n}\n\n/**\n * EncodedAudioChunk | EncodedVideoChunk 转换为 MP4 addSample 需要的参数\n */\nfunction chunk2MP4SampleOpts(\n  chunk: EncodedAudioChunk | EncodedVideoChunk,\n): SampleOpts & {\n  data: ArrayBuffer;\n} {\n  const buf = new ArrayBuffer(chunk.byteLength);\n  chunk.copyTo(buf);\n  const dts = chunk.timestamp;\n  return {\n    duration: chunk.duration ?? 0,\n    dts,\n    cts: dts,\n    is_sync: chunk.type === 'key',\n    data: buf,\n  };\n}\n\n/**\n * 快速拼接多个mp4 文件流，要求所有 mp4 的属性一致，\n * 属性包括（不限于）：音视频编码格式、分辨率、采样率\n *\n * @param streams 一个包含 Uint8Array 的可读流数组。\n * @returns 返回一个 Promise，该 Promise 在解析时返回一个包含合并后的 MP4 数据的可读流。\n * @throws 如果无法从流生成文件，将抛出错误。\n *\n * @example\n * const streams = [stream1, stream2, stream3];\n * const resultStream = await fastConcatMP4(streams);\n */\nexport async function fastConcatMP4(\n  streams: ReadableStream<Uint8Array>[],\n): Promise<ReadableStream<Uint8Array>> {\n  const outfile = mp4box.createFile();\n\n  const dumpFile = fixMP4BoxFileDuration(outfile);\n  await concatStreamsToMP4BoxFile(streams, outfile);\n  const outStream = await dumpFile();\n  if (outStream == null) throw Error('Can not generate file from streams');\n  return outStream;\n}\n\nasync function concatStreamsToMP4BoxFile(\n  streams: ReadableStream<Uint8Array>[],\n  outfile: MP4File,\n) {\n  let vTrackId = 0;\n  let vDTS = 0;\n  let vCTS = 0;\n  let aTrackId = 0;\n  let aDTS = 0;\n  let aCTS = 0;\n  // ts bug, 不能正确识别类型\n  let lastVSamp: any = null;\n  let lastASamp: any = null;\n  for (const stream of streams) {\n    await new Promise<void>(async (resolve) => {\n      autoReadStream(stream.pipeThrough(new SampleTransform()), {\n        onDone: resolve,\n        onChunk: async ({ chunkType, data }) => {\n          if (chunkType === 'ready') {\n            const { videoTrackConf, audioTrackConf } = extractFileConfig(\n              data.file,\n              data.info,\n            );\n            if (vTrackId === 0 && videoTrackConf != null) {\n              vTrackId = outfile.addTrack(videoTrackConf);\n            }\n            if (aTrackId === 0 && audioTrackConf != null) {\n              aTrackId = outfile.addTrack(audioTrackConf);\n            }\n          } else if (chunkType === 'samples') {\n            const { type, samples } = data;\n            const trackId = type === 'video' ? vTrackId : aTrackId;\n            const offsetDTS = type === 'video' ? vDTS : aDTS;\n            const offsetCTS = type === 'video' ? vCTS : aCTS;\n\n            samples.forEach((s) => {\n              outfile.addSample(trackId, s.data, {\n                duration: s.duration,\n                dts: s.dts + offsetDTS,\n                cts: s.cts + offsetCTS,\n                is_sync: s.is_sync,\n              });\n            });\n\n            const lastSamp = samples.at(-1);\n            if (lastSamp == null) return;\n            if (type === 'video') {\n              lastVSamp = lastSamp;\n            } else if (type === 'audio') {\n              lastASamp = lastSamp;\n            }\n          }\n        },\n      });\n    });\n    if (lastVSamp != null) {\n      vDTS += lastVSamp.dts;\n      vCTS += lastVSamp.cts;\n    }\n    if (lastASamp != null) {\n      aDTS += lastASamp.dts;\n      aCTS += lastASamp.cts;\n    }\n  }\n}\n\n/**\n * 为 WebAV 生成的 fmp4 文件设置正确的时长值\n */\nexport async function fixFMP4Duration(\n  stream: ReadableStream<Uint8Array>,\n): Promise<ReadableStream<Uint8Array>> {\n  return await fastConcatMP4([stream]);\n}\n\n/**\n * 创建 MP4 音频样本解码器。\n * @param adConf - 音频解码器配置参数 {@link AudioDecoderConfig}。\n * @returns 返回一个对象，包含 `decode` 和 `close` 方法。\n * - `decode` 方法用于解码 MP4 音频样本，返回解码后的音频数据数组。\n * - `close` 方法用于关闭音频解码器。\n */\nfunction createMP4AudioSampleDecoder(\n  adConf: Parameters<AudioDecoder['configure']>[0],\n) {\n  let cacheAD: AudioData[] = [];\n  const adDecoder = new AudioDecoder({\n    output: (ad) => {\n      cacheAD.push(ad);\n    },\n    error: Log.error,\n  });\n  adDecoder.configure(adConf);\n\n  return {\n    decode: async (ss: MP4Sample[]) => {\n      ss.forEach((s) => {\n        adDecoder.decode(\n          new EncodedAudioChunk({\n            type: s.is_sync ? 'key' : 'delta',\n            timestamp: (1e6 * s.cts) / s.timescale,\n            duration: (1e6 * s.duration) / s.timescale,\n            data: s.data,\n          }),\n        );\n      });\n\n      await adDecoder.flush();\n\n      const rs = cacheAD;\n      cacheAD = [];\n\n      return rs;\n    },\n    close: () => {\n      adDecoder.close();\n    },\n  };\n}\n\n// 音频编码与解码API有很大区别，\n// 是因为编码中途调用 AudioEncoder.flush ，会导致声音听起来卡顿\nfunction createMP4AudioSampleEncoder(\n  aeConf: Parameters<AudioEncoder['configure']>[0],\n  onOutput: (s: ReturnType<typeof chunk2MP4SampleOpts>) => void,\n) {\n  const encoderConf = {\n    codec: aeConf.codec,\n    sampleRate: aeConf.sampleRate,\n    numberOfChannels: aeConf.numberOfChannels,\n  } as const;\n\n  const adEncoder = new AudioEncoder({\n    output: (chunk) => {\n      onOutput(chunk2MP4SampleOpts(chunk));\n    },\n    error: (err) => {\n      Log.error('AudioEncoder error:', err, ', config:', encoderConf);\n    },\n  });\n\n  adEncoder.configure(encoderConf);\n\n  // 保留一个音频数据，用于最后做声音淡出\n  let lastData: { data: Float32Array; ts: number } | null = null;\n\n  function createAD(data: Float32Array, ts: number) {\n    return new AudioData({\n      timestamp: ts,\n      numberOfChannels: aeConf.numberOfChannels,\n      numberOfFrames: data.length / aeConf.numberOfChannels,\n      sampleRate: aeConf.sampleRate,\n      format: 'f32-planar',\n      data,\n    });\n  }\n  return {\n    encode: async (data: Float32Array, ts: number) => {\n      if (lastData != null) {\n        adEncoder.encode(createAD(lastData.data, lastData.ts));\n      }\n      lastData = { data, ts };\n    },\n    stop: async () => {\n      if (lastData != null) {\n        // 副作用修改数据\n        audioFade(lastData.data, aeConf.numberOfChannels, aeConf.sampleRate);\n        adEncoder.encode(createAD(lastData.data, lastData.ts));\n        lastData = null;\n      }\n      await adEncoder.flush();\n      adEncoder.close();\n    },\n  };\n}\n\n/**\n * 音频线性淡出，避免 POP 声\n * 副作用调整音量值\n */\nfunction audioFade(pcmData: Float32Array, chanCnt: number, sampleRate: number) {\n  const dataLen = pcmData.length - 1;\n  // 避免超出边界，最长 500ms 的淡出时间\n  const fadeLen = Math.min(sampleRate / 2, dataLen);\n  for (let i = 0; i < fadeLen; i++) {\n    for (let j = 1; j <= chanCnt; j++) {\n      // 从尾部开始，调整每个声道音量值\n      pcmData[Math.floor(dataLen / j) - i] *= i / fadeLen;\n    }\n  }\n}\n\n/**\n * 视频配音；混合 MP4 与音频文件，仅重编码音频，视频轨道不变\n * @param mp4Stream - MP4 流\n * @param audio - 音频信息\n * @param audio.stream - 音频数据流\n * @param audio.volume - 音频音量\n * @param audio.loop - 音频时长小于视频时，是否循环使用音频流\n * @returns 输出混合后的音频流\n */\nexport function mixinMP4AndAudio(\n  mp4Stream: ReadableStream<Uint8Array>,\n  audio: {\n    stream: ReadableStream<Uint8Array>;\n    volume: number;\n    loop: boolean;\n  },\n) {\n  Log.info('mixinMP4AndAudio, opts:', {\n    volume: audio.volume,\n    loop: audio.loop,\n  });\n\n  const outfile = mp4box.createFile();\n  const { stream: outStream, stop: stopOut } = file2stream(outfile, 500);\n\n  let audioSampleDecoder: ReturnType<\n    typeof createMP4AudioSampleDecoder\n  > | null = null;\n\n  let audioSampleEncoder: ReturnType<\n    typeof createMP4AudioSampleEncoder\n  > | null = null;\n\n  let inputAudioPCM: Float32Array[] = [];\n\n  let vTrackId = 0;\n  let aTrackId = 0;\n  let audioOffset = 0;\n  let mp4HasAudio = true;\n  let sampleRate = DEFAULT_AUDIO_CONF.sampleRate as number;\n  autoReadStream(mp4Stream.pipeThrough(new SampleTransform()), {\n    onDone: async () => {\n      await audioSampleEncoder?.stop();\n      audioSampleDecoder?.close();\n      stopOut();\n    },\n    onChunk: async ({ chunkType, data }) => {\n      if (chunkType === 'ready') {\n        const { videoTrackConf, audioTrackConf, audioDecoderConf } =\n          extractFileConfig(data.file, data.info);\n        if (vTrackId === 0 && videoTrackConf != null) {\n          vTrackId = outfile.addTrack(videoTrackConf);\n        }\n\n        const safeAudioTrackConf = audioTrackConf ?? {\n          timescale: 1e6,\n          samplerate: sampleRate,\n          channel_count: DEFAULT_AUDIO_CONF.channelCount,\n          hdlr: 'soun',\n          name: 'SoundHandler',\n          type: 'mp4a',\n        };\n        if (aTrackId === 0) {\n          aTrackId = outfile.addTrack(safeAudioTrackConf);\n          sampleRate = audioTrackConf?.samplerate ?? sampleRate;\n          mp4HasAudio = audioTrackConf == null ? false : true;\n        }\n        const audioCtx = new AudioContext({ sampleRate });\n        inputAudioPCM = extractPCM4AudioBuffer(\n          await audioCtx.decodeAudioData(\n            await new Response(audio.stream).arrayBuffer(),\n          ),\n        );\n\n        if (audioDecoderConf != null) {\n          audioSampleDecoder = createMP4AudioSampleDecoder(audioDecoderConf);\n        }\n        audioSampleEncoder = createMP4AudioSampleEncoder(\n          audioDecoderConf ?? {\n            codec:\n              safeAudioTrackConf.type === 'mp4a'\n                ? DEFAULT_AUDIO_CONF.codec\n                : safeAudioTrackConf.type,\n            numberOfChannels: safeAudioTrackConf.channel_count,\n            sampleRate: safeAudioTrackConf.samplerate,\n          },\n          (s) => outfile.addSample(aTrackId, s.data, s),\n        );\n      } else if (chunkType === 'samples') {\n        const { id, type, samples } = data;\n        if (type === 'video') {\n          samples.forEach((s) => outfile.addSample(id, s.data, s));\n\n          if (!mp4HasAudio) await addInputAudio2Track(samples);\n          return;\n        }\n\n        if (type === 'audio') await mixinAudioSampleAndInputPCM(samples);\n      }\n    },\n  });\n\n  function getInputAudioSlice(len: number) {\n    const rs = inputAudioPCM.map((chanBuf) =>\n      audio.loop\n        ? ringSliceFloat32Array(chanBuf, audioOffset, audioOffset + len)\n        : chanBuf.slice(audioOffset, audioOffset + len),\n    );\n    audioOffset += len;\n\n    if (audio.volume !== 1) {\n      for (const buf of rs)\n        for (let i = 0; i < buf.length; i++) buf[i] *= audio.volume;\n    }\n\n    return rs;\n  }\n\n  async function addInputAudio2Track(vdieoSamples: MP4Sample[]) {\n    const firstSamp = vdieoSamples[0];\n    const lastSamp = vdieoSamples[vdieoSamples.length - 1];\n    const pcmLength = Math.floor(\n      ((lastSamp.cts + lastSamp.duration - firstSamp.cts) /\n        lastSamp.timescale) *\n        sampleRate,\n    );\n    const audioDataBuf = mixinPCM([getInputAudioSlice(pcmLength)]);\n    if (audioDataBuf.length === 0) return;\n    audioSampleEncoder?.encode(\n      audioDataBuf,\n      (firstSamp.cts / firstSamp.timescale) * 1e6,\n    );\n  }\n\n  async function mixinAudioSampleAndInputPCM(samples: MP4Sample[]) {\n    if (audioSampleDecoder == null) return;\n\n    // 1. 先解码mp4音频\n    // [[chan0, chan1], [chan0, chan1]...]\n    const pcmFragments = (await audioSampleDecoder.decode(samples)).map(\n      extractPCM4AudioData,\n    );\n    // [chan0, chan1]\n    const mp4AudioPCM = concatPCMFragments(pcmFragments);\n    const inputAudioPCM = getInputAudioSlice(mp4AudioPCM[0].length);\n    const firstSamp = samples[0];\n\n    // 3. 重编码音频\n    audioSampleEncoder?.encode(\n      // 2. 混合输入的音频\n      mixinPCM([mp4AudioPCM, inputAudioPCM]),\n      (firstSamp.cts / firstSamp.timescale) * 1e6,\n    );\n  }\n\n  return outStream;\n}\n","// 改编自 https://jameshfisher.com/2020/08/11/production-ready-green-screen-in-the-browser/\nconst vertexShader = `#version 300 es\n  layout (location = 0) in vec4 a_position;\n  layout (location = 1) in vec2 a_texCoord;\n  out vec2 v_texCoord;\n  void main () {\n    gl_Position = a_position;\n    v_texCoord = a_texCoord;\n  }\n`;\n\nconst fragmentShader = `#version 300 es\nprecision mediump float;\nout vec4 FragColor;\nin vec2 v_texCoord;\n\nuniform sampler2D frameTexture;\nuniform vec3 keyColor;\n\n// 色度的相似度计算\nuniform float similarity;\n// 透明度的平滑度计算\nuniform float smoothness;\n// 降低绿幕饱和度，提高抠图准确度\nuniform float spill;\n\nvec2 RGBtoUV(vec3 rgb) {\n  return vec2(\n    rgb.r * -0.169 + rgb.g * -0.331 + rgb.b *  0.5    + 0.5,\n    rgb.r *  0.5   + rgb.g * -0.419 + rgb.b * -0.081  + 0.5\n  );\n}\n\nvoid main() {\n  // 获取当前像素的rgba值\n  vec4 rgba = texture(frameTexture, v_texCoord);\n  // 计算当前像素与绿幕像素的色度差值\n  vec2 chromaVec = RGBtoUV(rgba.rgb) - RGBtoUV(keyColor);\n  // 计算当前像素与绿幕像素的色度距离（向量长度）, 越相像则色度距离越小\n  float chromaDist = sqrt(dot(chromaVec, chromaVec));\n  // 设置了一个相似度阈值，baseMask为负，则表明是绿幕，为正则表明不是绿幕\n  float baseMask = chromaDist - similarity;\n  // 如果baseMask为负数，fullMask等于0；baseMask为正数，越大，则透明度越低\n  float fullMask = pow(clamp(baseMask / smoothness, 0., 1.), 1.5);\n  rgba.a = fullMask; // 设置透明度\n  // 如果baseMask为负数，spillVal等于0；baseMask为整数，越小，饱和度越低\n  float spillVal = pow(clamp(baseMask / spill, 0., 1.), 1.5);\n  float desat = clamp(rgba.r * 0.2126 + rgba.g * 0.7152 + rgba.b * 0.0722, 0., 1.); // 计算当前像素的灰度值\n  rgba.rgb = mix(vec3(desat, desat, desat), rgba.rgb, spillVal);\n  FragColor = rgba;\n}\n`;\n\nconst POINT_POS = [-1, 1, -1, -1, 1, -1, 1, -1, 1, 1, -1, 1];\nconst TEX_COORD_POS = [0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1];\n\n//  初始化着色器程序，让 WebGL 知道如何绘制我们的数据\nfunction initShaderProgram(\n  gl: WebGLRenderingContext,\n  vsSource: string,\n  fsSource: string,\n) {\n  const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource)!;\n  const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource)!;\n\n  // 创建着色器程序\n  const shaderProgram = gl.createProgram()!;\n  gl.attachShader(shaderProgram, vertexShader);\n  gl.attachShader(shaderProgram, fragmentShader);\n  gl.linkProgram(shaderProgram);\n\n  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {\n    throw Error(\n      gl.getProgramInfoLog(shaderProgram) ??\n        'Unable to initialize the shader program',\n    );\n  }\n\n  return shaderProgram;\n}\n\n// 创建指定类型的着色器，上传 source 源码并编译\nfunction loadShader(gl: WebGLRenderingContext, type: number, source: string) {\n  const shader = gl.createShader(type)!;\n\n  // Send the source to the shader object\n  gl.shaderSource(shader, source);\n\n  // Compile the shader program\n  gl.compileShader(shader);\n\n  // See if it compiled successfully\n  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n    const errMsg = gl.getShaderInfoLog(shader);\n    gl.deleteShader(shader);\n    throw Error(errMsg ?? 'An error occurred compiling the shaders');\n  }\n\n  return shader;\n}\n\nfunction updateTexture(\n  gl: WebGLRenderingContext,\n  img: TImgSource,\n  texture: WebGLTexture,\n) {\n  gl.bindTexture(gl.TEXTURE_2D, texture);\n  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);\n  gl.drawArrays(gl.TRIANGLES, 0, 6);\n}\n\nfunction initTexture(gl: WebGLRenderingContext) {\n  const texture = gl.createTexture();\n  if (texture == null) throw Error('Create WebGL texture error');\n  gl.bindTexture(gl.TEXTURE_2D, texture);\n\n  // put a single pixel in the texture so we can use it immediately.\n  const level = 0;\n  const internalFormat = gl.RGBA;\n  const width = 1;\n  const height = 1;\n  const border = 0;\n  const srcFormat = gl.RGBA;\n  const srcType = gl.UNSIGNED_BYTE;\n  const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue\n  gl.texImage2D(\n    gl.TEXTURE_2D,\n    level,\n    internalFormat,\n    width,\n    height,\n    border,\n    srcFormat,\n    srcType,\n    pixel,\n  );\n\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\n  return texture;\n}\n\ninterface IChromakeyOpts {\n  keyColor: [number, number, number];\n  similarity: number;\n  smoothness: number;\n  spill: number;\n}\n\nfunction initCvs(\n  opts: {\n    width: number;\n    height: number;\n  } & IChromakeyOpts,\n) {\n  const cvs =\n    'document' in globalThis\n      ? globalThis.document.createElement('canvas')\n      : new OffscreenCanvas(opts.width, opts.height);\n  cvs.width = opts.width;\n  cvs.height = opts.height;\n\n  const gl = cvs.getContext('webgl2', {\n    premultipliedAlpha: false,\n    alpha: true,\n  }) as WebGL2RenderingContext | null;\n\n  if (gl == null) throw Error('Cant create gl context');\n\n  const shaderProgram = initShaderProgram(gl, vertexShader, fragmentShader);\n  gl.useProgram(shaderProgram);\n\n  gl.uniform3fv(\n    gl.getUniformLocation(shaderProgram, 'keyColor'),\n    opts.keyColor.map((v) => v / 255),\n  );\n  gl.uniform1f(\n    gl.getUniformLocation(shaderProgram, 'similarity'),\n    opts.similarity,\n  );\n  gl.uniform1f(\n    gl.getUniformLocation(shaderProgram, 'smoothness'),\n    opts.smoothness,\n  );\n  gl.uniform1f(gl.getUniformLocation(shaderProgram, 'spill'), opts.spill);\n\n  const posBuffer = gl.createBuffer();\n  gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);\n  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(POINT_POS), gl.STATIC_DRAW);\n  const a_position = gl.getAttribLocation(shaderProgram, 'a_position');\n  gl.vertexAttribPointer(\n    a_position,\n    2,\n    gl.FLOAT,\n    false,\n    Float32Array.BYTES_PER_ELEMENT * 2,\n    0,\n  );\n  gl.enableVertexAttribArray(a_position);\n\n  const texCoordBuffer = gl.createBuffer();\n  gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);\n  gl.bufferData(\n    gl.ARRAY_BUFFER,\n    new Float32Array(TEX_COORD_POS),\n    gl.STATIC_DRAW,\n  );\n  const a_texCoord = gl.getAttribLocation(shaderProgram, 'a_texCoord');\n  gl.vertexAttribPointer(\n    a_texCoord,\n    2,\n    gl.FLOAT,\n    false,\n    Float32Array.BYTES_PER_ELEMENT * 2,\n    0,\n  );\n  gl.enableVertexAttribArray(a_texCoord);\n\n  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);\n\n  return { cvs, gl };\n}\n\ntype TImgSource =\n  | HTMLVideoElement\n  | HTMLCanvasElement\n  | HTMLImageElement\n  | ImageBitmap\n  | OffscreenCanvas\n  | VideoFrame;\n\nfunction getSourceWH(imgSource: TImgSource) {\n  return imgSource instanceof VideoFrame\n    ? { width: imgSource.codedWidth, height: imgSource.codedHeight }\n    : { width: imgSource.width, height: imgSource.height };\n}\n\nfunction getKeyColor(imgSource: TImgSource) {\n  const cvs = new OffscreenCanvas(1, 1);\n  const ctx = cvs.getContext('2d')!;\n  ctx.drawImage(imgSource, 0, 0);\n  const {\n    data: [r, g, b],\n  } = ctx.getImageData(0, 0, 1, 1);\n  return [r, g, b] as [number, number, number];\n}\n\n/**\n * 绿幕抠图\n * keyColor 需要扣除的背景色，若不传则取第一个像素点\n * similarity 背景色相似度阈值，过小可能保留背景色，过大可能扣掉更多非背景像素点\n * smoothness 平滑度；过小可能出现锯齿，过大导致整体变透明\n * spill      饱和度；过小可能保留绿色混合，过大导致图片变灰度\n * @param opts: {\n *   keyColor?: [r, g, b]\n *   similarity: number\n *   smoothness: number\n *   spill: number\n * }\n */\nexport const createChromakey = (\n  opts: Omit<IChromakeyOpts, 'keyColor'> & {\n    keyColor?: [number, number, number];\n  },\n) => {\n  let cvs: HTMLCanvasElement | OffscreenCanvas | null = null;\n  let gl: WebGLRenderingContext | null = null;\n  let keyC = opts.keyColor;\n  let texture: WebGLTexture | null = null;\n\n  return async (imgSource: TImgSource) => {\n    if (cvs == null || gl == null || texture == null) {\n      if (keyC == null) keyC = getKeyColor(imgSource);\n      ({ cvs, gl } = initCvs({\n        ...getSourceWH(imgSource),\n        keyColor: keyC,\n        ...opts,\n      }));\n      texture = initTexture(gl);\n    }\n\n    updateTexture(gl, imgSource, texture);\n\n    if (\n      globalThis.VideoFrame != null &&\n      imgSource instanceof globalThis.VideoFrame\n    ) {\n      const rs = new VideoFrame(cvs, {\n        alpha: 'keep',\n        timestamp: imgSource.timestamp,\n        duration: imgSource.duration ?? undefined,\n      });\n      imgSource.close();\n      return rs;\n    }\n\n    return createImageBitmap(cvs, {\n      imageOrientation: imgSource instanceof ImageBitmap ? 'flipY' : 'none',\n    });\n  };\n};\n","import { EventTool } from '@webav/internal-utils';\n\ninterface IPoint {\n  x: number;\n  y: number;\n}\n\nexport interface IRectBaseProps {\n  x: number;\n  y: number;\n  w: number;\n  h: number;\n  angle: number;\n}\n\n/**\n * 用于记录素材在视频或画布中的空间属性：位置、大小、旋转\n *\n * 并提供控制点位置，支持用户在画布中缩放、旋转素材\n *\n * 一般由内部 WebAV SDK 内部创建维护\n *\n * @see {@link Combinator}, {@link OffscreenSprite}\n * @see [AVCanvas](../../av-canvas/classes/AVCanvas.html), {@link VisibleSprite}\n *\n * @see [视频剪辑](https://webav-tech.github.io/WebAV/demo/6_4-video-editor)\n */\nexport class Rect implements IRectBaseProps {\n  #evtTool = new EventTool<{\n    propsChange: (props: Partial<IRectBaseProps>) => void;\n  }>();\n  /**\n   * 监听属性变更事件\n   * @example\n   * rect.on('propsChange', (changedProps) => {})\n   */\n  on = this.#evtTool.on;\n\n  #x = 0;\n  /**\n   * x 坐标\n   */\n  get x() {\n    return this.#x;\n  }\n  set x(v) {\n    this.#setBaseProps('x', v);\n  }\n  #y = 0;\n  get y() {\n    return this.#y;\n  }\n  /**\n   * y 坐标\n   */\n  set y(v) {\n    this.#setBaseProps('y', v);\n  }\n  #w = 0;\n  /**\n   * 宽\n   */\n  get w() {\n    return this.#w;\n  }\n  set w(v) {\n    this.#setBaseProps('w', v);\n  }\n  #h = 0;\n  /**\n   * 高\n   */\n  get h() {\n    return this.#h;\n  }\n  set h(v) {\n    this.#setBaseProps('h', v);\n  }\n  #angle = 0;\n  /**\n   * 旋转角度\n   * @see [MDN Canvas rotate](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/rotate)\n   */\n  get angle() {\n    return this.#angle;\n  }\n  set angle(v) {\n    this.#setBaseProps('angle', v);\n  }\n\n  #setBaseProps(prop: keyof IRectBaseProps, v: number) {\n    const changed = this[prop] !== v;\n    switch (prop) {\n      case 'x':\n        this.#x = v;\n        break;\n      case 'y':\n        this.#y = v;\n        break;\n      case 'w':\n        this.#w = v;\n        break;\n      case 'h':\n        this.#h = v;\n        break;\n      case 'angle':\n        this.#angle = v;\n        break;\n    }\n    if (changed) this.#evtTool.emit('propsChange', { [prop]: v });\n  }\n\n  /**\n   * 如果当前实例是 Rect 控制点之一，`master` 将指向该 Rect\n   *\n   * 控制点的坐标是相对于它的 `master` 定位\n   */\n  #master: Rect | null = null;\n\n  constructor(\n    x?: number,\n    y?: number,\n    w?: number,\n    h?: number,\n    master?: Rect | null,\n  ) {\n    this.x = x ?? 0;\n    this.y = y ?? 0;\n    this.w = w ?? 0;\n    this.h = h ?? 0;\n    this.#master = master ?? null;\n  }\n\n  /**\n   * 根据坐标、宽高计算出来的矩形中心点\n   */\n  get center(): IPoint {\n    const { x, y, w, h } = this;\n    return { x: x + w / 2, y: y + h / 2 };\n  }\n\n  /**\n   * 是否保持固定宽高比例，禁止变形缩放\n   *\n   * 值为 true 时，将缺少上下左右四个控制点\n   */\n  fixedAspectRatio = false;\n\n  /**\n   * 是否固定中心点进行缩放\n   * 值为 true 时，固定中心点不变进行缩放\n   * 值为 false 时，固定对角点不变进行缩放\n   */\n  fixedScaleCenter = false;\n\n  clone(): Rect {\n    const { x, y, w, h } = this;\n    const rect = new Rect(x, y, w, h, this.#master);\n    rect.angle = this.angle;\n    rect.fixedAspectRatio = this.fixedAspectRatio;\n    rect.fixedScaleCenter = this.fixedScaleCenter;\n    return rect;\n  }\n\n  /**\n   * 检测目标坐标是否命中当前实例\n   * @param tx 目标点 x 坐标\n   * @param ty 目标点 y 坐标\n   */\n  checkHit(tx: number, ty: number): boolean {\n    let { angle, center, x, y, w, h } = this;\n    // ctrls 的中心点、旋转角度都取自于 master （sprite）\n    const cnt = this.#master?.center ?? center;\n    const agl = this.#master?.angle ?? angle;\n    // ctrl 初始化时其坐标就是相对于 master 的，参见 get ctrls()\n    // 所以此处不用转换\n    if (this.#master == null) {\n      x = x - cnt.x;\n      y = y - cnt.y;\n    }\n    // 鼠标点击坐标映射成以中点为原点的坐标\n    const tOX = tx - cnt.x;\n    const tOY = ty - cnt.y;\n    // 如果有旋转，映射成相对 sprite 原点，旋转前的坐标\n    let mx = tOX;\n    let my = tOY;\n    if (agl !== 0) {\n      // 推导公式 https://github.com/hughfenghen/hughfenghen.github.io/issues/96\n      mx = tOX * Math.cos(agl) + tOY * Math.sin(agl);\n      my = tOY * Math.cos(agl) - tOX * Math.sin(agl);\n    }\n\n    if (mx < x || mx > x + w || my < y || my > y + h) return false;\n\n    return true;\n  }\n}\n","import { EventTool } from '@webav/internal-utils';\nimport { IRectBaseProps, Rect } from './rect';\n\ninterface IAnimationOpts {\n  duration: number;\n  delay?: number;\n  iterCount?: number;\n}\n\ntype TAnimateProps = IRectBaseProps & { opacity: number };\n\nexport type TAnimationKeyFrame = Array<[number, Partial<TAnimateProps>]>;\n\ntype TKeyFrameOpts = Partial<\n  Record<`${number}%` | 'from' | 'to', Partial<TAnimateProps>>\n>;\n\n/**\n * Sprite 基类\n *\n * @see {@link OffscreenSprite}\n * @see {@link VisibleSprite}\n */\nexport abstract class BaseSprite {\n  /**\n   * 控制素材在视频中的空间属性（坐标、旋转、缩放）\n   */\n  rect = new Rect();\n\n  /**\n   * 控制素材在的时间偏移、时长、播放速率，常用于剪辑场景时间轴（轨道）模块\n   * duration 不能大于引用 {@link IClip} 的时长，单位 微秒\n   *\n   * playbackRate 控制当前素材的播放速率，1 表示正常播放；\n   * **注意**\n   *    1. 设置 playbackRate 时需要主动修正 duration\n   *    2. 音频使用最简单的插值算法来改变速率，所以改变速率后音调会产生变化，自定义算法请使用 {@link MP4Clip.tickInterceptor} 配合实现\n   *\n   */\n  #time = {\n    offset: 0,\n    duration: 0,\n    playbackRate: 1,\n  };\n  get time(): { offset: number; duration: number; playbackRate: number } {\n    return this.#time;\n  }\n  set time(v: { offset: number; duration: number; playbackRate?: number }) {\n    Object.assign(this.#time, v);\n  }\n\n  #evtTool = new EventTool<{\n    propsChange: (\n      value: Partial<{ rect: Partial<Rect>; zIndex: number }>,\n    ) => void;\n  }>();\n  /**\n   * 监听属性变更事件\n   * @example\n   * sprite.on('propsChange', (changedProps) => {})\n   */\n  on = this.#evtTool.on;\n\n  #zIndex = 0;\n  get zIndex(): number {\n    return this.#zIndex;\n  }\n\n  /**\n   * 控制素材间的层级关系，zIndex 值较小的素材会被遮挡\n   */\n  set zIndex(v: number) {\n    const changed = this.#zIndex !== v;\n    this.#zIndex = v;\n    if (changed) this.#evtTool.emit('propsChange', { zIndex: v });\n  }\n\n  /**\n   * 不透明度\n   */\n  opacity = 1;\n\n  /**\n   * 水平或垂直方向翻转素材\n   */\n  flip: 'horizontal' | 'vertical' | null = null;\n\n  #animatKeyFrame: TAnimationKeyFrame | null = null;\n\n  #animatOpts: Required<IAnimationOpts> | null = null;\n\n  /**\n   * @see {@link IClip.ready}\n   */\n  ready = Promise.resolve();\n\n  constructor() {\n    this.rect.on('propsChange', (props) => {\n      this.#evtTool.emit('propsChange', { rect: props });\n    });\n  }\n\n  protected _render(\n    ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n  ): void {\n    const {\n      rect: { center, angle },\n    } = this;\n    ctx.setTransform(\n      // 水平 缩放、倾斜\n      this.flip === 'horizontal' ? -1 : 1,\n      0,\n      // 垂直 倾斜、缩放\n      0,\n      this.flip === 'vertical' ? -1 : 1,\n      // 坐标原点偏移 x y\n      center.x,\n      center.y,\n    );\n    // 任意方向翻转，旋转角度转为负值，才能与控制点同步\n    ctx.rotate((this.flip == null ? 1 : -1) * angle);\n\n    ctx.globalAlpha = this.opacity;\n  }\n\n  /**\n   * 给素材添加动画，使用方法参考 css animation\n   *\n   * @example\n   * sprite.setAnimation(\n   *   {\n   *     '0%': { x: 0, y: 0 },\n   *     '25%': { x: 1200, y: 680 },\n   *     '50%': { x: 1200, y: 0 },\n   *     '75%': { x: 0, y: 680 },\n   *     '100%': { x: 0, y: 0 },\n   *   },\n   *   { duration: 4e6, iterCount: 1 },\n   * );\n   *\n   * @see [视频水印动画](https://webav-tech.github.io/WebAV/demo/2_1-concat-video)\n   */\n  setAnimation(keyFrame: TKeyFrameOpts, opts: IAnimationOpts): void {\n    this.#animatKeyFrame = Object.entries(keyFrame).map(([k, val]) => {\n      const numK = { from: 0, to: 100 }[k] ?? Number(k.slice(0, -1));\n      if (isNaN(numK) || numK > 100 || numK < 0) {\n        throw Error('keyFrame must between 0~100');\n      }\n      return [numK / 100, val];\n    }) as TAnimationKeyFrame;\n    this.#animatOpts = Object.assign({}, this.#animatOpts, {\n      duration: opts.duration,\n      delay: opts.delay ?? 0,\n      iterCount: opts.iterCount ?? Infinity,\n    });\n  }\n\n  /**\n   * 如果当前 sprite 已被设置动画，将 sprite 的动画属性设定到指定时间的状态\n   */\n  animate(time: number): void {\n    if (\n      this.#animatKeyFrame == null ||\n      this.#animatOpts == null ||\n      time < this.#animatOpts.delay\n    )\n      return;\n    const updateProps = linearTimeFn(\n      time,\n      this.#animatKeyFrame,\n      this.#animatOpts,\n    );\n    for (const k in updateProps) {\n      switch (k) {\n        case 'opacity':\n          this.opacity = updateProps[k] as number;\n          break;\n        case 'x':\n        case 'y':\n        case 'w':\n        case 'h':\n        case 'angle':\n          this.rect[k] = updateProps[k] as number;\n          break;\n      }\n    }\n  }\n\n  /**\n   * 将当前 sprite 的属性赋值到目标\n   *\n   * 用于 clone，或 {@link VisibleSprite} 与 {@link OffscreenSprite} 实例间的类型转换\n   */\n  copyStateTo<T extends BaseSprite>(target: T) {\n    target.#animatKeyFrame = this.#animatKeyFrame;\n    target.#animatOpts = this.#animatOpts;\n    target.zIndex = this.zIndex;\n    target.opacity = this.opacity;\n    target.flip = this.flip;\n    target.rect = this.rect.clone();\n    target.time = { ...this.time };\n  }\n\n  protected destroy() {\n    this.#evtTool.destroy();\n  }\n}\n\nexport function linearTimeFn(\n  time: number,\n  kf: TAnimationKeyFrame,\n  opts: Required<IAnimationOpts>,\n): Partial<TAnimateProps> {\n  const offsetTime = time - opts.delay;\n  if (offsetTime / opts.duration >= opts.iterCount) return {};\n\n  const t = offsetTime % opts.duration;\n\n  const process = offsetTime === opts.duration ? 1 : t / opts.duration;\n  const idx = kf.findIndex((it) => it[0] >= process);\n  if (idx === -1) return {};\n\n  const startState = kf[idx - 1];\n  const nextState = kf[idx];\n  const nextFrame = nextState[1];\n  if (startState == null) return nextFrame;\n  const startFrame = startState[1];\n\n  const rs: Partial<TAnimateProps> = {};\n  // 介于两个Frame状态间的进度\n  const stateProcess =\n    (process - startState[0]) / (nextState[0] - startState[0]);\n  for (const prop in nextFrame) {\n    const p = prop as keyof TAnimateProps;\n    if (startFrame[p] == null) continue;\n    // @ts-expect-error\n    // eslint-disable-next-line\n    rs[p] = (nextFrame[p] - startFrame[p]) * stateProcess + startFrame[p];\n  }\n\n  return rs;\n}\n","import { BaseSprite } from './base-sprite';\nimport { IClip } from '../clips';\nimport { Log } from '@webav/internal-utils';\nimport { changePCMPlaybackRate } from '../av-utils';\n\n/**\n * 包装 {@link IClip} 给素材扩展坐标、层级、透明度等信息，用于 {@link Combinator} 在后台合成视频\n *\n * 跟 {@link VisibleSprite} 非常相似，应用场景不同\n *\n * @example\n * const spr = new OffscreenSprite(\n *   new MP4Clip((await fetch('<mp4 url>')).body),\n * );\n * spr.opacity = 0.5 // 半透明\n * spr.rect.x = 100 // x 坐标偏移 100 像素\n * spr.time.offset = 10e6 // 视频第 10s 开始绘制该视频素材\n *\n * @see [视频合成](https://webav-tech.github.io/WebAV/demo/2_1-concat-video)\n */\nexport class OffscreenSprite extends BaseSprite {\n  #clip: IClip;\n\n  // 保持最近一帧，若 clip 在当前帧无数据，则绘制最近一帧\n  #lastVf: VideoFrame | ImageBitmap | null = null;\n\n  #destroyed = false;\n\n  constructor(clip: IClip) {\n    super();\n    this.#clip = clip;\n    this.ready = clip.ready.then(({ width, height, duration }) => {\n      this.rect.w = this.rect.w === 0 ? width : this.rect.w;\n      this.rect.h = this.rect.h === 0 ? height : this.rect.h;\n      this.time.duration =\n        this.time.duration === 0 ? duration : this.time.duration;\n    });\n  }\n\n  /**\n   * 绘制素材指定时刻的图像到 canvas 上下文，并返回对应的音频数据\n   * @param time 指定时刻，微秒\n   */\n  async offscreenRender(\n    ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n    time: number,\n  ): Promise<{\n    audio: Float32Array[];\n    done: boolean;\n  }> {\n    const ts = time * this.time.playbackRate;\n    this.animate(ts);\n    super._render(ctx);\n    const { w, h } = this.rect;\n    const { video, audio, state } = await this.#clip.tick(ts);\n    let outAudio = audio ?? [];\n    if (audio != null && this.time.playbackRate !== 1) {\n      outAudio = audio.map((pcm) =>\n        changePCMPlaybackRate(pcm, this.time.playbackRate),\n      );\n    }\n\n    if (state === 'done') {\n      return {\n        audio: outAudio,\n        done: true,\n      };\n    }\n\n    const imgSource = video ?? this.#lastVf;\n    if (imgSource != null) {\n      ctx.drawImage(imgSource, -w / 2, -h / 2, w, h);\n    }\n\n    if (video != null) {\n      this.#lastVf?.close();\n      this.#lastVf = video;\n    }\n\n    return {\n      audio: outAudio,\n      done: false,\n    };\n  }\n\n  async clone() {\n    const spr = new OffscreenSprite(await this.#clip.clone());\n    await spr.ready;\n    this.copyStateTo(spr);\n    return spr;\n  }\n\n  destroy(): void {\n    if (this.#destroyed) return;\n    this.#destroyed = true;\n\n    Log.info('OffscreenSprite destroy');\n    super.destroy();\n    this.#lastVf?.close();\n    this.#lastVf = null;\n    this.#clip.destroy();\n  }\n}\n","import { Log } from '@webav/internal-utils';\nimport { changePCMPlaybackRate } from '../av-utils';\nimport { IClip } from '../clips';\nimport { BaseSprite } from './base-sprite';\n\n/**\n * 包装 {@link IClip} 给素材扩展坐标、层级、透明度等信息，用于 {@link [AVCanvas](../../av-canvas/classes/AVCanvas.html)} 响应用户交互\n *\n * 跟 {@link OffscreenSprite} 非常相似，应用场景不同\n *\n * @example\n * const spr = new VisibleSprite(\n *   new MP4Clip((await fetch('<mp4 url>')).body),\n * );\n * spr.opacity = 0.5 // 半透明\n * spr.rect.x = 100 // x 坐标偏移 100 像素\n * spr.time.offset = 10e6 // 视频第 10s 开始绘制素材\n *\n * @see [视频剪辑](https://webav-tech.github.io/WebAV/demo/6_4-video-editor)\n *\n */\nexport class VisibleSprite extends BaseSprite {\n  #clip: IClip;\n  getClip() {\n    return this.#clip;\n  }\n\n  /**\n   * 元素是否可见，用于不想删除，期望临时隐藏 Sprite 的场景\n   */\n  visible = true;\n\n  constructor(clip: IClip) {\n    super();\n    this.#clip = clip;\n    this.ready = clip.ready.then(({ width, height, duration }) => {\n      this.rect.w = this.rect.w === 0 ? width : this.rect.w;\n      this.rect.h = this.rect.h === 0 ? height : this.rect.h;\n      this.time.duration =\n        this.time.duration === 0 ? duration : this.time.duration;\n    });\n  }\n\n  // 保持最近一帧，若 clip 在当前帧无数据，则绘制最近一帧\n  #lastVf: VideoFrame | ImageBitmap | null = null;\n  #lastAudio: Float32Array[] = [];\n  #ticking = false;\n  #update(time: number) {\n    if (this.#ticking) return;\n    this.#ticking = true;\n\n    this.#clip\n      .tick(time * this.time.playbackRate)\n      .then(({ video, audio }) => {\n        if (video != null) {\n          this.#lastVf?.close();\n          this.#lastVf = video ?? null;\n        }\n        this.#lastAudio = audio ?? [];\n        if (audio != null && this.time.playbackRate !== 1) {\n          this.#lastAudio = audio.map((pcm) =>\n            changePCMPlaybackRate(pcm, this.time.playbackRate),\n          );\n        }\n      })\n      .finally(() => {\n        this.#ticking = false;\n      });\n  }\n\n  /**\n   * 提前准备指定 time 的帧\n   */\n  preFrame(time: number) {\n    if (this.#lastTime === time) return;\n    this.#lastVf?.close();\n    this.#lastVf = null;\n    this.#lastAudio = [];\n    this.#update(time);\n    this.#lastTime = time;\n  }\n\n  #lastTime = -1;\n  /**\n   * 绘制素材指定时刻的图像到 canvas 上下文，并返回对应的音频数据\n   * @param time 指定时刻，微秒\n   */\n  render(\n    ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,\n    time: number,\n  ): { audio: Float32Array[] } {\n    this.animate(time);\n    super._render(ctx);\n    const { w, h } = this.rect;\n    if (this.#lastTime !== time) this.#update(time);\n    this.#lastTime = time;\n\n    const audio = this.#lastAudio;\n    this.#lastAudio = [];\n    const video = this.#lastVf;\n    if (video != null) ctx.drawImage(video, -w / 2, -h / 2, w, h);\n\n    return { audio };\n  }\n\n  copyStateTo<T extends BaseSprite>(target: T): void {\n    super.copyStateTo(target);\n    if (target instanceof VisibleSprite) {\n      target.visible = this.visible;\n    }\n  }\n\n  #destroyed = false;\n  destroy(): void {\n    if (this.#destroyed) return;\n    this.#destroyed = true;\n\n    Log.info('VisibleSprite destroy');\n    super.destroy();\n    this.#lastVf?.close();\n    this.#lastVf = null;\n    this.#clip.destroy();\n  }\n}\n","import { Log, EventTool, file2stream, recodemux } from '@webav/internal-utils';\nimport { OffscreenSprite } from './sprite/offscreen-sprite';\nimport { sleep } from './av-utils';\nimport { DEFAULT_AUDIO_CONF } from './clips';\n\nexport interface ICombinatorOpts {\n  width?: number;\n  height?: number;\n  bitrate?: number;\n  fps?: number;\n  bgColor?: string;\n  videoCodec?: string;\n  /**\n   * false 合成的视频文件中排除音轨\n   */\n  audio?: false;\n  /**\n   * 向输出的视频中写入 meta tags 数据\n   */\n  metaDataTags?: Record<string, string>;\n  /**\n   * 不安全，随时可能废弃\n   */\n  __unsafe_hardwareAcceleration__?: HardwarePreference;\n}\n\nlet COM_ID = 0;\n\n/**\n * 避免 VideoEncoder 队列中的 VideoFrame 过多，打爆显存\n */\nasync function letEncoderCalmDown(getQSize: () => number) {\n  if (getQSize() > 50) {\n    await sleep(15);\n    await letEncoderCalmDown(getQSize);\n  }\n}\n\n/**\n * 视频合成器；能添加多个 {@link OffscreenSprite}，根据它们位置、层级、时间偏移等信息，合成输出为视频文件\n * @see [视频合成](https://webav-tech.github.io/WebAV/demo/2_1-concat-video)\n * @see [视频配音](https://webav-tech.github.io/WebAV/demo/2_2-video-add-audio)\n * @example\n * const spr1 = new OffscreenSprite(\n *   new MP4Clip((await fetch('<mp4 url>')).body),\n * );\n * const spr2 = new OffscreenSprite(\n *   new AudioClip((await fetch('<audio url>')).body),\n * );\n * const com = new Combinator({ width: 1280, height: 720, });\n\n * await com.addSprite(spr1);\n * await com.addSprite(spr2);\n\n * com.output(); // => ReadableStream\n *\n */\nexport class Combinator {\n  /**\n   * 检测当前环境的兼容性\n   * @param args.videoCodec 指定视频编码格式，默认 avc1.42E032\n   * @param args.width 指定视频宽度，默认 1920\n   * @param args.height 指定视频高度，默认 1080\n   * @param args.bitrate 指定视频比特率，默认 5e6\n   */\n  static async isSupported(\n    args: {\n      videoCodec?: string;\n      width?: number;\n      height?: number;\n      bitrate?: number;\n    } = {},\n  ): Promise<boolean> {\n    return (\n      (self.OffscreenCanvas != null &&\n        self.VideoEncoder != null &&\n        self.VideoDecoder != null &&\n        self.VideoFrame != null &&\n        self.AudioEncoder != null &&\n        self.AudioDecoder != null &&\n        self.AudioData != null &&\n        ((\n          await self.VideoEncoder.isConfigSupported({\n            codec: args.videoCodec ?? 'avc1.42E032',\n            width: args.width ?? 1920,\n            height: args.height ?? 1080,\n            bitrate: args.bitrate ?? 7e6,\n          })\n        ).supported ??\n          false) &&\n        (\n          await self.AudioEncoder.isConfigSupported({\n            codec: DEFAULT_AUDIO_CONF.codec,\n            sampleRate: DEFAULT_AUDIO_CONF.sampleRate,\n            numberOfChannels: DEFAULT_AUDIO_CONF.channelCount,\n          })\n        ).supported) ??\n      false\n    );\n  }\n\n  #log = Log.create(`id:${COM_ID++},`);\n\n  #destroyed = false;\n\n  #sprites: Array<OffscreenSprite & { main: boolean; expired: boolean }> = [];\n\n  #cvs;\n\n  #ctx;\n\n  // 中断输出\n  #stopOutput: (() => void) | null = null;\n\n  #opts: Required<ICombinatorOpts>;\n\n  #hasVideoTrack: boolean;\n\n  #evtTool = new EventTool<{\n    OutputProgress: (progress: number) => void;\n    error: (err: Error) => void;\n  }>();\n  on = this.#evtTool.on;\n\n  /**\n   * 根据配置创建合成器实例\n   * @param opts ICombinatorOpts\n   */\n  constructor(opts: ICombinatorOpts = {}) {\n    const { width = 0, height = 0 } = opts;\n    this.#cvs = new OffscreenCanvas(width, height);\n    // this.#cvs = document.querySelector('#canvas') as HTMLCanvasElement\n    const ctx = this.#cvs.getContext('2d', { alpha: false });\n    if (ctx == null) throw Error('Can not create 2d offscreen context');\n    this.#ctx = ctx;\n    this.#opts = Object.assign(\n      {\n        bgColor: '#000',\n        width: 0,\n        height: 0,\n        videoCodec: 'avc1.42E032',\n        audio: true,\n        bitrate: 5e6,\n        fps: 30,\n        metaDataTags: null,\n      },\n      opts,\n    );\n\n    this.#hasVideoTrack = width * height > 0;\n  }\n\n  /**\n   * 添加用于合成视频的 Sprite，视频时长默认取所有素材 duration 字段的最大值\n   * @param os Sprite\n   * @param opts.main 如果 main 为 true，视频时长为该素材的 duration 值\n   */\n  async addSprite(\n    os: OffscreenSprite,\n    opts: { main?: boolean } = {},\n  ): Promise<void> {\n    const logAttrs = {\n      rect: pick(['x', 'y', 'w', 'h'], os.rect),\n      time: { ...os.time },\n      zIndex: os.zIndex,\n    };\n    this.#log.info('Combinator add sprite', logAttrs);\n    const newOS = await os.clone();\n    this.#log.info('Combinator add sprite ready');\n    this.#sprites.push(\n      Object.assign(newOS, {\n        main: opts.main ?? false,\n        expired: false,\n      }),\n    );\n    this.#sprites.sort((a, b) => a.zIndex - b.zIndex);\n  }\n\n  #startRecodeMux(duration: number) {\n    const { fps, width, height, videoCodec, bitrate, audio, metaDataTags } =\n      this.#opts;\n    const recodeMuxer = recodemux({\n      video: this.#hasVideoTrack\n        ? {\n            width,\n            height,\n            expectFPS: fps,\n            codec: videoCodec,\n            bitrate,\n            __unsafe_hardwareAcceleration__:\n              this.#opts.__unsafe_hardwareAcceleration__,\n          }\n        : null,\n      audio:\n        audio === false\n          ? null\n          : {\n              codec: 'aac',\n              sampleRate: DEFAULT_AUDIO_CONF.sampleRate,\n              channelCount: DEFAULT_AUDIO_CONF.channelCount,\n            },\n      duration,\n      metaDataTags: metaDataTags,\n    });\n    return recodeMuxer;\n  }\n\n  /**\n   * 输出视频文件二进制流\n   */\n  output(): ReadableStream<Uint8Array> {\n    if (this.#sprites.length === 0) throw Error('No sprite added');\n\n    const mainSpr = this.#sprites.find((it) => it.main);\n    // 最大时间，优先取 main sprite，不存在则取最大值\n    const maxTime =\n      mainSpr != null\n        ? mainSpr.time.offset + mainSpr.time.duration\n        : Math.max(\n            ...this.#sprites.map((it) => it.time.offset + it.time.duration),\n          );\n    if (maxTime === Infinity) {\n      throw Error(\n        'Unable to determine the end time, please specify a main sprite, or limit the duration of ImgClip, AudioCli',\n      );\n    }\n    // 主视频（main）的 videoTrack duration 值为 0\n    if (maxTime === -1) {\n      this.#log.warn(\n        \"Unable to determine the end time, process value don't update\",\n      );\n    }\n\n    this.#log.info(`start combinate video, maxTime:${maxTime}`);\n    const remux = this.#startRecodeMux(maxTime);\n    let starTime = performance.now();\n    const stopReCodeMux = this.#run(remux, maxTime, {\n      onProgress: (prog) => {\n        this.#log.debug('OutputProgress:', prog);\n        this.#evtTool.emit('OutputProgress', prog);\n      },\n      onEnded: async () => {\n        await remux.flush();\n        this.#log.info(\n          '===== output ended =====, cost:',\n          performance.now() - starTime,\n        );\n        this.#evtTool.emit('OutputProgress', 1);\n        this.destroy();\n      },\n      onError: (err) => {\n        this.#evtTool.emit('error', err);\n        closeOutStream(err);\n        this.destroy();\n      },\n    });\n\n    this.#stopOutput = () => {\n      stopReCodeMux();\n      remux.close();\n      closeOutStream();\n    };\n    const { stream, stop: closeOutStream } = file2stream(\n      remux.mp4file,\n      500,\n      this.destroy,\n    );\n\n    return stream;\n  }\n\n  /**\n   * 销毁实例，释放资源\n   */\n  destroy() {\n    if (this.#destroyed) return;\n    this.#destroyed = true;\n\n    this.#stopOutput?.();\n    this.#evtTool.destroy();\n  }\n\n  #run(\n    remux: ReturnType<typeof recodemux>,\n    maxTime: number,\n    {\n      onProgress,\n      onEnded,\n      onError,\n    }: {\n      onProgress: (prog: number) => void;\n      onEnded: () => Promise<void>;\n      onError: (err: Error) => void;\n    },\n  ): () => void {\n    let progress = 0;\n    const aborter = { aborted: false };\n    let err: Error | null = null;\n\n    const _run = async () => {\n      const { fps, bgColor, audio: outputAudio } = this.#opts;\n      const timeSlice = Math.round(1e6 / fps);\n\n      const ctx = this.#ctx;\n      const sprRender = createSpritesRender({\n        ctx,\n        bgColor,\n        sprites: this.#sprites,\n        aborter,\n      });\n      const encodeData = createAVEncoder({\n        remux,\n        ctx,\n        cvs: this.#cvs,\n        outputAudio,\n        hasVideoTrack: this.#hasVideoTrack,\n        timeSlice,\n        fps,\n      });\n\n      let ts = 0;\n      while (true) {\n        if (err != null) return;\n        if (\n          aborter.aborted ||\n          (maxTime === -1 ? false : ts > maxTime) ||\n          this.#sprites.length === 0\n        ) {\n          exit();\n          await onEnded();\n          return;\n        }\n        progress = ts / maxTime;\n\n        const { audios, mainSprDone } = await sprRender(ts);\n        if (mainSprDone) {\n          exit();\n          await onEnded();\n          return;\n        }\n\n        if (aborter.aborted) return;\n\n        encodeData(ts, audios);\n\n        ts += timeSlice;\n\n        await letEncoderCalmDown(remux.getEncodeQueueSize);\n      }\n    };\n\n    _run().catch((e) => {\n      err = e;\n      this.#log.error(e);\n      exit();\n      onError(e);\n    });\n\n    const outProgTimer = setInterval(() => {\n      onProgress(progress);\n    }, 500);\n\n    const exit = () => {\n      if (aborter.aborted) return;\n      aborter.aborted = true;\n      clearInterval(outProgTimer);\n      this.#sprites.forEach((it) => it.destroy());\n    };\n\n    return exit;\n  }\n}\n\nfunction createSpritesRender(opts: {\n  ctx: OffscreenCanvasRenderingContext2D;\n  bgColor: string;\n  sprites: Array<OffscreenSprite & { main: boolean; expired: boolean }>;\n  aborter: { aborted: boolean };\n}) {\n  const { ctx, bgColor, sprites, aborter } = opts;\n  const { width, height } = ctx.canvas;\n  return async (ts: number) => {\n    ctx.fillStyle = bgColor;\n    ctx.fillRect(0, 0, width, height);\n\n    const audios: Float32Array[][] = [];\n    let mainSprDone = false;\n    for (const s of sprites) {\n      if (aborter.aborted) break;\n      if (ts < s.time.offset || s.expired) continue;\n\n      ctx.save();\n      const { audio, done } = await s.offscreenRender(ctx, ts - s.time.offset);\n      audios.push(audio);\n      ctx.restore();\n\n      // 超过设定时间主动掐断，或资源结束\n      if (\n        (s.time.duration > 0 && ts > s.time.offset + s.time.duration) ||\n        done\n      ) {\n        if (s.main) mainSprDone = true;\n\n        s.destroy();\n        s.expired = true;\n      }\n    }\n    return {\n      audios,\n      mainSprDone,\n    };\n  };\n}\n\nfunction createAVEncoder(opts: {\n  remux: ReturnType<typeof recodemux>;\n  ctx: OffscreenCanvasRenderingContext2D;\n  cvs: OffscreenCanvas;\n  outputAudio?: boolean;\n  hasVideoTrack: boolean;\n  timeSlice: number;\n  fps: number;\n}) {\n  const { ctx, cvs, outputAudio, remux, hasVideoTrack, timeSlice } = opts;\n  const { width, height } = cvs;\n  let frameCnt = 0;\n  // 3s 一个 GOP\n  const gopSize = Math.floor(3 * opts.fps);\n\n  const audioTrackBuf = createAudioTrackBuf(1024);\n\n  return (ts: number, audios: Float32Array[][]) => {\n    if (outputAudio !== false) {\n      for (const ad of audioTrackBuf(ts, audios)) remux.encodeAudio(ad);\n    }\n\n    if (hasVideoTrack) {\n      const vf = new VideoFrame(cvs, {\n        duration: timeSlice,\n        timestamp: ts,\n      });\n\n      remux.encodeVideo(vf, {\n        keyFrame: frameCnt % gopSize === 0,\n      });\n      ctx.resetTransform();\n      ctx.clearRect(0, 0, width, height);\n\n      frameCnt += 1;\n    }\n  };\n}\n\n/**\n * 缓冲输入的数据，转换成固定帧数的 AudioData\n * @param adFrames 一个 AudioData 实例的音频帧数\n */\nexport function createAudioTrackBuf(adFrames: number) {\n  const adDataSize = adFrames * DEFAULT_AUDIO_CONF.channelCount;\n  // pcm 数据缓存区\n  const chanBuf = new Float32Array(adDataSize * 3);\n  let putOffset = 0;\n\n  let audioTs = 0;\n  const adDuration = (adFrames / DEFAULT_AUDIO_CONF.sampleRate) * 1e6;\n\n  // 缺少音频数据时占位\n  const placeholderData = new Float32Array(adDataSize);\n\n  const getAudioData = (ts: number) => {\n    let readOffset = 0;\n    const adCnt = Math.floor(putOffset / adDataSize);\n    const rs: AudioData[] = [];\n    // 从缓存区按指定帧数获取数据构造 AudioData\n    for (let i = 0; i < adCnt; i++) {\n      rs.push(\n        new AudioData({\n          timestamp: audioTs,\n          numberOfChannels: DEFAULT_AUDIO_CONF.channelCount,\n          numberOfFrames: adFrames,\n          sampleRate: DEFAULT_AUDIO_CONF.sampleRate,\n          format: 'f32',\n          data: chanBuf.subarray(readOffset, readOffset + adDataSize),\n        }),\n      );\n      readOffset += adDataSize;\n      audioTs += adDuration;\n    }\n    chanBuf.set(chanBuf.subarray(readOffset, putOffset), 0);\n    putOffset -= readOffset;\n\n    // 已有音频数据不足，使用占位数据填充\n    while (ts - audioTs > adDuration) {\n      rs.push(\n        new AudioData({\n          timestamp: audioTs,\n          numberOfChannels: DEFAULT_AUDIO_CONF.channelCount,\n          numberOfFrames: adFrames,\n          sampleRate: DEFAULT_AUDIO_CONF.sampleRate,\n          format: 'f32',\n          data: placeholderData,\n        }),\n      );\n      audioTs += adDuration;\n    }\n    return rs;\n  };\n\n  return (ts: number, trackAudios: Float32Array[][]) => {\n    const maxLen = Math.max(...trackAudios.map((a) => a[0]?.length ?? 0));\n    for (let bufIdx = 0; bufIdx < maxLen; bufIdx++) {\n      let chan0 = 0;\n      let chan1 = 0;\n      for (let trackIdx = 0; trackIdx < trackAudios.length; trackIdx++) {\n        const _c0 = trackAudios[trackIdx][0]?.[bufIdx] ?? 0;\n        // 如果是单声道 PCM，第二声道复用第一声道数据\n        const _c1 = trackAudios[trackIdx][1]?.[bufIdx] ?? _c0;\n        chan0 += _c0;\n        chan1 += _c1;\n      }\n      // 合成多个素材的音频数据写入缓存区\n      chanBuf[putOffset] = chan0;\n      chanBuf[putOffset + 1] = chan1;\n      putOffset += 2;\n    }\n    // 消费缓存区数据，生成 AudioData\n    return getAudioData(ts);\n  };\n}\n\nfunction pick<K extends keyof T, T extends object>(keys: K[], obj: T) {\n  return keys.reduce(\n    (acc, key) => {\n      acc[key] = obj[key];\n      return acc;\n    },\n    {} as Record<K, T[K]>,\n  );\n}\n"],"names":["createEl","tagName","arrayBufferToBase64","buffer","binary","bytes","len","i","renderTxt2Img","txt","cssText","opts","div","width","height","img","fontFaceStr","svgStr","resolve","renderTxt2ImgBitmap","imgEl","cvs","concatFloat32Array","bufs","rs","buf","a","b","offset","concatPCMFragments","fragments","chanListPCM","j","extractPCM4AudioData","ad","idx","chanBufSize","chanBuf","convertF32ToPlanar","convertS16ToF32Planar","pcmS16Data","numChannels","numSamples","planarData","channel","sample","pcmF32Data","extractPCM4AudioBuffer","ab","_","decodeImg","stream","type","init","imageDecoder","frameCnt","mixinPCM","audios","maxLen","data","bufIdx","chan0","chan1","trackIdx","_c0","_c1","audioResample","pcmData","curRate","target","chanCnt","emptyPCM","c","p","waveResampler","ctx","abSource","sleep","time","stop","workerTimer","ringSliceFloat32Array","start","end","cnt","changePCMPlaybackRate","playbackRate","newLength","newPcmData","originalIndex","intIndex","frac","DEFAULT_AUDIO_CONF","extractFileConfig","file","info","vTrack","videoDesc","parseVideoCodecDesc","descKey","aTrack","esdsBox","getESDSBoxFromMP4File","parseAudioInfo4ESDSBox","track","entry","box","mp4box","codec","t","esds","decoderConf","byte1","byte2","sampleRateIdx","numberOfChannels","quickParseMP4File","reader","onReady","onSamples","mp4boxFile","vTrackId","aTrackId","parse","cursor","maxReadSize","nextPos","CLIP_ID","isOTFile","obj","MP4Clip","#insId","#log","Log","#destroyed","#meta","#localFile","#headerBoxPos","oFile","size","#volume","#videoSamples","#audioSamples","#videoFrameFinder","#audioFrameFinder","#decoderConf","#opts","source","initByStream","write","tmpfile","otFile","mp4FileToSamples","videoSamples","audioSamples","headerBoxPos","videoFrameFinder","audioFrameFinder","genDecoder","genMeta","tickRet","audio","video","#thumbAborter","imgWidth","aborterSignal","abortMsg","convtr","createVF2BlobConvtr","reject","pngPromises","vc","resolver","it","pushPngPromise","vf","step","cur","VideoFrameFinder","thumbnailByKeyFrame","done","preVideoSlice","postVideoSlice","splitVideoSampleByTime","preAudioSlice","postAudioSlice","splitAudioSampleByTime","preClip","postClip","clip","clips","videoClip","audioClip","meta","vDuration","aDuration","s","lastSampele","localFileReader","volume","AudioFrameFinder","mp4Info","videoDeltaTS","audioDeltaTS","findedFirstSync","ftyp","moov","ac","samples","normalizeTimescale","fixFirstBlackFrame","delta","sampleType","isFirstSync","isVideoSync","idrOffset","idrNALUOffset","is_idr","conf","#dec","#ts","#curAborter","#reset","#parseFrame","#sleepCnt","#lastVfDur","#downgradeSoftDecode","#videoDecCusorIdx","#videoFrames","#outputFrameCnt","#inputChunkCnt","#predecodeErr","dec","aborter","#startDecode","err","#decoding","#getState","endIdx","hasValidFrame","readStarTime","chunks","videosamples2Chunks","readCost","first","last","rangSize","decodeGoP","f","keyIdx","encoderConf","rsVf","errMsg","memoryUsageInfo","findIndexOfSamples","#sampleRate","needResetTime","#decCusorIdx","deltaTime","#pcmData","emitFrameCnt","ramainFrameCnt","emitAudioFrames","createAudioChunksDecoder","pcmArr","outputCb","inputCnt","outputCnt","outputHandler","pcm","resampleQ","createPromiseQueue","needResample","adec","handleDecodeError","prefixStr","chunk","onResult","rsCache","waitingIdx","updateRs","emitIdx","emitRs","addIdx","task","emitCnt","gapCnt","gopStartIdx","gopEndIdx","hitIdx","hitSample","preSlice","postSlice","u8Arr","startOffset","dv","nalUnitType","localFile","decConf","abortSingl","onOutput","fileReader","createVideoDec","downgrade","iframeCnt","minCtsSample","mem","ImgClip","#img","#frames","dataSource","initWithImgBitmap","imgBitmap","frame","acc","#initAnimateImg","firstVf","tt","newClip","AudioClip","#chan0Buf","#chan1Buf","#init","tStart","parseStream2PCM","chan","#frameOffset","MediaStreamClip","#stopRenderCvs","#cvs","#ms","ms","videoTrack","renderVideoTrackToCvs","onOffscreenCanvasReady","emitFF","cvsCtx","autoReadStream","displayHeight","displayWidth","EmbedSubtitlesClip","#subtitles","#ctx","#lastVF","#lineHeight","#linePadding","content","parseSrt","text","fontSize","fontFamily","fontWeight","fontStyle","videoWidth","videoHeight","letterSpacing","#renderTxt","lines","color","textBgColor","textShadow","strokeStyle","lineWidth","lineCap","lineJoin","bottomOffset","bottomDistance","lineStr","txtMeas","centerX","sub","preLastIt","postFirstIt","srtTimeToSeconds","match","hours","minutes","seconds","milliseconds","srt","str","SampleTransform","#inputBufOffset","streamCancelled","ctrl","releasedCnt","id","ui8Arr","inputBuf","fixMP4BoxFileDuration","inMP4File","sendedBoxIdx","boxes","tracks","totalDuration","write2TmpFile","box2Buf","tmpFileWriter","moovPrevBoxes","moovBoxReady","moovIdx","timerId","postFile","initPromise","stoped","rsFile","startIdx","ds","chunk2MP4SampleOpts","dts","fastConcatMP4","streams","outfile","dumpFile","concatStreamsToMP4BoxFile","outStream","vDTS","vCTS","aDTS","aCTS","lastVSamp","lastASamp","chunkType","videoTrackConf","audioTrackConf","trackId","offsetDTS","offsetCTS","lastSamp","fixFMP4Duration","createMP4AudioSampleDecoder","adConf","cacheAD","adDecoder","ss","createMP4AudioSampleEncoder","aeConf","adEncoder","lastData","createAD","ts","audioFade","sampleRate","dataLen","fadeLen","mixinMP4AndAudio","mp4Stream","stopOut","file2stream","audioSampleDecoder","audioSampleEncoder","inputAudioPCM","audioOffset","mp4HasAudio","audioDecoderConf","safeAudioTrackConf","audioCtx","addInputAudio2Track","mixinAudioSampleAndInputPCM","getInputAudioSlice","vdieoSamples","firstSamp","pcmLength","audioDataBuf","pcmFragments","mp4AudioPCM","vertexShader","fragmentShader","POINT_POS","TEX_COORD_POS","initShaderProgram","gl","vsSource","fsSource","loadShader","shaderProgram","shader","updateTexture","texture","initTexture","level","internalFormat","border","srcFormat","srcType","pixel","initCvs","v","posBuffer","a_position","texCoordBuffer","a_texCoord","getSourceWH","imgSource","getKeyColor","r","g","createChromakey","keyC","Rect","#evtTool","EventTool","#x","#setBaseProps","#y","#w","#h","#angle","prop","changed","#master","x","y","w","h","master","rect","tx","ty","angle","center","agl","tOX","tOY","mx","my","BaseSprite","#time","#zIndex","#animatKeyFrame","#animatOpts","props","keyFrame","k","val","numK","updateProps","linearTimeFn","kf","offsetTime","process","startState","nextState","nextFrame","startFrame","stateProcess","OffscreenSprite","#clip","#lastVf","duration","state","outAudio","spr","VisibleSprite","#lastAudio","#ticking","#update","#lastTime","COM_ID","letEncoderCalmDown","getQSize","Combinator","args","#sprites","#stopOutput","#hasVideoTrack","os","logAttrs","pick","newOS","#startRecodeMux","fps","videoCodec","bitrate","metaDataTags","recodemux","mainSpr","maxTime","remux","starTime","stopReCodeMux","#run","prog","closeOutStream","onProgress","onEnded","onError","progress","bgColor","outputAudio","timeSlice","sprRender","createSpritesRender","encodeData","createAVEncoder","exit","mainSprDone","e","outProgTimer","sprites","hasVideoTrack","gopSize","audioTrackBuf","createAudioTrackBuf","adFrames","adDataSize","putOffset","audioTs","adDuration","placeholderData","getAudioData","readOffset","adCnt","trackAudios","keys","key"],"mappings":"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"}