{"version":3,"file":"FrameStream.cjs","sources":["../../../src/utils/FrameStream.js"],"sourcesContent":["import { Readable } from 'stream';\r\n\r\n/**\r\n * 帧流类 - 实现\"池塘-井-桶\"机制（参考 FFCreator）\r\n * \r\n * 工作原理：\r\n * 1. 从\"池塘\"（渲染函数）中\"打水\"（渲染帧）\r\n * 2. 将\"水\"倒入\"井\"（缓冲区）\r\n * 3. 从\"井\"中\"打水\"（读取数据）倒入\"桶\"（FFmpeg 管道）\r\n * 4. 如果\"井\"空了，再从\"池塘\"中\"打水\"\r\n * \r\n * 优势：\r\n * - 支持并行渲染多帧\r\n * - 自动管理缓冲区大小\r\n * - 减少内存占用和延迟\r\n */\r\nexport class FrameStream extends Readable {\r\n  constructor(options = {}) {\r\n    super({\r\n      highWaterMark: options.highWaterMark || options.size || 1024 * 1024, // 默认 1MB\r\n    });\r\n\r\n    this.size = options.size || 1024 * 1024; // 每次读取的数据大小（字节）\r\n    this.parallel = options.parallel || 1; // 并行渲染的帧数\r\n    this.pullFunc = null; // 拉取帧数据的函数\r\n    this.data = null; // 当前缓冲区数据\r\n    this.cursor = 0; // 当前读取位置\r\n    this.index = 0; // 当前帧索引\r\n    this.fillTime = 0; // 填充数据的总耗时（用于性能统计）\r\n  }\r\n\r\n  /**\r\n   * 设置拉取帧数据的函数\r\n   * @param {Function} func - 返回 Promise<Buffer> 的函数\r\n   */\r\n  addPullFunc(func) {\r\n    this.pullFunc = func;\r\n  }\r\n\r\n  /**\r\n   * 填充新数据（并行渲染多帧）\r\n   * @returns {Promise<Buffer>} 合并后的缓冲区\r\n   */\r\n  async fillNewData() {\r\n    const { parallel } = this;\r\n    const buffs = [];\r\n\r\n    // 并行渲染多帧\r\n    for (let i = 0; i < parallel; i++) {\r\n      const buff = await this.pullFunc();\r\n      if (buff && buff.length > 0) {\r\n        buffs.push(buff);\r\n      }\r\n    }\r\n\r\n    // 如果只渲染一帧，直接返回；否则合并所有帧\r\n    return parallel === 1 ? buffs[0] : Buffer.concat(buffs);\r\n  }\r\n\r\n  /**\r\n   * 读取数据（Stream 内部调用）\r\n   * @private\r\n   */\r\n  async _read() {\r\n    const { size } = this;\r\n\r\n    // 如果缓冲区为空，填充新数据\r\n    if (!this.data) {\r\n      try {\r\n        const startTime = Date.now();\r\n        this.data = await this.fillNewData();\r\n        this.fillTime += Date.now() - startTime;\r\n      } catch (e) {\r\n        console.error('[FrameStream] 填充数据失败:', e);\r\n        this.push(null); // 结束流\r\n        return;\r\n      }\r\n    }\r\n\r\n    // 检查数据是否为空\r\n    if (this.isEmpty(this.data)) {\r\n      this.push(null); // 结束流\r\n      return;\r\n    }\r\n\r\n    // 从缓冲区中读取指定大小的数据\r\n    let end = size + this.cursor;\r\n    if (end > this.data.length) {\r\n      // 读取剩余的所有数据\r\n      end = this.data.length;\r\n      this.push(this.data.slice(this.cursor, end));\r\n      this.cursor = 0;\r\n      this.data = null; // 清空缓冲区，下次重新填充\r\n    } else {\r\n      // 读取指定大小的数据\r\n      this.push(this.data.slice(this.cursor, end));\r\n      this.cursor = end;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * 检查数据是否为空\r\n   * @param {Buffer} data - 数据\r\n   * @returns {boolean}\r\n   */\r\n  isEmpty(data) {\r\n    if (!data) return true;\r\n    return !data.length || data.length === 0;\r\n  }\r\n\r\n  /**\r\n   * 销毁流\r\n   */\r\n  destroy() {\r\n    super.destroy();\r\n    this.unpipe();\r\n    this.removeAllListeners();\r\n    this.pullFunc = null;\r\n    this.data = null;\r\n  }\r\n}\r\n\r\n"],"names":["Readable"],"mappings":"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"}