기술나눔

VR 헤드셋은 어떻게 짧은 지연 시간으로 8K RTSP|RTMP 스트림을 재생합니까?

2024-07-12

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

기술적 배경

Unity 플랫폼용 RTSP 및 RTMP 플레이어를 만들 때 일부 회사에서는 파노라마 8K RTSP|RTMP 라이브 스트림과 8K 데이터를 헤드셋에서 재생할 수 있기를 희망하면서 이러한 기술 요구 사항을 제시했으며 이는 헤드셋과 플레이어에 대한 새로운 아이디어를 제안했습니다. 요구 사항에 따라 VR 헤드 장착 장치가 8K RTSP|RTMP 스트리밍 데이터를 여러 측면에서 재생하는 방법에 대해 논의하겠습니다.

1. 플레이어 지원

  1. 호환성 : 먼저 RTSP|RTMP 플레이어는 8K 해상도 비디오 스트리밍을 지원해야 합니다. 즉, 플레이어는 8K 비디오를 디코딩하고 8K 해상도를 지원하는 디스플레이 장치에서 재생할 수 있어야 합니다. 말할 필요도 없이 우리는 이미 이를 지원합니다.
  2. 디코딩 능력 : 8K 비디오 스트림의 대용량 데이터를 처리하려면 플레이어에 강력한 디코딩 기능이 필요합니다. 이를 위해서는 일반적으로 플레이어가 효율적인 디코딩 알고리즘을 사용하고 하드웨어 가속 기능(예: GPU 가속)을 최대한 활용해야 하며, 이를 위해서는 헤드셋이 8K 하드 디코딩을 지원해야 합니다.

2. 네트워크 요구 사항

  1. 대역폭 : 8K 비디오 스트리밍은 실시간 전송을 지원하기 위해 매우 높은 네트워크 대역폭이 필요합니다. 재생 중 지연, 지연, 버퍼링 등의 문제를 피할 수 있을 만큼 네트워크 대역폭이 충분한지 확인하세요. 인트라넷 환경이라면 기본적으로 대역폭 문제를 걱정하지 마세요.
  2. 안정 : 네트워크 연결의 안정성도 매우 중요합니다. 네트워크 연결이 불안정하면 비디오 스트리밍이 중단되거나 품질이 저하될 수 있습니다.

3. 하드웨어 요구사항

  1. 프로세서 및 메모리: VR 헤드셋은 8K 비디오 스트림을 재생하므로 VR 헤드셋 성능에 대한 요구 사항이 매우 높습니다. 예를 들어, Quest3은 좋은 선택입니다.

4. 재생 단계

  1. RTSP 플레이어 선택: 우리의 접근 방식은 하드 디코딩 모드에서 Daniu Live SDK의 기본 RTSP|RTMP 플레이어를 사용하여 디코딩된 YUV 또는 RGB 데이터를 단일화하도록 콜백하는 것입니다. 매우 큰 데이터, 특히 디코딩된 데이터이므로 조건이 허용하는 경우 복사본을 가능한 한 적게 줄여야 합니다.

기술적 실현

이 문서에서는 Daniu Live SDK의 Android 플랫폼 Unity3D RTSP|RTMP 재생 모듈을 예로 들어 설명합니다.

재생 시작:

  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. }

해당 OpenPlayer() 구현은 다음과 같습니다.

  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. }

플레이어 닫기:

  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. }

새로 고침 데이터 업데이트:

  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. }

요약하다

VR 헤드셋이 8K RTSP 또는 RTSP 스트림을 재생해야 하는 경우 하드웨어 및 네트워크 요구 사항이 매우 높으므로 실제 애플리케이션에서 몇 가지 문제에 직면할 수 있습니다. 실제 테스트를 통해 RTSP|RTMP 플레이어와 결합된 Quest3 헤드셋은 Unity에서 8K 비디오 데이터 재생의 밀리초 수준 지연을 달성하여 밸런스 제어와 같은 매우 높은 실시간 요구 사항이 있는 사용 시나리오를 충족할 수 있습니다. 관심 있는 개발자는 이에 대해 논의할 수 있습니다. 나랑 개별적으로.