前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >VR头显Unity下如何实现毫秒级延迟的RTMP或RTSP播放?

VR头显Unity下如何实现毫秒级延迟的RTMP或RTSP播放?

原创
作者头像
音视频牛哥
发布2023-09-12 12:09:54
2950
发布2023-09-12 12:09:54
举报

技术背景

虚拟现实(VR)技术的互动性和沉浸感,为我们提供了一种全新的视觉体验,不过,如果需要实现真正的沉浸式体验,VR播放的延迟问题非常重要。

好多VR场景下,如果存在延迟,用户在移动头部时可能会感觉到画面反应不及时,导致影响视频的流畅度。在VR电影或VR直播中,延迟则可能导致画面和声音的实时同步出现问题,使用户产生晕眩感。

如何尽可能降低Unity环境下的直播播放延迟,是摆在我们面前的大问题,目前,硬件厂商也采用一些更加好的传输技术,例如5G网络等高速网络技术,以降低传输延迟。当然,还可以通过优化传输链路及数据处理流程等方法,提高传输速度和实时性。今天,我们主要介绍的是VR头显下,如何低延迟的实现RTMP或RTSP的播放。

技术实现

说了这么多,如何在VR头显端,尽可能的降低直播播放延迟呢?常用的方法,比如可以用专门的播放器中间件,实现数据的拉流解码会数据回调,在unity下,优化数据处理和渲染。

以大牛直播SDK为例,我们在做Unity下RTMP或RTSP直播播放的时候,大多场景对延迟和资源占有要求非常高,鉴于好多时候,特别是头显终端,性能可能不尽人意,需要播放的视频分辨率码率又高,如何高效率的实现低延迟的RTMP或RTSP播放,是Unity下比较棘手的问题,特别是毫秒级延迟。

以Android平台为例,原生模块拿到RTSP或RTSP流后,解码获取到原始的YUV或RGB数据,然后投递给Unity,Unity下如果需要更低的延迟,数据交互这块,尽可能的高效率,尽可能的减少数据的投递和拷贝。

开始播放:

代码语言:c#
复制
private void OnStartStopBtnClicked()
{
  if (is_running)
  {
    Close();
    btn_start_stop_playback_.GetComponentInChildren<Text>().text = "开始播放";
    btn_decode_mode_.GetComponent<Button>().interactable = true;
    btn_latency_mode_.GetComponent<Button>().interactable = true;
  }
  else
  {
    Play();
    btn_start_stop_playback_.GetComponentInChildren<Text>().text = "停止播放";
    btn_decode_mode_.GetComponent<Button>().interactable = false;
    btn_latency_mode_.GetComponent<Button>().interactable = false;
  }
}

对应Play()实现,这里主要是播放前参数设定,比如采用软解还是硬解码、buffer time是多少,是不是开启低延迟模式等:

代码语言:c#
复制
//Author: daniusdk.com
public void Play()
{
  if (is_running)
  {
    Debug.Log("已经在播放。。");   
    return;
  }
  
  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_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;  
}

开始播放后,native模块,回调yuv/rgb数据,unity3d模块,做相应的绘制处理即可。

代码语言:c#
复制
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)
    return;

  if ( !is_need_init_texture_)
  {
    if (converted_video_frame.width_ != video_width_
        || converted_video_frame.height_ != video_height_
        || converted_video_frame.y_stride_ != y_row_bytes_
        || converted_video_frame.u_stride_ != u_row_bytes_
        || converted_video_frame.v_stride_ != 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 = null;
}

如果需要关闭RTMP或RTSP流,调用对应的ClosePlayer()就好:

代码语言:c#
复制
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_width_ = 0;
  video_height_ = 0;
}

以上以Android平台为例,大概介绍了Unity环境下的RTMP、RTSP直播播放,如果需要延迟和稳定性都靠谱,建议数据接收、解包解码回调yuv/rgb数据在原始native模块处理,然后把解码后的数据,高效率的投递到unity测,实现unity环境下的低延迟RTMP或RTSP,感兴趣的开发者可以酌情了解。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 技术背景
  • 技术实现
相关产品与服务
播放器 SDK
播放器 SDK 是音视频终端 SDK(腾讯云视立方)的子产品之一,提供直播、点播场景的视频播放能力,支持Web/H5、iOS、Android、Flutter平台。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档