大牛直播SDK(SmartMediaKit)Windows平台多路RTSP转RTMP推流集成说明

文档概述

在安防监控、智慧园区、应急指挥、工业视觉、低空经济、无人机回传和多路摄像头上云等场景中,现场设备通常以 RTSP 方式输出视频流,而云端平台、直播分发平台或业务中台往往更倾向于接收 RTMP 流。此时,系统需要在边缘侧或 Windows 工控机侧部署一个稳定、低延迟、可长期运行的流媒体转发模块,将多路 RTSP 摄像头流实时转推到 RTMP Server、CDN 或业务直播平台。

大牛直播SDK(SmartMediaKit)Windows 平台多路 RTSP 转 RTMP 推流方案,正是面向这类场景设计的协议转换与转发能力。它并不是简单的播放器叠加推流工具,而是基于 SmartPlayerSDK 和 SmartPublisherSDK 的组合能力,通过"拉流端编码数据回调 + 推流端编码数据输入"的方式,实现 RTSP 到 RTMP 的低延迟转发。

本文以 Windows C# Demo SmartRelayDemo 为基础,介绍多路 RTSP 转 RTMP 推流的工程结构、配置方式、SDK 初始化、拉流流程、推流流程、音频策略、事件回调、单路预览、资源释放和部署注意事项,帮助开发者快速完成 Windows 平台下的多路转发集成。


产品定位

多路 RTSP 转 RTMP 推流模块主要解决的问题是:把来自 IPC、NVR、编码器、无人机、车载终端或其他 RTSP 设备的实时流,批量转发到 RTMP 服务器或直播平台。

在这一过程中,SDK 更强调三个工程价值:

第一,低延迟转发。转发链路优先采用编码后数据直通方式,避免不必要的解码、重编码和画面合成处理。

第二,多路并发与稳定运行。每一路转发独立维护拉流端和推流端实例,便于单路异常定位,也便于后续扩展为服务进程、边缘网关或无人值守转发程序。

第三,状态可观测。拉流端和推流端均提供事件回调,能够区分"摄像头拉不到流""RTMP 服务器连接失败""网络抖动""缓冲异常"等不同问题,便于现场排查。

官网资料中也将该转发 SDK 定位为跨平台 RTSP/RTMP 转 RTMP 转发能力,强调低延迟、稳定性、状态反馈、资源占用和跨平台 SDK 交付能力。


适用场景

场景 说明
安防视频上云 将内网 IPC/NVR 的 RTSP 流批量转推到云端 RTMP 平台
园区/工厂视频汇聚 多品牌摄像头统一接入,输出标准 RTMP 地址
边缘节点转发 在 Windows 工控机、边缘网关上完成 RTSP 到 RTMP 的协议适配
应急指挥/无人机回传 将现场实时视频转推到调度平台或指挥大屏
直播平台接入 将 RTSP 源转换为 RTMP 后接入自建流媒体服务器、CDN 或业务平台
无人值守转发 配合配置文件和开机自启动,实现多路自动拉流和自动转推

能力概览

能力项 说明
多路转发 支持多路RTSP转RTMP推送,可根据授权和工程需要扩展
拉流协议 支持 RTSP、RTMP 等流媒体输入,Windows 平台也可扩展本地 FLV 转发
推流协议 支持 RTMP 推送
视频编码 支持 H.264、H.265 编码数据转发
音频处理 支持 AAC,并可将 PCMA、PCMU、Speex 等音频转 AAC 后推送
本地预览 转发过程中可按需预览任意一路输入流
事件回调 支持拉流连接状态、下载速度、缓冲状态、推流连接状态等反馈
配置化管理 通过 configure.xml 配置每一路 PullUrl、PushUrl 和音频模式
开机自启动 Demo 提供注册表方式实现程序开机自启动

官网能力列表中也提到支持本地预览、URL 切换、录像扩展、内网 RTSP 网关扩展、音频转 AAC、H.264/H.265 转发和整体网络状态反馈等能力。


开发环境

建议使用如下环境集成:

项目 建议配置
操作系统 Windows 7 / Windows 10 / Windows 11
开发语言 C#
UI 框架 Windows Forms,Demo 以 WinForms 为例
开发工具 Visual Studio 2013 及以上
目标平台 x64 或 x86,需与 SDK DLL 位数保持一致
核心库 SmartPlayerSDK.dll、SmartPublisherSDK.dll
可选库 SmartLog.dll,用于日志输出和现场问题定位

C# 工程平台架构必须与 SDK 动态库保持一致。例如使用 x64 版本 SDK 时,项目平台建议明确设置为 x64,不建议使用 Any CPU,避免运行时出现 DLL 加载失败。


工程文件结构

Demo 可按如下结构理解:

复制代码
SmartRelayDemo
├── SmartStreamRelayDemo.cs          # 主窗口业务逻辑
├── StreamRelayConfig.cs             # 单路转发配置模型
├── nt_relay_wrapper.cs              # 单路转发协调层
├── nt_player_wrapper.cs             # 拉流端封装,基于 SmartPlayerSDK
├── nt_publisher_wrapper.cs          # 推流端封装,基于 SmartPublisherSDK
├── smart_player_sdk.cs              # SmartPlayerSDK P/Invoke 接口声明
├── nt_smart_publisher_sdk.cs        # SmartPublisherSDK P/Invoke 接口声明
└── configure.xml                    # 多路转发配置文件

其中,SmartStreamRelayDemo.cs 负责 UI、配置读取、SDK 初始化、8 路 relay 实例创建、一键拉流和一键转推;nt_relay_wrapper.cs 负责将单路拉流端和推流端组合起来;nt_player_wrapper.cs 负责拉流、事件、视频数据回调和音频数据回调;nt_publisher_wrapper.cs 负责 RTMP 推送、编码数据输入和推流状态回调。


推荐工程分层

建议业务工程不要把所有 SDK 调用直接写在 Form 按钮事件中,而是保持 Demo 中这种分层方式:

复制代码

这种分层的好处是:

层级 职责
SmartStreamRelayDemo 读取配置、管理多路实例、处理按钮和状态显示
nt_relay_wrapper 每一路转发的协调器,连接拉流端和推流端
nt_player_wrapper 拉取 RTSP/RTMP 流,并通过回调输出编码音视频数据
nt_publisher_wrapper 创建推流实例,将编码数据投递给 RTMP 推送模块
SDK DLL 提供底层拉流、推流、事件、音视频处理能力

后续如果要改造成 Windows Service、控制台程序、边缘网关进程或后台无人值守程序,也可以复用 nt_relay_wrappernt_player_wrappernt_publisher_wrapper 这几层。


configure.xml 多路配置

Demo 启动后会读取 exe 同级目录下的 configure.xml。每一个 <Relay> 节点对应一路转发配置。

示例:

XML 复制代码
<?xml version="1.0" encoding="utf-8" ?>
<StreamRelays>
  <Relay>
    <id>0</id>
    <AudioOption>4</AudioOption>
    <PullUrl>rtsp://admin:password@192.168.0.120:554/h264/ch1/main/av_stream</PullUrl>
    <PushUrl>rtmp://192.168.0.103:1935/live/stream00</PushUrl>
  </Relay>

  <Relay>
    <id>1</id>
    <AudioOption>0</AudioOption>
    <PullUrl>rtsp://admin:password@192.168.0.121:554/cam/realmonitor?channel=1&amp;subtype=0</PullUrl>
    <PushUrl>rtmp://192.168.0.103:1935/live/stream01</PushUrl>
  </Relay>
</StreamRelays>

字段说明:

字段 说明
id 路序号,便于标识
AudioOption 音频模式
PullUrl RTSP/RTMP 拉流地址
PushUrl RTMP 推流地址

需要注意的是,XML 中如果 RTSP 地址包含 &,必须写成 &amp;,否则 XML 解析会失败。

StreamRelayConfig.cs 中对应的数据模型很简单,只包含 IdAudioOptionPullUrlPushUrl 四个字段。


音频模式说明

Demo 中 AudioOption 用于控制每一路推流的音频来源:

AudioOption 含义 适用场景
0 无音频 只需要视频监控画面
1 采集本机麦克风 需要本地讲解、语音注入
2 采集本机扬声器 需要转发本机播放声音
3 麦克风 + 扬声器混音 需要本地混音
4 使用拉流端编码后音频数据 摄像头原始音频随视频一起转发

在 RTSP 摄像头转 RTMP 的典型场景中,推荐优先使用 AudioOption=4。这样可以直接使用拉流端输出的编码音频数据。如果摄像头输出的是 PCMA、PCMU、Speex 等格式,也可以通过拉流端音频转 AAC 能力,转成 RTMP 更常用的 AAC 后推送。nt_player_wrapper 中可以看到,订阅音频时会设置音频数据回调,并开启拉流音频转 AAC。

如果现场没有音频需求,建议配置为 AudioOption=0,减少无意义的音频处理和带宽占用。


SDK 初始化与生命周期

Windows C# 工程中,SmartPlayerSDK 和 SmartPublisherSDK 都属于进程级 SDK 初始化。通常建议在程序启动后只初始化一次,在程序退出前统一反初始化。

Demo 中初始化逻辑大致如下:

cs 复制代码
private bool InitSDK()
{
    UInt32 pub_init_ret  = NTSmartPublisherSDK.NT_PB_Init(0, IntPtr.Zero);
    UInt32 pull_init_ret = NTSmartPlayerSDK.NT_SP_Init(0, IntPtr.Zero);

    if (NTBaseCodeDefine.NT_ERC_OK == pub_init_ret &&
        NTBaseCodeDefine.NT_ERC_OK == pull_init_ret)
    {
        is_sdk_has_inited_ = true;
        return true;
    }

    if (NTBaseCodeDefine.NT_ERC_OK == pub_init_ret)
        NTSmartPublisherSDK.NT_PB_UnInit();

    if (NTBaseCodeDefine.NT_ERC_OK == pull_init_ret)
        NTSmartPlayerSDK.NT_SP_UnInit();

    is_sdk_has_inited_ = false;
    return false;
}

退出时按反向顺序释放:

复制代码
private bool UnInitSDK()
{
    if (is_sdk_has_inited_)
    {
        NTSmartPlayerSDK.NT_SP_UnInit();
        NTSmartPublisherSDK.NT_PB_UnInit();
    }

    return true;
}

这里要注意:不能在某一路转发仍在运行时直接调用 SDK 反初始化。正确顺序应为:先停止预览、停止拉流、停止推流、释放 wrapper,再调用 SDK UnInit。Demo 的 FormClosingHandling() 中已经按这个思路做了统一释放。


多路实例创建

Demo 默认定义了 8 路:

复制代码
const int PULL_PUSH_GROUP_NUM = 8;

同时维护 8 组拉流地址、推流地址、播放按钮、拉流状态、推流状态和音频模式显示。程序读取 configure.xml 后,会根据实际配置数量计算 stream_relay_instance_count_,只创建实际配置的 relay 实例,未配置的 UI 控件会被自动禁用。

核心逻辑可以理解为:

复制代码
读取 configure.xml
    ↓
计算实际配置路数 stream_relay_instance_count_
    ↓
为每一路创建 nt_relay_wrapper
    ↓
绑定拉流状态回调、分辨率回调、推流状态回调
    ↓
设置每一路 AudioOption
    ↓
启动拉流
    ↓
启动 RTMP 转推

这种方式便于做成"配置即运行"的无人值守程序。现场只需要修改配置文件,即可调整拉流源和推流目标。


单路转发核心:nt_relay_wrapper

nt_relay_wrapper 是整个 Demo 中最关键的协调层。每一路转发都有一个独立的 nt_relay_wrapper 实例,内部同时持有一个拉流端 nt_player_wrapper 和一个推流端 nt_publisher_wrapper

结构可以概括为:

复制代码
nt_relay_wrapper
├── nt_player_wrapper       # 拉流端
├── nt_publisher_wrapper    # 推流端
├── StartPull()             # 启动拉流
├── StopPull()              # 停止拉流
├── StartPublisher()        # 启动推流
├── StopPublisher()         # 停止推流
├── StartPlayer()           # 本地预览
└── StopPlayer()            # 停止预览

它的核心价值不是做复杂业务,而是把"拉到的数据"转交给"推流模块"。视频数据和音频数据的路由逻辑如下:

cs 复制代码
private void OnVideoDataHandle(IntPtr handle, IntPtr user_data,
    UInt32 video_codec_id, IntPtr data, UInt32 size,
    IntPtr info, IntPtr reserve)
{
    if (publisher_wrapper_.is_rtmp_publishing())
    {
        publisher_wrapper_.OnVideoDataHandle(
            handle, user_data, video_codec_id, data, size, info, reserve);
    }
}

private void OnAudioDataHandle(IntPtr handle, IntPtr user_data,
    UInt32 audio_codec_id, IntPtr data, UInt32 size,
    IntPtr info, IntPtr reserve)
{
    if (publisher_wrapper_.is_rtmp_publishing())
    {
        publisher_wrapper_.OnAudioDataHandle(
            handle, user_data, audio_codec_id, data, size, info, reserve);
    }
}

这就是 RTSP 转 RTMP 的核心链路:拉流端通过回调吐出编码后的音视频数据,推流端再将这些编码数据输入到 RTMP 推送模块。


拉流流程

拉流由 nt_player_wrapper 负责。启动拉流时,建议按以下顺序处理:

Demo 中 nt_relay_wrapper.StartPull() 会先设置 SetBuffer(0),再订阅视频和音频回调,然后调用 player_wrapper_.StartPull()。这里"先订阅事件,再启动 SDK"非常重要,因为 SDK 启动后可能很快在后台线程触发数据回调。

nt_player_wrapper.StartPull() 中会设置:

cs 复制代码
NTSmartPlayerSDK.NT_SP_SetPullStreamVideoDataCallBack(...);

if (subscribe_audio)
{
    NTSmartPlayerSDK.NT_SP_SetPullStreamAudioDataCallBack(...);
    NTSmartPlayerSDK.NT_SP_SetPullStreamAudioTranscodeAAC(...);
}

NTSmartPlayerSDK.NT_SP_StartPullStream(player_handle_);

也就是说,拉流端并不是为了播放而播放,而是为了从 RTSP/RTMP 源中拿到可用于后续转推的音视频数据。


推流流程

推流由 nt_publisher_wrapper 负责。启动推流时,nt_relay_wrapper.StartPublisher() 会先打开推流 handle,再设置 RTMP URL,最后调用推流启动接口。

流程如下:

复制代码

nt_publisher_wrapper 中,编码后视频数据最终通过:

复制代码
NTSmartPublisherSDK.NT_PB_PostVideoEncodedDataV2(...)

投递给 SDK;编码后音频数据则通过:

复制代码
NTSmartPublisherSDK.NT_PB_PostAudioEncodedData(...)

投递给 SDK。底层接口声明中也可以看到,这两个接口分别用于投递编码后视频数据和编码后音频数据。

这类方式的优势是明显的:如果原始 RTSP 源已经是 H.264/H.265 视频编码,就不需要在转发端重新编码视频,CPU 占用和转发延迟都会更可控。


一键拉流与一键转推

Demo 中提供了两个关键入口:

复制代码
PullStream()
PushStream()

PullStream() 负责遍历所有已配置的 relay,逐路启动拉流;如果当前已经在拉流,则停止所有推流和拉流。

PushStream() 负责遍历所有已配置的 relay,逐路启动 RTMP 推送;如果当前已经在推流,则停止所有推送。

简化后的逻辑如下:

cs 复制代码
for (int i = 0; i < stream_relay_instance_count_; i++)
{
    stream_relay_config_[i].PullUrl = edit_pull_urls_[i].Text;
    relays[i].StartPull(stream_relay_config_[i].PullUrl);
}
cs 复制代码
for (int i = 0; i < stream_relay_instance_count_; i++)
{
    stream_relay_config_[i].PushUrl = edit_push_urls_[i].Text;
    relays[i].SetPusherOption(video_option_, (uint)stream_relay_config_[i].AudioOption);
    relays[i].StartPublisher(stream_relay_config_[i].PushUrl);
}

这种设计适合 Demo 展示,也适合很多项目中的"批量转发"场景。如果业务侧需要更细粒度控制,也可以改造成单路启动、单路停止、单路重连或异常单路恢复。


本地预览

多路转发不一定需要始终预览画面。为了降低资源消耗,建议默认只做拉流和转推,现场调试或运维时再按需打开某一路预览。

Demo 中每一路都有一个预览按钮,点击后调用:

复制代码
relays[index].StartPlayer(
    stream_relay_config_[index].PullUrl,
    is_rtsp_tcp_mode,
    is_mute);

预览能力复用了 nt_player_wrapper 的播放能力。也就是说,一路 RTSP 流既可以作为"拉流转推"的输入,也可以在需要时做本地播放预览。这样可以方便现场确认摄像头画面、码流分辨率、网络连接状态和转发效果。

工程上建议:多路并发转发时不要默认打开全部预览,尤其是 4 路、8 路甚至更多路摄像头同时转发时,解码和渲染会显著增加 CPU/GPU 占用。只有在调试、巡检或人工查看时,再打开指定路预览。


状态回调与问题定位

多路转发系统最怕的问题不是"某一路失败",而是失败后不知道问题发生在哪一段。因此,推拉流状态回调非常重要。

拉流端重点关注:

状态 说明
连接中 正在连接 RTSP/RTMP 源
连接成功 摄像头或流媒体源可访问
连接失败 URL、鉴权、网络或设备异常
断开连接 源端断流或网络断开
下载速度 当前拉流带宽,可用于判断是否有数据持续到达
缓冲状态 网络抖动或源端不稳定时用于辅助判断

推流端重点关注:

状态 说明
连接中 正在连接 RTMP Server
已连接 RTMP 推送连接建立
连接失败 推流地址、服务器、网络或鉴权异常
断开连接 RTMP 服务器断开或网络异常

Demo 中 GetPlayerEventMsgInfo()GetPublisherEventMsgInfo() 会将事件信息显示到对应路的 Label 上,便于现场观察每一路状态。

实际项目中建议将这些状态进一步写入日志系统,例如:

复制代码
[Relay-03] Pull connecting: rtsp://...
[Relay-03] Pull connected
[Relay-03] Push connecting: rtmp://...
[Relay-03] Push connected
[Relay-03] DownloadSpeed: 3200kbps
[Relay-03] Pull disconnected

这样可以快速判断故障属于摄像头侧、网络侧、转发程序侧,还是 RTMP 服务端侧。


资源释放与退出处理

多路 SDK 程序一定要重视释放顺序。尤其是 C# WinForms 程序中,SDK 回调可能来自后台线程,UI 控件释放后,如果回调仍然访问 Label、Button 或窗口句柄,就可能导致异常。

建议退出顺序如下:

复制代码
解绑事件回调
    ↓
停止本地预览
    ↓
停止拉流
    ↓
释放播放器 wrapper
    ↓
停止推流
    ↓
释放推流 wrapper
    ↓
SDK UnInit

Demo 中退出处理已经体现了这个思路:

cs 复制代码
relays[i].GetPlayerWrapper().EventGetPlayerEventMsg -= GetPlayerEventMsgInfo;
relays[i].GetPlayerWrapper().EventGetVideoSize -= GetVideoSize;
relays[i].GetPublisherWrapper().EventGetPublisherEventMsg -= GetPublisherEventMsgInfo;

relays[i].StopPlayer();
relays[i].StopPull();
relays[i].PlayerDispose();
relays[i].StopPublisher();
relays[i].PublisherDispose();

UnInitSDK();

这个顺序比简单粗暴地直接关闭窗口更可靠,适合长期运行的工程项目。


开机自启动

对于无人值守转发场景,Windows 程序常见部署方式是开机后自动启动。Demo 中提供了写入注册表的方式,将程序加入:

复制代码
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

实现当前用户登录后自动启动。

实现时要注意两点:

第一,路径需要加双引号,避免安装目录中包含空格时启动失败。

第二,删除自启动项时,最好先根据 exe 路径查找实际键名,再删除对应键值,避免键名和程序名不一致时删除失败。

Demo 中 SetAutoStart() 已经考虑了这类细节。


部署建议

1. 明确 SDK DLL 位数

如果使用 x64 SDK,C# 工程必须设置为 x64。不要使用 Any CPU,否则可能出现运行时找不到 DLL 或加载失败。

2. 配置文件和 exe 放同级目录

configure.xml 建议放在 exe 同级目录,方便运维人员直接修改,不需要重新编译程序。

3. 日志目录提前创建

如果启用 SmartLog,确保日志目录存在,并对运行账号有写入权限。

4. 多路转发尽量关闭默认预览

批量转发时,预览会引入解码和绘制开销。转发服务默认只做拉流和推流,需要看画面时再打开单路预览。

5. 按路记录状态

建议日志中带上 relay index、PullUrl、PushUrl、拉流状态、推流状态和下载速度,便于多路场景定位问题。

6. 区分"拉不到"和"推不上"

现场排查时要分两段看:

复制代码
RTSP 源 → 拉流端
拉流端 → RTMP 推流端 → RTMP Server

如果拉流失败,重点检查摄像头地址、账号密码、RTSP 端口、网络连通性;如果推流失败,重点检查 RTMP Server 地址、应用名、流名、防火墙和服务端状态。


常见问题

1. 为什么 RTSP 地址在 XML 中配置后读取失败?

如果 URL 中包含 &,需要写成 &amp;。这是 XML 语法要求,不是 SDK 限制。

2. 为什么程序启动后某些路按钮不可用?

Demo 会根据 configure.xml 中实际 <Relay> 数量计算配置路数,未配置的路不会创建 relay 实例,对应 UI 控件也会禁用。

3. 为什么建议转发时设置 Buffer 为 0?

转发场景追求尽可能低的链路延迟,不需要播放器为了平滑播放额外缓存数据。Demo 在拉流前调用 SetBuffer(0),目的就是减少不必要的内部缓冲。

4. 为什么有的视频可以转推,有的音频没有声音?

需要检查 AudioOption。如果希望转发摄像头自带音频,建议使用 AudioOption=4。如果摄像头没有音频或业务不需要音频,可设置为 0。

5. 为什么不建议所有路都打开预览?

预览意味着解码和渲染,会增加 CPU/GPU 消耗。多路转发时,建议默认不预览,只在调试时打开指定一路。

6. 推流失败如何定位?

先看拉流状态是否连接成功,再看下载速度是否持续更新。如果拉流正常但推流失败,重点检查 RTMP 地址、服务器状态、网络出口、防火墙和鉴权配置。


总结

大牛直播SDK(SmartMediaKit)Windows 平台多路 RTSP 转 RTMP 推流方案,适合需要在边缘侧、工控机、Windows 客户端或后台服务中批量接入 RTSP 摄像头,并统一转发到 RTMP 平台的项目。

从工程结构看,Demo 采用了比较清晰的分层方式:SmartStreamRelayDemo 负责业务控制和多路管理,nt_relay_wrapper 负责单路协调,nt_player_wrapper 负责拉流和数据回调,nt_publisher_wrapper 负责 RTMP 推送和编码数据输入。这样的设计既便于理解,也便于后续扩展为更稳定的后台服务或边缘网关程序。

从实际集成角度看,开发者重点关注几件事即可:配置好每一路 PullUrl 和 PushUrl,正确设置 AudioOption,确保 SDK 初始化和释放顺序正确,默认关闭多路预览以降低资源占用,并通过事件回调和日志区分拉流侧问题与推流侧问题。

对于安防视频上云、园区视频汇聚、无人值守转发、应急指挥回传等场景,这种"多路 RTSP 输入 + RTMP 标准输出"的方式,能够在不改造前端摄像头和后端平台的前提下,快速完成协议适配和实时视频转发。


📎 CSDN官方博客:音视频牛哥-CSDN博客

相关推荐
私人珍藏库15 小时前
[Android] 全能语音计算器v4.6
人工智能·windows·语音识别·工具·软件·多功能
蓝影灵15 小时前
升级node以及nvm
windows
m0_535817551 天前
从0到1上手Claude Code:Windows安装+88api配置全流程
windows·gpt·node.js·claude·claudecode·88api
阿昭L1 天前
Windows用户态hook技术总结
windows·逆向·hook
fengchengwu20121 天前
Jupyter 安装与使用指南:从环境配置到效率翻倍
ide·windows·jupyter
呉師傅1 天前
东芝e-STUDIO 3525ac提示黄色和品红色墨粉盒在耗尽前被更换。请重新插入之前的墨粉盒并用至耗尽如何操作
运维·windows·电脑
玖釉-1 天前
二叉树展开为链表:从先序遍历到原地指针重排
c++·windows·算法·leetcode·链表
MinterFusion1 天前
如何在Windows下查看某个文件的MD5和SHA256值(v0.1.0)
windows·md5·sha256·系统运维·明德融创
idolao1 天前
Autodesk Alias AutoStudio 2025安装教程 Windows版:自定义路径指南
windows