Android實時監控專案第四篇:後臺執行緒傳送預覽幀視訊資料

yangxi_001發表於2014-01-14

還記得上篇提到的setPreviewCallback(Camera.PreviewCallback cb)函式嗎?我們在開始預覽幀視訊之前,呼叫的它,這裡要注意其內部的Camera.PreviewCallback型別的引數,我們需要寫一個類繼承Camera.PreviewCallback的類,在該類中覆寫public void onPreviewFrame(byte[] data, Camera camera)方法,這裡的data引數儲存的即是預覽幀是視訊資料,一旦程式呼叫Camera.PreviewCallback介面,便會自動呼叫發方法,因此當我們在開始預覽幀視訊之前呼叫setPreviewCallback(Camera.PreviewCallback cb)函式時,便會回撥該方法,理論上來說我們在這個方法中寫傳送幀視訊的程式碼就行了,但實際上我們並不能這麼做,因為傳送視訊資料是一個很耗時的操作,為了防止UI執行緒阻塞,我們需要另外開啟一個執行緒,在該執行緒中實現視訊的傳送操作

    這裡我們採用AsyncTask<Void, Void, Void>後臺執行緒,因此我們需要再寫一個類,繼承AsyncTask<Void, Void, Void>抽象類,並覆寫其中的protected Void doInBackground(Void... params)方法,在該方法中編寫傳送視訊資料的程式即可,這裡要注意形參的含義,因為的專案中不需要用到這三個引數,因此全部傳入Void,關於AsyncTask的詳細使用,可以參見這兩篇部落格:http://blog.csdn.net/ns_code/article/details/12889455http://blog.csdn.net/liuhe688/article/details/6532519,我在該方法中寫的程式碼如下:

[java] view plaincopy
  1. //該方法執行在後臺執行緒中,主要負責執行耗時的後臺計算傳輸等工作,  
  2. //實際的後臺操作被UI Thread呼叫時,該方法被回撥  
  3. @Override  
  4. protected Void doInBackground(Void... params) {  
  5.     //cam = (CameraActivity)context;  
  6.     Size size = cam.getCamera().getParameters().getPreviewSize();  
  7.     int wide = size.width;  
  8.     int high = size.height;  
  9.     YuvImage image = new YuvImage(data, ImageFormat.NV21, wide, high, null);  
  10.     //因為要實時處理視訊流,因此用記憶體操作流比較合適  
  11.     ByteArrayOutputStream os = new ByteArrayOutputStream(data.length);  
  12.     if(!image.compressToJpeg(new Rect(00, wide, high), 100, os)){  
  13.         return null;  
  14.     }  
  15.     send(os);  
  16.     return null;  
  17. }  

    這裡倒數第三行的send(os)記為傳送視訊的操作,當然,如果你是做其他的操作,而不是傳輸視訊資料,你也可以將其改為其他的函式,比如做街景檢測、人臉車牌識別等,而其他程式碼基本不用修改,傳送視訊的send方法基本就是按照TCP協議編寫,在JAVA中是用Socket類編寫客戶端的程式碼:

[java] view plaincopy
  1. //傳送視訊流到PC端,這裡傳遞過來的引數os中儲存的是視訊輸出流資料  
  2. private void send(ByteArrayOutputStream os) {  
  3.       
  4.     //定義用來儲存從輸入流中讀取的視訊流資料的byte陣列  
  5.     byte[] buffer = new byte[1024];  
  6.     try {  
  7.         Socket client = new Socket(ipName,30000);  
  8.         OutputStream outSocket = client.getOutputStream();  
  9.         //例項化記憶體輸入流,將視訊流資料寫入到記憶體中  
  10.         ByteArrayInputStream inputFromOs = new ByteArrayInputStream(os.toByteArray());  
  11.         //不斷從記憶體中讀取資料到buffer中,不斷再從buffer中將視訊資料傳送到outSocket流中  
  12.         int amount;  
  13.         while((amount =inputFromOs.read(buffer) ) != -1)  
  14.             outSocket.write(buffer, 0, amount);  
  15.         //這裡需要重新整理用到緩衝區的輸出流  
  16.         os.flush();  
  17.         inputFromOs.close();  
  18.         os.close();  
  19.         outSocket.close();  
  20.     } catch (UnknownHostException e) {  
  21.         e.printStackTrace();  
  22.         System.out.println("無法找到要連線的伺服器");  
  23.     } catch (IOException e) {  
  24.         e.printStackTrace();  
  25.         System.out.println("IO錯誤");  
  26.     }  
  27. }     

最後,什麼時候呼叫protected Void doInBackground(Void... params)方法呢?看了上面那兩篇部落格,應該也會明白了,當呼叫execute(Params... params)方法時,便會自動回撥該方法,從而執行其內部程式碼。


至此,整個專案的Android客戶端程式碼已經編寫完畢,下篇將講述PC端(亦即服務端)程式碼的實現

相關文章