{"version":3,"file":"av-cliper.umd.cjs","sources":["../src/chromakey.ts","../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/combinator.ts","../src/sprite/rect.ts","../src/sprite/base-sprite.ts","../src/sprite/offscreen-sprite.ts","../src/sprite/visible-sprite.ts"],"sourcesContent":["// 改编自 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","// 在主线程中执行的 工具函数\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 preEl = createEl('pre');\n  preEl.style.cssText = `margin: 0; ${cssText}; position: fixed;`;\n  preEl.textContent = txt;\n  document.body.appendChild(preEl);\n  opts.onCreated?.(preEl);\n\n  // 避免重复覆盖其他字体他\n  const tmpFontName = 'TMP_FONT_NAME_' + crypto.randomUUID();\n  let fontFace: FontFace | null = null;\n  // 等待字体加载完成后再计算尺寸\n  if (opts.font != null) {\n    preEl.style.fontFamily = tmpFontName;\n    fontFace = new FontFace(tmpFontName, `url(${opts.font.url})`);\n    await fontFace.load();\n    // @ts-expect-error https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/add\n    document.fonts.add(fontFace);\n    await document.fonts.ready;\n  }\n\n  const { width, height } = preEl.getBoundingClientRect();\n  // 计算出 rect，立即从dom移除\n  preEl.remove();\n  if (fontFace != null) {\n    // @ts-expect-error https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/delete\n    document.fonts.delete(fontFace);\n  }\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: '${tmpFontName}';\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\">${preEl.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    const audioInfo = esdsBox == null ? {} : parseAudioInfoFromESDSBox(esdsBox);\n\n    rs.audioTrackConf = {\n      timescale: aTrack.timescale,\n      samplerate: audioInfo.sampleRate ?? aTrack.audio.sample_rate,\n      channel_count: audioInfo.numberOfChannels ?? aTrack.audio.channel_count,\n      hdlr: 'soun',\n      type: aTrack.codec.startsWith('mp4a') ? 'mp4a' : aTrack.codec,\n      description: esdsBox,\n    };\n\n    rs.audioDecoderConf = {\n      codec: audioInfo.codec ?? DEFAULT_AUDIO_CONF.codec,\n      numberOfChannels:\n        audioInfo.numberOfChannels ?? aTrack.audio.channel_count,\n      sampleRate: audioInfo.sampleRate ?? aTrack.audio.sample_rate,\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// 从 ESDS Box 中解析出音频配置信息，解决封装层音频信息标识错误，导致解码异常\nfunction parseAudioInfoFromESDSBox(esds: ESDSBoxParser): {\n  codec?: string;\n  sampleRate?: number;\n  numberOfChannels?: number;\n} {\n  let codec = 'mp4a';\n  const decConfDesc = esds.esd.descs[0];\n  if (decConfDesc == null) return {};\n  codec += '.' + decConfDesc.oti.toString(16);\n\n  const decSpecInfo = decConfDesc.descs[0];\n  if (decSpecInfo == null) {\n    if (codec.endsWith('40')) codec += '.2';\n    return { codec };\n  }\n\n  // ref: https://wiki.multimedia.cx/index.php/MPEG-4_Audio#Audio_Specific_Config\n  const audioObjectType = (decSpecInfo.data[0] & 0xf8) >> 3;\n  codec += '.' + audioObjectType;\n\n  const [byte1, byte2] = decSpecInfo.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\n  return {\n    codec,\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\nexport function parseMatrix(matrix?: Int32Array) {\n  if (matrix?.length !== 9) return {};\n\n  const signedMatrix = new Int32Array(matrix.buffer);\n\n  // 提取并转成浮点数\n  const a = signedMatrix[0] / 65536.0;\n  const b = signedMatrix[1] / 65536.0;\n  const c = signedMatrix[3] / 65536.0;\n  const d = signedMatrix[4] / 65536.0;\n  const tx = signedMatrix[6] / 65536.0; // 一般是 0\n  const ty = signedMatrix[7] / 65536.0; // 一般是 0\n  const w = signedMatrix[8] / (1 << 30); // 一般是 1\n\n  // 缩放\n  const scaleX = Math.sqrt(a * a + c * c);\n  const scaleY = Math.sqrt(b * b + d * d);\n\n  // 旋转角度（弧度）\n  const rotationRad = Math.atan2(c, a);\n  const rotationDeg = (rotationRad * 180) / Math.PI;\n\n  return {\n    scaleX,\n    scaleY,\n    rotationRad,\n    rotationDeg,\n    translateX: tx,\n    translateY: ty,\n    perspective: w,\n  };\n}\n\n/**\n * 旋转 VideoFrame\n */\nexport function createVFRotater(\n  width: number,\n  height: number,\n  rotationDeg: number,\n) {\n  const normalizedRotation = (Math.round(rotationDeg / 90) * 90 + 360) % 360;\n  if (normalizedRotation === 0) return (vf: VideoFrame | null) => vf;\n\n  const rotatedWidth =\n    normalizedRotation === 90 || normalizedRotation === 270 ? height : width;\n  const rotatedHeight =\n    normalizedRotation === 90 || normalizedRotation === 270 ? width : height;\n\n  const canvas = new OffscreenCanvas(rotatedWidth, rotatedHeight);\n  const ctx = canvas.getContext('2d')!;\n\n  ctx.translate(rotatedWidth / 2, rotatedHeight / 2);\n  ctx.rotate((-normalizedRotation * Math.PI) / 180);\n  ctx.translate(-width / 2, -height / 2);\n\n  return (vf: VideoFrame | null) => {\n    if (vf == null) return null;\n\n    ctx.drawImage(vf, 0, 0);\n    const newVF = new VideoFrame(canvas, {\n      timestamp: vf.timestamp,\n      duration: vf.duration ?? undefined,\n    });\n    vf.close();\n    return newVF;\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  createVFRotater,\n  extractFileConfig,\n  parseMatrix,\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\nexport interface IMP4ClipOpts {\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  /** 存储视频头（box: ftyp, moov）的二进制数据 */\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  /**存储视频平移旋转信息，目前只还原旋转 */\n  #parsedMatrix = {\n    perspective: 1,\n    rotationRad: 0,\n    rotationDeg: 0,\n    scaleX: 1,\n    scaleY: 1,\n    translateX: 0,\n    translateY: 0,\n  };\n  #vfRotater: (vf: VideoFrame | null) => VideoFrame | null = (vf) => vf;\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: IMP4ClipOpts = { audio: true };\n\n  constructor(\n    source: OPFSToolFile | ReadableStream<Uint8Array> | MPClipCloneArgs,\n    opts: IMP4ClipOpts = {},\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 ({\n        videoSamples,\n        audioSamples,\n        decoderConf,\n        headerBoxPos,\n        parsedMatrix,\n      }) => {\n        this.#videoSamples = videoSamples;\n        this.#audioSamples = audioSamples;\n        this.#decoderConf = decoderConf;\n        this.#headerBoxPos = headerBoxPos;\n        this.#parsedMatrix = parsedMatrix;\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        const { codedWidth, codedHeight } = decoderConf.video ?? {};\n        if (codedWidth && codedHeight) {\n          this.#vfRotater = createVFRotater(\n            codedWidth,\n            codedHeight,\n            parsedMatrix.rotationDeg,\n          );\n        }\n\n        this.#meta = genMeta(\n          decoderConf,\n          videoSamples,\n          audioSamples,\n          parsedMatrix.rotationDeg,\n        );\n\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).then(this.#vfRotater),\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        parsedMatrix: this.#parsedMatrix,\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        parsedMatrix: this.#parsedMatrix,\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        parsedMatrix: this.#parsedMatrix,\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          parsedMatrix: this.#parsedMatrix,\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          parsedMatrix: this.#parsedMatrix,\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  rotationDeg: number,\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    // 90, 270 度，需要交换宽高\n    const normalizedRotation = (Math.round(rotationDeg / 90) * 90 + 360) % 360;\n    if (normalizedRotation === 90 || normalizedRotation === 270) {\n      [meta.width, meta.height] = [meta.height, meta.width];\n    }\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: IMP4ClipOpts = {}) {\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  const parsedMatrix = {\n    perspective: 1,\n    rotationRad: 0,\n    rotationDeg: 0,\n    scaleX: 1,\n    scaleY: 1,\n    translateX: 0,\n    translateY: 0,\n  };\n\n  let videoDeltaTS = -1;\n  let audioDeltaTS = -1;\n  const reader = await otFile.createReader();\n  await quickParseMP4File(\n    reader,\n    async (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      Object.assign(parsedMatrix, parseMatrix(mp4Info.videoTracks[0]?.matrix));\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      if (ac != null) {\n        const { supported } = await AudioDecoder.isConfigSupported(ac);\n        if (!supported) {\n          Log.error(`MP4Clip audio codec is not supported: ${ac.codec}`);\n        }\n      }\n      if (vc != null) {\n        const { supported } = await VideoDecoder.isConfigSupported(vc);\n        if (!supported) {\n          Log.error(`MP4Clip video codec is not supported: ${vc.codec}`);\n        }\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 (const s of samples) {\n          videoSamples.push(normalizeTimescale(s, videoDeltaTS, 'video'));\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    parsedMatrix,\n  };\n}\n\nfunction normalizeTimescale(\n  s: MP4Sample,\n  delta = 0,\n  sampleType: 'video' | 'audio',\n) {\n  // todo: perf 丢弃多余字段，小尺寸对象性能更好\n  let offset = s.offset;\n  // 当 IDR 帧前面包含非图像帧数据（如 SEI），可能导致解码失败\n  const idrOffset =\n    sampleType === 'video' && s.is_sync\n      ? idrNALUOffset(s.data, s.description.type)\n      : -1;\n\n  let size = s.size;\n  if (idrOffset > 0) {\n    // 此处通过控制 offset、size 字段 跳过非图像帧数据\n    offset += idrOffset;\n    size -= idrOffset;\n  }\n\n  return {\n    ...s,\n    is_idr: idrOffset >= 0,\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(\n  audioSamples: ExtMP4Sample[],\n  time: number,\n): [ExtMP4Sample[] | undefined, ExtMP4Sample[] | undefined] {\n  if (audioSamples.length === 0) return [undefined, undefined];\n  if (audioSamples[0].cts >= time) {\n    return [undefined, audioSamples.map((s) => ({ ...s }))];\n  }\n\n  const last = audioSamples[audioSamples.length - 1];\n  if (last.cts < time) {\n    return [audioSamples.map((s) => ({ ...s })), undefined];\n  }\n\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) {\n  if (type !== 'avc1' && type !== 'hvc1') return 0;\n\n  const dv = new DataView(u8Arr.buffer);\n  for (let i = 0; i < u8Arr.byteLength - 4; ) {\n    if (type === 'avc1') {\n      const nalUnitType = dv.getUint8(i + 4) & 0x1f;\n      // 5: IDR 帧, 7: SPS, 8: PPS\n      if (nalUnitType === 5 || nalUnitType === 7 || nalUnitType === 8) return i;\n    } else if (type === 'hvc1') {\n      const nalUnitType = (dv.getUint8(i + 4) >> 1) & 0x3f;\n      // 19-20: IDR 帧, 32-34: VPS SPS PPS\n      if (\n        nalUnitType === 19 ||\n        nalUnitType === 20 ||\n        nalUnitType === 32 ||\n        nalUnitType === 33 ||\n        nalUnitType === 34\n      )\n        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) {\n    onOutput(null, true);\n    return;\n  }\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');\n    expect(normalized.offset).toBe(48);\n    expect(normalized.size).toBe(1000);\n    expect(normalized.is_sync).toBe(normalized.is_idr);\n  });\n\n  it('genMeta adjusts width and height based on rotation', () => {\n    const meta = genMeta(\n      {\n        video: {\n          codedWidth: 1920,\n          codedHeight: 1080,\n        },\n        audio: null,\n      } as any,\n      [{ cts: 0, duration: 1000 }] as any,\n      [],\n      90,\n    );\n    expect(meta.width).toBe(1080);\n    expect(meta.height).toBe(1920);\n\n    const meta2 = genMeta(\n      {\n        video: {\n          codedWidth: 1920,\n          codedHeight: 1080,\n        },\n        audio: null,\n      } as any,\n      [{ cts: 0, duration: 1000 }] as any,\n      [],\n      180,\n    );\n    expect(meta2.width).toBe(1920);\n    expect(meta2.height).toBe(1080);\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  static concatAudioClip = concatAudioClip;\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  #wrapText(text: string, maxWidth: number): string[] {\n    const lines: string[] = [];\n    let currentLine = '';\n\n    // 检测是否包含中文字符\n    const hasChinese = /[\\u4e00-\\u9fff]/.test(text);\n\n    if (hasChinese) {\n      // 中英混合文本：优先在标点符号和英文单词边界换行\n      for (let i = 0; i < text.length; i++) {\n        const char = text[i];\n        const testLine = currentLine + char;\n        const metrics = this.#ctx.measureText(testLine);\n\n        if (metrics.width <= maxWidth) {\n          currentLine = testLine;\n        } else {\n          if (currentLine) {\n            lines.push(currentLine);\n            currentLine = char;\n          } else {\n            // 单个字符就超宽，强制换行\n            lines.push(char);\n            currentLine = '';\n          }\n        }\n      }\n      if (currentLine) {\n        lines.push(currentLine);\n      }\n    } else {\n      // 纯英文按单词换行\n      const words = text.split(' ');\n\n      for (const word of words) {\n        const testLine = currentLine ? `${currentLine} ${word}` : word;\n        const metrics = this.#ctx.measureText(testLine);\n\n        if (metrics.width <= maxWidth) {\n          currentLine = testLine;\n        } else {\n          if (currentLine) {\n            lines.push(currentLine);\n            currentLine = word;\n          } else {\n            lines.push(word);\n          }\n        }\n      }\n\n      if (currentLine) {\n        lines.push(currentLine);\n      }\n    }\n\n    return lines;\n  }\n\n  #renderTxt(txt: string) {\n    const { width, height } = this.#cvs;\n    const maxTextWidth = width * 0.9; // 留出10%的边距\n\n    const lines = txt\n      .split('\\n')\n      .flatMap((line) => this.#wrapText(line.trim(), maxTextWidth))\n      .reverse();\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    // reset first sample timestamps for each stream to enable normalization\n    let firstVDTS: number | null = null;\n    let firstVCTS: number | null = null;\n    let firstADTS: number | null = null;\n    let firstACTS: number | null = null;\n\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              let normalizedDTS: number;\n              let normalizedCTS: number;\n\n              if (type === 'video') {\n                // capture first sample timestamps for normalization\n                if (firstVDTS === null) {\n                  firstVDTS = s.dts;\n                  firstVCTS = s.cts;\n                }\n                // normalize to start from 0, then add offset\n                normalizedDTS = s.dts - firstVDTS;\n                normalizedCTS = s.cts - (firstVCTS ?? 0);\n              } else {\n                // same for audio\n                if (firstADTS === null) {\n                  firstADTS = s.dts;\n                  firstACTS = s.cts;\n                }\n                normalizedDTS = s.dts - firstADTS;\n                normalizedCTS = s.cts - (firstACTS ?? 0);\n              }\n\n              outfile.addSample(trackId, s.data, {\n                duration: s.duration,\n                dts: normalizedDTS + offsetDTS,\n                cts: normalizedCTS + 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    // calculate offsets based on normalized timestamps\n    if (lastVSamp != null && firstVDTS !== null && firstVCTS !== null) {\n      // duration of this normalized stream\n      const normalizedVDTS = lastVSamp.dts - firstVDTS + lastVSamp.duration;\n      const normalizedVCTS = lastVSamp.cts - firstVCTS + lastVSamp.duration;\n      vDTS += normalizedVDTS;\n      vCTS += normalizedVCTS;\n    }\n    // coerce audio timing to match video timing by converting video timescale to audio timescale\n    if (lastASamp != null && lastVSamp != null) {\n      const videoToAudioRatio = lastASamp.timescale / lastVSamp.timescale;\n      aDTS = Math.round(vDTS * videoToAudioRatio);\n      aCTS = Math.round(vCTS * videoToAudioRatio);\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","import { EventTool, file2stream, Log, recodemux } from '@webav/internal-utils';\nimport { sleep } from './av-utils';\nimport { DEFAULT_AUDIO_CONF } from './clips';\nimport { OffscreenSprite } from './sprite/offscreen-sprite';\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   * @param opts.maxTime 允许输出视频的最大时长，超过时长的素材内容会被忽略\n   */\n  output(opts: { maxTime?: number } = {}): 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      opts.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","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  const t = offsetTime % opts.duration;\n  const progress =\n    offsetTime / opts.duration >= opts.iterCount || offsetTime === opts.duration\n      ? 1\n      : t / opts.duration;\n\n  const idx = kf.findIndex((it) => it[0] >= progress);\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    (progress - 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  /**\n   * 控制 Sprite 的交互状态\n   * - 'interactive': 可选中，可进行移动、缩放、旋转等所有交互\n   * - 'selectable': 仅可选中，但不可进行移动、缩放、旋转等交互\n   * - 'disabled': 不可选中，也不可交互\n   * @default 'interactive'\n   */\n  interactable: 'interactive' | 'selectable' | 'disabled' = 'interactive';\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  async #update(time: number) {\n    if (this.#ticking) return;\n    this.#ticking = true;\n\n    try {\n      const { video, audio } = await this.#clip.tick(\n        time * this.time.playbackRate,\n      );\n      if (this.#destroyed) {\n        video?.close();\n        return;\n      }\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    } finally {\n      this.#ticking = false;\n    }\n  }\n\n  /**\n   * 提前准备指定 time 的帧\n   */\n  async preFrame(time: number) {\n    if (this.#lastTime === time) return;\n    await 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      target.interactable = this.interactable;\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"],"names":["vertexShader","fragmentShader","POINT_POS","TEX_COORD_POS","initShaderProgram","gl","vsSource","fsSource","loadShader","shaderProgram","type","source","shader","errMsg","updateTexture","img","texture","initTexture","level","internalFormat","width","height","border","srcFormat","srcType","pixel","initCvs","opts","cvs","v","posBuffer","a_position","texCoordBuffer","a_texCoord","getSourceWH","imgSource","getKeyColor","ctx","r","g","b","createChromakey","keyC","rs","createEl","tagName","arrayBufferToBase64","buffer","binary","bytes","len","i","renderTxt2Img","txt","cssText","preEl","tmpFontName","fontFace","fontFaceStr","svgStr","resolve","renderTxt2ImgBitmap","imgEl","concatFloat32Array","bufs","buf","a","offset","concatPCMFragments","fragments","chanListPCM","j","extractPCM4AudioData","ad","idx","chanBufSize","chanBuf","convertF32ToPlanar","convertS16ToF32Planar","pcmS16Data","numChannels","numSamples","planarData","channel","sample","pcmF32Data","extractPCM4AudioBuffer","ab","_","decodeImg","stream","init","imageDecoder","frameCnt","mixinPCM","audios","maxLen","data","bufIdx","chan0","chan1","trackIdx","_c0","_c1","audioResample","pcmData","curRate","target","chanCnt","emptyPCM","c","p","waveResampler","abSource","d","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","audioInfo","parseAudioInfoFromESDSBox","track","entry","box","mp4box","codec","t","esds","decConfDesc","decSpecInfo","audioObjectType","byte1","byte2","sampleRateIdx","numberOfChannels","quickParseMP4File","reader","onReady","onSamples","mp4boxFile","vTrackId","aTrackId","parse","cursor","maxReadSize","nextPos","parseMatrix","matrix","signedMatrix","tx","ty","w","scaleX","scaleY","rotationRad","rotationDeg","createVFRotater","normalizedRotation","vf","rotatedWidth","rotatedHeight","canvas","newVF","CLIP_ID","isOTFile","obj","MP4Clip","#insId","#log","Log","#destroyed","#meta","#localFile","#headerBoxPos","oFile","size","#parsedMatrix","#vfRotater","#volume","#videoSamples","#audioSamples","#videoFrameFinder","#audioFrameFinder","#decoderConf","#opts","initByStream","s","write","tmpfile","otFile","mp4FileToSamples","videoSamples","audioSamples","decoderConf","headerBoxPos","parsedMatrix","videoFrameFinder","audioFrameFinder","genDecoder","codedWidth","codedHeight","genMeta","tickRet","audio","video","#thumbAborter","imgWidth","aborterSignal","abortMsg","convtr","createVF2BlobConvtr","reject","pngPromises","vc","resolver","it","pushPngPromise","step","cur","VideoFrameFinder","thumbnailByKeyFrame","done","preVideoSlice","postVideoSlice","splitVideoSampleByTime","preAudioSlice","postAudioSlice","splitAudioSampleByTime","preClip","postClip","clip","clips","videoClip","audioClip","meta","vDuration","aDuration","lastSampele","localFileReader","volume","AudioFrameFinder","mp4Info","videoDeltaTS","audioDeltaTS","ftyp","moov","ac","supported","samples","normalizeTimescale","fixFirstBlackFrame","delta","sampleType","idrOffset","idrNALUOffset","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","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","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","concatAudioClip","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","#wrapText","maxWidth","lines","currentLine","char","testLine","words","word","#renderTxt","maxTextWidth","line","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","firstVDTS","firstVCTS","firstADTS","firstACTS","chunkType","videoTrackConf","audioTrackConf","trackId","offsetDTS","offsetCTS","normalizedDTS","normalizedCTS","lastSamp","normalizedVDTS","normalizedVCTS","videoToAudioRatio","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","COM_ID","letEncoderCalmDown","getQSize","Combinator","args","#sprites","#stopOutput","#hasVideoTrack","#evtTool","EventTool","os","logAttrs","pick","newOS","#startRecodeMux","duration","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","Rect","#x","#setBaseProps","#y","#w","#h","#angle","prop","changed","#master","x","y","h","master","rect","angle","center","agl","tOX","tOY","mx","my","BaseSprite","#time","#zIndex","#animatKeyFrame","#animatOpts","props","keyFrame","k","val","numK","updateProps","linearTimeFn","kf","offsetTime","startState","nextState","nextFrame","startFrame","stateProcess","OffscreenSprite","#clip","#lastVf","state","outAudio","spr","VisibleSprite","#lastAudio","#ticking","#update","#lastTime"],"mappings":"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"}