大牛直播SDK(SmartMediaKit)Android平台Unity3D RTSP/RTMP播放器集成实践
摘要
本文介绍大牛直播SDK(SmartMediaKit)在 Android 平台 Unity3D 工程中的 RTSP/RTMP 播放器集成方式,适用于安防监控、工业可视化、无人机回传、智慧教室、数字孪生、远程巡检、车载视频、AR/VR 可视化等需要在 Unity 场景中嵌入实时视频画面的应用场景。
通过大牛直播SDK提供的 Android Unity3D 播放器能力,开发者可以在 Unity3D 工程中快速接入 RTSP/RTMP 实时流播放,实现低延迟播放、播放状态回调、视频画面显示、本地录像、软硬解码配置、RTSP 传输策略配置、下载速度提示等能力。Unity 侧主要完成场景搭建、UI 控制、播放参数配置和视频承载,SDK 侧负责实时流媒体播放、解码、事件回调和录像等核心能力。
关键词:大牛直播SDK、SmartMediaKit、Unity3D、Android、RTSP播放器、RTMP播放器、低延迟播放、Unity播放器集成、实时音视频SDK
1. 方案概述
在 Unity3D 项目中接入 RTSP/RTMP 实时流播放,与普通本地视频文件播放不同。RTSP/RTMP 播放涉及实时网络连接、流媒体拉取、解码、缓冲控制、状态回调、画面显示和异常处理等多个环节。如果业务侧从零实现完整播放链路,不仅开发周期长,还需要持续处理不同视频源、不同 Android 设备、不同网络环境下的兼容性问题。
大牛直播SDK Android Unity3D 播放器方案,面向 Unity3D 实时视频集成场景提供标准化播放器能力。SDK 侧负责 RTSP/RTMP 拉流、播放控制、解码适配、状态事件、下载速度统计和录像等能力;Unity 侧负责 UI 交互、播放地址输入、状态展示、显示区域承载和业务逻辑组织。
当前示例工程已将 SDK 接口、播放器封装、播放实例、播放器管理器和 UI 控制进行了分层。NTSmartPlayerAPI负责 Android SDK JNI 包装,业务层无需直接持有 AndroidJavaObject;NTPlayerWrapper负责句柄生命周期、播放/录像状态和配置下发;PlayerInstance负责单路播放器实例、画面显示适配和事件处理;PlayerManager负责 SDK 初始化、实例创建销毁和事件分发;UIController负责界面按钮、输入框、状态文本和 RawImage 适配。
2. 适用场景
大牛直播SDK Android Unity3D RTSP/RTMP 播放器适用于需要在 Unity3D 中显示实时视频画面的 Android 应用。典型场景包括工业数字孪生系统中的现场视频叠加、无人机或车载系统中的实时回传画面显示、安防监控系统中的 RTSP 预览、智慧教室和无纸化会议中的实时同屏、远程巡检中的现场视频查看,以及 AR/VR、仿真训练和应急指挥中的实时视频融合。
这类项目通常更关注以下能力:视频画面能否稳定播放、延迟是否可控、播放状态是否可感知、是否支持 RTSP/RTMP 常见视频源、是否便于接入 Unity UI 或场景对象、是否可以在播放过程中进行录像留痕。大牛直播SDK通过播放器模块和 Unity3D 示例工程,将这些能力封装为相对清晰的集成流程,便于开发者快速完成工程验证和业务集成。
3. 集成前准备
开始集成前,建议准备以下内容:
| 项目 | 说明 |
|---|---|
| Unity3D 工程 | Android 目标平台工程 |
| Android Native 插件 | 大牛直播SDK提供的.so及相关依赖 |
| C# 脚本文件 | 播放器接口层、管理层、实例层和 UI 控制层 |
| Material 与 Shader | 用于 Unity 侧视频画面显示 |
| 测试流地址 | RTSP 或 RTMP 播放地址 |
| Android 真机 | 建议使用真实设备验证播放、录像和性能 |
Android 工程至少需要网络访问权限:
<uses-permission android:name="android.permission.INTERNET" />如果项目需要录像,推荐优先使用应用私有目录保存录像文件。示例工程中提供了录像目录配置项,当未指定录像目录时,可使用Application.persistentDataPath/Record作为默认录像路径。
4. 工程目录说明
建议将播放器相关文件按职责放入 Unity 工程目录,便于后续维护和复用。推荐目录结构如下:
Assets/ ├── NTSmartPlayerAPI.cs ├── NTPlayerEvent.cs ├── NTPlayerWrapper.cs ├── PlayerConfig.cs ├── PlayerInstance.cs ├── PlayerManager.cs ├── UIController.cs ├── Plugins/ │ └── Android/ │ ├── libSmartPlayer.so │ └── 其他依赖 .jar ├── Materials/ │ ├── MaterialYuv420.mat │ ├── MaterialNV21.mat │ └── MaterialNV12.mat └── Shaders/ ├── Unlit_YUV420.shader ├── Unlit_NV21.shader └── Unlit_NV12.shader各文件职责建议理解如下:
| 文件 | 说明 |
|---|---|
NTSmartPlayerAPI.cs | Android SDK JNI 静态包装层,统一封装播放器底层接口 |
NTPlayerEvent.cs | 播放器事件 ID 常量定义 |
NTPlayerWrapper.cs | 播放器句柄生命周期、播放控制、录像控制和参数下发封装 |
PlayerConfig.cs | 播放配置数据结构,可在 Inspector 中调整 |
PlayerInstance.cs | 单路播放器实例,负责播放状态、事件处理和视频显示适配 |
PlayerManager.cs | 单例管理器,负责 SDK 初始化、播放器创建销毁和事件分发 |
UIController.cs | UI 控制器,负责按钮、输入框、状态文本和 RawImage 显示适配 |
其中,NTSmartPlayerAPI是 SDK 接口层,内部负责初始化 Android 侧播放器对象并提供统一的NT_U3D_*接口;NTPlayerWrapper进一步封装播放器句柄、播放状态、录像状态和配置下发;PlayerManager是场景中的单例管理器;UIController负责用户交互和显示区域适配。
5. 插件、Material 与 Shader 放置
Android Native 插件应放入:
Assets/Plugins/Android/ ├── libSmartPlayer.so └── 其他依赖 .jarUnity 在构建 Android APK 时,会自动将该目录下的.so和.jar依赖打入应用包。请确保 SDK 库文件与目标 CPU 架构匹配,例如 arm64-v8a 或 armeabi-v7a。
视频显示相关 Material 和 Shader 建议放入:
Assets/Materials/ ├── MaterialYuv420.mat ├── MaterialNV21.mat └── MaterialNV12.mat Assets/Shaders/ ├── Unlit_YUV420.shader ├── Unlit_NV21.shader └── Unlit_NV12.shaderSDK 已对不同视频格式的显示链路进行适配,开发者通常只需在 Inspector 中正确配置对应 Material,即可完成 Unity 侧视频画面显示。示例工程中,UIController会实例化 I420、NV21、NV12 三类 Material,播放实例会根据实际视频格式选择对应显示材质。
6. Unity 场景搭建
推荐使用以下场景层级:
Canvas ├── PlayerManager 挂载 PlayerManager.cs └── Panel 挂载 UIController.cs ├── VideoArea │ └── RawImage └── ControlsArea ├── StatusText ├── InputField_Url └── ButtonRow ├── BtnDecoder ├── BtnMute ├── BtnStartStopPlayback └── BtnRecordPlayerManager用于 SDK 初始化、播放器实例管理和事件接收,场景中通常只需要一个。Panel作为播放器 UI 面板,挂载UIController,用于控制播放、停止、录像、静音、软硬解码切换和状态显示。
VideoArea用于承载视频显示区域,建议设置黑色背景。其子节点RawImage用于显示视频画面。示例工程中,UIController会根据视频分辨率和显示区域大小自动调整 RawImage 尺寸,使画面按比例显示,避免拉伸变形。
PlayerManager在Awake()中完成 SDK 初始化,并在Update()中驱动播放器刷新;UIController在Start()中完成 Material 实例化、按钮事件绑定、默认 URL 设置和显示区域初始化。
7. Inspector 接线说明
完成场景搭建后,需要在UIController的 Inspector 中配置以下字段:
| 字段 | 赋值对象 |
|---|---|
| Render View | 场景中的 RawImage |
| Video Area RT | VideoArea 的 RectTransform |
| Status Text | StatusText 的 Text 组件 |
| Url Input | InputField_Url |
| Btn Decoder | 软硬解码切换按钮 |
| Btn Mute | 静音/取消静音按钮 |
| Btn Play | 开始/停止播放按钮 |
| Btn Record | 开始/停止录像按钮 |
| Mat I420 | I420 对应 Material |
| Mat NV21 | NV21 对应 Material |
| Mat NV12 | NV12 对应 Material |
| Default Url | 默认 RTSP/RTMP 播放地址 |
| Record Directory | 录像目录,留空则使用默认目录 |
PlayerManager上的PlayerConfig可直接在 Inspector 中调整播放参数,包括解码模式、缓冲时长、RTSP 传输策略、音频设置、低延迟模式、旋转角度和录像文件大小等。PlayerConfig已将常用配置项通过可视化字段暴露,便于调试和项目集成。
8. 播放参数配置
PlayerConfig是播放器参数配置入口,建议开发者优先通过 Inspector 调整。主要参数如下:
| 参数 | 说明 |
|---|---|
enableHardwareDecoder | 是否启用硬件解码 |
bufferTimeMs | 播放缓冲时长,0 表示最低延迟 |
rtspUseTcp | 是否强制 RTSP TCP 模式 |
rtspTimeoutSec | RTSP 连接超时时间 |
rtspAutoSwitchTcpUdp | UDP 连接失败后是否自动切换 TCP |
mute | 默认是否静音 |
volume | 默认音量,范围 0–100 |
useAudioTrack | 是否使用 AudioTrack 输出 |
fastStartup | 是否启用快速首帧 |
lowLatencyMode | 是否启用低延迟模式 |
rotateDegrees | 视频旋转角度 |
recMaxFileSizeMB | 单个录像文件最大体积 |
对于低延迟场景,可以将bufferTimeMs设置得较低,并结合fastStartup、lowLatencyMode、RTSP TCP/UDP 策略进行测试。需要注意的是,低延迟和抗网络抖动之间通常存在平衡关系。缓冲越低,延迟越小,但在网络波动较大的情况下,卡顿或丢帧概率可能增加。因此,建议结合实际视频源、码率、网络环境和目标设备进行综合调优。
using System; using UnityEngine; [Serializable] public class PlayerConfig { [Header("解码")] [Tooltip("启用硬件解码(H264/H265);不支持时 SDK 自动回退到软解")] public bool enableHardwareDecoder = false; [Header("缓冲")] [Tooltip("播放缓冲时长(毫秒);0 = 不缓冲(最低延迟)")] [Range(0, 8000)] public int bufferTimeMs = 0; [Header("RTSP 传输")] [Tooltip("true = 强制 TCP;false = 优先 UDP")] public bool rtspUseTcp = false; [Tooltip("连接超时(秒)")] [Range(1, 30)] public int rtspTimeoutSec = 10; [Tooltip("UDP 连接失败后自动切换为 TCP")] public bool rtspAutoSwitchTcpUdp = true; [Header("音频")] public bool mute = false; [Range(0, 100)] public int volume = 100; [Tooltip("true = AudioTrack 模式;false = 自动选择")] public bool useAudioTrack = true; [Header("播放行为")] [Tooltip("快速首帧(减少起播黑屏)")] public bool fastStartup = true; [Tooltip("超低延迟模式(会增加丢帧概率)")] public bool lowLatencyMode = false; [Tooltip("画面顺时针旋转角度:0 / 90 / 180 / 270")] [Range(0, 270)] public int rotateDegrees = 0; [Header("录像")] [Tooltip("单个录像文件最大体积(MB)")] public int recMaxFileSizeMB = 500; }9. RTSP/RTMP 播放流程
在示例工程中,播放流程已经封装到 UI 和播放器管理逻辑中。开发者在输入框中填入 RTSP 或 RTMP 地址后,点击播放按钮即可启动播放。
业务流程如下:
输入 RTSP/RTMP 地址 → 点击开始播放 → 创建播放器实例 → 应用播放配置 → 绑定 RawImage 显示目标 → 启动播放 → Unity 主线程刷新视频画面当前工程中,UIController.OnPlayClicked()会根据当前状态决定开始播放或停止播放。开始播放时,会获取或创建播放器实例,绑定 RawImage 和 Material,然后启动播放;停止播放时,会停止播放并清理画面,如果当前没有录像任务,则释放播放器实例。
public void StartPlay() { if (!wrapper_.StartPlay()) return; need_get_frame_ = true; need_init_texture_ = true; StatusText = "连接中..."; } public void StopPlay() { if (!is_playing) return; wrapper_.StopPlay(); need_get_frame_ = false; need_init_texture_ = false; VideoWidth = 0; VideoHeight = 0; downloadSpeedBps_ = -1; flrHundredths_ = -1; lrHundredths_ = -1; bufferStatus_ = 0; bufferPercent_ = 0; connectionStatus_ = 0; StatusText = ""; }播放前需要确保 RawImage 和 Material 已正确配置。示例工程通过SetRenderTarget()将 RawImage 和三类 Material 绑定到播放器实例中,保证播放器启动后可以将视频画面显示到 Unity UI 中。
10. 视频画面显示说明
播放器采用 Unity 主线程刷新画面的方式。SDK 负责准备解码后的视频帧,Unity 侧在主线程中刷新并显示到 RawImage。该方式符合 Unity 的渲染机制,便于与 Unity UI 和场景显示逻辑结合。
开发者在集成时主要关注以下几点:
- RawImage 是最终视频显示控件;
- Material 和 Shader 用于不同视频格式的显示适配;
- 播放器会根据实际视频格式选择对应显示材质;
- 分辨率变化后,UI 层会自动调整 RawImage 尺寸;
- 建议直接使用 SDK 示例工程提供的 Material 和 Shader。
示例工程中,播放器实例负责视频画面刷新,UI 层负责根据显示区域和视频分辨率做等比适配。收到分辨率信息后,UIController会记录视频宽高,并在后续显示刷新中调整 RawImage 的尺寸。
11. 事件回调与状态显示
SDK 会向 Unity 层回调播放状态、连接状态、分辨率信息、缓冲状态、下载速度、录像文件生成等事件。业务层可根据这些事件更新 UI 状态、提示网络质量、展示当前分辨率或处理录像文件路径。
常见事件类型包括:
连接中 / 已连接 / 连接失败 / 断开 分辨率变化 长时间无媒体数据 RTSP 状态码提示 缓冲开始 / 缓冲中 / 缓冲结束 下载速度 快照结果 录像文件生成 / 录像文件完成事件由 SDK 发送到PlayerManager,再由播放器实例进行处理。示例工程中,PlayerManager.onNTSmartEvent()是事件入口,播放器实例会根据事件更新连接状态、缓冲状态、下载速度、录像状态和分辨率信息,最终形成状态文本供 UI 展示。
12. 下载速度与网络状态提示
播放器支持下载速度上报,可用于辅助判断网络质量。示例工程会在状态文本中展示当前速度信息,并结合缓冲状态反馈播放体验。
public static class NTPlayerEvent { private const int PLAYER_SDK = 0x01000000; // EVENT_DANIULIVE_PLAYER_SDK // ── 连接 / 播放状态 ─────────────────────────────────────────────────── /// <summary>开始播放(SDK 内部状态机已启动)</summary> public const int STARTED = PLAYER_SDK | 0x1; /// <summary>连接中</summary> public const int CONNECTING = PLAYER_SDK | 0x2; /// <summary>连接失败</summary> public const int CONNECTION_FAILED = PLAYER_SDK | 0x3; /// <summary>已连接</summary> public const int CONNECTED = PLAYER_SDK | 0x4; /// <summary>断开连接</summary> public const int DISCONNECTED = PLAYER_SDK | 0x5; /// <summary>停止播放</summary> public const int STOP = PLAYER_SDK | 0x6; // ── 媒体信息 ────────────────────────────────────────────────────────── /// <summary>视频解码分辨率。p1=width, p2=height</summary> public const int RESOLUTION_INFO = PLAYER_SDK | 0x7; /// <summary>收不到媒体数据(检查 URL 或网络)</summary> public const int NO_MEDIADATA = PLAYER_SDK | 0x8; /// <summary>切换 URL 中</summary> public const int SWITCH_URL = PLAYER_SDK | 0x9; /// <summary>截取快照结果。p1=0 成功,非 0 失败</summary> public const int CAPTURE_IMAGE = PLAYER_SDK | 0xA; /// <summary>RTSP 状态码上报(目前只上报 401)。p1=status code</summary> public const int RTSP_STATUS_CODE = PLAYER_SDK | 0xB; // ── 录像 ────────────────────────────────────────────────────────────── /// <summary>录像写入新文件。p3=新文件完整路径</summary> public const int RECORDER_NEW_FILE = PLAYER_SDK | 0x21; /// <summary>一个录像文件写入完成。p3=已完成文件完整路径</summary> public const int RECORDER_FILE_DONE = PLAYER_SDK | 0x22; // ── 缓冲 ────────────────────────────────────────────────────────────── /// <summary>开始缓冲</summary> public const int START_BUFFERING = PLAYER_SDK | 0x81; /// <summary>缓冲中。p1=百分比进度(0–100)</summary> public const int BUFFERING = PLAYER_SDK | 0x82; /// <summary>缓冲结束,恢复正常播放</summary> public const int STOP_BUFFERING = PLAYER_SDK | 0x83; // ── 下载速度 ────────────────────────────────────────────────────────── /// <summary> /// 下载速度上报。p1=速度(Byte/s)。 /// p2 bit 编码: /// bit31=1 → 高 15 位为前向丢包率 FLR(Q8.8 定点,÷256 得实际比例) /// bit15=1 → 低 15 位为综合丢包率 LR (Q8.8 定点,÷256 得实际比例) /// </summary> public const int DOWNLOAD_SPEED = PLAYER_SDK | 0x91; }当用户反馈“播放卡顿”“延迟高”“画面不连续”时,可优先检查以下内容:
- 当前是否已连接;
- 是否频繁进入缓冲;
- 下载速度是否低于视频实际码率;
- 视频分辨率和码率是否过高;
- RTSP 使用 TCP 还是 UDP;
- 当前网络是否存在明显抖动;
- 目标设备解码性能是否满足要求。
示例工程中已经包含下载速度事件和缓冲事件的状态处理逻辑,业务侧可根据项目需要决定是否在 UI 中展示。
13. 录像能力说明
播放器支持播放过程中启动本地录像,适用于安防留痕、巡检归档、远程会诊记录、问题复盘等场景。录像目录可在 Inspector 中配置;如果未配置,可使用应用私有目录下的默认路径。
业务流程如下:
点击开始录像 → 确定录像目录 → 启动录像 → SDK 写入录像文件 → 回调新文件路径或文件完成事件 → 业务侧展示、上传或归档当前工程中,录像和播放可以共享同一个播放器实例。点击录像按钮时,如果播放器实例不存在,会先创建播放器实例再启动录像;停止录像时,如果当前没有播放任务,可以释放播放器资源。
// ── 录像控制 ────────────────────────────────────────────────────────────── public void StartRecorder(string directory) { int maxMB = PlayerManager.Instance?.config?.recMaxFileSizeMB ?? 500; if (!wrapper_.StartRecorder(directory, maxMB)) Debug.LogError("[PlayerInstance] StartRecorder failed"); } public void StopRecorder() { wrapper_.StopRecorder(); }录像过程中,SDK 会通过事件回调通知新文件生成和文件完成,业务侧可以根据回调路径进行文件展示、上传、归档或后续处理。
14. 软硬解码与低延迟建议
示例工程提供软硬解码切换能力。硬解码通常 CPU 占用更低,适合分辨率较高或设备性能有限的场景;软解码兼容性相对更稳,适合调试阶段或设备差异较大的项目。首次集成时,建议先使用软解码验证播放链路,确认播放地址、网络连通性和场景接线均正常后,再切换硬解码进行性能测试。
示例工程中,软硬解码按钮用于切换当前解码模式,并在播放或录像运行中禁用,避免运行过程中切换关键解码参数。静音按钮则支持播放中实时切换。
低延迟调优可重点关注以下参数:
| 调优项 | 建议 |
|---|---|
bufferTimeMs | 越低延迟越小,但抗抖动能力下降 |
fastStartup | 可用于减少起播等待 |
lowLatencyMode | 适合强实时场景 |
rtspUseTcp | 网络复杂时 TCP 更稳 |
rtspAutoSwitchTcpUdp | 建议保留自动切换能力 |
enableHardwareDecoder | 需结合目标设备测试兼容性 |
低延迟播放效果受视频源、编码参数、网络质量、缓冲策略、解码方式和设备性能共同影响。建议在目标场景中进行端到端测试,以获得更符合业务需求的参数组合。
15. Android 打包注意事项
打包 Android 前,建议重点检查以下内容:
Assets/Plugins/Android/下的.so和.jar依赖是否完整;- AndroidManifest 是否包含网络权限;
- 目标 CPU 架构是否与 SDK 库匹配;
- Material 和 Shader 是否正确放入工程并参与打包;
- Scene 中是否存在
PlayerManager对象; UIController的 Inspector 字段是否全部接好;- 默认播放地址是否可访问;
- Android 真机是否与播放源处于可连通网络中;
- 录像目录是否可写;
- 目标设备是否满足播放分辨率和解码性能要求。
建议优先使用真机进行测试。编辑器环境可用于 UI 布局和业务逻辑预览,Android Native SDK 相关能力应以真机测试结果为准。
16. 常见问题
16.1 点击播放后没有画面
建议按以下顺序排查:
- 确认 RTSP/RTMP 地址是否可用;
- 确认 Android 设备与播放源网络是否互通;
- 确认 AndroidManifest 已配置网络权限;
- 确认 RawImage、Material、Shader 已正确接线;
- 查看是否收到连接、分辨率或错误状态事件;
- 尝试切换软解码或 RTSP TCP 模式。
16.2 连接失败
可以先使用 VLC 或其他播放器验证播放地址。如果是 RTSP 流,可尝试启用 TCP 模式,或开启 UDP 失败后自动切 TCP。若播放源需要鉴权,请确认 URL 中的用户名、密码和端口配置正确。
16.3 画面比例异常
示例工程会根据视频分辨率和显示区域自动调整 RawImage 尺寸。请确认 VideoArea 的 RectTransform 设置合理,RawImage 不要额外添加会影响尺寸计算的布局组件。收到分辨率信息后,UI 层会按视频宽高比进行显示适配。
16.4 播放卡顿或延迟较高
可结合缓冲时长、低延迟模式、RTSP TCP/UDP 策略、下载速度和视频码率综合调整。低延迟场景建议减少缓冲,但在网络波动明显时可适当增加缓冲以提升流畅性。
16.5 录像文件找不到
请检查 Inspector 中的录像目录配置。如果留空,通常使用应用私有目录下的默认 Record 路径。可通过录像文件事件查看实际文件路径。
16.6 是否支持播放中切换软硬解码
不建议在播放过程中切换软硬解码。软硬解码属于播放前关键配置,示例工程中播放或录像运行时会禁用解码模式切换按钮。
17. 总结
大牛直播SDK(SmartMediaKit)Android Unity3D RTSP/RTMP 播放器方案,面向需要在 Unity3D 场景中接入实时音视频画面的 Android 应用,提供了低延迟播放、播放状态回调、视频画面适配、本地录像、软硬解码配置、RTSP/RTMP 地址接入等能力。通过示例工程提供的分层封装,开发者可以快速完成 SDK 插件放置、场景搭建、Inspector 接线、播放参数配置和真机运行验证。
在实际项目中,开发者可根据业务需求灵活调整 UI 布局、播放地址、缓冲策略、软硬解码模式、录像目录和状态展示方式。对于安防监控、工业可视化、无人机回传、智慧教室、数字孪生、远程巡检、车载视频、AR/VR 可视化等场景,该方案能够帮助开发者在 Unity3D 中快速构建稳定、低延迟、易集成的 RTSP/RTMP 实时视频播放能力。
大牛直播SDK将持续围绕跨平台低延迟播放、实时音视频采集、推流、录像、轻量级服务、GB28181 接入等方向完善产品能力,为企业级实时音视频应用提供稳定可靠的底层技术支撑。
📎 CSDN官方博客:音视频牛哥-CSDN博客
