VR头显如何低延迟播放8K的RTSP|RTMP流

本文涉及的产品
视觉智能开放平台,分割抠图1万点
视觉智能开放平台,视频资源包5000点
视觉智能开放平台,图像资源包5000点
简介: 本文探讨了在Unity平台上实现VR头显播放8K RTSP/RTMP直播流的技术方案。需确保播放器兼容8K并具高效解码能力,利用GPU加速;网络须稳定且带宽充足;VR头显如Quest 3需拥有高性能处理器与内存。文中以大牛直播SDK为例,详细介绍了播放流程及参数设置,最终实现在Quest 3上毫秒级延迟的8K视频播放,适用于高实时性需求的应用场景。

技术背景

我们在做Unity平台RTSP、RTMP播放器的时候,有公司提出来这样的技术需求,希望在头显播放全景的8K RTSP|RTMP直播流,8K的数据,对头显和播放器,都提出了新的要求,我们从几个方面,探讨下VR头显设备如何播放8K的RTSP|RTMP流数据:

一、播放器支持

  1. 兼容性:首先,RTSP|RTMP播放器需要支持8K分辨率的视频流。这意味着播放器必须能够解码8K视频,并在支持8K分辨率的显示设备上播放,这个不必多说,我们已经支持。
  2. 解码能力:播放器需要具备强大的解码能力,以处理8K视频流中的大量数据。这通常要求播放器使用高效的解码算法,并充分利用硬件加速功能(如GPU加速),这就需要头显支持8K的硬解码。

二、网络要求

  1. 带宽:8K视频流需要极高的网络带宽来支持实时传输。确保网络带宽足够大,以避免播放过程中出现卡顿、延迟或缓冲等问题,如果是内网环境下,基本不要纠结带宽问题。
  2. 稳定性:网络连接的稳定性也非常重要。不稳定的网络连接可能导致视频流中断或质量下降。

三、硬件要求

  1. 处理器与内存:VR头显播放8K的视频流,对VR头显的性能,提了很高的要求,比如说quest3,就是不错的选择。

四、播放步骤

  1. 选择RTSP播放器:我们的做法,是用大牛直播SDK的原生的RTSP|RTMP播放器,硬解码模式,回调解码后的YUV或RGB数据到unity,需要注意的是,由于8K的RTSP|RTMP流,数据量非常大,特别是解码后的数据,条件允许的情况下,需要尽可能少的减少拷贝。

技术实现

本文以大牛直播SDK的Android平台Unity3D RTSP|RTMP播放模块为例:

image.gif 编辑

开始播放:

/*
 * SmartPlayerAndroidMono.cs
 * Author: daniusdk.com
 * QQ:89030985
 */
public void Play()
{
    if (is_running)
    {
        Debug.Log("已经在播放。。");   
        return;
    }
    //获取输入框的url
    string url = input_url_.text.Trim();
    if (!url.StartsWith("rtmp://") && !url.StartsWith("rtsp://"))
    {
        videoUrl = "rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream";
    }
    else
    {
        videoUrl = url;
    }
    OpenPlayer();
    if ( player_handle_ == 0 )
        return;
    NT_U3D_Set_Game_Object(player_handle_, game_object_);
    /* ++ 播放前参数配置可加在此处 ++ */
    int is_using_tcp = 0;        //TCP/UDP模式设置
    NT_U3D_SetRTSPTcpMode(player_handle_, is_using_tcp);
    int is_report = 0;
    int report_interval = 1;
    NT_U3D_SetReportDownloadSpeed(player_handle_, is_report, report_interval);  //下载速度回调
    NT_U3D_SetBuffer(player_handle_, play_buffer_time_);                        //设置buffer time
    NT_U3D_SetPlayerLowLatencyMode(player_handle_, is_low_latency_ ? 1 : 0);    //设置是否启用低延迟模式
    NT_U3D_SetMute(player_handle_, is_mute_ ? 1 : 0);                           //是否启动播放的时候静音
    NT_U3D_SetAudioVolume(player_handle_, cur_audio_volume_);                   //设置播放音量
    NT_U3D_SetVideoDecoderMode(player_handle_, is_hw_decode_ ? 1 : 0);          //设置H.264软硬解模式
    NT_U3D_SetVideoHevcDecoderMode(player_handle_, is_hw_decode_ ? 1 : 0);          //设置H.265软硬解模式
    int is_output = 1;
    int disable_use_image_planes = 0;
    bool is_supports_texture_format = SystemInfo.SupportsTextureFormat(TextureFormat.RG16);
    Debug.Log("is_supports_texture_format: " + is_supports_texture_format);
    int is_supported_multiple_format = is_supports_texture_format? 1:0;
    int max_images = 3;
    int buffer_pool_max_size = 0;
    NT_U3D_SetImageReaderOutput(player_handle_, is_output, disable_use_image_planes, is_supported_multiple_format, max_images, buffer_pool_max_size);  //硬解码image reader
    int is_fast_startup = 1;
    NT_U3D_SetFastStartup(player_handle_, is_fast_startup);                     //设置快速启动模式
    int rtsp_timeout = 10;
    NT_U3D_SetRTSPTimeout(player_handle_, rtsp_timeout);                        //设置RTSP超时时间
    int is_auto_switch_tcp_udp = 1;
    NT_U3D_SetRTSPAutoSwitchTcpUdp(player_handle_, is_auto_switch_tcp_udp);    //设置TCP/UDP模式自动切换
    int is_audiotrack = 1;
    NT_U3D_SetAudioOutputType(player_handle_, is_audiotrack);                   //设置音频输出模式: if 0: 自动选择; if with 1: audiotrack模式
    NT_U3D_SetUrl(player_handle_, videoUrl);
    /* -- 播放前参数配置可加在此处 -- */
    int flag = NT_U3D_StartPlay(player_handle_);
    if (flag  == DANIULIVE_RETURN_OK)
    {
        is_need_get_frame_ = true;
        Debug.Log("播放成功");
    }
    else
    {
        is_need_get_frame_ = false;
        Debug.LogError("播放失败");
    }
    is_running = true;  
}

image.gif

对应的OpenPlayer()实现如下:

private void OpenPlayer()
{
    if ( java_obj_cur_activity_ == null )
    {
        Debug.LogError("getApplicationContext is null");
        return;
    }
    player_handle_ = NT_U3D_Open();
    if (player_handle_ != 0)
        Debug.Log("open success");
    else
        Debug.LogError("open fail");
}

image.gif

关闭Player:

private void ClosePlayer()
{
    is_need_get_frame_ = false;
    is_need_init_texture_ = false;
    int flag = NT_U3D_StopPlay(player_handle_);
    if (flag == DANIULIVE_RETURN_OK)
    {
        Debug.Log("停止成功");
    }
    else
    {
        Debug.LogError("停止失败");
    }
    flag = NT_U3D_Close(player_handle_);
    if (flag == DANIULIVE_RETURN_OK)
    {
        Debug.Log("关闭成功");
    }
    else
    {
        Debug.LogError("关闭失败");
    }
    player_handle_ = 0;
    NT_U3D_UnInit();
    is_running = false;
    video_format_ = VideoFrame.FORMAT_UNKNOWN;
    video_width_ = 0;
    video_height_ = 0;
}

image.gif

Update刷新数据:

private void Update()
{
    if (!is_need_get_frame_)
        return;
    if (player_handle_ == 0)
        return;
    AndroidJavaObject u3d_video_frame_obj = NT_U3D_GetVideoFrame(player_handle_);
    if (u3d_video_frame_obj == null)
    {
        return;
    }
    VideoFrame converted_video_frame = ConvertToVideoFrame(u3d_video_frame_obj);
    if (converted_video_frame == null)
    {
        u3d_video_frame_obj.Call("release");
       u3d_video_frame_obj = null;
       return;
    }
    if (!is_need_init_texture_)
    {
        if (converted_video_frame.format_ != video_format_)
        {
            is_need_init_texture_ = true;
        }
        else if (converted_video_frame.width_ != video_width_
            || converted_video_frame.height_ != video_height_
            || converted_video_frame.stride0_ != y_row_bytes_
            || converted_video_frame.stride1_ != u_row_bytes_
            || converted_video_frame.stride2_ != v_row_bytes_)
        {
            is_need_init_texture_ = true;
        }
    }
    if (is_need_init_texture_)
    {
        if (InitYUVTexture(converted_video_frame))
        {
            is_need_init_texture_ = false;
        }
    }
    UpdateYUVTexture(converted_video_frame);
    converted_video_frame.java_frame_obj_ = null;
    converted_video_frame = null;
    u3d_video_frame_obj.Call("release");
    u3d_video_frame_obj = null;
}

image.gif

总结

VR头显如果需要播放8K的RTSP或RTSP流,对硬件和网络的要求非常高,因此在实际应用中可能会遇到一些挑战。通过实际测试,在quest3头显,配合我们的RTSP|RTMP播放器,在unity下,可以实现毫秒级延迟的8K视频数据播放,以满足平衡操控等对实时性要求非常高的使用场景,感兴趣的开发者,可以单独跟我探讨。

相关文章
|
2月前
|
编解码 vr&ar 图形学
|
1月前
|
编解码 vr&ar 图形学
超时空穿越!揭秘如何在VR头显端实现毫秒级低延迟的RTSP|RTMP播放,打造沉浸式直播新纪元!
本文详细介绍了如何在VR头显端实现低延迟的RTSP或RTMP播放。首先,确保开发环境已安装Unity编辑器及相关插件。接着,通过初始化客户端、解码视频数据并渲染到VR头显屏幕,实现流畅的视频播放。最后,提供了优化低延迟的方法,包括硬件加速、减少缓冲区大小和选择合适的编解码器。示例代码展示了具体实现步骤。
27 1
|
4月前
|
编解码 网络协议 vr&ar
Android平台下VR头显如何低延迟播放4K以上超高分辨率RTSP|RTMP流
这段内容讲述了VR头显中实现高分辨率视频播放的技术背景与实现方法,并强调了其重要性。高分辨率对于提升VR体验至关重要,它能提供更清晰的画面、增强沉浸感、补偿透镜放大效应,并维持宽广视场角下的图像质量。文中提到的大牛直播SDK具备极低的延迟(200-400ms),支持多种协议与格式,并具有丰富的功能特性,如多实例播放、事件回调、视频及音频格式支持等。此外,提供了基于Unity的播放器示例代码,展示了如何配置播放参数并开始播放。最后,作者指出此类技术在远程控制、虚拟仿真等应用场景中的重要意义。
|
4月前
|
编解码 vr&ar C#
### 超时空穿越!揭秘如何在VR头显端实现毫秒级低延迟的RTSP|RTMP播放,打造沉浸式直播新纪元!
【8月更文挑战第14天】随着VR技术进步,VR头显在直播领域的应用日益广泛。为提升用户体验,本文介绍如何在VR头显上实现低延迟的RTSP/RTMP播放,包括环境搭建、依赖引入、客户端初始化、视频解码与渲染及优化技巧,并提供C#示例代码,帮助开发者快速上手,打造流畅直播体验。
42 2
|
新零售 编解码 vr&ar
VR头显价格天差地别,究竟哪一款最适合你?
盘点时下比较火热的VR头显的价格,参考个人收入看看可以入手哪些VR硬件设备? 2016年应该算得上是VR认知的元年,越来越多的普通用户开始知道虚拟现实这个概念,不过大部分人可能会在电影院旁的蛋椅或者一些线下体验店里,第一次接触到这个新兴的硬件设备。
1354 0
|
新零售 编解码 vr&ar
VR头显价格天差地别,究竟哪一款最适合你?
盘点时下比较火热的VR头显的价格,参考个人收入看看可以入手哪些VR硬件设备? 2016年应该算得上是VR认知的元年,越来越多的普通用户开始知道虚拟现实这个概念,不过大部分人可能会在电影院旁的蛋椅或者一些线下体验店里,第一次接触到这个新兴的硬件设备。
1607 0
|
6月前
|
人工智能 编解码 5G
虚拟现实(VR)与增强现实(AR)的融合:开启全新交互时代
【6月更文挑战第17天】虚拟现实(VR)与增强现实(AR)融合成混合现实(MR),打造全新交互体验。MR结合VR的沉浸感和AR的现实增强,应用于教育、游戏、设计和营销,带来创新教学方式、沉浸式游戏体验和高效设计工具。尽管面临技术挑战,随着5G和AI的发展,MR有望引领未来交互的革命。
|
6月前
|
传感器 数据可视化 安全
【虚拟现实】二、主要的AR/VR硬件设备
【虚拟现实】二、主要的AR/VR硬件设备
102 3
|
3月前
|
5G 测试技术 语音技术
5G赋能沉浸式体验:VR/AR时代的网络基石
5G赋能沉浸式体验:VR/AR时代的网络基石
83 1
|
4月前
|
编解码 vr&ar 芯片
VR与AR:未来的科技趋势
【8月更文挑战第26天】VR与AR技术正以前所未有的速度发展,并在市场规模、技术创新、应用场景以及竞争格局等方面展现出强劲的增长潜力。随着技术的不断进步和应用场景的不断拓展,VR/AR/MR技术将为人们带来更加丰富的虚拟体验和更加便捷的生活方式。未来,我们有理由相信,VR与AR技术将在多个领域实现更广泛的应用和更深入的融合,成为推动社会进步的重要力量。

热门文章

最新文章