一、打开相机、预览
打开相机预览流程是Camera2的默认流程
可参考:https://blog.csdn.net/kk3087961/article/details/135616576
二、开启录像功能
开启录像主要包括以下3步:
private void startRecording() {
// 1. 停止预览并关闭会话
if (mCameraSession != null) {
try {
mCameraSession.stopRepeating();
mCameraSession.close();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
//2. 配置MediaRecorder
setUpMediaRecord();
// 3. 创建混合会话
createCameraRecordSession();
}
1. 停止预览并关闭会话。
2. 配置MediaRecorder。
/**
* 初始化MediaRecord
*/
private void setUpMediaRecord() {
Logger.i(TAG, "setUpMediaRecord() 11 w:" + Config.SHOOT_PIC_WIDTH + " h:" + Config.SHOOT_PIC_HEIGHT);
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mMediaRecorder.setVideoSize(Config.SHOOT_PIC_WIDTH, Config.SHOOT_PIC_HEIGHT);
mMediaRecorder.setVideoFrameRate(30);
File videoFile = new File(Config.DEFAULT_VIDEO_DIR_PREFIX + StringUtil.getTimeTextWithSSS(System.currentTimeMillis()) + ".mp4");
mMediaRecorder.setOutputFile(videoFile.getAbsolutePath());
try {
mMediaRecorder.prepare();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
3. 创建相机录像混合会话
/**
* 创建相机录像会话
*/
private void createCameraRecordSession() {
// 获取TextureView纹理视图的表面纹理
SurfaceTexture texture = mPlayerView.getSurfaceTexture();
// 设置表面纹理的默认缓存尺寸
texture.setDefaultBufferSize(mPreViewSize.getWidth(), mPreViewSize.getHeight());
// 创建一个该表面纹理的表面对象
mSurface = new Surface(texture);
try {
synchronized (this) {
if (isCameraClosed) {//创建Session逻辑都是在mHandler中异步执行的,所以需要对isCameraClosed部分进行同步
Logger.w(TAG, "createCameraPreviewSession : method close has been invoked.");
return;
}
mRecordBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
// 把纹理视图添加到预览目标
mRecordBuilder.addTarget(mSurface);
mRecordBuilder.addTarget(mMediaRecorder.getSurface());
// 设置自动对焦模式
mRecordBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 设置自动曝光模式
mRecordBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// 开始对焦
mRecordBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_START);
// 设置预览的方向
mRecordBuilder.set(CaptureRequest.JPEG_ORIENTATION, (mCameraType == CameraCharacteristics.LENS_FACING_FRONT) ? 90 : 270);
//配置 ImageReader
if (mImageReader != null) {
mImageReader.close();
mImageReader = null;
}
mImageReader = ImageReader.newInstance(Config.SHOOT_PIC_WIDTH,
Config.SHOOT_PIC_HEIGHT, ImageFormat.JPEG, 1);
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
//处理拍照回调,保存拍照数据
Bitmap bitmap = getByteFromReader(reader);
Log.i(TAG, "onImageAvailable() null != bitmap?" + (null != bitmap));
//bitmap 判空处理放在doCaptureAfterGetImage中处理
doCaptureAfterGetImage(bitmap);
}
}, null);
// 创建一个相片捕获会话。此时预览画面显示在纹理视图上,该对象负责管理处理预览请求和拍照请求以及录像
mCameraDevice.createCaptureSession(Arrays.asList(mSurface, mImageReader.getSurface(), mMediaRecorder.getSurface()),
mVidioSessionStateCallback, mHandler);
}
} catch (CameraAccessException e) {
e.printStackTrace();
Logger.w(TAG, "createCameraPreviewSession: exc=" + e.getLocalizedMessage());
onCameraError();
} catch (Exception e) {
e.printStackTrace();
onCameraError();
Logger.e(TAG, "createCameraPreviewSession: exception." + e.getLocalizedMessage());
}
}
// 录像会话配置就绪后,将预览画面呈现到手机屏幕上,然后开启录像
private CameraCaptureSession.StateCallback mVidioSessionStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
synchronized (this) {
if (isCameraClosed) {//回调逻辑都是在mHandler中顺序执行的,所以需要对isCameraClosed部分进行同步
Logger.w(TAG, "mVidioSessionStateCallback#onConfigured : method close has been invoked.");
return;
}
Logger.i(TAG, "mVidioSessionStateCallback : onConfigured.");
mCameraSession = session;
// 设置连拍请求。此时预览画面只会发给手机屏幕
mCameraSession.setRepeatingRequest(mRecordBuilder.build(), mPreviewCallback, mHandler);
mMediaRecorder.start();
}
onCameraPrepared();
} catch (CameraAccessException e) {
e.printStackTrace();
Logger.e(TAG, "mVidioSessionStateCallback::onConfigured exc = " + e.getMessage());
onCameraError();
} catch (Exception e) {
e.printStackTrace();
Logger.e(TAG, "mVidioSessionStateCallback: exception." + e.getLocalizedMessage());
onCameraError();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
onCameraError();
Logger.i(TAG, "mVidioSessionStateCallback::onConfigureFailed.");
}
};
三、停止录像功能
停止录像主要包括两步
1.停止录像释放资源
private void stopRecording() {
Logger.i(TAG, "stopRecording()");
try {
mMediaRecorder.stop();
mMediaRecorder.reset();
mMediaRecorder.release();
mCameraSession.stopRepeating();
mCameraSession.close();
//mMediaRecorder = null;
} catch (Exception e) {
e.printStackTrace();
}
}
2.重新创建预览会话开启预览
// 创建相机预览会话
protected void createCameraPreviewSession() {
// 获取纹理视图的表面纹理
SurfaceTexture texture = mPlayerView.getSurfaceTexture();
// 设置表面纹理的默认缓存尺寸
texture.setDefaultBufferSize(mPreViewSize.getWidth(), mPreViewSize.getHeight());
// 创建一个该表面纹理的表面对象
mSurface = new Surface(texture);
try {
synchronized (this) {
if (isCameraClosed) {//创建Session逻辑都是在mHandler中异步执行的,所以需要对isCameraClosed部分进行同步
Logger.w(TAG, "createCameraPreviewSession : method close has been invoked.");
return;
}
mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
// 把纹理视图添加到预览目标
mPreviewBuilder.addTarget(mSurface);
// 设置自动对焦模式
mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 设置自动曝光模式
mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
// 开始对焦
mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_START);
// 设置预览的方向
mPreviewBuilder.set(CaptureRequest.JPEG_ORIENTATION, (mCameraType == CameraCharacteristics.LENS_FACING_FRONT) ? 90 : 270);
//配置 ImageReader
if (mImageReader != null) {
mImageReader.close();
mImageReader = null;
}
mImageReader = ImageReader.newInstance(Config.SHOOT_PIC_WIDTH,
Config.SHOOT_PIC_HEIGHT, ImageFormat.JPEG, 1);
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
//处理拍照回调,保存拍照数据
Bitmap bitmap = getByteFromReader(reader);
Log.i(TAG, "onImageAvailable() null != bitmap?" + (null != bitmap));
//bitmap 判空处理放在doCaptureAfterGetImage中处理
doCaptureAfterGetImage(bitmap);
}
}, null);
// 创建一个相片捕获会话。此时预览画面显示在纹理视图上,该对象负责管理处理预览请求和拍照请求
mCameraDevice.createCaptureSession(Arrays.asList(mSurface, mImageReader.getSurface()),
mSessionStateCallback, mHandler);
}
} catch (CameraAccessException e) {
e.printStackTrace();
Logger.w(TAG, "createCameraPreviewSession: exc=" + e.getLocalizedMessage());
onCameraError();
} catch (Exception e) {
e.printStackTrace();
onCameraError();
Logger.e(TAG, "createCameraPreviewSession: exception." + e.getLocalizedMessage());
}
}
// 影像配置就绪后,将预览画面呈现到手机屏幕上
private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
synchronized (this) {
if (isCameraClosed) {//回调逻辑都是在mHandler中顺序执行的,所以需要对isCameraClosed部分进行同步
Logger.w(TAG, "mSessionStateCallback#onConfigured : method close has been invoked.");
return;
}
Logger.i(TAG, "SessionStateCallback : onConfigured.");
mCameraSession = session;
// 设置连拍请求。此时预览画面只会发给手机屏幕
mCameraSession.setRepeatingRequest(mPreviewBuilder.build(), mPreviewCallback, mHandler);
}
onCameraPrepared();
} catch (CameraAccessException e) {
e.printStackTrace();
Logger.e(TAG, "mSessionStateCallback::onConfigured exc = " + e.getMessage());
onCameraError();
} catch (Exception e) {
e.printStackTrace();
Logger.e(TAG, "mSessionStateCallback: exception." + e.getLocalizedMessage());
onCameraError();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
onCameraError();
Logger.i(TAG, "mSessionStateCallback::onConfigureFailed.");
}
};