FreeSWITCH 简单图形化界面 63 - 编写 mod_avsource 模块,将 RTSP/RTMP 源桥接为视频通道

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 镜像三种安装方式,适配不同系统环境:

  1. Docker 安装:快速部署;
  2. 脚本安装:适配 Almalinux、RockyLinux、Debian、Ubuntu 、欧拉等系统,提供一键安装命令,安装后需重启初始化;
  3. 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_fpsavsource_widthavsource_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整理,比我写的好。脚本为实测脚本,图形化界面及系统为自行开发并测试)

相关推荐
纽格立科技4 小时前
动力的转向:当智能不再依赖碳基
车载系统·音视频·信息与通信·传媒
烟雨江南7855 小时前
会议语音识别私有化部署方案:音频不出内网,如何接入现有会议系统
人工智能·websocket·音视频·语音识别·ai客服
镭封19 小时前
短剧多人对白怎么配?一人完成多角色配音的办法
人工智能·音视频·语音识别·媒体
技灵AI21 小时前
Wan 3.0 API怎么做多参考商品视频?从图片、视频、音频分工到30秒交付
人工智能·prompt·aigc·音视频·wan 3.0
赛博仓鼠1 天前
MiniMax H3 实测:8GB显存跑2K视频+原生音频,开源视频生成的新卷王
开源·aigc·音视频
byte轻骑兵1 天前
【LE Audio】PBP精讲[4]: 公共广播通告的设计逻辑与数据交互流程
人工智能·音视频·le audio·低功耗蓝牙音频
hz567891 天前
手术室视频示教系统解决方案:让临床教学突破空间限制
安全·音视频·实时音视频·信息与通信·智能硬件
可乐鸡翅yeah_1 天前
混合内容 Mixed‑Content 安全策略,HLS HTTPS 页面加载 HTTP 资源排错实战
运维·ffmpeg·音视频·媒体·m3u8
揽秀亭长1 天前
音轨分离处理方案及效果测试分析
人工智能·音视频
美狐美颜SDK开放平台1 天前
直播APP如何兼顾画质与低延迟?视频美颜SDK性能优化开发思路
人工智能·深度学习·音视频·美颜sdk·第三方美颜sdk