Technology Sharing

How to play 8K RTSP|RTMP stream with low latency in VR headset

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

technical background

When we were developing the RTSP and RTMP players for the Unity platform, a company proposed a technical requirement to play panoramic 8K RTSP|RTMP live streams on head-mounted displays. 8K data puts forward new requirements for head-mounted displays and players. We will explore how VR head-mounted display devices can play 8K RTSP|RTMP stream data from several aspects:

1. Player support

  1. compatibility: First of all, the RTSP|RTMP player needs to support 8K resolution video streaming. This means that the player must be able to decode 8K video and play it on a display device that supports 8K resolution. Needless to say, we already support this.
  2. Decoding capability: The player needs to have powerful decoding capabilities to process the large amount of data in the 8K video stream. This usually requires the player to use efficient decoding algorithms and make full use of hardware acceleration functions (such as GPU acceleration), which requires the headset to support 8K hard decoding.

2. Network requirements

  1. bandwidth: 8K video streaming requires extremely high network bandwidth to support real-time transmission. Make sure the network bandwidth is large enough to avoid problems such as freezing, delay or buffering during playback. If it is in an intranet environment, basically do not worry about bandwidth issues.
  2. stability: The stability of the network connection is also very important. An unstable network connection may cause video streaming interruptions or quality degradation.

3. Hardware Requirements

  1. Processor and Memory: VR headsets playing 8K video streams place very high demands on the performance of VR headsets. For example, Quest3 is a good choice.

4. Playback steps

  1. Select RTSP player: What we do is use the native RTSP|RTMP player of the Danniu Live SDK, in hard decoding mode, and call back the decoded YUV or RGB data to Unity. It should be noted that due to the 8K RTSP|RTMP stream, the amount of data is very large, especially the decoded data. If conditions permit, it is necessary to reduce the copying as much as possible.

Technical realization

This article takes the Android platform Unity3D RTSP|RTMP playback module of Danniu Live SDK as an example:

Start playing:

  1. /*
  2. * SmartPlayerAndroidMono.cs
  3. * Author: daniusdk.com
  4. * QQ:89030985
  5. */
  6. public void Play()
  7. {
  8. if (is_running)
  9. {
  10. Debug.Log("已经在播放。。");
  11. return;
  12. }
  13. //获取输入框的url
  14. string url = input_url_.text.Trim();
  15. if (!url.StartsWith("rtmp://") && !url.StartsWith("rtsp://"))
  16. {
  17. videoUrl = "rtsp://admin:[email protected]:554/h264/ch1/main/av_stream";
  18. }
  19. else
  20. {
  21. videoUrl = url;
  22. }
  23. OpenPlayer();
  24. if ( player_handle_ == 0 )
  25. return;
  26. NT_U3D_Set_Game_Object(player_handle_, game_object_);
  27. /* ++ 播放前参数配置可加在此处 ++ */
  28. int is_using_tcp = 0; //TCP/UDP模式设置
  29. NT_U3D_SetRTSPTcpMode(player_handle_, is_using_tcp);
  30. int is_report = 0;
  31. int report_interval = 1;
  32. NT_U3D_SetReportDownloadSpeed(player_handle_, is_report, report_interval); //下载速度回调
  33. NT_U3D_SetBuffer(player_handle_, play_buffer_time_); //设置buffer time
  34. NT_U3D_SetPlayerLowLatencyMode(player_handle_, is_low_latency_ ? 1 : 0); //设置是否启用低延迟模式
  35. NT_U3D_SetMute(player_handle_, is_mute_ ? 1 : 0); //是否启动播放的时候静音
  36. NT_U3D_SetAudioVolume(player_handle_, cur_audio_volume_); //设置播放音量
  37. NT_U3D_SetVideoDecoderMode(player_handle_, is_hw_decode_ ? 1 : 0); //设置H.264软硬解模式
  38. NT_U3D_SetVideoHevcDecoderMode(player_handle_, is_hw_decode_ ? 1 : 0); //设置H.265软硬解模式
  39. int is_output = 1;
  40. int disable_use_image_planes = 0;
  41. bool is_supports_texture_format = SystemInfo.SupportsTextureFormat(TextureFormat.RG16);
  42. Debug.Log("is_supports_texture_format: " + is_supports_texture_format);
  43. int is_supported_multiple_format = is_supports_texture_format? 1:0;
  44. int max_images = 3;
  45. int buffer_pool_max_size = 0;
  46. NT_U3D_SetImageReaderOutput(player_handle_, is_output, disable_use_image_planes, is_supported_multiple_format, max_images, buffer_pool_max_size); //硬解码image reader
  47. int is_fast_startup = 1;
  48. NT_U3D_SetFastStartup(player_handle_, is_fast_startup); //设置快速启动模式
  49. int rtsp_timeout = 10;
  50. NT_U3D_SetRTSPTimeout(player_handle_, rtsp_timeout); //设置RTSP超时时间
  51. int is_auto_switch_tcp_udp = 1;
  52. NT_U3D_SetRTSPAutoSwitchTcpUdp(player_handle_, is_auto_switch_tcp_udp); //设置TCP/UDP模式自动切换
  53. int is_audiotrack = 1;
  54. NT_U3D_SetAudioOutputType(player_handle_, is_audiotrack); //设置音频输出模式: if 0: 自动选择; if with 1: audiotrack模式
  55. NT_U3D_SetUrl(player_handle_, videoUrl);
  56. /* -- 播放前参数配置可加在此处 -- */
  57. int flag = NT_U3D_StartPlay(player_handle_);
  58. if (flag == DANIULIVE_RETURN_OK)
  59. {
  60. is_need_get_frame_ = true;
  61. Debug.Log("播放成功");
  62. }
  63. else
  64. {
  65. is_need_get_frame_ = false;
  66. Debug.LogError("播放失败");
  67. }
  68. is_running = true;
  69. }

The corresponding OpenPlayer() implementation is as follows:

  1. private void OpenPlayer()
  2. {
  3. if ( java_obj_cur_activity_ == null )
  4. {
  5. Debug.LogError("getApplicationContext is null");
  6. return;
  7. }
  8. player_handle_ = NT_U3D_Open();
  9. if (player_handle_ != 0)
  10. Debug.Log("open success");
  11. else
  12. Debug.LogError("open fail");
  13. }

Close the Player:

  1. private void ClosePlayer()
  2. {
  3. is_need_get_frame_ = false;
  4. is_need_init_texture_ = false;
  5. int flag = NT_U3D_StopPlay(player_handle_);
  6. if (flag == DANIULIVE_RETURN_OK)
  7. {
  8. Debug.Log("停止成功");
  9. }
  10. else
  11. {
  12. Debug.LogError("停止失败");
  13. }
  14. flag = NT_U3D_Close(player_handle_);
  15. if (flag == DANIULIVE_RETURN_OK)
  16. {
  17. Debug.Log("关闭成功");
  18. }
  19. else
  20. {
  21. Debug.LogError("关闭失败");
  22. }
  23. player_handle_ = 0;
  24. NT_U3D_UnInit();
  25. is_running = false;
  26. video_format_ = VideoFrame.FORMAT_UNKNOWN;
  27. video_width_ = 0;
  28. video_height_ = 0;
  29. }

Update refresh data:

  1. private void Update()
  2. {
  3. if (!is_need_get_frame_)
  4. return;
  5. if (player_handle_ == 0)
  6. return;
  7. AndroidJavaObject u3d_video_frame_obj = NT_U3D_GetVideoFrame(player_handle_);
  8. if (u3d_video_frame_obj == null)
  9. {
  10. return;
  11. }
  12. VideoFrame converted_video_frame = ConvertToVideoFrame(u3d_video_frame_obj);
  13. if (converted_video_frame == null)
  14. {
  15. u3d_video_frame_obj.Call("release");
  16. u3d_video_frame_obj = null;
  17. return;
  18. }
  19. if (!is_need_init_texture_)
  20. {
  21. if (converted_video_frame.format_ != video_format_)
  22. {
  23. is_need_init_texture_ = true;
  24. }
  25. else if (converted_video_frame.width_ != video_width_
  26. || converted_video_frame.height_ != video_height_
  27. || converted_video_frame.stride0_ != y_row_bytes_
  28. || converted_video_frame.stride1_ != u_row_bytes_
  29. || converted_video_frame.stride2_ != v_row_bytes_)
  30. {
  31. is_need_init_texture_ = true;
  32. }
  33. }
  34. if (is_need_init_texture_)
  35. {
  36. if (InitYUVTexture(converted_video_frame))
  37. {
  38. is_need_init_texture_ = false;
  39. }
  40. }
  41. UpdateYUVTexture(converted_video_frame);
  42. converted_video_frame.java_frame_obj_ = null;
  43. converted_video_frame = null;
  44. u3d_video_frame_obj.Call("release");
  45. u3d_video_frame_obj = null;
  46. }

Summarize

If VR headsets need to play 8K RTSP or RTSP streams, the hardware and network requirements are very high, so there may be some challenges in actual applications. Through actual tests, on the Quest3 headset, with our RTSP|RTMP player, under Unity, 8K video data playback with millisecond delay can be achieved to meet the use scenarios with very high real-time requirements such as balance control. Interested developers can discuss with me separately.