Windows平台Unity3d下如何同时播放多路RTSP或RTMP流

简介: 好多开发者在做AR、VR或者教育类产品时,苦于如何在windows平台构建一个稳定且低延迟的RTSP或者RTMP播放器,如果基于Unity3d完全重新开发一个播放器,代价大、而且周期长,不适合快速出产品,我们认为当前最好的方式就是集成现有Native平台上成熟稳定播放器,回调rgb/yuv数据到上层,上层做绘制即可。

好多开发者在做AR、VR或者教育类产品时,苦于如何在windows平台构建一个稳定且低延迟的RTSP或者RTMP播放器,如果基于Unity3d完全重新开发一个播放器,代价大、而且周期长,不适合快速出产品,我们认为当前最好的方式就是集成现有Native平台上成熟稳定播放器,回调rgb/yuv数据到上层,上层做绘制即可。


废话不多说,以Windows平台多路播放为例:

1.Native播放器SDK支持吐RGB/YUV420/NV12等其中的一种未压缩的图像格式

比如Windows平台,我们回调YUV上来(NT_SP_E_VIDEO_FRAME_FROMAT_I420),本文以调用大牛直播SDK(Github)的Windows平台RTSP、RTMP播放器SDK为例,具体代码如下:

    public void Play(int sel)
    {
        if (videoctrl[sel].is_running)
        {
            Debug.Log("已经在播放..");
            return;
        }
        lock (videoctrl[sel].frame_lock_)
        {
            videoctrl[sel].cur_video_frame_ = null;
        }
        OpenPlayer(sel);
        if (videoctrl[sel].player_handle_ == IntPtr.Zero)
            return;
        //设置播放URL
        NTSmartPlayerSDK.NT_SP_SetURL(videoctrl[sel].player_handle_, videoctrl[sel].videoUrl);
        /* ++ 播放前参数配置可加在此处 ++ */
        int play_buffer_time_ = 100;
        NTSmartPlayerSDK.NT_SP_SetBuffer(videoctrl[sel].player_handle_, play_buffer_time_);                 //设置buffer time
        int is_using_tcp = 0;        //TCP模式
        NTSmartPlayerSDK.NT_SP_SetRTSPTcpMode(videoctrl[sel].player_handle_, is_using_tcp);
        int timeout = 10;
        NTSmartPlayerSDK.NT_SP_SetRtspTimeout(videoctrl[sel].player_handle_, timeout);
        int is_auto_switch_tcp_udp = 1;
        NTSmartPlayerSDK.NT_SP_SetRtspAutoSwitchTcpUdp(videoctrl[sel].player_handle_, is_auto_switch_tcp_udp);
        Boolean is_mute_ = false;
        NTSmartPlayerSDK.NT_SP_SetMute(videoctrl[sel].player_handle_, is_mute_ ? 1 : 0);                    //是否启动播放的时候静音
        int is_fast_startup = 1;
        NTSmartPlayerSDK.NT_SP_SetFastStartup(videoctrl[sel].player_handle_, is_fast_startup);              //设置快速启动模式
        Boolean is_low_latency_ = false;
        NTSmartPlayerSDK.NT_SP_SetLowLatencyMode(videoctrl[sel].player_handle_, is_low_latency_ ? 1 : 0);    //设置是否启用低延迟模式
        //设置旋转角度(设置0, 90, 180, 270度有效,其他值无效)
        int rotate_degrees = 0;
        NTSmartPlayerSDK.NT_SP_SetRotation(videoctrl[sel].player_handle_, rotate_degrees);
    int volume = 100;
    NTSmartPlayerSDK.NT_SP_SetAudioVolume(videoctrl[sel].player_handle_, volume); //设置播放音量, 范围是[0, 100], 0是静音,100是最大音量, 默认是100
        // 设置上传下载报速度
        int is_report = 0;
        int report_interval = 1;
        NTSmartPlayerSDK.NT_SP_SetReportDownloadSpeed(videoctrl[sel].player_handle_, is_report, report_interval);
        /* -- 播放前参数配置可加在此处 -- */
        //video frame callback (YUV/RGB)
        videoctrl[sel].video_frame_call_back_ = new SP_SDKVideoFrameCallBack(NT_SP_SetVideoFrameCallBack);
        NTSmartPlayerSDK.NT_SP_SetVideoFrameCallBack(videoctrl[sel].player_handle_, (Int32)NT.NTSmartPlayerDefine.NT_SP_E_VIDEO_FRAME_FORMAT.NT_SP_E_VIDEO_FRAME_FROMAT_I420, window_handle_, videoctrl[sel].video_frame_call_back_);
        UInt32 flag = NTSmartPlayerSDK.NT_SP_StartPlay(videoctrl[sel].player_handle_);
        if (flag == DANIULIVE_RETURN_OK)
        {
            videoctrl[sel].is_need_get_frame_ = true;
            Debug.Log("播放成功");
        }
        else
        {
            videoctrl[sel].is_need_get_frame_ = false;
            Debug.LogError("播放失败");
        }
        videoctrl[sel].is_running = true;
    }

2. 处理回调上来的数据

    private void SDKVideoFrameCallBack(UInt32 status, IntPtr frame, int sel)
    {
        //这里拿到回调frame,进行相关操作
        NT_SP_VideoFrame video_frame = (NT_SP_VideoFrame)Marshal.PtrToStructure(frame, typeof(NT_SP_VideoFrame));
        VideoFrame  u3d_frame = new VideoFrame();
        u3d_frame.width_  = video_frame.width_;
        u3d_frame.height_ = video_frame.height_;
        u3d_frame.timestamp_ = (UInt64)video_frame.timestamp_;
        int d_y_stride = video_frame.width_;
        int d_u_stride = (video_frame.width_ + 1) / 2;
        int d_v_stride = d_u_stride;
        int d_y_size = d_y_stride * video_frame.height_;
        int d_u_size = d_u_stride * ((video_frame.height_ + 1) / 2);
        int d_v_size = d_u_size;
        int u_v_height = ((u3d_frame.height_ + 1) / 2);
        u3d_frame.y_stride_ = d_y_stride;
        u3d_frame.u_stride_ = d_u_stride;
        u3d_frame.v_stride_ = d_v_stride;
        u3d_frame.y_data_ = new byte[d_y_size];
        u3d_frame.u_data_ = new byte[d_u_size];
        u3d_frame.v_data_ = new byte[d_v_size];
        CopyFramePlane(u3d_frame.y_data_, d_y_stride,
            video_frame.plane0_, video_frame.stride0_, u3d_frame.height_);
        CopyFramePlane(u3d_frame.u_data_, d_u_stride,
           video_frame.plane1_, video_frame.stride1_, u_v_height);
        CopyFramePlane(u3d_frame.v_data_, d_v_stride,
           video_frame.plane2_, video_frame.stride2_, u_v_height);
        lock (videoctrl[sel].frame_lock_ )
        {
            videoctrl[sel].cur_video_frame_ = u3d_frame;
            //Debug.LogError("sel: " + sel + " w:" + u3d_frame.width_ + "h:" + u3d_frame.height_);
        }
    }

3.Unity3D创建相应的RGB/YUV420等Shader,获取图像数据来填充纹理即可

    private void UpdateYUVTexture(VideoFrame video_frame, int sel)
    {
        if (video_frame.y_data_ == null || video_frame.u_data_ == null || video_frame.v_data_ == null)
        {
            Debug.Log("video frame with null..");
            return;
        }
        if (videoctrl[sel].yTexture_ != null)
        {
            videoctrl[sel].yTexture_.LoadRawTextureData(video_frame.y_data_);
            videoctrl[sel].yTexture_.Apply();
        }
        if (videoctrl[sel].uTexture_ != null)
        {
            videoctrl[sel].uTexture_.LoadRawTextureData(video_frame.u_data_);
            videoctrl[sel].uTexture_.Apply();
        }
        if (videoctrl[sel].vTexture_ != null)
        {
            videoctrl[sel].vTexture_.LoadRawTextureData(video_frame.v_data_);
            videoctrl[sel].vTexture_.Apply();
        }
    }

4. 具体播放效果如下

20210312140514272.png

总结

Unity3d下,做多路播放的话,首先确保调用的拉流解码数据的模块具备回调yuv/rgb数据能力,回上来后,再上层直接刷新显示即可,是不是没有你想的那么复杂?

相关文章
|
2月前
|
XML C# 数据格式
掌握了在Windows平台上查看DLL依赖的方法
掌握了在Windows平台上查看DLL依赖的方法
304 4
|
2月前
|
编解码 vr&ar 图形学
Unity下如何实现低延迟的全景RTMP|RTSP流渲染
随着虚拟现实技术的发展,全景视频成为新的媒体形式。本文详细介绍了如何在Unity中实现低延迟的全景RTMP或RTSP流渲染,包括环境准备、引入依赖、初始化客户端、解码与渲染、优化低延迟等步骤,并提供了具体的代码示例。适用于远程教育、虚拟旅游等实时交互场景。
77 5
|
3月前
|
监控 Windows
Windows平台RTSP|RTMP播放器如何实时调节音量
我们在做Windows平台RTSP、RTMP播放器的时候,有这样的技术需求,特别是多路监控的时候,并不是每一路audio都需要播放出来的,所以,这时候,需要有针对音量调节的设计
|
1月前
|
编解码 vr&ar 图形学
Unity下如何实现低延迟的全景RTMP|RTSP流渲染
随着虚拟现实技术的发展,全景视频逐渐成为新的媒体形式。本文详细介绍了如何在Unity中实现低延迟的全景RTMP或RTSP流渲染,包括环境准备、引入依赖、初始化客户端、解码与渲染、优化低延迟等步骤,并提供了具体的代码示例。适用于远程教育、虚拟旅游等实时交互场景。
32 2
|
2月前
|
NoSQL Shell MongoDB
Windows 平台安装 MongoDB
10月更文挑战第10天
63 0
Windows 平台安装 MongoDB
|
3月前
|
监控 C# 块存储
Windows平台RTSP|RTMP播放器如何叠加OSD文字
做Windows平台RTSP|RTMP播放器的时候,特别是多路播放场景下,开发者希望可以给每一路RTSP或RTMP流添加个额外的OSD台标,以区分不同的设备信息(比如添加摄像头所在位置),本文主要探讨,如何动态添加OSD台标。
Windows平台RTSP|RTMP播放器如何叠加OSD文字
|
3月前
|
Linux Android开发 iOS开发
Windows平台RTSP|RTMP播放器如何实现实时录像功能
Windows平台RTSP、RTMP播放器实时录像接口设计,实际上,除了Windows平台,我们Linux、Android、iOS平台也是一样的设计,单纯的录像模块,如果做的全面,也不是一两个接口可以搞定的
|
2月前
|
并行计算 开发工具 异构计算
在Windows平台使用源码编译和安装PyTorch3D指定版本
【10月更文挑战第6天】在 Windows 平台上,编译和安装指定版本的 PyTorch3D 需要先安装 Python、Visual Studio Build Tools 和 CUDA(如有需要),然后通过 Git 获取源码。建议创建虚拟环境以隔离依赖,并使用 `pip` 安装所需库。最后,在源码目录下运行 `python setup.py install` 进行编译和安装。完成后即可在 Python 中导入 PyTorch3D 使用。
273 0
|
3月前
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
159 0
|
4月前
|
开发者 图形学 API
从零起步,深度揭秘:运用Unity引擎及网络编程技术,一步步搭建属于你的实时多人在线对战游戏平台——详尽指南与实战代码解析,带你轻松掌握网络化游戏开发的核心要领与最佳实践路径
【8月更文挑战第31天】构建实时多人对战平台是技术与创意的结合。本文使用成熟的Unity游戏开发引擎,从零开始指导读者搭建简单的实时对战平台。内容涵盖网络架构设计、Unity网络API应用及客户端与服务器通信。首先,创建新项目并选择适合多人游戏的模板,使用推荐的网络传输层。接着,定义基本玩法,如2D多人射击游戏,创建角色预制件并添加Rigidbody2D组件。然后,引入网络身份组件以同步对象状态。通过示例代码展示玩家控制逻辑,包括移动和发射子弹功能。最后,设置服务器端逻辑,处理客户端连接和断开。本文帮助读者掌握构建Unity多人对战平台的核心知识,为进一步开发打下基础。
153 0