Kurento實戰之四:應用開發指南
歡迎訪問我的GitHub
http://github.com/zq2599/blog_demos
內容:所有原創文章分類彙總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等;
本篇概覽
- 本文是《Kurento實戰》的第四篇,前面的文章中,咱們先部署KMS再啟動官方demo,還把Kurento的重要概念都分類學習過,接下來要開始應用開發了;
- 本文的主要內容是分析官方的<font color="blue">kurento-hello-world</font>專案,瞭解Kurento應用開發的基本流程和知識點,本文使用的程式碼是官方釋出的6.15.0版本,地址:http://github.com/Kurento/kurento-tutorial-java/archive/6.15.0.zip
- 閱讀程式碼時,如果能從整體上將劃分清楚功能模組,再有針對性的逐個攻破細節,將會更高效的學習和理解原始碼,接下來咱們就按照Kurento官方的標準套路去拆分並逐個攻破;
如何劃分功能模組
按照不同的職責劃分,整個程式碼被拆分為三部分:
- WebSocket相關:WebSocket相關的通用處理,例如連線建立、關閉、異常的回撥,業務邏輯的分發等;
- WebRTC信令相關:ICE、SDP相關的處理;
- 業務邏輯:如果說1和2代表的是WebRTC的通用處理,那麼剩下的就是如何使用Kurento來實現業務需求了,這部分的主要內容是業務應用使用Kurento官方client和KMS互動,控制KMS為端側提供服務,互動方式如下圖:
- 按照上述方式將程式碼做好拆分,劃定邊界,不論是閱讀官方demo還是自己開發應用,都能條理清晰的應對,接下來一起學習官方的hello-world原始碼,看看一個完整的Kurento應用是如何開發出來的
WebSocket相關
最簡單的邏輯應該是通用的WebSocket處理了,咱們先看這部分,複雜的稍後再說,Handler類中和WebSockert相關的邏輯如下:
- 繼承自TextWebSocketHandler(只處理text型別的資料,對於二進位制資料直接關閉會話);
- 重寫afterConnectionEstablished:WebSocket連線建立的回撥,只打了一行日誌;
- 重寫handleTransportError:WebSocket發生異常時候的回撥,僅關閉WebSocketSession;
- 重寫afterConnectionClosed:不論WebSocket是正常關閉還是發生異常,此方法都會執行,邏輯也很簡單,就是呼叫stop方法,這個方法是用來釋放KMS資源的,有好幾處都會呼叫,我們留到稍後和其他處理KMS的地方一起講;
- WebSockert部分最重要的程式碼是handleTextMessage方法,裡面是收到前端資料時的處理邏輯:先把資料轉為JsonObject物件,此物件的messageId欄位有四種值,每一種id及其對應的處理方法如下表格所示:
messageId | 處理方法 | 說明 |
---|---|---|
PROCESS_SDP_OFFER | handleProcessSdpOffer | 收到前端SDPOffer資料後的處理邏輯 |
ADD_ICE_CANDIDATE | handleAddIceCandidate | 收到前端ICE資料後的處理邏輯 |
STOP | handleStop | HashMap刪除使用者資料,再遠端呼叫MediaPipeline.release |
ERROR | handleError | HashMap刪除使用者資料,再遠端呼叫MediaPipeline.release |
- 並不是所有的應用都需要重寫上訴全部程式碼,還是以實際需求出發決定是否要重寫,以<font color="blue">kurento-one2one-call</font>專案為例,只重寫了handleTextMessage和afterConnectionClosed,其他的使用父類的即可,如下圖:
7. 還有一個傳送訊息到瀏覽器側的sendMessage方法,以及傳送錯誤資訊的sendError方法;
信令相關
- <font color="blue">kurento-hello-world</font>應用的功能是和KMS實現實時音視訊通訊,因此WebRTC標準的信令處理是必不可少的,可惜Kurento官方並沒有對信令處理做太多封裝(也可能是信令和不同的業務處理邏輯都不一樣,導致不好抽象),結果就是一堆信令處理的程式碼散落在業務程式碼中;
- 就算業務和信令的處理程式碼同時出現在Handler類中,只要熟悉WebRTC的信令處理流程,也很容易讀懂程式碼,下圖結合了WebRTC標準的信令處理流程,對前端和服務端的程式碼串聯在一起就行分析,左邊是瀏覽器上執行的js程式碼,右邊是服務端,這些程式碼都用紅色箭頭標識了處於WebRTC信令處理流程的具體位置,至此,整個流程都清晰的展現出來:
- 如果您在電腦或手機上看上圖覺得模糊,請下載原始檔案,用<font color="blue">draw.io</font>開啟,檔案所在目錄是:http://github.com/zq2599/blog_demos/tree/master/files ,檔名為<font color="red">helloworld-flow.drawio</font>
- 上圖列出了信令相關的所有程式碼,等到看完這些,剩下的就是業務程式碼了,也就是圖中紫色部分的<font color="blue">handleProcessSdpOffer</font>方法;
業務相關
- <font color="blue">kurento-hello-world</font>應用是把本地攝像頭和麥克風資料傳到KMS,再從KMS取得這些資料在頁面展示,先看看官方是如何描述KMS pipeline的:
- 從上圖可見pipeline邏輯非常簡單:只有一個WebRtcEndpoint,把自己的Src和Sink接上就完成了,咱們來看看對應的程式碼,在方法handleProcessSdpOffer中:
// 建立pipeline
final MediaPipeline pipeline = kurento.createMediaPipeline();
user.setMediaPipeline(pipeline);
// 建立webRtcEndpoint
final WebRtcEndpoint webRtcEp =
new WebRtcEndpoint.Builder(pipeline).build();
user.setWebRtcEndpoint(webRtcEp);
// 自己的sink連線上自己的src
webRtcEp.connect(webRtcEp);
// ---- Endpoint configuration
String sdpOffer = jsonMessage.get("sdpOffer").getAsString();
// 註冊各類監聽,例如媒體資源狀態變化、ICE變化等
// 通過websocket回覆SDP Offer
initWebRtcEndpoint(session, webRtcEp, sdpOffer);
log.info("[Handler::handleStart] New WebRtcEndpoint: {}",
webRtcEp.getName());
// ---- Endpoint startup
// 取得ICE資訊
startWebRtcEndpoint(webRtcEp);
- 再來看看停止WebRtc的stop方法,其實就是向KMS傳送了release指令:
private void stop(final WebSocketSession session) {
// Remove the user session and release all resources
final UserSession user = users.remove(session.getId());
if (user != null) {
MediaPipeline mediaPipeline = user.getMediaPipeline();
if (mediaPipeline != null) {
log.info("[Handler::stop] Release the Media Pipeline");
mediaPipeline.release();
}
}
}
小結
以上就是整個<font color="blue">kurento-hello-world</font>的原始碼分析,整個工程的程式碼在拆分後再分析時,變得異常清晰和簡單:
- WebSocket和常規的java開發無異,向標準靠攏即可;
- WebRTC相關程式碼佔了較大比重,但是嚴格遵循了標準的信令流程,只要熟悉WebRTC就很容易閱讀和理解;
- 業務邏輯其實是和業務需求相關聯的,這裡需要熟悉KMS提供的能力,才能充分發揮KMS的例項,而pipeline編排和各個element的使用,也會是咱們後面文章的重點,用好這些element,打磨出更強大靈活的服務;
你不孤單,欣宸原創一路相伴
歡迎關注公眾號:程式設計師欣宸
微信搜尋「程式設計師欣宸」,我是欣宸,期待與您一同暢遊Java世界... http://github.com/zq2599/blog_demos
「其他文章」
- 瀏覽器上寫程式碼,4核8G微軟伺服器免費用,Codespaces真香
- Java擴充套件Nginx之三:基礎配置項
- Java擴充套件Nginx之一:你好,nginx-clojure
- JavaCV的攝像頭實戰之十四:口罩檢測
- JavaCV人臉識別三部曲之二:訓練
- JavaCV人臉識別三部曲之一:視訊中的人臉儲存為圖片
- JavaCV的攝像頭實戰之八:人臉檢測
- 超詳細的編碼實戰,讓你的springboot應用識別圖片中的行人、汽車、狗子、喵星人(JavaCV YOLO4)
- Java應用日誌如何與Jaeger的trace關聯
- Spring Cloud Gateway實戰之五:內建filter
- Spring Cloud Gateway的斷路器(CircuitBreaker)功能
- Java版流媒體編解碼和影象處理(JavaCPP FFmpeg)
- DL4J實戰之六:圖形化展示訓練過程
- 純淨Ubuntu16安裝CUDA(9.1)和cuDNN
- disruptor筆記之六:常見場景
- Spring Cloud Gateway過濾器精確控制異常返回(分析篇)
- disruptor筆記之四:事件消費知識點小結
- disruptor筆記之二:Disruptor類分析
- disruptor筆記之一:快速入門
- Spring Native實戰(暢快體驗79毫秒啟動springboot應用)