相机帧捕获
Camera操作类,包括相机预览、录像、拍照等功能接口。
用户一般都是先看见预览画面才执行拍照或者其他功能,所以对于一个普通的相机应用,预览是必不可少的。启动预览的建议步骤如下:
1. 通过getFrameConfigBuilder(FRAME_CONFIG_PREVIEW)方法获取预览配置模板,常用帧配置项见下表,更多的帧配置项以及详细使用方法请参考API接口说明的FrameConfig.Builder部分。
2. 通过triggerLoopingCapture(FrameConfig)方法实现循环帧捕获(如预览/录像)。
private final class CameraStateCallbackImpl extends CameraStateCallback { @Override public void onConfigured(Camera camera) { // 获取预览配置模板 frameConfigBuilder = camera.getFrameConfigBuilder(FRAME_CONFIG_PREVIEW); // 配置预览Surface frameConfigBuilder.addSurface(previewSurface); previewFrameConfig = frameConfigBuilder.build(); try { // 启动循环帧捕获 int triggerId = camera.triggerLoopingCapture(previewFrameConfig); } catch (IllegalArgumentException e) { HiLog.error(LABEL, "Argument Exception"); } catch (IllegalStateException e) { HiLog.error(LABEL, "State Exception"); } } }
经过以上的操作,相机应用已经可以正常进行实时预览了。在预览状态下,开发者还可以执行其他操作,比如:
当预览帧配置更改时,可以通过triggerLoopingCapture(FrameConfig)方法实现预览帧配置的更新;
// 预览帧变焦值变更 frameConfigBuilder.setZoom(1.2f); // 调用triggerLoopingCapture方法实现预览帧配置更新 triggerLoopingCapture(frameConfigBuilder.build());
通过stopLoopingCapture()方法停止循环帧捕获(停止预览)。
// 停止预览帧捕获 camera.stopLoopingCapture()
拍照功能属于相机应用的最重要功能之一,而且照片质量对用户至关重要。相机模块基于相机复杂的逻辑,从应用接口层到器件驱动层都已经默认的做好了最适合用户的配置,这些默认配置尽可能地保证用户拍出的每张照片的质量。发起拍照的建议步骤如下:
1. 通过getFrameConfigBuilder(FRAME_CONFIG_PICTURE)方法获取拍照配置模板,并且设置拍照帧配置,如下表
2. 拍照前准备图像帧数据的接收实现
// 图像帧数据接收处理对象 private ImageReceiver imageReceiver; // 执行回调的EventHandler private EventHandler eventHandler = new EventHandler(EventRunner.create("CameraCb")); // 拍照支持分辨率 private Size pictureSize; // 单帧捕获生成图像回调Listener private final ImageReceiver.IImageArrivalListener imageArrivalListener = new ImageReceiver.IImageArrivalListener() { @Override public void onImageArrival(ImageReceiver imageReceiver) { StringBuffer fileName = new StringBuffer("picture_"); fileName.append(UUID.randomUUID()).append(".jpg"); // 定义生成图片文件名 File myFile = new File(dirFile, fileName.toString()); // 创建图片文件 imageSaver = new ImageSaver(imageReceiver.readNextImage(), myFile); // 创建一个读写线程任务用于保存图片 eventHandler.postTask(imageSaver); // 执行读写线程任务生成图片 } }; // 保存图片, 图片数据读写,及图像生成见run方法 class ImageSaver implements Runnable { private final Image myImage; private final File myFile; ImageSaver(Image image, File file) { myImage = image; myFile = file; } @Override public void run() { Image.Component component = myImage.getComponent(ImageFormat.ComponentType.JPEG); byte[] bytes = new byte[component.remaining()]; component.read(bytes); FileOutputStream output = null; try { output = new FileOutputStream(myFile); output.write(bytes); // 写图像数据 } catch (IOException e) { HiLog.error(LABEL, "save picture occur exception!"); } finally { if (output != null) { try { output.close(); // 关闭流 } catch (IOException e) { HiLog.error(LABEL, "image release occur exception!"); } } myImage.release(); } } } private void takePictureInit() { List<Size> pictureSizes = cameraAbility.getSupportedSizes(ImageFormat.JPEG); // 获取拍照支持分辨率列表 pictureSize = getPictureSize(pictureSizes) // 根据拍照要求选择合适的分辨率 imageReceiver = ImageReceiver.create(Math.max(pictureSize.width, pictureSize.height), Math.min(pictureSize.width, pictureSize.height), ImageFormat.JPEG, 5); // 创建ImageReceiver对象,注意create函数中宽度要大于高度;5为最大支持的图像数,请根据实际设置。 imageReceiver.setImageArrivalListener(imageArrivalListener); }
3. 通过triggerSingleCapture(FrameConfig)方法实现单帧捕获(如拍照)。
private void capture() { // 获取拍照配置模板 framePictureConfigBuilder = cameraDevice.getFrameConfigBuilder(FRAME_CONFIG_PICTURE); // 配置拍照Surface framePictureConfigBuilder.addSurface(imageReceiver.getRecevingSurface()); // 配置拍照其他参数 framePictureConfigBuilder.setImageRotation(90); try { // 启动单帧捕获(拍照) cameraDevice.triggerSingleCapture(framePictureConfigBuilder.build()); } catch (IllegalArgumentException e) { HiLog.error(LABEL, "Argument Exception"); } catch (IllegalStateException e) { HiLog.error(LABEL, "State Exception"); } }
为了捕获到质量更高和效果更好的图片,还可以在帧结果中实时监测自动对焦和自动曝光的状态,一般而言,在自动对焦完成,自动曝光收敛后的瞬间是发起单帧捕获的最佳时机。
实现连拍(多帧捕获)
连拍功能方便用户一次拍照获取多张照片,用于捕捉精彩瞬间。同普通拍照的实现流程一致,但连拍需要使用triggerMultiCapture(List<FrameConfig> frameConfigs)方法。
启动录像(循环帧捕获)
启动录像和启动预览类似,但需要另外配置录像Surface才能使用。
1. 录像前需要进行音视频模块的配置。
private Source source; // 音视频源 private AudioProperty.Builder audioPropertyBuilder; // 音频属性构造器 private VideoProperty.Builder videoPropertyBuilder; // 视频属性构造器 private StorageProperty.Builder storagePropertyBuilder; // 音视频存储属性构造器 private Recorder mediaRecorder; // 录像操作对象 private String recordName; // 音视频文件名 private Size mRecordSize; // 录像分辨率 private void initMediaRecorder() { videoPropertyBuilder.setRecorderBitRate(10000000); // 设置录制比特率 int rotation = DisplayManager.getInstance().getDefaultDisplay(this).get().getRotation(); videoPropertyBuilder.setRecorderDegrees(getOrientation(rotation)); // 设置录像方向 videoPropertyBuilder.setRecorderFps(30); // 设置录制采样率 videoPropertyBuilder.setRecorderHeight(Math.min(recordSize.height, recordSize.width)); // 设置录像支持的分辨率,需保证width > height videoPropertyBuilder.setRecorderWidth(Math.max(recordSize.height, recordSize.width)); videoPropertyBuilder.setRecorderVideoEncoder(Recorder.VideoEncoder.H264); // 设置视频编码方式 videoPropertyBuilder.setRecorderRate(30); // 设置录制帧率 source.setRecorderAudioSource(Recorder.AudioSource.MIC); // 设置录制音频源 source.setRecorderVideoSource(Recorder.VideoSource.SURFACE); // 设置视频窗口 mediaRecorder.setSource(source); // 设置音视频源 mediaRecorder.setOutputFormat(Recorder.OutputFormat.MPEG_4); // 设置音视频输出格式 StringBuffer fileName = new StringBuffer("record_"); // 生成随机文件名 fileName.append(UUID.randomUUID()).append(".mp4"); recordName = fileName.toString(); File file = new File(dirFile, recordName); // 创建录像文件对象 storagePropertyBuilder.setRecorderFile(file); // 设置存储音视频文件名 mediaRecorder.setStorageProperty(storagePropertyBuilder.build()); audioPropertyBuilder.setRecorderAudioEncoder(Recorder.AudioEncoder.AAC); // 设置音频编码格式 mediaRecorder.setAudioProperty(audioPropertyBuilder.build()); // 设置音频属性 mediaRecorder.setVideoProperty(videoPropertyBuilder.build()); // 设置视频属性 mediaRecorder.prepare(); // 准备录制 HiLog.info(LABEL, "initMediaRecorder end"); }
2. 配置录像帧,启动录像
private final class CameraStateCallbackImpl extends CameraStateCallback { @Override public void onConfigured(Camera camera) { // 获取录像配置模板 frameConfigBuilder = camera.getFrameConfigBuilder(FRAME_CONFIG_RECORD); // 配置预览Surface frameConfigBuilder.addSurface(previewSurface); // 配置录像的Surface mRecorderSurface = mediaRecorder.getVideoSurface(); frameConfigBuilder.addSurface(mRecorderSurface); previewFrameConfig = frameConfigBuilder.build(); try { // 启动循环帧捕获 int triggerId = camera.triggerLoopingCapture(previewFrameConfig); } catch (IllegalArgumentException e) { HiLog.error(LABEL, "Argument Exception"); } catch (IllegalStateException e) { HiLog.error(LABEL, "State Exception"); } } }
相机设备释放
使用完相机后,必须通过release()来关闭相机和释放资源,否则可能导致其他相机应用无法启动。一旦相机被释放,它所提供的操作就不能再被调用,否则会导致不可预期的结果,或是会引发状态异常。
相机设备释放的示例代码如下:
private void releaseCamera() { if (camera != null) { // 关闭相机和释放资源 camera.release(); camera = null; } // 拍照配置模板置空 framePictureConfigBuilder = null; // 预览配置模板置空 previewFrameConfig = null; }