出處:http://blog.csdn.net/ns_code/article/details/13005125
還記得上篇提到的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.
//該方法運行在后臺線程中,主要負責執行耗時的后臺計算傳輸等工作,
//實際的后臺操作被UI Thread調用時,該方法被回調
@Override
protected Void doInBackground(Void... params) {
//cam = (CameraActivity)context;
Size size = cam.getCamera().getParameters().getPreviewSize();
int wide = size.width;
int high = size.height;
YuvImage image = new YuvImage(data, ImageFormat.NV21, wide, high, null);
//因為要實時處理視頻流,因此用內存操作流比較合適
ByteArrayOutputStream os = new ByteArrayOutputStream(data.length);
if(!image.compressToJpeg(new Rect(0, 0, wide, high), 100, os)){
return null;
}
send(os);
return null;
}
這里倒數第三行的send(os)記為發送視頻的操作,當然,如果你是做其他的操作,而不是傳輸視頻數據,你也可以將其改為其他的函數,比如做街景檢測、人臉車牌識別等,而其他代碼基本不用修改,發送視頻的send方法基本就是按照TCP協議編寫,在JAVA中是用Socket類編寫客戶端的代碼:
//發送視頻流到PC端,這里傳遞過來的參數os中保存的是視頻輸出流數據
private void send(ByteArrayOutputStream os) {
//定義用來保存從輸入流中讀取的視頻流數據的byte數組
byte[] buffer = new byte[1024];
try {
Socket client = new Socket(ipName,30000);
OutputStream outSocket = client.getOutputStream();
//實例化內存輸入流,將視頻流數據寫入到內存中
ByteArrayInputStream inputFromOs = new ByteArrayInputStream(os.toByteArray());
//不斷從內存中讀取數據到buffer中,不斷再從buffer中將視頻數據發送到outSocket流中
int amount;
while((amount =inputFromOs.read(buffer) ) != -1)
outSocket.write(buffer, 0, amount);
//這里需要刷新用到緩沖區的輸出流
os.flush();
inputFromOs.close();
os.close();
outSocket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
System.out.println("無法找到要連接的服務器");
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO錯誤");
}
}
最后,什么時候調用protected Void doInBackground(Void... params)方法呢?看了上面那兩篇博客,應該也會明白了,當調用execute(Params... params)方法時,便會自動回調該方法,從而執行其內部代碼。