FreeSWITCH 简单图形化界面 63 - 编写 mod_avsource 模块,将 RTSP/RTMP 源桥接为视频通道
00、 一个fs的web配置界面预览
如果您想通过图形化界面管理 FreeSWITCH 的场景,可直接访问以下 Web 配置界面。
01、 Web 配置界面预览
| 界面地址 | 备用地址 | 登录信息 |
|---|---|---|
| http://www.fspbx.cn/ | http://myfs.f3322.net:8020/ | 用户名:admin密码:admin |
02、 Web界面安装参考
若需自行部署 FreeSWITCH 图形化界面,包含 Docker、脚本、ISO 镜像三种安装方式,适配不同系统环境:
- 安装指南地址:https://blog.csdn.net/jia198810/article/details/137820796
- 核心安装方式概览:
- Docker 安装:快速部署;
- 脚本安装:适配 Almalinux、RockyLinux、Debian、Ubuntu 、欧拉等系统,提供一键安装命令,安装后需重启初始化;
- ISO 镜像安装:基于 Almalinux制作的镜像,刻录到 U 盘或光盘后可自动安装,适合全新服务器部署。
1、为什么需要 mod_avsource?
之前我们介绍过,使用 playback 应用可以播放 RTSP、RTMP 等媒体流:
xml
<action application="playback" data="rtsp://192.168.0.151:554/Streaming/Channels/101" />
但 playback 存在一个本质限制:它不是一个 endpoint,没有独立的媒体通道。这意味着:
- ❌ 无法对播放的媒体流进行录像;
- ❌ 无法将媒体流桥接到视频会议;
- ❌ 无法进行媒体转发或转码控制;
- ❌ FreeSWITCH 对该媒体流基本处于"不可控"状态。
为了解决这些问题,mod_avsource 应运而生。
2、mod_avsource 模块简介
mod_avsource 是一个 FreeSWITCH endpoint 模块,它利用 FFmpeg 的 libavformat 库,将 RTSP、RTMP、HTTP、HLS 等网络媒体流变成可桥接、可加入视频会议的视频通道。
核心能力:
- H264/H265 全支持:H264 源输出 H264;H265 源可转码输出 H264,也可直通输出 H265。
- 自动编码继承:未指定输出编码时,自动继承主叫 A-leg 已协商的视频编码。
- RTSP 传输选择:支持 RTSP over TCP/UDP,TCP 更稳定,推荐默认使用。
- 录像支持 :由于是完整视频通道,配合
record_session可正常录像。 - 会议桥接:可将 RTSP 源作为参会方拉入视频会议。
- 缩放与帧率控制:支持自定义输出分辨率与帧率。
| 源编码 | 输出编码 | 处理方式 |
|---|---|---|
| H264 | H264 | 默认解码后重新编码;参数兼容时可直通 |
| H265 | H264 | 解码 H265,再编码 H264 |
| H265 | H265 | 默认解码后重新编码;参数兼容时可直通 H265 |
| H264 | H265 | 解码 H264,再编码 H265 |
转码依赖
mod_av以及 FFmpeg 提供的编解码器。passthrough直通模式不重新编码,但要求终端完全兼容源流的 profile、分辨率、码率等参数。
3、基本用法
3.1 最简单的呼叫
xml
<action application="bridge" data="avsource/rtsp://192.168.0.151:554/Streaming/Channels/101" />
支持的地址格式:
text
avsource/rtsp://user:pass@camera/stream
avsource/rtmp://127.0.0.1:1935/live
avsource/http://127.0.0.1:8080/stream
avsource/http://127.0.0.1:8080/index.m3u8
avsource/av://rtsp://user:pass@camera/stream
3.2 指定输出编码
xml
<action application="export" data="nolocal:avsource_video_output_codec=H264"/>
<action application="bridge" data="avsource/rtsp://user:pass@camera/h265"/>
3.3 RTSP 传输方式
xml
<action application="export" data="nolocal:avsource_rtsp_transport=tcp"/>
<action application="bridge" data="avsource/rtsp://camera/stream"/>
默认使用 TCP,网络丢包严重时建议保持 TCP;追求更低延迟时可改为 UDP。
3.4 视频模式
xml
<!-- 转码模式(默认),解码后重新编码,兼容性最好 -->
<action application="export" data="nolocal:avsource_video_mode=transcode"/>
<!-- 直通模式,不解码直接 RTP 打包输出,省 CPU 但要求终端兼容 -->
<action application="export" data="nolocal:avsource_video_mode=passthrough"/>
passthrough要求源编码与输出编码一致,且未设置avsource_width/avsource_height/avsource_fps,否则呼叫会以INCOMPATIBLE_DESTINATION拒绝。
3.5 缩放与帧率控制
xml
<action application="export" data="nolocal:avsource_width=1280"/>
<action application="export" data="nolocal:avsource_height=720"/>
<action application="export" data="nolocal:avsource_fps=25"/>
<action application="bridge" data="avsource/rtsp://camera/stream"/>
宽高必须同时设置,设置后自动走转码路径。
4、使用场景与图形化界面优势
4.1 场景一:RTSP 摄像头作为视频源接入会议
将 RTSP 监控摄像头作为一路"参会方"拉入 FreeSWITCH 视频会议,实现监控与会议融合。
bash
conference 3000000 bgdial avsource/rtsp://user:pass@camera/stream 100101 Camera1
- 图形化界面优势:无需手写 dialplan,表单配置 RTSP 地址、编码、传输方式即可。
4.2 场景二:RTSP 摄像头录像
与普通视频通话一样,对 avsource 通道进行录像。
xml
<action application="record_session" data="{av_video_codec=libx264}/tmp/camera-record.mp4"/>
<action application="bridge" data="avsource/rtsp://camera/stream"/>
- 图形化界面优势:在设备管理中为 RTSP 源开启"自动录像",录像文件自动归档。
4.3 场景三:H265 摄像头兼容老旧终端
H265 摄像头码流直出,但老旧 SIP 终端或浏览器不支持 H265。使用 mod_avsource 实时转码为 H264。
xml
<action application="export" data="nolocal:avsource_video_output_codec=H264"/>
<action application="bridge" data="avsource/rtsp://h265-camera/stream"/>
- 图形化界面优势:在 RTSP 源配置中勾选"输出编码为 H264",系统自动完成转码编排。
4.4 场景四:RTMP 直播源拉流
支持 RTMP 直播流接入,将互联网直播源桥接到 SIP 终端或会议中。
xml
<action application="bridge" data="avsource/rtmp://live.example.com/stream"/>
5、呼叫脚本
以下是一个完整的 Lua 脚本示例(handle-onvif-call.lua),用于将 ONVIF 设备(通过 RTSP 地址)桥接到 avsource,实现 SIP 号码观看摄像头:
lua
#!/usr/bin/env lua
-- 处理ONVIF通道的通话流程
-- ONVIF设备本身不是SIP终端,这里通过RTSP地址桥接到avsource,实现SIP号码观看摄像头。
local flag = true
local level = "NOTICE"
local log = function(session, str)
if (flag == true and session:ready()) then
session:consoleLog(level, str)
end
end
session:setAutoHangup(false)
if (session:ready()) then
log(session, "#========== handle-onvif-call.lua ==========\r\n")
-- 1. 获取会话相关信息
local caller_id_number = session:getVariable("caller_id_number")
local destination_number = session:getVariable("destination_number")
local path = session:getVariable("path") or "" -- RTSP地址
local record_format = session:getVariable("record_format") or "false"
local avsource_fps = session:getVariable("avsource_fps") or ""
local avsource_width = session:getVariable("avsource_width") or ""
local avsource_height = session:getVariable("avsource_height") or ""
local vol_in = session:getVariable("vol_in") or "0.0"
local vol_out = session:getVariable("vol_out") or "0.0"
-- 2. 检查RTSP地址是否有效
if (path == "") then
log(session, "# 结果: ONVIF通道没有RTSP地址,结束呼叫.\r\n")
session:hangup()
return
end
-- 3. 配置录像(如果设备开启了录像功能)
if (record_format == "mkv" or record_format == "mov" or record_format == "mp4") then
local record_time = os.date("%H_%M_%S")
local record_filename = caller_id_number .. "-" .. destination_number .. "-" .. record_time .. "." .. record_format
local record_filepath = "$${recordings_dir}/phone/${strftime(%Y%m%d)}/" .. record_filename
session:execute("mkdir", "$${recordings_dir}/phone/${strftime(%Y%m%d)}")
session:execute("export", "nolocal:RECORD_READ_ONLY=true")
session:execute("export", string.format("nolocal:execute_on_answer=record_session av://%s", record_filepath))
end
-- 4. 音量设置
session:execute("set", string.format("set_audio_level=read %s", vol_in))
session:execute("set", string.format("set_audio_level=write %s", vol_out))
-- 5. 设置avsource视频参数(分辨率、帧率)
if (avsource_fps ~= "") then
session:execute("export", string.format("nolocal:avsource_fps=%s", avsource_fps))
end
if (avsource_width ~= "" and avsource_height ~= "") then
session:execute("export", string.format("nolocal:avsource_width=%s", avsource_width))
session:execute("export", string.format("nolocal:avsource_height=%s", avsource_height))
end
-- 6. 应答并桥接到avsource
session:sleep(500)
session:execute("answer")
session:execute("bridge", string.format("avsource/%s", path))
log(session, "#=======end handle-onvif-call.lua ==========\r\n")
end
脚本逻辑说明:
- RTSP 地址校验:如果通道没有配置 RTSP 地址,直接挂断并记录日志。
- 录像处理 :支持 MP4/MKV/MOV 格式,在 B 腿(avsource 侧)设置
execute_on_answer自动录制。 - 音量调节 :通过
set_audio_level调节 SIP 会话侧的音量。 - 视频参数 :支持通过
avsource_fps、avsource_width、avsource_height控制输出分辨率与帧率。 - 桥接呼叫 :执行
bridge avsource/${path},将 RTSP 地址作为视频源接入通话。
6、相关API介绍
bash
# 查看模块运行状态
fs_cli -x "module info mod_avsource"
# 加载模块
fs_cli -x "load mod_avsource"
# 卸载模块
fs_cli -x "unload mod_avsource"
# 查看系统已加载的视频编码(确认 H264/H265 可用)
fs_cli -x "show codec"
# 查看 Sofia profile 的编码配置
fs_cli -x "sofia status profile internal"
7、模块获取/使用
可在FreeSWITCH 简单图形化界软件中获取/使用。
祝君好运!
💗 2026年
🐂 祝君成功,好运连连,牛气冲天
(注:本文博客部分内容由AI整理,比我写的好。脚本为实测脚本,图形化界面及系统为自行开发并测试)