鸿蒙HarmonyOS开发实战—多媒体开发(相机开发 二)_鸿蒙开发录制视频(1)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新HarmonyOS鸿蒙全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img

img
img
htt

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注鸿蒙)
img

正文

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)方法获取拍照配置模板,并且设置拍照帧配置,如下表
接口名描述是否必选
FrameConfig.Builder addSurface(Surface)实现拍照Surface和帧的绑定。必选
FrameConfig.Builder setImageRotation(int)设置图片旋转角度。可选
FrameConfig.Builder setLocation(Location)设置图片地理位置信息。可选
FrameConfig.Builder setParameter(Key, T)配置其他属性(如自拍镜像等)。可选

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 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 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;
// 预览配置模板置空

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

amePictureConfigBuilder = null;
// 预览配置模板置空

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
[外链图片转存中…(img-DxaLBZqG-1713655134777)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 16
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值