鸿蒙harmony 自定义相机录像功能实现
原理:借助XComponent的输入流,结合AVRecorder进行录像,示例功能有闪光灯,摄像头翻转等功能。
相关权限
- 允许应用使用相机:ohos.permission.CAMERA。
- 允许应用使用麦克风:ohos.permission.MICROPHONE。
- 允许应用访问用户媒体文件中的地理位置信息:ohos.permission.MEDIA_LOCATION
实现思路
- 通过cameraInput,获取相机采集数据,创建相机输入。
- 创建previewOutput,获取预览输出流,通过XComponent的surfaceId连接,送显XComponent。
- 通过AVRecorder的surfaceId创建录像输出流VideoOutput输出到文件中。
步骤:
- 获取相机管理器
let cameraManager: camera.CameraManager = camera.getCameraManager(getContext());
2.获取可使用的相机列表
let cameraArray: Array<camera.CameraDevice> = cameraManager.getSupportedCameras();
3.获取指定相机所支持的模式 如 拍照 录像 安全相机等
let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]);
4.根据 指定相机(如后置摄像头) 获取该相机设备输入对象 会话中Session可能会使用的相机信息
let cameraInput: camera.CameraInput = cameraManager.createCameraInput(cameraArray[0]);
5.查询相机设备在模式下支持的输出能力 如后置摄像头的录像功能输出能力(如支持的 预览配置信息集合 拍照配置信息集合 录像配置信息集合 metadata流类型信息集合)
let cameraOutputCapability: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraArray[0], sceneModes[0]);
6.创建预览输出对象 传入xComponent的surfaceId 以及 特定相机特定模式的 预览输出流配置信息,将相机的预览输出流展示在xComponent
let previewOutput: camera.PreviewOutput = cameraManager.createPreviewOutput(cameraOutputCapability.previewProfiles[0], surfaceId);
7.avRecorder音视频录制参数设置 音视频录制的参数配置
let avRecorder: media.AVRecorder = await media.createAVRecorder(); avRecorder.prepare(aVRecorderConfig);
8.创建录像输出对象 videoOutput = cameraManager.createVideoOutput(videoProfile, videoSurfaceId);
9.创建会话 videoSession = cameraManager.createSession(sceneModes[0])
10.会话配置
videoSession.beginConfig();
// 向会话中添加 相机输入流 CameraInput
videoSession.addInput(cameraInput);
// 向会话中添加预览输出流
videoSession.addOutput(previewOutput);
// 向会话中添加录像输出流
videoSession.addOutput(videoOutput);
// 提交会话配置
videoSession.commitConfig();
// 启动会话
videoSession.start();
11.录像开始
// 启动录像输出流
if(this.videoOutput){
this.videoOutput.start()
// 开始录像
if(this.avRecorder){
this.avRecorder.start();
}
}
12.停止录像
if(this.avRecorder){
this.avRecorder.stop()
}
Demo:
import { camera } from '@kit.CameraKit';
import { media } from '@kit.MediaKit';
import fileIo from '@ohos.file.fs';
@Entry
@Component
struct TakeVideoPage {
xComponentController: XComponentController = new XComponentController()
@State xComponentSurfaceId: string = ''
@State videoOutput:camera.VideoOutput|null = null
@State avRecorder: media.AVRecorder|null = null
aboutToAppear(){
}
async getCameraVideo(surfaceId:string){
// 获取相机管理器
let cameraManager: camera.CameraManager = camera.getCameraManager(getContext());
//获取可使用的相机列表
let cameraArray: Array<camera.CameraDevice> = cameraManager.getSupportedCameras();
//获取指定相机所支持的模式 如 拍照 录像 安全相机等
let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]);
//根据 指定相机(如后置摄像头) 获取该相机设备输入对象 会话中Session可能会使用的相机信息
let cameraInput: camera.CameraInput = cameraManager.createCameraInput(cameraArray[0]);
//查询相机设备在模式下支持的输出能力 如后置摄像头的录像功能输出能力(如支持的 预览配置信息集合 拍照配置信息集合 录像配置信息集合 metadata流类型信息集合)
let cameraOutputCapability: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraArray[0], sceneModes[0]);
//创建预览输出对象 传入xComponent的surfaceId 以及 特定相机的 预览配置信息 并返回预览输出类
let previewOutput: camera.PreviewOutput = cameraManager.createPreviewOutput(cameraOutputCapability.previewProfiles[0], surfaceId);
//获取音视频录制管理实例
let avRecorder: media.AVRecorder = await media.createAVRecorder();
this.avRecorder = avRecorder
// 获取录制视频需要的surface,此surface将提供给调用者 调用者从此surface中获取surfaceBuffer 填入相应的视频数据
let videoSurfaceId = await avRecorder.getInputSurface();
let aVRecorderProfile: media.AVRecorderProfile = {
fileFormat : media.ContainerFormatType.CFT_MPEG_4, // 视频文件封装格式,只支持MP4
videoBitrate : 100000, // 视频比特率
videoCodec : media.CodecMimeType.VIDEO_AVC, // 视频文件编码格式,支持avc格式
videoFrameWidth : 640, // 视频分辨率的宽
videoFrameHeight : 480, // 视频分辨率的高
videoFrameRate : 30 // 视频帧率
};
//在应用沙箱中创建文件,并开启读写权限
let filePath = getContext().filesDir + '/' + Date.now() + '.mp4'
let file = fileIo.openSync(filePath,fileIo.OpenMode.READ_WRITE)
let aVRecorderConfig: media.AVRecorderConfig = {
videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
profile: aVRecorderProfile,
url: "fd://" + file.fd,//获取文件的fd,且创建的文件要有读写权限
rotation: 90 // 90°为默认竖屏显示角度,如果由于设备原因或应用期望以其他方式显示等原因,请根据实际情况调整该参数
};
// avRecorder准备 音视频录制的参数配置
avRecorder.prepare(aVRecorderConfig);
// 创建VideoOutput对象
let videoOutput: camera.VideoOutput | undefined = undefined;
// createVideoOutput传入的videoProfile对象的宽高需要和aVRecorderProfile保持一致。
let videoProfile: undefined | camera.VideoProfile = cameraOutputCapability.videoProfiles.find((profile: camera.VideoProfile) => {
return profile.size.width === aVRecorderProfile.videoFrameWidth && profile.size.height === aVRecorderProfile.videoFrameHeight;
});
//创建录像输出对象
videoOutput = cameraManager.createVideoOutput(videoProfile, videoSurfaceId);
this.videoOutput = videoOutput
//创建会话
let videoSession: camera.Session | undefined = undefined;
videoSession = cameraManager.createSession(sceneModes[0])
//普通录像模式会话类 保存一次相机运行所需要的所有资源 相机输入流、输出流等
videoSession.beginConfig();
// 向会话中添加 相机输入流 CameraInput
videoSession.addInput(cameraInput);
// 向会话中添加预览输出流
videoSession.addOutput(previewOutput);
// 向会话中添加录像输出流
videoSession.addOutput(videoOutput);
// 提交会话配置
videoSession.commitConfig();
// 启动会话
videoSession.start();
}
build() {
Column(){
XComponent({type:XComponentType.SURFACE,controller:this.xComponentController})
.width('100%')
.height('50%')
.onLoad(() => {
this.xComponentSurfaceId = this.xComponentController.getXComponentSurfaceId()
this.getCameraVideo(this.xComponentSurfaceId)
})
Button('开始录像')
.width('90%')
.height(50)
.margin({bottom:50})
.onClick(() => {
// 启动录像输出流
if(this.videoOutput){
this.videoOutput.start()
// 开始录像
if(this.avRecorder){
this.avRecorder.start();
}
}
})
Button('停止录像')
.width('90%')
.height(50)
.onClick(() => {
if(this.avRecorder){
this.avRecorder.stop()
}
})
}
.width('100%')
.height('100%')
}
}