(原)閱讀Android-Camera2Video的demo源碼和調試心得


轉載請注明出處:http://www.cnblogs.com/lihaiping/p/6142512.html
 
最近因為項目需要使用到camera的功能,所以針對官方的demo源碼進行一番閱讀,並修改了一個record錄像以后程序崩潰的bug。
 
這里主要記錄下調試過程的情況:
 
1)打開rk3288-walkera-board上基於android5.1的camera以后,出現無視頻畫面的黑屏情況。
  經過查找主要是因為camera適用720P打開,而在程序的預覽過程中,選擇用了1080p打開camera,導致視頻畫面出不來。
相關代碼在opencamera函數中:
            //選擇滿足4:3的長寬比例的尺寸分辨率
mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));
//獲取一下攝像頭支持的最大分辨率,防止攝像頭不支持,導致沒有圖像
Size cameraLargest= Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),new CompareSizesByArea());
//獲取最佳的長寬比預覽尺寸
// mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
// width, height, mVideoSize);
//針對rk3288-walkera-board,camera只能打開720p
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
1280, 720, cameraLargest);
我們先看一下chooseVideoSize函數:
//這個函數根據長寬比,選擇只支持長寬比為4:3的分辨率,同時寬小於1080p
private static Size chooseVideoSize(Size[] choices) {
for (Size size : choices) {
        if (size.getWidth() == size.getHeight() * 4 / 3 && size.getWidth() <= 1080) {
return size;
}
}
Log.e(TAG, "Couldn't find any suitable video size");
return choices[choices.length - 1];
}
在這個函數執行以后,得到的分辨率為800x600的分辨率。
然后再來看一下chooseOptimalSize 的選擇策略:
//這個函數選擇長比寬為aspectRotio一樣的分辨率,同時如果長寬大於指定的寬高,就選用中間最小的一個,否則選用choices[0]
private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
// Collect the supported resolutions that are at least as big as the preview Surface
//選擇合適的長寬比的分辨率
List<Size> bigEnough = new ArrayList<Size>();
int w = aspectRatio.getWidth();
int h = aspectRatio.getHeight();
for (Size option : choices) {
if (option.getHeight() == option.getWidth() * h / w &&
option.getWidth() >= width && option.getHeight() >= height) {
bigEnough.add(option);
}

}

// Pick the smallest of those, assuming we found any
if (bigEnough.size() > 0) {
return Collections.min(bigEnough, new CompareSizesByArea());
}
else {
Log.e(TAG, "Couldn't find any suitable preview size");
return choices[0];
}
}
針對這種選擇策略,我個人覺得很不適用。函數執行結果因為找不到滿足的分辨率,所以會進入else的選擇,最好返回choices[0],即選擇1080p的預覽分辨率。
所以導致后面打開攝像頭會出現無視頻畫面的情況。
其實我個人的認為,在選擇最佳分辨率的情況,應該是當設備支持的分辨率中有比預覽指定的分辨率大的集合的時候,選集合中最小的設備分辨率。否則選擇比預覽分辨率小的集合中最大的分辨率。
 
         
2)點擊錄像,然后錄像停止以后,生成錄像文件,但程序崩潰。
出現問題的崩潰點為 startPreview 中從新創建預覽請求的函數:
 
         
// 創建預覽需要的CaptureRequest.Builder
mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
后面經過搜索和查找,解決方法為:
    private void stopRecordingVideo() {
// UI
mIsRecordingVideo = false;
mButtonVideo.setText(R.string.record);
//modefy by lihaiping1603@aliyun.com on20161207
//這個地方需要優化一下,防止錄像的時候會崩潰
// try{
// mPreviewSession.abortCaptures();
// }catch (CameraAccessException e) {
// e.printStackTrace();
// }

//試一下使用close方式,在錄像stop前,調用關閉也是可以解決崩潰的問題的
closePreviewSession();

// Stop recording
mMediaRecorder.stop();
mMediaRecorder.reset();
//在停止錄像以后調用關閉會話,程序會崩潰
// //試一下使用close方式
// closePreviewSession();

Activity activity = getActivity();
if (null != activity) {
Toast.makeText(activity, "Video saved: " + mNextVideoAbsolutePath,
Toast.LENGTH_SHORT).show();
Log.d(TAG, "Video saved: " + mNextVideoAbsolutePath);
}
mNextVideoAbsolutePath = null;
startPreview();
}
后面經過翻看abortCaptures()的官方解釋:

public abstract void abortCaptures ()

Added in  API level 21

Discard all captures currently pending and in-progress as fast as possible.

The camera device will discard all of its current work as fast as possible. Some in-flight captures may complete successfully and call onCaptureCompleted(CameraCaptureSession, CaptureRequest, TotalCaptureResult), while others will trigger their onCaptureFailed(CameraCaptureSession, CaptureRequest, CaptureFailure) callbacks. If a repeating request or a repeating burst is set, it will be cleared.

This method is the fastest way to switch the camera device to a new session with createCaptureSession(List, CameraCaptureSession.StateCallback, Handler) orcreateReprocessableCaptureSession(InputConfiguration, List, CameraCaptureSession.StateCallback, Handler), at the cost of discarding in-progress work. It must be called before the new session is created. Once all pending requests are either completed or thrown away, the onReady(CameraCaptureSession) callback will be called, if the session has not been closed. Otherwise, the onClosed(CameraCaptureSession)callback will be fired when a new session is created by the camera device.

Cancelling will introduce at least a brief pause in the stream of data from the camera device, since once the camera device is emptied, the first new request has to make it through the entire camera pipeline before new output buffers are produced.

This means that using abortCaptures() to simply remove pending requests is not recommended; it's best used for quickly switching output configurations, or for cancelling long in-progress requests (such as a multi-second capture).

至於原因,暫時還不是太清楚,但加上我上面幾個函數,就可以解決崩潰的問題。
 
         
 
         
 
         
 
         
 
        
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM