ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TEN Framework VTT Recorder 扩展实战:用 Node.js/TypeScript 录制音频并生成 WebVTT 字幕文件

TEN Framework VTT Recorder 扩展实战:用 Node.js/TypeScript 录制音频并生成 WebVTT 字幕文件 人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载本文围绕 TEN Framework 仓库中transcriber_demo示例应用内置的vtt_nodejs扩展VTT Recorder Extension展开系统讲解它如何以 Node.js/TypeScript 实现「音频帧 ASR 识别结果 → WAV 录音 WebVTT 字幕 JSON 转写」的完整录制链路。读完本文你将掌握该扩展的命令协议start/stop/list/get/delete 五类会话命令、数据输入格式pcm_frame与asr_result、输出目录结构、配置方式以及如何将其接入 TEN Framework 的预定义图predefined graph并实现浏览器端的字幕回放。扩展定位与核心能力vtt_nodejs是 TEN Framework 的一个 Node.js/TypeScript 扩展Addon 名为vtt_nodejs位于 packages/example_apps/transcriber_demo/ten_packages/extension/vtt_nodejs。它位于语音转录示例应用transcriber_demo内部职责单一而明确一边接收音频帧一边接收 ASR 识别结果在会话结束时落盘为标准产物。其核心能力包括音频录制Audio Recording将流式到来的 PCM 音频帧累积并编码为 WAV 文件VTT 生成VTT Generation依据 ASR 最终结果自动切分字幕段生成标准 WebVTT 字幕文件会话管理Session Management以 UUID 为标识管理多次录制会话并维护会话元数据多格式导出Multi-format Export一次会话同时产出 VTT、JSON 与 WAV 三种文件实时处理Real-time Processing通过 TEN 框架的onAudioFrame/onData回调对流式音频与文本进行逐帧、逐条处理。扩展的 manifest 声明见 manifest.json给出了完整的 API 契约命令入口cmd_in为start_recording、stop_recording、list_sessions、get_session、delete_session音频帧入口audio_frame_in为pcm_frame数据入口data_in为携带text、final、start_ms、duration_ms属性的asr_result。架构与数据流扩展 README 给出了如下架构示意┌─────────────────┐ │ Audio Source │ (e.g., Audio Player, Microphone) └────────┬────────┘ │ pcm_frame ↓ ┌─────────────────┐ │ │ │ VTT Recorder │ ← asr_result ← ASR Engine │ (Node.js) │ │ │ └────────┬────────┘ │ ↓ ┌─────────────────┐ │ Local Storage │ ├─────────────────┤ │ • audio.wav │ │ • transcript.vtt│ │ • metadata.json │ └─────────────────┘对照源码 src/index.ts 可以确认这一数据流的具体实现onAudioFrame(tenEnv, audioFrame)在会话激活时把每个pcm_frame交给AudioRecorder.writeFrame()缓冲onData(tenEnv, data)判断数据名是否为asr_result解析出text、final、start_ms、duration_ms后交给VTTGenerator.addAsrResult()只有finaltrue的结果才会进入字幕生成逻辑源码中addAsrResult对非 final 结果直接 return避免把 ASR 的中间猜测写入字幕会话停止时AudioRecorder.stop()合并缓冲写出 WAVVTTGenerator.save()写出 VTT同时生成transcript.json与metadata.json。从源码结构看扩展内部由三个职责清晰的模块组成模块文件职责SessionManagersrc/session-manager.ts会话创建/结束、目录与文件路径管理、元数据读写、会话列表与删除AudioRecordersrc/audio-recorder.ts接收音频帧、自动探测音频格式、PCM 转 Float32、编码为 16-bit WAVVTTGeneratorsrc/vtt-generator.ts字幕段切分、时间戳格式化、VTT/JSON 内容生成命令协议详解扩展通过onCmd分发处理五类命令命令名来自Cmd.getName()见 src/index.ts#L71-L108未知命令会以StatusCode.ERROR返回Unknown command: name详情。start_recording开始一个新的录制会话。前置校验若已有活跃的AudioRecorderisActive()为 true直接返回错误Already recording保证同一时刻只有一个会话在录制处理流程SessionManager.createSession()生成 UUID 会话 ID → 创建会话目录 → 初始化AudioRecorder与VTTGenerator→audioRecorder.start()开始缓冲音频帧响应CmdResult属性session_id本次会话唯一标识detail状态消息成功为Recording started。stop_recording停止当前录制会话并保存所有产物文件。前置校验未在录制时返回Not recording状态异常缺少会话 ID 或 VTT 生成器时返回Invalid session state处理流程见 src/index.ts#L149-L225以当前音频累计时间戳finalize字幕生成器处理最后一段未闭合的字幕文本audioRecorder.stop()合并全部缓冲并编码写出audio.wavvttGenerator.save()写出transcript.vtt写出transcript.jsonsessionManager.endSession()落盘metadata.json清理内部状态音频录制器、字幕生成器、当前会话 ID 置空。响应属性session_id会话标识duration录制时长毫秒由audioRecorder.getDuration()换算而来segments字幕段数量words总词数按空白切分估算。list_sessions列出所有已完成的录制会话。实现读取recordings根目录下每个含metadata.json的子目录解析后按startTime倒序排列见 src/session-manager.ts#L143-L164响应属性sessions会话元数据 JSON 数组以 JSON 字符串形式写入属性count会话数量。get_session查询指定会话的元数据。参数session_id字符串必填缺失时返回Missing session_id实现读取对应会话目录下的metadata.json目录或文件不存在时返回Session not found响应属性metadata会话元数据 JSON 对象含sessionId、startTime、endTime、duration、totalWords、totalSegments、audioFile、vttFile等字段。delete_session删除指定会话及其全部产物文件。参数session_id字符串必填实现递归删除会话目录fs.promises.rm(sessionPath, { recursive: true, force: true })响应成功返回Session deleted目录不存在则返回Session not found。输入数据格式音频帧pcm_frame名称pcm_frame类型为音频帧audio_frame格式PCM 音频数据README 推荐 16kHz、单声道、16-bit用法会话激活时自动被录制。源码实现中AudioRecorder.writeFrame()从第一帧开始自动探测sampleRate、channels、bytesPerSample由audioFrame.getSampleRate()、getNumberOfChannels()、getBytesPerSample()读取并将后续帧统一按该格式累积时间戳由累计采样数换算(totalSamplesReceived / sampleRate) * 1000见 src/audio-recorder.ts#L45-L93。ASR 结果asr_result名称asr_result类型为 data属性textstring识别出的文本finalbool是否为最终结果只有 final 结果才会进入字幕start_msint64识别结果相对音频时间轴的起始时间毫秒duration_msint64识别结果持续时长毫秒用法onData先尝试以data.getPropertyToJson()整体解析根对象失败时逐个读取text、final、start_ms、duration_ms属性见 src/index.ts#L348-L425。字幕时间轴直接采用 ASR 提供的start_ms duration_ms而非录制器的累计时间从而保证字幕与音频内容精确对齐。输出文件与目录结构每次录制会话在recordings_path默认./recordings下创建一个以会话 ID 命名的目录包含四个文件recordings/ └── session-id/ ├── audio.wav # Recorded audio ├── transcript.vtt # WebVTT subtitle file ├── transcript.json # JSON format transcript └── metadata.json # Session metadataaudio.wav由AudioRecorder统一编码为16-bit、非压缩 PCM WAVwav.encode时固定float: false, bitDepth: 16与浏览器audio标签及多数播放器天然兼容transcript.vtt标准 WebVTT 字幕格式如下WEBVTT 1 00:00:00.000 -- 00:00:05.000 Hello, this is a test. 2 00:00:05.000 -- 00:00:10.000 The quick brown fox jumps over the lazy dog.transcript.json结构化的转写结果包含segments每段含start、end、text、totalSegments、totalWords与plainText全文字幕拼接便于前端渲染或后续检索处理metadata.json会话元数据会话 ID、起止时间、时长、词数、段数、产物文件路径由SessionManager在endSession时写入。配置项扩展的配置集中在 property.json 中目前仅有一个可选参数{ recordings_path: ./recordings }recordings_path录制产物的根目录支持相对路径相对扩展运行目录或绝对路径读取逻辑onInit阶段通过tenEnv.getPropertyString(recordings_path)读取见 src/index.ts#L46-L65读取失败或未配置时回退到默认值./recordings并打印告警日志。依赖与安装扩展的 npm 依赖见 package.jsonnode-wav^0.0.2WAV 文件编码负责把 Float32 采样数据打包为标准 WAV 头 PCM 数据19KB 级轻量库uuid^9.0.0会话 ID 生成uuidv4()ten-runtime-nodejsTEN Framework 的 Node.js 运行时绑定以本地路径方式依赖file:../../../ten_packages/system/ten_runtime_nodejs提供Extension、TenEnv、Cmd、AudioFrame、Data等核心类。安装与构建命令在扩展目录下执行cd ten_packages/extension/vtt_nodejs npm install npm run build其中build脚本为tsc --listEmittedFiles即使用 TypeScript 编译输出到build/目录package.json 的main指向./build/index.js。若以独立方式脱离 demo 目录安装运行时绑定可执行npm run standalone-install即npm install .ten/app/ten_packages/system/ten_runtime_nodejs。扩展还通过 manifest.json 声明了对系统依赖ten_runtime_nodejs版本 0.11的引用。集成到 TEN Framework 应用预定义图配置在应用的property.json中将扩展挂入predefined_graphs并把音频源与 ASR 引擎的输出连接到本扩展{ ten: { predefined_graphs: [ { nodes: [ { type: extension, name: vtt_recorder, addon: vtt_nodejs } ], connections: [ { extension: audio_source, audio_frame: [ { name: pcm_frame, dest: [{extension: vtt_recorder}] } ] }, { extension: asr_engine, data: [ { name: asr_result, dest: [{extension: vtt_recorder}] } ] } ] } ] } }真实示例可参考 transcriber_demo 应用的 property.json其中vtt_nodejs与azure_asr_python、web_audio_control_go、audio_file_player_python组成一个图——web_audio_control_go与audio_file_player_python的pcm_frame同时流向azure_asr_python和vtt_nodejsazure_asr_python的asr_result同时流向web_audio_control_go和vtt_nodejs同时web_audio_control_go把start_recording、stop_recording命令路由到vtt_nodejs形成「网页控制 → 播放 → 转录 → 录制」的完整闭环。控制录制在扩展或客户端侧通过TenEnv.sendCmd驱动录制// Start recording await tenEnv.sendCmd(start_recording); // ... audio and ASR data flows automatically ... // Stop recording await tenEnv.sendCmd(stop_recording);start_recording与stop_recording之间pcm_frame与asr_result沿图中连接自动流入扩展无需额外编码。浏览器端回放录制产物可直接用于 HTML5 音视频的字幕轨道audio controls source src/recordings/session-id/audio.wav typeaudio/wav track src/recordings/session-id/transcript.vtt kindsubtitles srclangen labelEnglish /audio浏览器会按transcript.vtt中的时间戳自动同步显示字幕。需确保 Web 服务将recordings_path目录作为静态资源暴露例如在 transcriber_demo 中由web_audio_control_go提供静态文件服务。技术细节源码级实现剖析音频处理多采样率支持自动探测以首个音频帧的sampleRate为准后续帧沿用多声道与位深支持支持 8/16/24/32-bit PCM分别对应 int8 无符号、int16、int24、int32 有符号在convertToFloat32()中按位深归一化到 [-1, 1]见 src/audio-recorder.ts#L178-L217多声道交织数据会拆分为每声道独立的Float32Array统一输出 16-bit WAV无论输入位深如何最终编码固定为 16-bit保证兼容性内存策略说明README 描述为“流式以最小化内存占用”但对照源码实现writeFrame实际是把每个帧的Buffer追加到audioBuffers数组直到stop()时才Buffer.concat合并并写盘。因此从实现角度内存占用与录制时长呈线性关系这一点在评估长会话场景时需注意可通过调大recordings_path所在磁盘、定期分段录制等方式规避。VTT 生成字幕分段策略见 src/vtt-generator.ts仅处理最终结果isFinal为 false 的结果直接丢弃文本格式化首字母大写、去除首尾空白、句末无标点时自动补句号英文.自动句子切分以[.!?。]结尾判定句子结束分段阈值maxSegmentDuration 7000毫秒超过则强制开新段minSegmentDuration 1000毫秒不足 1 秒不与累积文本合并成段时间间隙判定相邻 ASR 结果间隔超过 2000 毫秒时视为新的独立字幕段否则累积到当前段时间戳同步段起止时间直接取自 ASR 的start_ms与start_ms duration_ms与音频内容严格对应生成的 VTT 包含WEBVTT头、段序号与HH:MM:SS.mmm -- HH:MM:SS.mmm时间轴。会话管理UUID 会话 IDuuidv4()保证全局唯一自动元数据追踪SessionMetadata记录sessionId、startTime、endTime、duration、totalWords、totalSegments及产物路径并发安全start_recording的“已录制”校验保证同一时刻仅一个活跃会话异常清理onStop时若仍在录制会自动audioRecorder.cancel()丢弃缓冲、不落盘避免残留半成品stop_recording任何一步抛错都会返回StatusCode.ERROR并携带错误详情。性能说明扩展 README 给出的性能指标如下均为其自述未附基准数据实际表现与音频源、机器负载相关Memory文档称采用流式处理保持内存占用恒定——如上文源码分析实现上帧数据会先缓冲于内存直至会话结束长时录制时内存随时长增长请按实际场景评估Latency 10ms 每音频帧指单帧写入处理开销Storage16kHz 单声道 16-bit 时约 32 KB/s未压缩 PCM 的理论下限即16000 × 2 字节 ≈ 32 KB/sCPU文档称额外开销小于 5%。常见问题排查录制未启动检查是否已有活跃会话扩展同一时刻只允许一个录制会话再次start_recording会返回Already recording确认音频源确实在发送名为pcm_frame的音频帧对照图中连接名与 manifest.json 的audio_frame_in声明。VTT 文件为空确认 ASR 结果带有finaltrue扩展只处理最终结果中间结果不会写入字幕检查 ASR 是否把asr_result发送到了vtt_nodejs扩展图中 data 连接的 dest 是否包含它。文件体积过大默认 WAV 为未压缩格式16kHz 单声道约 32 KB/s如需减小体积可在录制后做二次压缩如转 Opus/MP3或调整采样参数。许可与文档扩展遵循 Apache License 2.0见仓库根目录 LICENSE完整技术说明可查阅扩展自带的 README.en-US.md 与 README.zh-CN.md。该扩展的测试入口tests/src/index.spec.ts与tests/src/main.spec.ts以及构建脚本tools/run_script.py也一并收录在扩展目录内可作为进一步研究其生命周期与运行方式的参考。赞分享人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载相关推荐TEN Framework 的 VTT 录制扩展vtt_nodejs基于 Node.js/TypeScript 的音频与字幕录制实战指南TEN Framework 的 VTT 录制扩展vtt_nodejs基于 Node.js/TypeScript 的音频与字幕录制实战指南 本文面向需要在人工智能AI Agent多模态语音AI 应用TEN Agent 对话录音扩展Conversation Recorder实战指南本地、GCS 与 S3 多后端录音实现TEN Agent 对话录音扩展Conversation Recorder实战指南本地、GCS 与 S3 多后端录音实现 本指南围绕 TEN framew人工智能AI Agent多模态语音AI 应用TEN Framework 中的 FFmpeg Muxer 扩展媒体流合并与音视频封装实战解析TEN Framework 中的 FFmpeg Muxer 扩展媒体流合并与音视频封装实战解析 本文围绕 TEN Framework 开源仓库中的 ffmpe人工智能AI Agent多模态语音AI 应用上一篇告别编辑卡顿ReactPage移动端手势操作全解析下一篇从 Astra 到 TerraEasydict 仓库 Agent 治理规则适配实录创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表