好久沒有寫博客,今天將前段時間做的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)是一種輕量的、簡單的、基於 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的協議封一個包
- DESCRIBE
- RTSP/1.0
- CSeq:0
- Accept:application/sdp
- Authorization: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);
- }
http://blog.csdn.net/yanjiee/article/details/18809107