当前位置: 首页 > news >正文

大牛直播SDK(SmartMediaKit)Android Unity3D 播放器集成文档

目标平台:Android(API 21+)
支持协议:RTSP、RTMP


目录

  1. 概述
  2. 环境要求
  3. 工程文件说明
  4. 快速集成步骤
  5. PlayerConfig 配置说明
  6. 核心 API 说明
  7. 事件回调说明
  8. 录像功能
  9. 视频渲染原理

1. 概述

本文档描述如何在 Android Unity3D 工程中集成大牛直播 SDK,实现 RTSP / RTMP 直播流的拉取、渲染与本地录像功能。

架构总览

帧渲染模型(PULL)

SDK 不主动推送帧数据,而是由 Unity 主线程每帧主动调用GetVideoFrame()拉取,避免跨线程写纹理的问题:

2. 环境要求

项目要求
Unity2019.4.13f1 或更高
Android API最低 21(Android 5.0),推荐 26+
构建工具IL2CPP 或 Mono 均可
权限INTERNETWRITE_EXTERNAL_STORAGE(录像)、READ_EXTERNAL_STORAGE

AndroidManifest.xml 权限

<uses-permissionandroid:name="android.permission.INTERNET"/><uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"android:maxSdkVersion="28"/><uses-permissionandroid:name="android.permission.READ_EXTERNAL_STORAGE"android:maxSdkVersion="32"/>

Android 10+ 录像路径建议使用Application.persistentDataPath,无需存储权限。


3. 工程文件说明

各文件职责

文件职责是否需要修改
NTSmartPlayerAPI.csJNI 接口定义,1:1 对应 Java 层❌ 通常不改
NTPlayerEvent.cs事件 ID 常量,对应NTSmartEventID.java❌ 不改
NTPlayerWrapper.cs句柄管理、状态机、配置下发⚠️ 如需扩展参数可改
PlayerConfig.cs序列化配置字段,Inspector 可调✅ 按需扩展
PlayerInstance.cs事件处理、YUV 渲染、状态文本✅ 按需扩展
PlayerManager.cs场景入口,SDK 初始化⚠️ 一般不改
UIController.cs业务 UI 逻辑✅ 按需修改

4. 快速集成步骤

Step 1 — 拷贝 SDK 文件

将大牛直播提供的以下文件放入工程对应目录:

Step 2 — 拷贝 C# 脚本

将本工程Assets/目录下的所有.cs文件拷贝到目标工程。

Step 3 — 配置 Player Settings

Edit → Project Settings → Player → Android:

设置项推荐值
Minimum API LevelAndroid 5.0 (API 21)
Scripting BackendIL2CPP(发布)/ Mono(调试)
Target ArchitectureARM64(必选)、ARMv7(可选)
Internet AccessRequired
Write PermissionExternal (SDCard)(如需 sdcard 录像)

Step 4 — 搭建场景 | 接线 Inspector

Step 5 — 授权 SDK

PlayerManager.Awake()初始化后调用:

NTSmartPlayerAPI.NT_U3D_SetSDKClientKey("your_cid","your_key",0);

或在PlayerConfig扩展字段统一管理。


Step 6. PlayerConfig 配置说明

PlayerManagerGameObject 上通过 Inspector 配置,也可在代码中访问PlayerManager.Instance.config

[Serializable]publicclassPlayerConfig{// ── 解码 ──────────────────────────────────────────────────────────────publicboolenableHardwareDecoder=false;// false = 软解(兼容性好);true = 硬解(性能好,需设备支持)// 运行中不可切换,需停止播放后修改再重新开始// ── 缓冲 ──────────────────────────────────────────────────────────────[Range(0,8000)]publicintbufferTimeMs=0;// 0 = 最低延迟模式;建议直播设 0,点播/不稳定网络设 500~2000// ── RTSP 传输 ──────────────────────────────────────────────────────────publicboolrtspUseTcp=false;// false = 优先 UDP,丢包时考虑改 true[Range(1,30)]publicintrtspTimeoutSec=10;// 连接超时(秒)publicboolrtspAutoSwitchTcpUdp=true;// UDP 失败自动切 TCP// ── 音频 ──────────────────────────────────────────────────────────────publicboolmute=false;[Range(0,100)]publicintvolume=100;publicbooluseAudioTrack=true;// true = AudioTrack 模式(推荐)// ── 播放行为 ──────────────────────────────────────────────────────────publicboolfastStartup=true;// 减少起播黑屏时间publicboollowLatencyMode=false;// 超低延迟,会增加丢帧概率[Range(0,270)]publicintrotateDegrees=0;// 顺时针旋转:0/90/180/270// ── 录像 ──────────────────────────────────────────────────────────────publicintrecMaxFileSizeMB=500;// 单个录像文件最大体积(MB),超过后自动切片}

6. 核心 API 说明

6.1 播放控制(通过 UIController 或直接调用)

// 创建播放器实例(内部调用 NT_U3D_Open + ApplyConfig)PlayerInstanceplayer=PlayerManager.Instance.CreatePlayer("rtsp://...");// 绑定渲染目标(必须在 StartPlay 前调用)player.SetRenderTarget(rawImage,matI420,matNV21,matNV12);// 开始播放player.StartPlay();// 停止播放player.StopPlay();// 销毁播放器(Close 句柄 + 释放纹理)PlayerManager.Instance.DestroyPlayer();

6.2 实时控制(播放中随时可调)

// 静音 / 取消静音player.SetMuteRealtime(true);// 调节音量(0–100)player.SetVolumeRealtime(80);

6.3 录像控制

// 开始录像(目录路径,SDK 自动创建)player.StartRecorder("/sdcard/daniulive/Record");// 或使用 persistentDataPath(Android 10+ 推荐,无需权限)player.StartRecorder(Application.persistentDataPath+"/Record");// 停止录像player.StopRecorder();// 查询状态boolisRecording=player.is_recording;

6.4 状态查询

boolisPlaying=player.is_playing;boolisRecording=player.is_recording;longhandle=player.handle;// 0 = 未初始化intwidth=player.VideoWidth;intheight=player.VideoHeight;stringstatus=player.StatusText;// 格式化状态文本,直接显示给用户

7. 事件回调说明

事件由 SDK 通过UnitySendMessage发到PlayerManager.onNTSmartEvent,再分发到PlayerInstance.HandleEvent()

消息格式(逗号分隔):

handle,code,param1,param2,param3,param4

所有事件 ID 定义在NTPlayerEvent.cs,与 Android SDK 的NTSmartEventID.java完全对应。

事件列表

常量说明参数
STARTED0x1000001SDK 内部状态机已启动
CONNECTING0x1000002连接中
CONNECTION_FAILED0x1000003连接失败
CONNECTED0x1000004已连接,即将收到视频
DISCONNECTED0x1000005连接断开
STOP0x1000006SDK 内部停止(如断流)
RESOLUTION_INFO0x1000007视频分辨率p1=width, p2=height
NO_MEDIADATA0x1000008长时间收不到媒体数据
SWITCH_URL0x1000009正在切换 URL
CAPTURE_IMAGE0x100000A快照结果p1=0 成功,非0 失败
RTSP_STATUS_CODE0x100000BRTSP 状态码上报p1=状态码(如 401)
RECORDER_NEW_FILE0x1000021录像开始写入新文件p3=完整文件路径
RECORDER_FILE_DONE0x1000022一个录像文件写完p3=完整文件路径
START_BUFFERING0x1000081开始缓冲
BUFFERING0x1000082缓冲中p1=进度百分比(0–100)
STOP_BUFFERING0x1000083缓冲结束,恢复播放
DOWNLOAD_SPEED0x1000091下载速度上报见下方说明

DOWNLOAD_SPEED 参数解析

p1 = 下载速度(Byte/s) p2 = 丢包率编码(long): bit31=1 → 高 15 位为前向丢包率 FLR(Q8.8 定点,÷256 得比例) bit15=1 → 低 15 位为综合丢包率 LR(Q8.8 定点,÷256 得比例)

示例解析(已在PlayerInstance.HandleEvent中实现):

// 转换为百分比floatflr=((loss>>16)&0x7FFF)/256.0f*100f;// 前向丢包率 %floatlr=(loss&0x7FFF)/256.0f*100f;// 综合丢包率 %

8. 录像功能

录像流程

StartRecorder(dir) ├─ NT_U3D_CreateFileDirectory(dir) ← 创建目录 ├─ NT_U3D_SetRecorderDirectory(handle, dir) ├─ NT_U3D_SetRecorderFileMaxSize(handle, MB) ├─ NT_U3D_SetRecorderAudioTranscodeAAC(handle, 1) ← 音频转 AAC(重要) └─ NT_U3D_StartRecorder(handle) └─ 触发 RECORDER_NEW_FILE 事件(p3 = 文件路径)

录像与播放的关系

  • 录像和播放共享同一个 SDK 句柄,可以同时进行
  • 单独录像(不播放):理论上支持,但建议同时启动播放保证视频数据正常拉取
  • 停止播放时,若录像仍在进行,录像不会中断

文件切片

recMaxFileSizeMB(默认 500MB)达到后 SDK 自动切片:

  • 触发RECORDER_FILE_DONE(旧文件写完)
  • 触发RECORDER_NEW_FILE(新文件开始)

录像路径建议

// Android 10+ 推荐,无需存储权限stringdir=Path.Combine(Application.persistentDataPath,"Record");// 对应路径:/data/data/<包名>/files/Record 或// /sdcard/Android/data/<包名>/files/Record(外部存储)// 旧版 Android(≤9)可用 sdcard 路径,需权限stringdir="/sdcard/daniulive/Record";

9. 视频渲染原理

帧格式

常量格式使用场景
FORMAT_I4203YUV 4:2:0 Planar软解默认输出
FORMAT_NV214YUV 4:2:0 Semi-Planar(VU 交错)硬解常见
FORMAT_NV125YUV 4:2:0 Semi-Planar(UV 交错)硬解常见

纹理结构

I420(三平面):

yTex_: Texture2D(stride0, height, Alpha8) ← Y 分量 uTex_: Texture2D(stride1, height/2, Alpha8) ← U 分量 vTex_: Texture2D(stride2, height/2, Alpha8) ← V 分量

NV21 / NV12(双平面):

yTex_: Texture2D(stride0, height, Alpha8) ← Y 分量 uTex_: Texture2D(width/2, height/2, RG16) ← UV 交错分量

Shader 属性

Shader 通过以下属性名接收纹理:

_NT_SDK_Y ← Y 纹理 _NT_SDK_U ← U(或 UV)纹理 _NT_SDK_V ← V 纹理(仅 I420 格式使用)

注意:Shader 和 Material 由大牛直播 SDK 提供,不要修改。


附录 A:事件回调格式参考

SDK 通过UnitySendMessage发出的原始消息格式:

"handle,code,param1,param2,param3,param4"

PlayerManager.onNTSmartEvent()中解析:

string[]parts=msg.Split(',');// parts[0] = handle(long,多播放器时用于路由)// parts[1] = code(int,事件 ID)// parts[2] = param1// parts[3] = param2// parts[4] = param3(录像事件中为文件路径)// parts[5] = param4(目前未使用)

附录 B:调用时序图

播放时序

UIController.OnPlayClicked() → PlayerManager.CreatePlayer(url) → PlayerInstance.Open(url, config, goName) → NTPlayerWrapper.Open() → NT_U3D_Open() ← 获取句柄 → NT_U3D_Set_Game_Object() ← 注册事件接收对象 → ApplyConfig() ← 一次性下发所有参数 → PlayerInstance.SetRenderTarget(...) ← 绑定渲染目标 → PlayerInstance.StartPlay() → NTPlayerWrapper.StartPlay() → NT_U3D_StartPlay() ← 开始拉流 每帧: PlayerManager.Update() → PlayerInstance.UpdateFrame() → NT_U3D_GetVideoFrame() ← 拉帧 → UploadTextures() ← 上传 YUV 纹理 事件: SDK → UnitySendMessage("PlayerManager", "onNTSmartEvent", msg) → PlayerInstance.HandleEvent(code, p1, p2, p3)

停止时序

UIController.OnPlayClicked()(再次点击) → PlayerInstance.StopPlay() → NTPlayerWrapper.StopPlay() → NT_U3D_StopPlay() → UIController.ClearRenderView() → 若 !is_recording → PlayerManager.DestroyPlayer() → PlayerInstance.Close() → NTPlayerWrapper.Close() → NT_U3D_StopPlay() (幂等,已停则跳过) → NT_U3D_StopRecorder() (幂等) → NT_U3D_Close() → DisposeTextures()

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

http://www.gsyq.cn/news/1340582.html

相关文章:

  • 从“卖算力”到“卖Token”:换的不是“秤”,是“货”!
  • 文档分析准确率从61%跃升至98.7%的关键转折点(附2024Q2最新Claude-3.5 Sonnet文档理解基准测试对比表)
  • 深入解析Android进程与线程间通信机制:原理、实践与优化
  • 安全IP哪家强|2026 五大主流厂商深度测评与选型指南
  • 【ElevenLabs印尼文语音实战指南】:20年AI语音工程师亲授7大避坑要点与本地化发音优化黄金法则
  • 观察不同时段调用taotoken聚合接口的响应速度差异
  • 《2026浦东5家初高中学科辅导机构横向测评:我帮你把坑踩完了》 - GrowthUME
  • 山东大学软件学院项目实训个人进展6
  • 一文搞懂:Git分支管理与团队协作规范——从GitFlow到GitHub Flow,从rebase到merge,打造高效协作流
  • 绝了!只需输入需求,这几款AI论文工具直接生成毕业论文!
  • MySQL中redo log 和 bin log的本质区别,别再搞混了!
  • 蒙古语TTS准确率仅73%?ElevenLabs 2024Q2基准测试报告曝光:词级准确率91.4%,但需绕过这2个API默认参数坑
  • 2026年福州汽车贴膜合规资质权威测评:4家主流门店横向对比,附避坑指南与选型推荐 - GrowthUME
  • 纯手打却大面积标红?深度测评5款降AIGC工具,送你高效“去机器味”提示词
  • Java继承:不只是extends,你还需要知道这些
  • 【Qt】界面优化(三)盒子模型的介绍和使用,给按钮,复选框,单行输入框设置样式
  • 编写跨部门沟通协作效率监测程序,统计沟通频次耗时,优化职场协作工作流程。
  • 设计个人职场技能成长图谱生成程序,根据岗位自动规划技能学习进阶路线。
  • 某AI漫剧超级工厂AI绘画与分镜自动化生成流水线详细设计方案(WORD)
  • 《CVPR2025-DEIM创新改进项目实战:从原理到部署的深度学习优化全攻略》019、TimeSformer-DEIM与SlowFast-DEIM
  • 《CVPR2025-DEIM创新改进项目实战:从原理到部署的深度学习优化全攻略》018、DeepLab-DEIM与SegFormer-DEIM语义分割优化全记录
  • * LangChain4j中的流式调用
  • Java实战:熵权法原理详解+房产价值评估系统设计(上)—— 构建客观多指标评价模型
  • 【Midjourney毛发质感生成终极指南】:20年AI图像专家亲授7大不可外传的提示词结构与参数微调公式
  • 为OpenClaw智能体工作流配置Taotoken作为稳定的模型供应后端
  • 巨亏47亿,市值5000亿:拆解智谱AI的定价逻辑
  • 初入职场:在琐碎中筑牢测试根基
  • 数据结构笔记(持续更新)
  • Continental CICP1800RB继电器扩展板
  • 长期项目使用Taotoken聚合API的稳定性与容灾感受