目标平台AndroidAPI 21支持协议RTSP、RTMP目录概述环境要求工程文件说明快速集成步骤PlayerConfig 配置说明核心 API 说明事件回调说明录像功能视频渲染原理1. 概述本文档描述如何在 Android Unity3D 工程中集成大牛直播 SDK实现 RTSP / RTMP 直播流的拉取、渲染与本地录像功能。架构总览帧渲染模型PULLSDK 不主动推送帧数据而是由 Unity 主线程每帧主动调用GetVideoFrame()拉取避免跨线程写纹理的问题2. 环境要求项目要求Unity2019.4.13f1 或更高Android API最低 21Android 5.0推荐 26构建工具IL2CPP 或 Mono 均可权限INTERNET、WRITE_EXTERNAL_STORAGE录像、READ_EXTERNAL_STORAGEAndroidManifest.xml 权限uses-permissionandroid:nameandroid.permission.INTERNET/uses-permissionandroid:nameandroid.permission.WRITE_EXTERNAL_STORAGEandroid:maxSdkVersion28/uses-permissionandroid:nameandroid.permission.READ_EXTERNAL_STORAGEandroid:maxSdkVersion32/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 SettingsEdit → Project Settings → Player → Android设置项推荐值Minimum API LevelAndroid 5.0 (API 21)Scripting BackendIL2CPP发布/ Mono调试Target ArchitectureARM64必选、ARMv7可选Internet AccessRequiredWrite PermissionExternal (SDCard)如需 sdcard 录像Step 4 — 搭建场景 | 接线 InspectorStep 5 — 授权 SDK在PlayerManager.Awake()初始化后调用NTSmartPlayerAPI.NT_U3D_SetSDKClientKey(your_cid,your_key,0);或在PlayerConfig扩展字段统一管理。Step 6. PlayerConfig 配置说明PlayerManagerGameObject 上通过 Inspector 配置也可在代码中访问PlayerManager.Instance.config。[Serializable]publicclassPlayerConfig{// ── 解码 ──────────────────────────────────────────────────────────────publicboolenableHardwareDecoderfalse;// false 软解兼容性好true 硬解性能好需设备支持// 运行中不可切换需停止播放后修改再重新开始// ── 缓冲 ──────────────────────────────────────────────────────────────[Range(0,8000)]publicintbufferTimeMs0;// 0 最低延迟模式建议直播设 0点播/不稳定网络设 500~2000// ── RTSP 传输 ──────────────────────────────────────────────────────────publicboolrtspUseTcpfalse;// false 优先 UDP丢包时考虑改 true[Range(1,30)]publicintrtspTimeoutSec10;// 连接超时秒publicboolrtspAutoSwitchTcpUdptrue;// UDP 失败自动切 TCP// ── 音频 ──────────────────────────────────────────────────────────────publicboolmutefalse;[Range(0,100)]publicintvolume100;publicbooluseAudioTracktrue;// true AudioTrack 模式推荐// ── 播放行为 ──────────────────────────────────────────────────────────publicboolfastStartuptrue;// 减少起播黑屏时间publicboollowLatencyModefalse;// 超低延迟会增加丢帧概率[Range(0,270)]publicintrotateDegrees0;// 顺时针旋转0/90/180/270// ── 录像 ──────────────────────────────────────────────────────────────publicintrecMaxFileSizeMB500;// 单个录像文件最大体积MB超过后自动切片}6. 核心 API 说明6.1 播放控制通过 UIController 或直接调用// 创建播放器实例内部调用 NT_U3D_Open ApplyConfigPlayerInstanceplayerPlayerManager.Instance.CreatePlayer(rtsp://...);// 绑定渲染目标必须在 StartPlay 前调用player.SetRenderTarget(rawImage,matI420,matNV21,matNV12);// 开始播放player.StartPlay();// 停止播放player.StopPlay();// 销毁播放器Close 句柄 释放纹理PlayerManager.Instance.DestroyPlayer();6.2 实时控制播放中随时可调// 静音 / 取消静音player.SetMuteRealtime(true);// 调节音量0–100player.SetVolumeRealtime(80);6.3 录像控制// 开始录像目录路径SDK 自动创建player.StartRecorder(/sdcard/daniulive/Record);// 或使用 persistentDataPathAndroid 10 推荐无需权限player.StartRecorder(Application.persistentDataPath/Record);// 停止录像player.StopRecorder();// 查询状态boolisRecordingplayer.is_recording;6.4 状态查询boolisPlayingplayer.is_playing;boolisRecordingplayer.is_recording;longhandleplayer.handle;// 0 未初始化intwidthplayer.VideoWidth;intheightplayer.VideoHeight;stringstatusplayer.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视频分辨率p1width, p2heightNO_MEDIADATA0x1000008长时间收不到媒体数据—SWITCH_URL0x1000009正在切换 URL—CAPTURE_IMAGE0x100000A快照结果p10 成功非0 失败RTSP_STATUS_CODE0x100000BRTSP 状态码上报p1状态码如 401RECORDER_NEW_FILE0x1000021录像开始写入新文件p3完整文件路径RECORDER_FILE_DONE0x1000022一个录像文件写完p3完整文件路径START_BUFFERING0x1000081开始缓冲—BUFFERING0x1000082缓冲中p1进度百分比(0–100)STOP_BUFFERING0x1000083缓冲结束恢复播放—DOWNLOAD_SPEED0x1000091下载速度上报见下方说明DOWNLOAD_SPEED 参数解析p1 下载速度Byte/s p2 丢包率编码long bit311 → 高 15 位为前向丢包率 FLRQ8.8 定点÷256 得比例 bit151 → 低 15 位为综合丢包率 LRQ8.8 定点÷256 得比例示例解析已在PlayerInstance.HandleEvent中实现// 转换为百分比floatflr((loss16)0x7FFF)/256.0f*100f;// 前向丢包率 %floatlr(loss0x7FFF)/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 推荐无需存储权限stringdirPath.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-PlanarVU 交错硬解常见FORMAT_NV125YUV 4:2:0 Semi-PlanarUV 交错硬解常见纹理结构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[]partsmsg.Split(,);// parts[0] handlelong多播放器时用于路由// parts[1] codeint事件 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博客