HarmonyOS学习路之开发篇—多媒体开发(相机开发 二)

简介: Camera操作类,包括相机预览、录像、拍照等功能接口。

相机帧捕获

Camera操作类,包括相机预览、录像、拍照等功能接口。



image.png

用户一般都是先看见预览画面才执行拍照或者其他功能,所以对于一个普通的相机应用,预览是必不可少的。启动预览的建议步骤如下:


1. 通过getFrameConfigBuilder(FRAME_CONFIG_PREVIEW)方法获取预览配置模板,常用帧配置项见下表,更多的帧配置项以及详细使用方法请参考API接口说明的FrameConfig.Builder部分。



image.png


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)方法获取拍照配置模板,并且设置拍照帧配置,如下表


image.png


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;
}
相关文章
|
1月前
|
存储 JavaScript 前端开发
“纯血鸿蒙”要来了,赶紧入手学习吧
1月18日,华为宣布HarmonyOSNEXT鸿蒙星河版面向开发者开放申请,这一最新版本的鸿蒙系统也被喻为“纯血鸿蒙”。赶紧入手学习吧
116 1
|
3月前
|
存储 前端开发 IDE
【华为鸿蒙系统学习】- 如何利用鸿蒙系统进行App项目开发|自学篇
【华为鸿蒙系统学习】- 如何利用鸿蒙系统进行App项目开发|自学篇
101 0
|
3月前
|
数据管理 API 调度
【华为鸿蒙系统学习】- HarmonyOS4.0开发|自学篇
【华为鸿蒙系统学习】- HarmonyOS4.0开发|自学篇
211 0
|
3月前
|
前端开发 JavaScript 开发者
鸿蒙2.0!用 JavaScript 开发鸿蒙应用
鸿蒙2.0!用 JavaScript 开发鸿蒙应用
|
3月前
|
开发者 索引 容器
【鸿蒙软件开发】Stage模型开发概述应用/组件级配置
【鸿蒙软件开发】Stage模型开发概述应用/组件级配置
115 0
【鸿蒙软件开发】Stage模型开发概述应用/组件级配置
|
3月前
|
人工智能 安全 开发者
中国移动APP启动鸿蒙原生应用开发,鸿蒙生态迎来通信领域民生应用
近日,在“鸿蒙生态全面合作签约暨开发启动仪式“上,中国移动通信有限公司在线营销服务中心宣布将与鸿蒙生态在技术互补、成果共享、商业共赢等方向进行合作,以共同推动鸿蒙智能化的移动开放生态业务发展,并正式启动中国移动APP鸿蒙原生应用及元服务开发。
|
5月前
|
安全 前端开发 Android开发
鸿蒙开发|鸿蒙系统的介绍(为什么要学习鸿蒙开发|鸿蒙系统的官方定义|鸿蒙和安卓、ios的对比)
鸿蒙开发学习是一项探索性的工作,旨在开发一个全场景分布式操作系统,覆盖所有设备,让消费者能够更方便、更直观地使用各种设备。
297 6
鸿蒙开发|鸿蒙系统的介绍(为什么要学习鸿蒙开发|鸿蒙系统的官方定义|鸿蒙和安卓、ios的对比)
|
5月前
|
文字识别 数据管理 API
双十一钜惠!三门不可多得的HarmonyOS学习教程
今年双十一,各大商城优惠不断。这里介绍三门不可多得的HarmonyOS学习教程,都有非常大的折扣优惠。
65 0
|
8月前
|
IDE JavaScript API
HarmonyOS开发第一步,熟知开发工具DevEco Studio
本文主要以常见的功能点作为概述希望可以帮助到学习HarmonyOS的开发者。
219 0
|
8月前
|
开发框架 API 开发者
HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 二)
在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。在进行 UI 界面开发时,通常不是简单的将系统组件进行组合使用,而是需要考虑代码可复用性、业务逻辑与UI分离,后续版本演进等因素。因此,将UI和部分业务逻辑封装成自定义组件是不可或缺的能力。