Android流媒體開發之路三:基於NDK開發Android平台RTSP播放器


基於NDK開發Android平台RTSP播放器

最近做了不少android端的開發,有推流、播放、直播、對講等各種應用,做了RTMP、RTSP、HTTP-FLV、自定義等各種協議,還是有不少收獲和心得的。我這邊做,核心模塊和核心代碼部分,都是基於NDK,用C++開發的,然后將so動態庫,在Android java環境中使用,這個既能保證核心部分的代碼性能,也能最大程度復用之前寫的流媒體相關的大量代碼,實踐證明,這樣的程序架構,還是很有效的。這篇文章里,我打算描述一下我對於開發Android端RTSP播放器的程序框架,和設計思路,有相關需求的,希望能借此擴展下思路。

邏輯思路

  1. 首先,既然是RTSP播放器,那必然要做RTSP的解析,這部分對我來說已經是非常熟悉了。我常用的RTSP解析代碼,一般是基於Live555和FFMpeg的庫,通過調用相關的接口,來實現RTSP客戶端協議的數據接收,然后再做數據分析。這兩種方式,各有適合的應用場景,兼容性也各有優劣,要根據具體項目具體選擇。除非是整套都是自己做的RTSP服務器和RTSP客戶端,否則我一般都是用他們兩個,為的是最大程度的兼容第三方RTSP服務器,比如各種網絡攝像頭、各種設備、以及其他公司自己寫的RTSP server等等,具體就不說了,做過類似的估計都清楚。當然,數據接收是需要做緩沖的,否則會卡頓,這個需要自己來做。

  2. 其次是解碼,對於這點,為了保證內存使用效率,以及避免JNI調用開銷,最好是在c++層來做。這個可以基於FFMpeg解碼器或者MediaCodec解碼器來寫,不過要注意后者對Android的版本有要求。解碼后需要對數據進行緩沖,按照時間戳進行排隊。這個不管是直播還是點播,都需要做隊列,否則同樣會出現卡頓、音視頻不同步,以及其他的情況,這個是非常重要的一點。

  3. 最后是渲染,這個可以選擇在c++層繪制,或者回調上層,交給EGL來進行繪制,后者需要編寫EGL代碼,創建EGL surface,在渲染線程中進行繪制。

總結一下:

  • 連接RTSP服務器,接收數據並進行分析,提取視頻和音頻數據
  • 對編碼數據,比如h.264、aac等,進行解碼,還原原始數據
  • 把原始數據,進行繪制或回調上層,opengl繪制

程序框架

結構示意圖:

c++部分是主要代碼,java層只需要做封裝和調用操作即可

框架圖:

Android c++工程編譯

本人的交叉編譯平台是ubuntu 64bit,編譯成動態庫,然后讓APP通過JNI來調用,跟其他程序的編譯方式差不多。當然,首先需要系統內布置好NDK編譯環境。Google提供了完整的編譯工具鏈,也包括SDK,下載地址在這里:“NDK Downloads”。我在之前的一篇文章里也寫了這部分,可以參考一下:"NDK開發Android端RTMP直播推流程序"。

1. 編譯依賴庫

對第三方庫,我通常都是首先嘗試NDK工具鏈的方式來編譯,這樣的好處,一個是工作量小,能直接使用項目的makefile,當前前提是先配置好編譯環境,指定好交叉編譯工具;另一個是不同的庫的編譯方式是相同的,很容易處理。這里以FFMpeg為例

第三方庫准備好,這樣就行了。

2. 編寫程序主體的Android.mk文件

程序主體,直接寫Android.mk,代碼和預編譯條件,鏈接參數等自己都清楚,也很方面控制編譯輸出。之前有篇文章里也有簡單介紹,可以參考"NDK開發Android端RTMP直播推流程序",具體的語法可以參考官方網站Android Developer。

寫好后,調用ndk-build腳本編譯,OK。

需要注意的地方和部分代碼

1.在寫JNI封裝接口的時候,一定要注意jni類型和c++類型的對應關系,尤其是注意返回值。本人就曾經因為jni接口返回值,和代碼實現時候的不對應,從而導致android app調用接口的時候異常退出

其中一個接口對應的JNI c語言代碼是這樣的:

2.在按照時間戳做播放隊列的時候,為了音頻和視頻的同步,必須注意音頻和視頻各自的時間戳,需要按照真實的時間進行還原。而當發現視頻和音頻不同步的時候,或者因為緩沖問題,導致視頻需要丟包的情況下,需要及時調整音頻播放隊列的基准時間戳,避免音視頻不同步的情況出現。同時,這樣做也能避免長期累積造成的計算誤差。

3.由於是手機端或者嵌入式設備端進行播放,因為需要考慮到設備性能不足的情況。這個時候,如果碼流較大而設備來不及解碼或者渲染,必須及時拋棄視頻數據,否則會造成內存溢出,程序崩潰。同時在拋棄數據的時候,要考慮到關鍵幀的問題,也就是如果發生了拋幀,那么整個GOP的數據都應當放棄,除非是有冗余編碼等編碼技術,以此來避免花屏的情況,以及第2點列出的音視頻同步問題。解決這幾點,基本上就可以了。

4.當需要回調給java層,讓EGL來渲染畫面時,需要用到c++回調Java的技術手段。首先寫好java層封裝的回調接口,然后在c++代碼中,通過JNI環境,獲取到java層封裝的類jclass對象和方法。注意在調用GetMethodID時,需要寫正確函數的簽名,例如我在java層的函數是

void OnVideoDataBuf(int width, int height, byte[] frameBuf)

那么對應的簽名是“(II[B)V”
以下是調用例子:

注意最后需要DetachCurrentThread()。

運行效果

在手機端運行畫面:


haibindev.cnblogs.com,合作請聯系QQ或微信。(轉載請注明作者和出處~)



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM