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整理,比我写的好。脚本为实测脚本,图形化界面及系统为自行开发并测试)

相关推荐
Blockchina40 分钟前
5个开源AI视频项目怎么选?从脚本、分镜到Shorts和剪映自动化
人工智能·开源·音视频
要开心吖ZSH1 小时前
腾讯 IM × TRTC 联动实战:后端如何撑起一次视频问诊(混流录制篇)
java·音视频·腾讯云·健康医疗·即时通讯·im
jike_202612 小时前
iPhone采访录音同时拍现场照片的APP:图片音频同步记录实测
笔记·智能手机·音视频·语音识别
小猴子爱上树13 小时前
跨境电商翻译工具推荐:批量图片翻译、视频字幕翻译、智能抠图一站式搞定
人工智能·python·音视频
林澈在路上13 小时前
AI翻唱软件哪个好 2026国产AI写歌工具对比推荐
大数据·人工智能·深度学习·github·aigc·音视频·音频
阿童木写作14 小时前
跨马翻译:AI批量图片翻译与视频字幕翻译工具推荐
人工智能·python·音视频
Dovis(誓平步青云)15 小时前
折叠屏悬停看视频,上半屏和下半屏应该各做什么
android·java·服务器·开发语言·安全·音视频
敢敢のwings20 小时前
Wan2.2 模型系统解读:从视频扩散到 WAM 世界模型模块
音视频
小猴子爱上树21 小时前
跨境电商AI批量图片翻译工具,视频字幕翻译免费试用
人工智能·python·音视频