人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载本文围绕 TEN Framework 开源仓库中的示例扩展包ffmpeg_demuxer系统讲解如何利用 FFmpeg 对本地媒体文件或流媒体输入进行解封装demux与解码并将分离后的音频帧、视频帧以 TEN 消息的形式注入应用图graph的后续节点。读完本文你将掌握该扩展包的命令协议prepare_demuxer/start_demuxer、输入流配置方式、输出帧格式约定以及其底层基于libavformat/libavcodec的完整工作链路能够直接把该扩展集成进自己的语音或视频 Agent 应用中。扩展包定位与核心功能ffmpeg_demuxer是 TEN Framework 官方仓库packages/example_extensions/ffmpeg_demuxer目录下的一个 C 示例扩展其官方定位见 manifest.json 中的描述字段是TEN Framework 中用于媒体流分离的 FFmpeg 解复用器扩展即接收一个媒体输入位置本地文件路径或可访问的流地址使用 FFmpeg 打开输入、分析流信息、创建视频/音频解码器逐包读取并解码把解码得到的音频帧、视频帧转换为 TEN 的audio_frame/video_frame消息通过ten_env将帧消息发送给图中的下游扩展。从包结构看它包含以下组成部分文件作用manifest.json包清单类型extension、名称ffmpeg_demuxer、版本0.11.73、依赖system/ffmpeg 7.1.0property.json扩展属性配置当前为空对象无预置参数main.cc扩展入口类demuxer_extension_t负责命令分发与生命周期demuxer_thread.h / demuxer_thread.cc专用解复用线程封装demuxer.h / demuxer.ccFFmpeg 解封装与解码核心实现约 1000 行BUILD.gnGN 构建描述依赖//third_party/ffmpeg:ffmpeg_for_ten_packagesres/test.mp4内置测试用视频样本前置条件与依赖清单级依赖根据 manifest.json 的dependencies字段本扩展声明了一个系统级依赖{ type: system, name: ffmpeg, version: 7.1.0 }也就是说运行环境需要具备与 TEN Framework 集成的 FFmpeg 7.1.0 库。在仓库中该依赖由 third_party/ffmpeg 提供构建时通过 BUILD.gn 中的deps直接链接//third_party/ffmpeg:ffmpeg_for_ten_packages目标因此无需开发者自行安装系统级 FFmpeg仓库自带的第三方源码即可完成编译。安装方式原文档README.ko-KR.md明确说明安装遵循TEN Framework 包安装指南。仓库提供了配套的包管理工具tman安装脚本见 tools/tman/install_tman.sh可将ffmpeg_demuxer作为依赖安装到目标应用使其出现在应用ten_packages/extension/目录下这也是main.cc中默认输入流路径的假设位置。包安装后即可按照框架规范在应用图graph中声明并使用该扩展。使用方式命令协议与输入流配置该扩展通过 TEN 的自定义命令custom cmd驱动入口类 demuxer_extension_t 在on_cmd中分发两个命令。prepare_demuxer准备输入流发送prepare_demuxer命令时可通过属性input_stream指定媒体输入位置auto input_stream_name cmd-get_property_string(input_stream); if (input_stream_name.empty()) { input_stream_name ten_packages/extension/ffmpeg_demuxer/res/test.mp4; }关键行为源码位于 main.cc若未提供input_stream属性则回退到包内自带的 res/test.mp4解复用是 CPU 密集操作因此扩展会先通过ten::ten_env_proxy_t::create(ten_env)创建一个ten_env_proxy再在独立线程demuxer_thread_t中执行解复用逻辑prepare_demuxer命令本身不会立即回复而是在解复用线程完成输入打开与流分析后再由线程回调ten_env返回结果对应demuxer_thread_t头文件中声明的return_success_result_to_prepare_cmd/return_error_result_to_prepare_cmd。start_demuxer启动解复用start_demuxer命令触发实际读取与解码void start_demuxer(ten_env_t ten_env, std::unique_ptrten::cmd_t cmd) { if (demuxer_thread_ nullptr) { // 返回错误You should prepare first. } else { demuxer_thread_-start_demuxing(); // 返回成功The demuxer has been started. } }即必须先成功执行prepare_demuxer再执行start_demuxer否则会返回TEN_STATUS_CODE_ERROR错误信息为 You should prepare first.。这也说明demuxer_thread_t内部通过事件ten_event_t见 demuxer_thread.h实现了wait_to_start_demuxing的等待语义确保解复用循环只在收到启动信号后运行。生命周期与停止扩展的on_stop会停止并回收解复用线程main.ccvoid on_stop(TEN_UNUSED ten_env_t ten_env) override { if (demuxer_thread_ ! nullptr) { demuxer_thread_-stop(); demuxer_thread_-wait_for_stop_completed(); delete demuxer_thread_; } ten_env.on_stop_done(); }线程停止后解复用循环内的open_input_with_retry也会因is_stopped()为真而放弃继续等待输入流详见下文。底层实现剖析解复用与解码链路核心逻辑集中在 demuxer.cc整体流程可以概括为打开输入 → 分析流信息 → 定位并打开音视频流 → 逐包读取解码 → 转换格式 → 发送 TEN 帧消息 → EOF 冲刷剩余帧。1. 打开输入流与重试机制open_input_stream首先调用open_input_with_retry打开输入open_input使用avformat_alloc_context分配AVFormatContext并通过avformat_open_input打开输入位置支持本地文件路径也支持流媒体地址为降低首帧延迟代码将analyzeduration显式设置为1000000约 1000ms若打开失败open_input_with_retry会循环重试直到成功或解复用线程被停止demuxer_thread-is_stopped()返回 true 时放弃。input_format_context-interrupt_callback.callback interrupt_cb; input_format_context-interrupt_callback.opaque interrupt_cb_param; av_dict_set(av_options, analyzeduration, 1000000, 0);2. 中断保护防止 FFmpeg 无限阻塞针对网络流可能出现的挂起阻塞 I/O、资源不可达等扩展注册了interrupt_cb回调int interrupt_cb(void *p) { auto *r reinterpret_castinterrupt_cb_param_t *(p); if (r-last_time 0) { if (time(nullptr) - r-last_time 20) { return 1; // 超过 20 秒立即中断当前 FFmpeg 操作 } } return 0; }其语义是任何单次 FFmpeg 操作若持续超过 20 秒回调返回非零值强制中断避免解复用线程被长时间卡死。在每次av_read_frame前代码都会刷新interrupt_cb_param-last_time保证计时基准是当前这次读取。3. 流信息分析与解码器初始化analyze_input调用avformat_find_stream_info完成流探测随后open_video_stream/open_audio_stream分别通过av_find_best_stream定位最佳音视频流视频分配AVCodecContext、avcodec_parameters_to_context填充参数、avcodec_open2绑定解码器并读取流元数据中的rotate旋转角度rotate_degree用于后续正确输出宽高音频同样流程创建音频解码器并记录标准化的输出参数audio_sample_rate、audio_channel_layout_mask、audio_num_of_channels对pcm_mulaw等未提供声道布局的编码器使用av_channel_layout_default按声道数推导默认布局。若视频解码器与音频解码器均未成功创建is_input_opened()为 falseopen_input_stream会返回失败并记录 Failed to find supported A/V codec 日志。4. 解复用主循环与解码decode_next_packet是核心循环while (true) { av_packet_unref(packet); interrupt_cb_param-last_time time(nullptr); int ffmpeg_rc av_read_frame(input_format_context, packet); if (ffmpeg_rc 0) { if (ffmpeg_rc AVERROR_EOF) { flush_remaining_video_frames(); flush_remaining_audio_frames(); return DECODE_STATUS_EOF; } return DECODE_STATUS_ERROR; } if (packet-stream_index video_stream_idx) { ... } else if (packet-stream_index audio_stream_idx) { ... } }读取到的包按stream_index分发给decode_next_video_packet或decode_next_audio_packet两者均采用 FFmpeg 推荐的avcodec_send_packet/avcodec_receive_frame异步解码模型AVERROR(EAGAIN)表示需要更多数据属于正常状态继续循环音频解码对 MP3 特判当avcodec_send_packet返回AVERROR_INVALIDDATA时跳过该损坏包继续下一个mp3 header is missing and lookup next packet到达AVERROR_EOF时向解码器发送nullptr包触发 flush把解码器内部缓存的剩余帧全部取出再结束对应flush_remaining_video_frames/flush_remaining_audio_frames。5. 帧格式转换与时间戳规整解码后的原始AVFrame需要转换为 TEN 帧消息音频to_ten_audio_frame通过swr_allocswr_init建立重采样器输出采样格式固定为AV_SAMPLE_FMT_S16宏DEMUXER_OUTPUT_AUDIO_FRAME_SAMPLE_FMT输出声道布局与采样率优先采用流元数据中记录的标准化值转换结果封装为ten::audio_frame_t设置TEN_AUDIO_FRAME_DATA_FMT_INTERLEAVE、字节数、采样率、声道布局、每声道样本数时间戳以流time_base为基准用frame-best_effort_timestamp - start_time归一化后通过av_rescale(..., time_base.num * 1000, time_base.den)换算为毫秒。视频to_ten_video_frame当解码帧格式为YUV420P/YUVJ420P时直接映射为TEN_PIXEL_FMT_I420用av_image_copy_plane分别拷贝 Y/U/V 三平面输出宏为DEMUXER_OUTPUT_VIDEO_FRAME_PIXEL_FMT即AV_PIX_FMT_RGB24但 I420 路径单独处理当解码帧格式恰为RGB24时映射为TEN_PIXEL_FMT_RGB24整帧拷贝其他像素格式会记录日志并跳过提示仅支持 YUV420P 与 RGB24时间戳同样按流起始时间归一化并换算为毫秒。6. 跨线程发送ten_env_proxy解码与转换发生在独立解复用线程因此帧消息的发送必须借助ten_env_proxy切回 TEN 线程ten_env_proxy-notify(audio_frame_shared { ten_env.send_audio_frame(std::move(*audio_frame_shared)); });视频帧同理调用ten_env.send_video_frame。下游扩展只需在图的cmd/data连接中声明接收audio_frame与video_frame消息即可消费解复用产生的媒体流。同时demuxer_thread_t头文件中还声明了send_video_eof/send_audio_eof/send_complete_cmd等接口用于在流结束或任务完成时向下游同步 EOF 与完成状态。7. 资源释放demuxer_t析构函数严格按 FFmpeg 规范释放资源av_packet_free/av_frame_free释放包与帧avcodec_free_context释放音视频解码上下文解码器对象本身归属 FFmpeg不主动释放sws_freeContext/swr_free释放转换器必须使用avformat_close_input关闭输入上下文——源码注释特别指出若使用其他方式释放如 HLS 等格式可能导致内存泄漏中断回调参数通过TEN_FREE回收。输出格式约定速查媒体类型输出像素/采样格式TEN 帧类型对应宏 / 常量音频S16交错布局ten::audio_frame_tDEMUXER_OUTPUT_AUDIO_FRAME_SAMPLE_FMT AV_SAMPLE_FMT_S16视频YUV420P 输入I420ten::video_frame_tTEN_PIXEL_FMT_I420视频RGB24 输入RGB24ten::video_frame_tTEN_PIXEL_FMT_RGB24所有帧消息的时间戳均归一化为相对流起始的毫秒数便于下游扩展直接按时间轴处理。构建与发布BUILD.gn 将本包声明为ten_packagepackage_kind extension资源列表包含LICENSE、manifest.json、property.json与res/test.mp4并递归打包docs/下的多语言 README。当启用 ten managerten_enable_ten_manager时还会生成upload_ffmpeg_demuxer_to_server发布目标用于将构建产物打包上传。许可证本扩展属于 TEN Framework 开源项目的一部分包内自带 LICENSEApache License 2.0源码文件头部也均标注了 Apache 2.0 声明。相关多语言文档见 docs 目录下的README.en-US.md、README.zh-CN.md、README.zh-TW.md、README.ja-JP.md与 README.ko-KR.md。小结ffmpeg_demuxer是理解 TEN Framework 媒体输入侧 集成方式的极佳范例它展示了如何用 C 扩展承载 CPU 密集任务独立线程 ten_env_proxy回传、如何用自定义命令prepare_demuxer/start_demuxer建立同步式的启动协议、如何对 FFmpeg 做生产级的健壮性处理中断回调、重试、EOF 冲刷、严格资源释放以及如何将标准媒体帧无损映射为 TEN 的audio_frame/video_frame消息。开发者可直接复用这套模式为自己的 Agent 应用接入本地文件或网络流媒体输入。赞分享人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载相关推荐TEN Framework 中基于 FFmpeg 的媒体解复用器扩展ffmpeg_demuxer实战指南TEN Framework 中基于 FFmpeg 的媒体解复用器扩展ffmpeg_demuxer实战指南 导读 本文以 TEN Framework 官方示例人工智能AI Agent多模态语音AI 应用TEN Framework 中的 FFmpeg Muxer 扩展音视频媒体流复用Muxing实战解析TEN Framework 中的 FFmpeg Muxer 扩展音视频媒体流复用Muxing实战解析 导读 ffmpeg_muxer 是 TEN Fram人工智能AI Agent多模态语音AI 应用TEN Framework 中的 FFmpeg Muxer 扩展媒体流合并与音视频封装实战解析TEN Framework 中的 FFmpeg Muxer 扩展媒体流合并与音视频封装实战解析 本文围绕 TEN Framework 开源仓库中的 ffmpe人工智能AI Agent多模态语音AI 应用上一篇You-need-to-know-css项目解析CSS鼠标光标样式全指南下一篇OpenCompass项目中的数据污染评估技术详解创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
