好久沒有寫部落格,今天將前段時間做的Onvif協議在Android上的實現分享給大家。
首先,我們先來瞭解一下什麼是Onvif協議:ONVIF 協議是由Open Network Video Interface Forum (開放型網路視頻介面論壇)制定的開放性行業標準。這一介面標準的目的是確保不同廠商生產的網路視頻產品具有互連性。
ONVIF規範中裝置管理和控制部分所定義的介面均以Web Services的形式提供。ONVIF規範涵蓋了完全的XML及WSDL的定義。每一個支援ONVIF規範的終端裝置均須提供與功能相應的Web Service。服務端與用戶端的資料互動採用SOAP協議。ONVIF中的其他部分比如音視頻流則通過RTP/RTSP進行 。
所示就是Onvif協議要實現的內容,其中IPCAM就是指網路攝像機(IP Camera)。
接下去我們分步驟來說:
(1)發現ipcam
用戶端首先發起ws-discovery,尋找所在網路段內的所有的ipcam。Ipcam在接收到ws-discovery之後可以進行響應。
在android下的實現方式
1)DatagramPacket類,UDP的方式
2)計算廣播位址(192.168.1.255)
3)socket send probe
4)開啟一個線程去receive
5)解析收到的probematch
(2)對IP Camera參數配置
通過soap調用web server的方法,可根據wsdl進行方法的請求封裝,實現具體的方法。
Soap:簡易物件存取通訊協定 (SOAP)(SOAP)是一種輕量的、簡單的、基於 XML 的協議,它被設計成在 WEB 上交換結構化的和固化的資訊。
Wsdl:Web Services Description Language的縮寫,是一個用來描述Web服務和說明如何與Web服務通訊的XML語言。為使用者提供詳細的介面說明書。
在android下實現方式:
1)執行個體化一個HttpURLConnection,並使用POST的方式
2)POST一個遵從WSDL定義的介面的包,例如GetStreamUri。
<GetStreamUri xmlns="http://www.onvif.org/ver10/media/wsdl"> <StreamSetup> <Stream xmlns="http://www.onvif.org/ver10/schema">RTP-Unicast</Stream> <Transport xmlns="http://www.onvif.org/ver10/schema"> <Protocol>UDP</Protocol> </Transport> </StreamSetup> <ProfileToken>profile-0_0</ProfileToken> </GetStreamUri>
IP Camera在接收到該調用後會返回如下:
<trt:GetStreamUriResponse> <trt:MediaUri> <tt:Uri>rtsp://192.168.0.105/live1.sdp</tt:Uri> <tt:InvalidAfterConnect>false</tt:InvalidAfterConnect> <tt:InvalidAfterReboot>false</tt:InvalidAfterReboot> <tt:Timeout>P1Y</tt:Timeout> </trt:MediaUri> </trt:GetStreamUriResponse>
3)接收返回值(保證傳回碼是200),解析XML,擷取RTSP的流地址。
(3)擷取視頻流
android下的實現方式
1)執行個體化Socket對象
2)根據RTSP的協議封一個包
DESCRIBERTSP/1.0CSeq:0Accept:application/sdpAuthorization:Basic (admin:12345的base64編碼)
3)解析RTSP流
PS:
實際上,在Android中,我們只要獲得IPCAM的rtsp地址之後,有幾種方法可以直接在SurfaceView上播放視頻了:1.JavaCV;2.VLC;3.支援硬解碼的MediaPlayer和VideoPlayer;(4)Vitamio
最後說一下第二步中SOAP的實現中,從開始設定ipcam的包中,需要在包頭中加入ipcam的鑒權。官方給的公式是:
其中base64編碼容易實現,nonce只是一個16位隨機數即可。Sha-1在JAVA中的實現方式是:
MessageDigest md = MessageDigest.getInstance("SHA-1");
範例程式碼:
public String getPasswordEncode(String nonce, String password, String date) {try {MessageDigest md = MessageDigest.getInstance("SHA-1");byte[] b1 = Base64.decode(nonce.getBytes(), Base64.DEFAULT);byte[] b2 = date.getBytes(); // "2013-09-17T09:13:35Z";byte[] b3 = password.getBytes();byte[] b4 = new byte[b1.length + b2.length + b3.length];md.update(b1, 0, b1.length);md.update(b2, 0, b2.length);md.update(b3, 0, b3.length);b4 = md.digest();String result = new String(Base64.encode(b4, Base64.DEFAULT));return result.replace("\n", "");} catch (Exception e) {e.printStackTrace();return "";}}public String getNonce() {String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < 24; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}private void createAuthString() {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'",Locale.CHINA);mCreated = df.format(new Date());mNonce = getNonce();mAuthPwd = getPasswordEncode(mNonce, mCamera.password, mCreated);}