录像实现,如何自定义录像功能的实现

​录像实现

HarmonyOS
2024-05-28 20:07:03
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
jennyhan

本文主要介绍自定义录像功能的实现

相关核心API

相机管理

媒体服务

核心代码介绍

初始化相机

async initCamera(baseContext: ESObject, surfaceId: string) { 
  cameraManager = camera.getCameraManager(baseContext); 
  if (!cameraManager) { 
    Logger.error(TAG, "camera.getCameraManager error"); 
    return; 
  } 
 
  //监听照相机状态变化 
  cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => { 
    Logger.info(TAG, `camera : ${cameraStatusInfo.camera.cameraId}`); 
    Logger.info(TAG, `status: ${cameraStatusInfo.status}`); 
  }); 
 
  // 获取相机列表 
  let cameraArray: Array<camera.CameraDevice> = cameraManager.getSupportedCameras(); 
  if (cameraArray.length <= 0) { 
    Logger.error(TAG, "cameraManager.getSupportedCameras error"); 
    return; 
  } 
 
  for (let index = 0; index < cameraArray.length; index++) { 
    Logger.info(TAG, 'cameraId : ' + cameraArray[index].cameraId); // 获取相机ID 
    Logger.info(TAG, 'cameraPosition : ' + cameraArray[index].cameraPosition); // 获取相机位置 
    Logger.info(TAG, 'cameraType : ' + cameraArray[index].cameraType); // 获取相机类型 
    Logger.info(TAG, 'connectionType : ' + cameraArray[index].connectionType); // 获取相机连接类型 
  } 
 
  // 获取相机设备支持的输出流能力 
  let cameraOutputCap: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraArray[0]); 
  if (!cameraOutputCap) { 
    Logger.error(TAG, "cameraManager.getSupportedOutputCapability error"); 
    return; 
  } 
  Logger.info(TAG, "outputCapability: " + JSON.stringify(cameraOutputCap)); 
 
  //预览流与录像输出流的分辨率的宽高比要保持一致 
  let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles; 
 
  let position: number = 0; 
  if (previewProfilesArray != null) { 
    previewProfilesArray.forEach((value: camera.Profile, index: number) => { 
      Logger.info(TAG, `支持的预览尺寸: [${value.size.width},${value.size.height}]`); 
      if (value.size.width === 1920 && value.size.height === 1080) { 
        position = index; 
      } 
    }) 
  } else { 
    Logger.error(TAG, "createOutput photoProfilesArray == null || undefined"); 
  } 
 
  let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles; 
  if (!photoProfilesArray) { 
    Logger.error(TAG, "createOutput photoProfilesArray == null || undefined"); 
  } 
 
  this.xComponentWidth = previewProfilesArray[position].size.width; 
  this.xComponentHeight = previewProfilesArray[position].size.height; 
 
  this.mXComponentController.setXComponentSurfaceSize({ 
    surfaceWidth: previewProfilesArray[position].size.width, 
    surfaceHeight: previewProfilesArray[position].size.height 
  }); 
 
  let videoProfilesArray: Array<camera.VideoProfile> = cameraOutputCap.videoProfiles; 
  if (!videoProfilesArray) { 
    console.error("createOutput videoProfilesArray == null || undefined"); 
  } 
 
  let metadataObjectTypesArray: Array<camera.MetadataObjectType> = cameraOutputCap.supportedMetadataObjectTypes; 
  if (!metadataObjectTypesArray) { 
    console.error("createOutput metadataObjectTypesArray == null || undefined"); 
  }
// videoProfile的宽高需要与AVRecorderProfile的宽高保持一致,并且需要使用AVRecorderProfile锁支持的宽高 
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; 
} 
 
// 配置参数以实际硬件设备支持的范围为准 
let aVRecorderProfile: media.AVRecorderProfile = { 
  audioBitrate: 48000, 
  audioChannels: 2, 
  audioCodec: media.CodecMimeType.AUDIO_AAC, 
  audioSampleRate: 48000, 
  fileFormat: media.ContainerFormatType.CFT_MPEG_4, 
  videoBitrate: 2000000, 
  videoCodec: media.CodecMimeType.VIDEO_MPEG4, 
  videoFrameWidth: videoSize.width, 
  videoFrameHeight: videoSize.height, 
  videoFrameRate: 30 
};
 let aVRecorderConfig: media.AVRecorderConfig = { 
    audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, 
    videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV, 
    profile: aVRecorderProfile, 
    url: this.url, // 文件需先由调用者创建,赋予读写权限,将文件fd传给此参数,eg.fd://45--file:///data/media/01.mp4 
    rotation: 0, // 合理值0、90、180、270,非合理值prepare接口将报错 
    location: { latitude: 30, longitude: 130 } 
  }; 
  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; 
  } 
 
  try { 
    await avRecorder.prepare(aVRecorderConfig); 
  } catch (error) { 
    let err = error as BusinessError; 
    console.error(`prepare call failed. error code: ${err.code}`); 
  } 
  let videoSurfaceId: string | undefined = undefined; // 该surfaceID用于传递给相机接口创造videoOutput 
  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; 
  } 
  // 创建VideoOutput对象 
  try { 
    videoOutput = cameraManager.createVideoOutput(videoProfilesArray[0], videoSurfaceId); 
  } catch (error) { 
    let err = error as BusinessError; 
    console.error(`Failed to create the videoOutput instance. error: ${JSON.stringify(err)}`); 
  } 
  if (videoOutput === undefined) { 
    return; 
  } 
  // 监听视频输出错误信息 
  videoOutput.on('error', (error: BusinessError) => { 
    console.log(`Preview output error code: ${error.code}`); 
  }); 
  //创建会话 
  try { 
    captureSession = cameraManager.createCaptureSession(); 
  } catch (error) { 
    let err = error as BusinessError; 
    console.error('Failed to create the CaptureSession instance. errorCode = ' + err.code); 
  } 
  if (captureSession === undefined) { 
    return; 
  } 
  // 监听session错误信息 
  captureSession.on('error', (error: BusinessError) => { 
    Logger.error(TAG, `Capture session error code: ${error.code}`); 
  }); 
  // 开始配置会话 
  try { 
    captureSession.beginConfig(); 
  } catch (error) { 
    let err = error as BusinessError; 
    Logger.error(TAG, 'Failed to beginConfig. errorCode = ' + err.code); 
  } 
  // 创建相机输入流 
  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; 
  } 
  // 监听cameraInput错误信息 
  let cameraDevice: camera.CameraDevice = cameraArray[0]; 
  cameraInput.on('error', cameraDevice, (error: BusinessError) => { 
    console.log(`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)}`); 
  } 
  // 向会话中添加相机输入流 
  try { 
    captureSession.addInput(cameraInput); 
  } catch (error) { 
    let err = error as BusinessError; 
    console.error(`Failed to add cameraInput. error: ${JSON.stringify(err)}`); 
  } 
  // 创建预览输出流,其中参数 surfaceId 参考下面 XComponent 组件,预览流为XComponent组件提供的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; 
  } 
  // 向会话中添加预览输入流 
  try { 
    captureSession.addOutput(previewOutput); 
  } catch (error) { 
    let err = error as BusinessError; 
    console.error(`Failed to add previewOutput. error: ${JSON.stringify(err)}`); 
  } 
  // 向会话中添加录像输出流 
  try { 
    captureSession.addOutput(videoOutput); 
  } catch (error) { 
    let err = error as BusinessError; 
    console.error(`Failed to add videoOutput. error: ${JSON.stringify(err)}`); 
  } 
  // 提交会话配置 
  try { 
    await captureSession.commitConfig(); 
  } catch (error) { 
    let err = error as BusinessError; 
    console.error(`captureSession commitConfig error: ${JSON.stringify(err)}`); 
  } 
  // 启动会话 
  try { 
    await captureSession.start(); 
  } catch (error) { 
    let err = error as BusinessError; 
    console.error(`captureSession start error: ${JSON.stringify(err)}`); 
  } 
  // 启动录像输出流 
  videoOutput.start((err: BusinessError) => { 
    if (err) { 
      console.error(`Failed to start the video output. error: ${JSON.stringify(err)}`); 
      return; 
    } 
    console.log('Callback invoked to indicate the video output start success.'); 
  }); 
}

开始录制

async startRecord() { 
  console.log(avRecorder.state) 
  // 开始录像 
  try { 
    avRecorder.start(); 
  } catch (error) { 
    let err = error as BusinessError; 
    console.error(`avRecorder start error: ${JSON.stringify(err)}`); 
  } 
}

停止录制

async stopRecord() { 
  // 停止录像 
  try { 
    // 停止录像输出流 
    videoOutput.stop((err: BusinessError) => { 
      if (err) { 
        console.error(`Failed to stop the video output. error: ${JSON.stringify(err)}`); 
        return; 
      } 
      console.log('Callback invoked to indicate the video output stop success.'); 
    }); 
    // 停止录像 
    try { 
      await avRecorder.stop(); 
     await this.avRecorder.release(); 
 
    } catch (error) { 
      let err = error as BusinessError; 
      console.error(`avRecorder stop error: ${JSON.stringify(err)}`); 
      console.log("失败") 
    } 
  } catch (error) { 
    let err = error as BusinessError; 
    Logger.error(TAG, `avRecorder stop error: ${JSON.stringify(err)}`); 
  } 
  // 停止当前会话 
  captureSession.stop(); 
  // 释放相机输入流 
  cameraInput.close(); 
  // 释放预览输出流 
  previewOutput.release(); 
  // 释放录像输出流 
  videoOutput.release(); 
  // 释放会话 
  captureSession.release(); 
  this.isFinished = true; 
}

申请权限

"requestPermissions": [ 
  { 
    "name": "ohos.permission.CAMERA" 
  }, 
  { 
    "name": "ohos.permission.MICROPHONE" 
  }, 
  { 
    "name": "ohos.permission.MEDIA_LOCATION" 
  }, 
  { 
    "name": "ohos.permission.WRITE_MEDIA" 
  }, 
  { 
    "name": "ohos.permission.READ_MEDIA" 
  } 
]

实现效果

适配版本信息

·IDE:DevEco Studio 4.0.1.501

·SDK:HarmoneyOS 4.0.0.38

分享
微博
QQ
微信
回复
2024-05-29 20:25:12
相关问题
如何实现H5自定义事件
711浏览 • 1回复 待解决
如何实现自定义应用入场动画
362浏览 • 1回复 待解决
如何实现一个自定义样式toast提示
575浏览 • 1回复 待解决
前后双摄录像怎样解决
5243浏览 • 2回复 待解决
自定义如何实现序列化
620浏览 • 1回复 待解决
后台录像需要怎么做呢?
2557浏览 • 1回复 待解决
鸿蒙怎么实现自定义布局Dialog
7651浏览 • 2回复 已解决
自定义弹窗自定义转场动画
382浏览 • 1回复 待解决
如何在全局实现一个自定义dialog弹窗
1054浏览 • 1回复 待解决