HarmonyOs 录像自定义配置

489 阅读4分钟

鸿蒙harmony 自定义相机录像功能实现

原理:借助XComponent的输入流,结合AVRecorder进行录像,示例功能有闪光灯,摄像头翻转等功能。

相关权限

  • 允许应用使用相机:ohos.permission.CAMERA。
  • 允许应用使用麦克风:ohos.permission.MICROPHONE。
  • 允许应用访问用户媒体文件中的地理位置信息:ohos.permission.MEDIA_LOCATION

实现思路

  1. 通过cameraInput,获取相机采集数据,创建相机输入。
  2. 创建previewOutput,获取预览输出流,通过XComponent的surfaceId连接,送显XComponent。
  3. 通过AVRecorder的surfaceId创建录像输出流VideoOutput输出到文件中。

步骤:

  1. 获取相机管理器 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%')
  }
}