Windows平臺Unity下實現camera場景推送RTMP|輕量級RTSP服務|實時錄像

技術背景

我們在對接Unity平臺camera場景采集的時候,除了常規的RTMP推送、錄像外,還有一些開發者,需要能實現輕量級RTSP服務,對外提供個拉流的RTSP URL。

目前我們在Windows平臺Unity下數據源可采集到以下部分:

  • 采集Unity camera場景;
  • 采集攝像頭;
  • 采集屏幕;
  • 采集Unity聲音;
  • 采集麥克風;
  • 采集揚聲器;
  • Unity PCM混音;

對外提供的技術能力有:

  • RTMP直播推送;
  • 輕量級RTSP服務;
  • 實時錄像、暫停|恢復錄像;
  • 實時預覽。

以下錄制下來的MP4文件是采集Unity camera場景,音頻是unity聲音。

Unity平臺實現camera場景實時錄像

技術實現

實際上,在實現Unity平臺音視頻能力之前,我們原生模塊已經有非常成熟的技術積累,Unity下還是調用的原生的推送模塊,不同的是,數據源需要采集Unity的audio、video,然后高效的投遞到底層模塊,底層模塊負責編碼打包,并投遞到RTMP或RTSP服務。

先說支持的音視頻類型:

    public void SelVideoPushType(int type){switch (type){case 0:video_push_type_ = (uint)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_LAYER;    //采集Unity窗體break;case 1:video_push_type_ = (uint)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_CAMERA;   //采集攝像頭break;case 2:video_push_type_ = (uint)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_SCREEN;   //采集屏幕break;case 3:video_push_type_ = (uint)NTSmartPublisherDefine.NT_PB_E_VIDEO_OPTION.NT_PB_E_VIDEO_OPTION_NO_VIDEO; //不采集視頻break;}Debug.Log("SelVideoPushType type: " + type + " video_push_type: " + video_push_type_);}public void SelAudioPushType(int type){switch (type){case 0:audio_push_type_ = (uint)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_EXTERNAL_PCM_DATA;    //采集Unity聲音break;case 1:audio_push_type_ = (uint)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_MIC;  //采集麥克風break;case 2:audio_push_type_ = (uint)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_CAPTURE_SPEAKER;  //采集揚聲器break;case 3:audio_push_type_ = (uint)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_TWO_EXTERNAL_PCM_MIXER;  //兩路Unity AudioClip混音測試break;case 4:audio_push_type_ = (uint)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_NO_AUDIO;   //不采集音頻break;}Debug.Log("SelAudioPushType type: " + type + " audio_push_type: " + audio_push_type_);}

采集音視頻數據:

    private void StartCaptureAvData(){if (audio_push_type_ == (uint)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_EXTERNAL_PCM_DATA|| audio_push_type_ == (uint)NTSmartPublisherDefine.NT_PB_E_AUDIO_OPTION.NT_PB_E_AUDIO_OPTION_TWO_EXTERNAL_PCM_MIXER){PostUnityAudioClipData();}textures_poll_ = new TexturesPool();post_image_worker_ = new PostImageWorker(textures_poll_, publisher_wrapper_);post_worker_thread_ = new Thread(post_image_worker_.run);post_worker_thread_.Start();}private void StopCaptureAvData(){if (post_image_worker_ != null){post_image_worker_.requestStop();post_image_worker_ = null;}if (post_worker_thread_ != null){post_worker_thread_.Join();post_worker_thread_ = null;}if (textures_poll_ != null){textures_poll_.clear();textures_poll_ = null;}StopAudioSource();}

RTMP推送:

    public void btn_start_rtmp_pusher_Click(){if (publisher_wrapper_.IsPushingRtmp()){StopPushRTMP();btn_rtmp_pusher_.GetComponentInChildren<Text>().text = "推送RTMP";return;}String url = rtmp_pusher_url_.text;if (url.Length < 8){publisher_wrapper_.Close();Debug.LogError("請輸入RTMP推送地址");return;}if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsRtspPublisherRunning() && !publisher_wrapper_.IsRecording()){publisher_wrapper_.SetVideoPushType(video_push_type_);publisher_wrapper_.SetAudioPushType(audio_push_type_);}if (!publisher_wrapper_.StartRtmpPusher(url)){Debug.LogError("調用StartPublisher失敗..");return;}btn_rtmp_pusher_.GetComponentInChildren<Text>().text = "停止推送";if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsRtspPublisherRunning() && !publisher_wrapper_.IsRecording()){StartCaptureAvData();coroutine_ = StartCoroutine(OnPostVideo());}}

輕量級RTSP服務相關調用:

    public void btn_rtsp_service_Click(){if (publisher_wrapper_.IsRtspServiceRunning()){publisher_wrapper_.StopRtspService();btn_rtsp_service_.GetComponentInChildren<Text>().text = "啟動RTSP服務";btn_rtsp_publisher_.interactable = false;return;}if (!publisher_wrapper_.StartRtspService()){Debug.LogError("調用StartRtspService失敗..");return;}btn_rtsp_publisher_.interactable = true;btn_rtsp_service_.GetComponentInChildren<Text>().text = "停止RTSP服務";}public void btn_rtsp_publisher_Click(){if (publisher_wrapper_.IsRtspPublisherRunning()){publisher_wrapper_.StopRtspStream();if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsPushingRtmp() && !publisher_wrapper_.IsRecording()){StopCaptureAvData();if (coroutine_ != null){StopCoroutine(coroutine_);coroutine_ = null;}}btn_rtsp_service_.interactable = true;btn_rtsp_publisher_.GetComponentInChildren<Text>().text = "發布RTSP";}else{if (!publisher_wrapper_.IsRtspServiceRunning()){Debug.LogError("RTSP service is not running..");return;}if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsPushingRtmp() && !publisher_wrapper_.IsRecording()){publisher_wrapper_.SetVideoPushType(video_push_type_);publisher_wrapper_.SetAudioPushType(audio_push_type_);}publisher_wrapper_.StartRtspStream();if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsPushingRtmp() && !publisher_wrapper_.IsRecording()){StartCaptureAvData();coroutine_ = StartCoroutine(OnPostVideo());}btn_rtsp_publisher_.GetComponentInChildren<Text>().text = "停止RTSP";btn_rtsp_service_.interactable = false;}}public void btn_get_rtsp_session_numbers_Click(){if (publisher_wrapper_.IsRtspServiceRunning()){btn_get_rtsp_session_numbers_.GetComponentInChildren<Text>().text = "RTSP會話數:" + publisher_wrapper_.GetRtspSessionNumbers();}}

實時錄像調用:

    public void btn_record_Click(){if (publisher_wrapper_.IsRecording()){StopRecord();btn_record_.GetComponentInChildren<Text>().text = "開始錄像";return;}if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsRtspPublisherRunning() && !publisher_wrapper_.IsPushingRtmp()){publisher_wrapper_.SetVideoPushType(video_push_type_);publisher_wrapper_.SetAudioPushType(audio_push_type_);}if (!publisher_wrapper_.StartRecorder()){Debug.LogError("調用StartRecorder失敗..");return;}btn_record_.GetComponentInChildren<Text>().text = "停止錄像";if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsRtspPublisherRunning() && !publisher_wrapper_.IsPushingRtmp()){StartCaptureAvData();coroutine_ = StartCoroutine(OnPostVideo());}}public void StopRecord(){if (!publisher_wrapper_.IsPreviewing() && !publisher_wrapper_.IsRtspPublisherRunning() && !publisher_wrapper_.IsPushingRtmp()){StopCaptureAvData();if (coroutine_ != null){StopCoroutine(coroutine_);coroutine_ = null;}}publisher_wrapper_.StopRecorder();}private void btn_pause_record_Click(){if (!publisher_wrapper_.IsPublisherHandleAvailable()){return;}String btn_pause_rec_text = btn_pause_record_.GetComponentInChildren<Text>().text;if ("暫停錄像" == btn_pause_rec_text){UInt32 ret = publisher_wrapper_.PauseRecorder(true);if ((UInt32)NT.NTSmartPublisherDefine.NT_PB_E_ERROR_CODE.NT_ERC_PB_NEED_RETRY == ret){Debug.LogError("暫停錄像失敗, 請重新嘗試!");return;}else if (NTBaseCodeDefine.NT_ERC_OK == ret){btn_pause_record_.GetComponentInChildren<Text>().text = "恢復錄像";}}else{UInt32 ret = publisher_wrapper_.PauseRecorder(false);if ((UInt32)NT.NTSmartPublisherDefine.NT_PB_E_ERROR_CODE.NT_ERC_PB_NEED_RETRY == ret){Debug.LogError("恢復錄像失敗, 請重新嘗試!");return;}else if (NTBaseCodeDefine.NT_ERC_OK == ret){btn_pause_record_.GetComponentInChildren<Text>().text = "暫停錄像";}}}

實時預覽相關:

    public void btn_preview_Click(){if (btn_preview_.GetComponentInChildren<Text>().text.Equals("本地預覽")){if (!publisher_wrapper_.IsPushingRtmp() && !publisher_wrapper_.IsRtspPublisherRunning() && !publisher_wrapper_.IsRecording()){publisher_wrapper_.SetVideoPushType(video_push_type_);}if (publisher_wrapper_.StartPreview()){btn_preview_.GetComponentInChildren<Text>().text = "停止預覽";}if (!publisher_wrapper_.IsPushingRtmp() && !publisher_wrapper_.IsRtspPublisherRunning() && !publisher_wrapper_.IsRecording()){StartCaptureAvData();coroutine_ = StartCoroutine(OnPostVideo());}}else{if (!publisher_wrapper_.IsPushingRtmp() && !publisher_wrapper_.IsRtspPublisherRunning() && !publisher_wrapper_.IsRecording()){StopCaptureAvData();if (coroutine_ != null){StopCoroutine(coroutine_);coroutine_ = null;}}publisher_wrapper_.StopPreview();btn_preview_.GetComponentInChildren<Text>().text = "本地預覽";}}

總結

Unity平臺下RTMP推送、錄像、輕量級RTSP服務,在虛擬仿真、醫療、教育等場景下,應用非常廣泛。要實現低延遲,除了需要高效率的音視頻數據采集,編碼和數據投遞外,還需要好的直播播放器支持。配合我們的SmartPlayer,可輕松實現毫秒級體驗,滿足絕大多數應用場景技術訴求。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/161746.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/161746.shtml
英文地址,請注明出處:http://en.pswp.cn/news/161746.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

@PostConstruct雖好,請勿亂用

1.問題說明 在日常的業務開發中&#xff0c;有時會利用PostConstruct在容器啟動時執行一些任務。例如&#xff1a; PostConstruct public void init(){System.out.println("service 初始化..............."); }一般情況這沒什么問題&#xff0c;但最近一個同事在做…

ui5使用echart

相關的代碼已經發布到github上。 展示下相關的實現功能 1、柱狀圖-1 2、柱狀圖-2 3.折線圖 4.餅狀圖 如何使用&#xff1a; 使用git clone項目到本地 git clone https://github.com/linhuang0405/com.joker.Zechart找到index.html。在vscode里右鍵選擇Open with Live Serve…

1

【任務 1】私有云服務搭建[10 分] 【題目 1】基礎環境配置[0.5 分] 【題目 2】Yum 源配置[0.5 分] 【題目 3】配置無秘鑰 ssh[0.5 分] 【題目 4】基礎安裝[0.5 分] 【題目 5】數據庫安裝與調優[0.5 分] 【題目 6】Keystone 服務安裝與使用[0.5 分] 【題目 7】Glance 安裝與使用…

BLE通用廣播包

文章目錄 1、藍牙廣播數據格式2、掃描響應數據 1、藍牙廣播數據格式 藍牙廣播包的最大長度是37個字節&#xff0c;其中設備地址占用了6個字節&#xff0c;只有31個字節是可用的。這31個可用的字節又按照一定的格式來組織&#xff0c;被分割為n個AD Structure。如下圖所示&…

npm命令

node -v --查看版本 npm install --安裝npm npm config get registry --查看npm當前鏡像 npm config set registry https://registry.npmmirror.com --設置淘寶鏡像 npm版本管理工具

VS Code 如何搭建C/C++環境

目錄 一、VS Code是什么&#xff1f; 二、VS Code下載和安裝 2.1下載 2.2安裝 2.3環境介紹 三、Vs Code配置C/C環境 3.1下載和配置MinGW-w64編譯器套件 3.1.1下載 3.1.2配置 一、VS Code是什么&#xff1f; 跨平臺&#xff0c;免費且開源的現代輕量級代碼編輯器 Vis…

【MATLAB源碼-第85期】基于farrow結構的濾波器仿真,截止頻率等參數可調。

操作環境&#xff1a; MATLAB 2022a 1、算法描述 Farrow結構是一種用于實現可變數字濾波器的方法&#xff0c;尤其適用于數字信號處理中的采樣率轉換和時變濾波。它通過多項式近似來實現對濾波器系數的平滑變化&#xff0c;使得濾波器具有可變的群延時或其他參數。 Farrow結…

mysql中數據是如何被用B+樹查詢到的

innoDB是按照頁為單位讀寫的 那頁中有很多行數據&#xff0c;是怎么執行查詢的呢&#xff0c;首先我們肯定&#xff0c;是以單向列表形式存儲的&#xff0c;提高了增刪的效率&#xff0c;但是查詢效率低。所以實際上對頁中的行數據進行了優化&#xff0c;能以二分的方式進行查…

Mac Goland無法調試

去github上下載golang的debug工具delve&#xff1a; go-delve/delve?github.com/go-delve/delve/blob/master/Documentation/installation/README.md?編輯 或者: go install github.com/go-delve/delve/cmd/dlvlatest按照他的安裝方式進行安裝&#xff0c;最后會在本地的…

基于北方蒼鷹算法優化概率神經網絡PNN的分類預測 - 附代碼

基于北方蒼鷹算法優化概率神經網絡PNN的分類預測 - 附代碼 文章目錄 基于北方蒼鷹算法優化概率神經網絡PNN的分類預測 - 附代碼1.PNN網絡概述2.變壓器故障診街系統相關背景2.1 模型建立 3.基于北方蒼鷹優化的PNN網絡5.測試結果6.參考文獻7.Matlab代碼 摘要&#xff1a;針對PNN神…

Java面試-框架篇-Mybatis

Java面試-框架篇-Mybatis MyBatis執行流程延遲加載使用及原理一, 二級緩存來源 MyBatis執行流程 讀取MyBatis配置文件: mybatis-config.xml加載運行環境和映射文件構造會話工廠SqlSessionFactory會話工廠創建SqlSession對象(包含了執行SQL語句的所有方法)操作數據庫的接口, Ex…

vue腳手架的基礎搭建過程

MVVM架構 Vue框架底層設計遵循MVVM架構。 Model層&#xff08;M&#xff09;模型層&#xff08;業務邏輯層&#xff09; View層&#xff08;V&#xff09;視圖層 主管UI ViewModel層&#xff08;VM&#xff09; 將項目代碼劃分清晰的層次結構后&#xff0c;非常有利于后期代…

IP地址定位技術發展與未來趨勢

隨著互聯網的快速發展&#xff0c;人們對網絡的需求和依賴程度越來越高。在海量的網絡數據傳輸中&#xff0c;IP地址定位技術作為網絡安全與信息追蹤的重要手段&#xff0c;其精準度一直備受關注。近年來&#xff0c;隨著技術的不斷進步&#xff0c;IP地址定位的精準度得到了顯…

【wireshark】基礎學習

TOC 查詢tcp tcp 查詢tcp握手請求的代碼 tcp.flags.ack 0 確定tcp握手成功的代碼 tcp.flags.ack 1 確定tcp連接請求的代碼 tcp.flags.ack 0 and tcp.flags.syn 1 3次握手后確定發送成功的查詢 tcp.flags.fin 1 查詢某IP對外發送的數據 ip.src_host 192.168.73.134 查詢某…

485 實驗

485(一般稱作 RS485/EIA-485)隸屬于 OSI 模型物理層&#xff0c;是串行通訊的一種。電氣特性規定 為 2 線&#xff0c;半雙工&#xff0c;多點通信的類型。它的電氣特性和 RS-232 大不一樣。用纜線兩端的電壓差值 來表示傳遞信號。RS485 僅僅規定了接受端和發送端的電氣特性。它…

python趣味編程-5分鐘實現一個太空大戰游戲(含源碼、步驟講解)

飛機戰爭游戲系統項目是使用Python編程語言開發的,是一個簡單的桌面應用程序。 Python 中的飛機戰爭游戲使用pygame導入和隨機導入。 Pygame 是一組跨平臺的 Python 模塊,專為編寫視頻游戲而設計。它包括設計用于 Python 編程語言的計算機圖形和聲音庫。

以jar包形式 部署Spring Boot項目

后端部署 當你將Spring Boot項目打包成JAR文件并上傳到服務器時&#xff0c;可以考慮在服務器上創建一些目錄來存放這個JAR文件以及相關的配置文件。以下是一些常見的目錄結構建議&#xff1a; /opt/your-project-name/&#xff1a; 在/opt目錄下創建一個與你的項目名稱相關的…

【word技巧】Word制作試卷,ABCD選項如何對齊?

使用word文件制作試卷&#xff0c;如何將ABCD選項全部設置對齊&#xff1f;除了一直按空格或者Tab鍵以外&#xff0c;還有其他方法嗎&#xff1f;今天分享如何將ABCD選項對齊。 首先&#xff0c;我們打開【替換和查找】&#xff0c;在查找內容輸入空格&#xff0c;然后點擊全部…

省市區編碼sql

CREATE TABLE area (id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主鍵,code varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 編碼,name varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT 名稱,parent_code varchar(64) COLLATE utf8mb4_bin DEFAULT NULL CO…

20個CSS函數-釋放設計創造力和響應能力

20個CSS函數-釋放設計創造力和響應能力 CSS是網頁設計的核心&#xff0c;使開發者和設計者能夠制作出令人嘆為觀止和反應迅速的網頁布局。CSS函數通過引入動態性和多功能性提升了我們的設計能力。在本文中&#xff0c;我們將開始講解20個CSS函數。 1.rgba()&#xff1a;定義顏…