HarmonyOS鸿蒙Next中应用如何自定义相机实现录像功能?
HarmonyOS鸿蒙Next中应用如何自定义相机实现录像功能?
鸿蒙应用如何自定义相机实现录像功能?
自定义相机录像 :
本文面向于相机应用开发场景,在相机应用中实现了基础视频录制功能。内容涵盖相机设备的创建与调用、录像的启动与停止、以及输出处理的完整流程,有效满足三方应用在不同硬件平台上对录像功能的开发需求。
基础录像
场景描述
录像功能是自定义相机应用的核心功能,提供实时预览和构图调整能力。通过点击界面上的录像按钮,用户即可开始视频录制。在录制过程中,相机应用会持续采集画面数据并将其保存为视频文件,用户可根据需要随时暂停或结束录制。
流程图:

1、申请相关权限 在开发相机应用时,需要先参考申请相关权限。
2、配置视频录制参数 通过相册管理模块 PhotoAccessHelper 创建一个视频资源,以便后续写入录像文件。 动态生成视频录制的配置profile。 音视频录制参数设置,包括采集源类型、编码方式、画质配置、保存路径等,为录制的启动和后续操作提供基础配置。
3、获取Surface 系统提供的media接口可以创建一个录像AVRecorder实例,通过该实例的getInputSurface()方法获取SurfaceId,用于后续录像输出流的关联,处理录像输出流的数据。
4、创建录像输出流 通过CameraOutputCapability模块中的videoProfiles属性,可获取当前设备支持的录像输出流配置,根据设备能力和目标配置,选择合适的视频配置。在当前示例中,演示了根据不同相机位置、图片质量去设置不同的分辨率和帧率,最后通过createVideoOutput()方法创建录像输出流。
5、启动录像 先通过videoOutput的start()方法启动录像输出流,再通过avRecorder的start()方法开始录像。如需实现前置摄像头录像功能,先通过isMirrorSupported()方法判断设备是否支持镜像功能;如果支持且当前为前置摄像头,则调用enableMirror()方法开启镜像效果。
6、暂停录像 先通过avRecorder的pause()方法暂停录像,再通过videoOutput的stop()方法停止录像输出流。
7、恢复录像 先通过videoOutput的start()方法启动录像输出流,再通过avRecorder的resume()方法恢复录像。
8、停止录像 先通过avRecorder的stop()方法停止录像,再通过videoOutput的stop()方法停止录像输出流。
9、释放资源 先通过avRecorder的release()方法释放录像资源,再通过videoOutput的release()方法释放输出流。
设置录像旋转角度 录像的旋转角度与重力方向(即设备旋转角度)相关。调用VideoOutput类中的getVideoRotation()可以获取到录像的旋转角度。详细请参见适配相机旋转角度(ArkTS) 。 deviceDegree:设备旋转角度。获取方式请见计算设备旋转角度。

更多关于HarmonyOS鸿蒙Next中应用如何自定义相机实现录像功能?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
一、结论
大体开发思路与相机拍照思路相同,都是通过CameraKit进行相机设备调用,视频输入输出流和会话的绑定,在会话里设置相关参数。
唯一与拍照不同的区别是,通过avRecorder进行录像生命周期的管理,开始录像,暂停录像都是通过这个渲染播放对象进行管控。相对于拍照而言,录像的开发要麻烦一些。
自定义相机录像开发思路步骤为: 1.设置相机界面
2.选择相机摄像头实例 根据cameraKit提供的CameraManager获取相机管理实例,拿到设备的相机列表,一般分为前后两个。选择你要用的相机。
3.相机输出流 传入你选择的相机实例给cameraManager.createCameraInput创建相机输出流,开启会话后cameraInput.open,进行相机的参数配置(拍照还是摄像模式,闪光灯,焦距比)
4.配置相机会话信息 创建会话,将输入流,输出流,拍照,摄像,绑定到会话上。完事之后,开启会话,相机流就能正常输出。你去操作拍照摄像也可以正常操作。你可以理解会话为一个组合器。
5.录像操作 使用avRecorder进行录像的开启,暂停,释放的处理。
6.退出界面销毁相机 相机资源不释放,会导致再次初始化黑屏,甚至影响系统相机等异常问题。
二、代码实现和详细解释
/**
* 相机管理类(单例模式)
* 功能:封装视频相机的初始化、录像控制、资源释放等核心能力
* 依赖:CameraKit、ImageKit、MediaKit、AbilityKit、MediaLibraryKit、CoreFileKit等系统套件
*/
import { camera } from '[@kit](/user/kit).CameraKit';
import { image } from '[@kit](/user/kit).ImageKit';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';
import { media } from '[@kit](/user/kit).MediaKit';
import { common } from '[@kit](/user/kit).AbilityKit';
import { photoAccessHelper } from '[@kit](/user/kit).MediaLibraryKit';
import { fileIo as fs } from '[@kit](/user/kit).CoreFileKit';
export class CameraMgr {
// 日志标签,用于控制台输出标识
private TAG: string = "CameraMgr";
// 单例实例,保证全局唯一的相机管理对象
private static mCameraMgr: CameraMgr | null = null;
// 相机输入流对象,用于获取相机硬件的输入数据
private mCameraInput: camera.CameraInput | undefined = undefined;
// 预览输出流对象,用于相机画面预览展示
private mPreviewOutput: camera.PreviewOutput | undefined = undefined;
// 视频会话对象,管理视频录制场景下的相机配置和数据流
private mVideoSession: camera.VideoSession | undefined = undefined;
// 视频输出流对象,用于将相机数据输出到录像编码器
private mVideoOutput: camera.VideoOutput | undefined = undefined;
// 音视频录制器对象,负责将视频/音频数据编码并写入文件
private mAvRecorder: media.AVRecorder | undefined = undefined;
// 录像文件句柄,用于操作录制后的视频文件
private mFile: fs.File | null = null;
/**
* 获取单例实例(懒汉式)
* [@returns](/user/returns) CameraMgr 单例对象
*/
public static Ins(): CameraMgr {
// 已存在实例则直接返回
if (CameraMgr.mCameraMgr) {
return CameraMgr.mCameraMgr
}
// 不存在则创建新实例
CameraMgr.mCameraMgr = new CameraMgr();
return CameraMgr.mCameraMgr
}
/**
* 初始化视频相机(核心方法)
* 流程:创建相机管理器→检查相机设备→配置录像参数→初始化录制器→创建会话/输入输出流→启动会话
* [@param](/user/param) context 应用上下文,用于获取系统服务
* [@param](/user/param) surfaceId 预览画面的Surface ID,绑定UI层的预览控件
* [@returns](/user/returns) Promise<void> 异步初始化结果
*/
public async initVideoCamera(context: common.Context, surfaceId: string): Promise<void> {
// 1. 获取相机管理器实例,是操作相机的核心入口
let cameraManager: camera.CameraManager = camera.getCameraManager(context);
if (!cameraManager) {
console.error("camera.getCameraManager error");
return;
}
// 2. 监听相机状态变化(如打开/关闭/异常)
cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => {
if (err !== undefined && err.code !== 0) {
console.error('cameraStatus with errorCode = ' + err.code);
return;
}
console.info(`camera : ${cameraStatusInfo.camera.cameraId}`); // 相机ID
console.info(`status: ${cameraStatusInfo.status}`); // 相机状态(如ACTIVE/INACTIVE)
});
// 3. 获取设备支持的相机列表(前后置相机等)
let cameraArray: Array<camera.CameraDevice> = [];
try {
cameraArray = cameraManager.getSupportedCameras();
} catch (error) {
let err = error as BusinessError;
console.error(`getSupportedCameras call failed. error code: ${err.code}`);
}
// 无可用相机则终止初始化
if (cameraArray.length <= 0) {
console.error("cameraManager.getSupportedCameras error");
return;
}
// 4. 检查相机是否支持视频录制模式
let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]);
let isSupportVideoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_VIDEO) >= 0;
if (!isSupportVideoMode) {
console.error('video mode not support');
return;
}
// 5. 获取相机输出能力(预览/拍照/录像的分辨率、格式等)
let cameraOutputCap: camera.CameraOutputCapability =
cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_VIDEO);
if (!cameraOutputCap) {
console.error("cameraManager.getSupportedOutputCapability error")
return;
}
console.info("outputCapability: " + JSON.stringify(cameraOutputCap));
// 提取预览/拍照/录像的配置文件列表
let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles;
if (!previewProfilesArray) {
console.error("createOutput previewProfilesArray == null || undefined");
}
let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles;
if (!photoProfilesArray) {
console.error("createOutput photoProfilesArray == null || undefined");
}
let videoProfilesArray: Array<camera.VideoProfile> = cameraOutputCap.videoProfiles;
if (!videoProfilesArray) {
console.error("createOutput videoProfilesArray == null || undefined");
}
// 6. 配置录像分辨率(需与硬件支持的配置匹配)
let videoSize: camera.Size = {
width: 640,
height: 480
}
// 从硬件支持的录像配置中匹配目标分辨率
let videoProfile: undefined | camera.VideoProfile = videoProfilesArray.find((profile: camera.VideoProfile) => {
return profile.size.width === videoSize.width && profile.size.height === videoSize.height;
});
if (!videoProfile) {
console.error('videoProfile is not found');
return;
}
// 7. 配置音视频录制参数(需与硬件编码能力匹配)
let aVRecorderProfile: media.AVRecorderProfile = {
audioBitrate: 48000, // 音频比特率
audioChannels: 2, // 音频声道数(2=立体声)
audioCodec: media.CodecMimeType.AUDIO_AAC, // 音频编码格式(AAC)
audioSampleRate: 48000, // 音频采样率
fileFormat: media.ContainerFormatType.CFT_MPEG_4, // 视频容器格式(MP4)
videoBitrate: 2000000, // 视频比特率(2Mbps)
videoCodec: media.CodecMimeType.VIDEO_AVC, // 视频编码格式(H.264)
videoFrameWidth: videoSize.width, // 视频宽度
videoFrameHeight: videoSize.height, // 视频高度
videoFrameRate: 30 // 视频帧率(30fps)
};
// 8. 创建录像文件(存入系统相册)
let options: photoAccessHelper.CreateOptions = {
title: Date.now().toString() // 文件标题(用时间戳命名避免重复)
};
// 获取相册访问助手
let accessHelper: photoAccessHelper.PhotoAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
// 创建视频资产(返回文件URI)
let videoUri: string = await accessHelper.createAsset(photoAccessHelper.PhotoType.VIDEO, 'mp4', options);
// 打开文件(读写模式,不存在则创建)
this.mFile = fs.openSync(videoUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
// 9. 配置音视频录制器参数
let aVRecorderConfig: media.AVRecorderConfig = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, // 音频源(麦克风)
videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV, // 视频源(Surface YUV数据)
profile: aVRecorderProfile, // 音视频编码配置
url: `fd://${this.mFile.fd.toString()}`, // 文件句柄(fd://+文件描述符)
rotation: 0, // 视频旋转角度(0/90/180/270)
location: { latitude: 30, longitude: 130 } // 视频地理位置信息(可选)
};
// 10. 创建音视频录制器实例
let avRecorder: media.AVRecorder | undefined = undefined;
try {
avRecorder = await media.createAVRecorder();
} catch (error) {
let err = error as BusinessError;
console.error(`createAVRecorder call failed. error code: ${err.code}`);
}
if (avRecorder === undefined) {
return;
}
this.mAvRecorder = avRecorder;
// 初始化录制器(加载编码配置)
try {
await avRecorder.prepare(aVRecorderConfig);
} catch (error) {
let err = error as BusinessError;
console.error(`prepare call failed. error code: ${err.code}`);
}
// 11. 获取录制器的输入Surface ID(用于绑定相机视频输出)
let videoSurfaceId: string | undefined = undefined;
try {
videoSurfaceId = await avRecorder.getInputSurface();
} catch (error) {
let err = error as BusinessError;
console.error(`getInputSurface call failed. error code: ${err.code}`);
}
if (videoSurfaceId === undefined) {
return;
}
// 12. 创建视频输出流(绑定录制器Surface)
let videoOutput: camera.VideoOutput | undefined = undefined;
try {
videoOutput = cameraManager.createVideoOutput(videoProfile, videoSurfaceId);
} catch (error) {
let err = error as BusinessError;
console.error(`Failed to create the videoOutput instance. error: ${JSON.stringify(err)}`);
}
if (videoOutput === undefined) {
return;
}
this.mVideoOutput = videoOutput;
// 监听视频输出流错误
videoOutput.on('error', (error: BusinessError) => {
console.error(`Preview output error code: ${error.code}`);
});
// 13. 创建视频会话(管理视频录制的数据流)
let videoSession: camera.VideoSession | undefined = undefined;
try {
videoSession = cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession;
} catch (error) {
let err = error as BusinessError;
console.error(`Failed to create the session instance. error: ${JSON.stringify(err)}`);
}
if (videoSession === undefined) {
return;
}
this.mVideoSession = videoSession;
// 监听视频会话错误
videoSession.on('error', (error: BusinessError) => {
console.error(`Video session error code: ${error.code}`);
});
// 14. 开始配置视频会话(添加输入/输出流前必须调用)
try {
videoSession.beginConfig();
} catch (error) {
let err = error as BusinessError;
console.error(`Failed to beginConfig. error: ${JSON.stringify(err)}`);
}
// 15. 创建相机输入流(绑定物理相机设备)
let cameraInput: camera.CameraInput | undefined = undefined;
try {
cameraInput = cameraManager.createCameraInput(cameraArray[0]);
} catch (error) {
let err = error as BusinessError;
console.error(`Failed to createCameraInput. error: ${JSON.stringify(err)}`);
}
if (cameraInput === undefined) {
return;
}
this.mCameraInput = cameraInput;
// 监听相机输入流错误
let cameraDevice: camera.CameraDevice = cameraArray[0];
cameraInput.on('error', cameraDevice, (error: BusinessError) => {
console.error(`Camera input error code: ${error.code}`);
});
// 打开相机(启动硬件相机)
try {
await cameraInput.open();
} catch (error) {
let err = error as BusinessError;
console.error(`Failed to open cameraInput. error: ${JSON.stringify(err)}`);
}
// 16. 向会话添加相机输入流(会话获取相机数据)
try {
videoSession.addInput(cameraInput);
} catch (error) {
let err = error as BusinessError;
console.error(`Failed to add cameraInput. error: ${JSON.stringify(err)}`);
}
// 17. 创建预览输出流(绑定UI预览Surface)
let previewOutput: camera.PreviewOutput | undefined = undefined;
try {
previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId);
} catch (error) {
let err = error as BusinessError;
console.error(`Failed to create the PreviewOutput instance. error: ${JSON.stringify(err)}`);
}
if (previewOutput === undefined) {
return;
}
this.mPreviewOutput = previewOutput;
// 向会话添加预览输出流(会话输出预览画面)
try {
videoSession.addOutput(previewOutput);
} catch (error) {
let err = error as BusinessError;
console.error(`Failed to add previewOutput. error: ${JSON.stringify(err)}`);
}
// 18. 向会话添加视频输出流(会话输出录像数据)
try {
videoSession.addOutput(videoOutput);
} catch (error) {
let err = error as BusinessError;
console.error(`Failed to add videoOutput. error: ${JSON.stringify(err)}`);
}
// 19. 提交会话配置(使配置生效)
try {
await videoSession.commitConfig();
} catch (error) {
let err = error as BusinessError;
console.error(`videoSession commitConfig error: ${JSON.stringify(err)}`);
}
// 20. 启动视频会话(开始预览和录像准备)
try {
await videoSession.start();
} catch (error) {
let err = error as BusinessError;
console.error(`videoSession start error: ${JSON.stringify(err)}`);
}
}
/**
* 开始录像
* 流程:启动视频输出流→启动音视频录制器
*/
public async startRecord() {
// 启动视频输出流(开始向录制器传输视频数据)
this.mVideoOutput?.start((err: BusinessError) => {
if (err) {
console.error(`Failed to start the video output. error: ${JSON.stringify(err)}`);
return;
}
console.info('Callback invoked to indicate the video output start success.');
});
// 启动音视频录制器(开始编码并写入文件)
try {
await this.mAvRecorder?.start();
} catch (error) {
let err = error as BusinessError;
console.error(`avRecorder start error: ${JSON.stringify(err)}`);
}
}
/**
* 停止录像
* 流程:停止视频输出流→停止音视频录制器
*/
public async stopRecorder() {
// 停止视频输出流(停止向录制器传输视频数据)
this.mVideoOutput?.stop((err: BusinessError) => {
if (err) {
console.error(`Failed to stop the video output. error: ${JSON.stringify(err)}`);
return;
}
console.info('Callback invoked to indicate the video output stop success.');
});
// 停止音视频录制器(停止编码并完成文件写入)
try {
await this.mAvRecorder?.stop();
} catch (error) {
let err = error as BusinessError;
console.error(`avRecorder stop error: ${JSON.stringify(err)}`);
}
}
/**
* 销毁视频相机(释放所有资源)
* 流程:停止会话→关闭文件→释放相机/预览/录像流→释放会话→置空对象
*/
public async destroyVideoCamera() {
// 停止当前视频会话(停止预览和录像)
await this.mVideoSession?.stop();
// 关闭录像文件(确保数据写入完成)
if (this.mFile) {
fs.closeSync(this.mFile);
this.mFile = null;
}
// 关闭相机输入流(释放物理相机)
await this.mCameraInput?.close();
// 释放预览输出流(释放UI预览资源)
await this.mPreviewOutput?.release();
// 释放录像输出流(释放录制器绑定的资源)
await this.mVideoOutput?.release();
// 释放视频会话(释放会话管理的所有资源)
await在HarmonyOS Next中,使用CameraKit API实现自定义相机录像。首先,通过cameraManager.getSupportedCameras()获取相机列表并创建CameraInput。然后,使用captureSession配置视频输出,通过VideoOutput类设置录像参数,如分辨率与帧率。调用captureSession.start()启动预览,使用VideoOutput.start()开始录制。录像数据可通过文件路径或回调接口获取。整个过程需在UI主线程外处理,并注意权限声明与资源释放。
在HarmonyOS Next中,应用可以通过CameraKit和AVRecorder等核心接口,自定义相机并实现录像功能。以下是关键步骤和代码示例:
1. 权限与配置
- 声明权限:在
module.json5中申请相机和麦克风权限。
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.CAMERA"
},
{
"name": "ohos.permission.MICROPHONE"
}
]
}
}
- 动态权限申请:在应用启动时通过
abilityAccessCtrl动态申请权限。
2. 初始化相机
使用CameraKit创建相机实例,并配置输出流。
import camera from '@ohos.multimedia.camera';
import image from '@ohos.multimedia.image';
import media from '@ohos.multimedia.media';
// 获取CameraManager实例
let cameraManager = camera.getCameraManager(context);
// 获取相机设备列表并选择后置摄像头
let cameraDevices = cameraManager.getSupportedCameras();
let cameraDevice = cameraDevices.find(device => device.lensFacing === camera.LensFacing.LENS_FACING_BACK);
// 创建相机输入流
let cameraInput = cameraManager.createCameraInput(cameraDevice);
// 创建预览输出流(用于画面预览)
let previewOutput = cameraManager.createPreviewOutput(previewSurfaceId);
// 创建视频输出流(用于录像)
let videoProfile = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
profile: {
audioBitrate: 48000,
audioChannels: 2,
audioCodec: media.CodecMimeType.AUDIO_AAC,
audioSampleRate: 48000,
fileFormat: media.ContainerFormatType.CFT_MPEG_4,
videoBitrate: 2000000,
videoCodec: media.CodecMimeType.VIDEO_AVC,
videoFrameWidth: 640,
videoFrameHeight: 480,
videoFrameRate: 30
}
};
let videoOutput = cameraManager.createVideoOutput(videoProfile);
3. 会话管理与录像控制
- 创建相机会话:绑定输入输出流,启动预览。
let captureSession = cameraManager.createCaptureSession();
await captureSession.beginConfig();
await captureSession.addInput(cameraInput);
await captureSession.addOutput(previewOutput);
await captureSession.addOutput(videoOutput);
await captureSession.commitConfig();
await captureSession.start();
- 开始/停止录像:通过
AVRecorder控制录像过程。
import media from '@ohos.multimedia.media';
// 创建AVRecorder实例
let avRecorder = await media.createAVRecorder();
// 准备录像配置
let avConfig = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
profile: {
audioBitrate: 48000,
audioChannels: 2,
audioCodec: media.CodecMimeType.AUDIO_AAC,
audioSampleRate: 48000,
fileFormat: media.ContainerFormatType.CFT_MPEG_4,
videoBitrate: 2000000,
videoCodec: media.CodecMimeType.VIDEO_AVC,
videoFrameWidth: 640,
videoFrameHeight: 480,
videoFrameRate: 30
},
url: 'file:///data/storage/el2/base/video.mp4' // 录像文件路径
};
await avRecorder.prepare(avConfig);
// 开始录像
await avRecorder.start();
// 停止录像
await avRecorder.stop();
await avRecorder.release();
4. 资源释放
在应用退出或相机不可用时,及时释放资源:
await captureSession.stop();
await cameraInput.release();
注意事项
- 帧率与分辨率:需根据设备支持的规格动态配置。
- 异步操作:所有相机操作均为异步,需使用
async/await或Promise处理。 - 兼容性:不同设备对编解码器和格式的支持可能存在差异,建议通过
CameraKit查询设备能力。
通过以上步骤,即可在HarmonyOS Next中实现自定义相机的录像功能。

