用安卓手機實現視頻監控

現代手機更新換代如此之快,以至於家中往往有閒置不用的手機。本APP用一部閒置手機作爲監控相機,在另一部手機上實現遠程監控。

作爲監控攝像機的手機稱爲服務器端,觀看監控視頻的手機稱爲客戶端

對於使用環境的要求是服務器端通過無線路由器WIFI接入互聯網,客戶端通過互聯網遠程訪問服務器端。
路由器需要進行簡單設置:

  • DHCP靜態IP分配,使服務端在局域網內的地址固定下來。
  • 端口轉發,將路由器的指定端口與服務器端的內網IP地址綁定,使客戶端可以從外部訪問服務器端。

圖1 系統構成
使用時,需要在客戶端輸入路由器的外網IP地址,家庭用戶路由器的IP地址通常是電信運營商動態分配的,時時會發生變化,客戶端獲取路由器IP地址的方法有兩種:

  • 在客戶端安裝路由器管理APP,路由器廠商通常都會提供路由器管理APP。通過路由器管理APP可以實時查看路由器的IP地址。
  • 在花生殼上用內網計算機註冊私有域名,私有域名與路由器綁定。在客戶端安裝花生殼管理APP,通過APP可以實時查看私有域名的IP地址,這個IP地址就是路由器的地址。如果不使用花生殼,其他域名解析提供者也有類似工具。

由於服務器端和客戶端都需要發送信息到對方,因此用TCP協議實現服務器端和客戶端的雙工通信。

服務器端構成

圖2 服務器端構成
圖中所示爲構成服務器端的主要類:

  • MainActivity 用戶交互主頁面
  • SettingsActivity 參數設置頁面,可以設置編碼器的圖像分辨率和視頻編碼標準。
  • AutoFitTextureView 服務器端的視頻顯示組件
  • MainViewModel Android官方推薦使用MVVM架構,ViewModel是MVVM架構的重要組件,它負責爲UI/View準備數據,它與外部通信通過LiveData進行。
  • CameraIntentService 提供與客戶端的通信服務,接收客戶端的通信請求。通信連接成功後,建立服務器通信線程。
  • ServerThread 服務器通信線程,發送編碼後的視頻數據流,接收客戶端的字符信息。
  • MediaCodecCallback 編碼器採用異步工作模式,必須對編碼器的各個回調函數重載,以實現本系統所需視頻編碼功能。
  • ByteBufferTransfer 承載視頻編碼後得到的數據以及解碼器需要的相關情報,它的每個實例代表一幀圖像,經過通信連接,以數據流發送到客戶端。

服務器端將鏡頭拍攝的視頻信號經編碼後逐幀傳送給客戶端,由於視頻拍攝和信號傳輸各自具有不同的時序,因此使用異步方式實現協同動作。
圖3 服務器端時序
服務器端的技術要點

  • 視頻編碼數據獲取
    在android camera2的基礎上,使用所推薦的流程控制相機。
    爲了取得相機幀數據,以下代碼把編碼器的InputSurface添加到CameraRequest的targets列表中,
    在預覽過程中,CameraDevice返回的幀數據就能夠送到編碼器中。
    val encoderInputSurface = MediaCodec.createPersistentInputSurface()
    mediaCodec.setInputSurface(encoderInputSurface)
    mediaCodec.start()
    previewRequestBuilder.addTarget(encoderInputSurface)
    
  • 視頻編碼器參數

    視頻編碼器需要設置的參數如下:
  • videoCodecMime(編碼格式) 可以在SettingsActivity中選擇,本系統僅有H264和H265可以選擇,注意!老的手機往往不支持H265。
  • videoCodecSize(採樣分辨率) 可以在SettingsActivity中選擇,是手機常用的分辨率。
  • KEY_FRAME_RATE(幀率) 這個參數在程序中固定爲30。
  • KEY_COLOR_FORMAT(顏色格式) 這個參數在程序中固定爲MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface。
  • KEY_I_FRAME_INTERVAL(關鍵幀間隔) 單位是秒,這個參數用於指定幀間預測所需關鍵幀在碼流中的間隔,間隔越大,數據壓縮率越高。但爲了畫面流暢,設爲零,也就是沒有用幀間預測。
  • KEY_BIT_RATE(比特率) 比特率 = 分辨率寬 x 分辨率高 x 比特率係數。比特率係數是固定值,H264爲14,H265爲10,因爲H265有更高的壓縮率。
  • 雙工通信

    收到客戶端的請求後,建立服務器通信線程。在線程中建立並行的兩個循環,一個用Loop+Handler實現,Handler負責接收視頻編碼器的輸出,送入objectOutputStream,發送到客戶端。
    另一個用While實現,從objectInputStream中讀取來自客戶端的數據,根據數據的類別進行相應處理。

客戶器端構成

圖4 客戶端構成
圖中所示爲構成客戶端的主要類:

  • MainActivity 主頁面容器
  • SettingsActivity參數設置頁面,僅用來設置服務器端的IP地址。
  • MainFragment 用戶交互主頁面
  • MainViewModel 是MVVM架構的重要組件,它負責爲UI/View準備數據,它與外部通信通過LiveData進行。
  • ClientThread 通信用客戶端線程
  • MonitorTextureView 客戶端的視頻顯示組件
  • MediaCodecAction 包裝瞭解碼器的靜態操作方法
  • DecoderCallback 解碼器的回調函數
  • ByteBufferTransfer 服務器端發送過來的實例,解碼後得到幀圖像。

接收到服務器端發送的視頻碼流,經解碼後把視頻信號交給視頻顯示組件。
圖5 客戶端時序
客戶端的技術要點

  • 指定解碼器輸出

    取得顯示View的surface,
val surface = Surface(super@MonitorTextureView.getSurfaceTexture())

設置解碼器時,把surface作爲參數傳給mediaCodec.configure()方法即可。

  • 視頻解碼器參數
  • mime(編碼格式) 服務器發送過來的ByteBufferTransfer的實例中,包含此參數,不需單獨設置。
  • size(採樣分辨率) 服務器發送過來的ByteBufferTransfer的實例中,包含此參數,不需單獨設置。
  • csd(Codec-specific數據) 服務器端編碼時產生該數據,包含在ByteBufferTransfer的實例中。
  • 雙工通信

    與服務器端相似,在線程中建立並行的兩個循環,一個用來接收,另一個用來發送。

在測試中,服務器端和客戶端均使用聯通的網絡時,視頻傳輸比較流暢。
但是服務器端使用聯通網絡,客戶端使用移動網絡時,卡頓非常嚴重,原因不得而知,也許運營商之間有壁壘。

源程序代碼公開,歡迎同行的指摘、建議,如有需要改進之處,我當盡力爲之。
有任何問題,請聯繫。郵箱:[email protected]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章