CreateCamera.ets 13.5 KB
import camera from '@ohos.multimedia.camera';
import media from '@ohos.multimedia.media';
import { BusinessError } from '@ohos.base';
import { promptAction, router } from '@kit.ArkUI'
import { common } from '@kit.AbilityKit';
import { FileUtil, CommonConstants } from '../utils/FileUtil';
import { uploadFileByTask } from '../utils/uploadCloud'

/**
 * 视频录制
 */

interface routerParams {
  cosKeyStr: string;
  relateId: number;
}

const TAG: string = 'Record';
let context = getContext(this) as common.UIAbilityContext;
let routerParamsData: routerParams = router.getParams() as routerParams;
@Entry
@Component
struct CreateCamera {
  @State xComponentWidth: number = 0;
  @State xComponentHeight: number = 0;
  @State recording: boolean = false;
  @State path: string = '';
  @State countTime: number = 0;
  @State cameraManager: camera.CameraManager | undefined = undefined;
  @State videoOutput: camera.VideoOutput | undefined = undefined;
  @State captureSession: camera.Session | undefined = undefined;
  @State cameraInput: camera.CameraInput | undefined = undefined;
  @State previewOutput: camera.PreviewOutput | undefined = undefined;
  @State avRecorder: media.AVRecorder | undefined = undefined;
  private mXComponentController: XComponentController = new XComponentController;
  private surfaceId: string = '';
  url: string = '';
  timer: number = 0;

  aboutToAppear() {
    // 创建存放视频文件
    this.path = context.filesDir + '/' + 'VIDEO_' + Date.parse(new Date().toString()) + '.mp4';
    let file = FileUtil.createOrOpen(this.path);
    this.url = 'fd://' + file.fd;
  }

  build() {
    Stack({ alignContent: Alignment.Top }) {
      XComponent({
        id: 'componentId',
        type: XComponentType.SURFACE,
        controller: this.mXComponentController,
      })
        .onLoad(async () => {
          this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
          await this.initCamera(getContext(this), this.surfaceId);
        })
        .height(CommonConstants.SEVENTY_PERCENT)
        .margin({
          top: CommonConstants.FIFTEEN_PERCENT
        })
      Column() {
        Text(`录制时长:${this.countTime}s`).fontSize(16).fontColor(Color.White)
        Blank()
        Row() {
          Image(this.recording ? $r('app.media.camera_pause_video_4x') : $r('app.media.camera_take_video_4x'))
            .width(px2vp(CommonConstants.IMAGE_SIZE))
            .height(px2vp(CommonConstants.IMAGE_SIZE))
            .onClick(async () => {
              if (this.recording) {
                clearInterval(this.timer);
                this.timer = 0
                await this.stopRecord();
                await uploadFileByTask(routerParamsData.cosKeyStr, routerParamsData.relateId, this.path);
                router.back()
              } else {
                this.timer = setInterval(async () => {
                  this.countTime++;
                  if(this.countTime >= 120){
                    clearInterval(this.timer);
                    this.timer = 0
                    await this.stopRecord();
                    await uploadFileByTask(routerParamsData.cosKeyStr, routerParamsData.relateId, this.path);
                    router.back()
                  }
                }, 1000);
                this.startRecord();
              }
              this.recording = !this.recording;
            })
        }
        .width(CommonConstants.FULL_PERCENT)
        .height(120)
        .margin({ bottom: 60 })
        .justifyContent(FlexAlign.Center)
        .alignItems(VerticalAlign.Center)
      }
      .width(CommonConstants.FULL_PERCENT)
      .height(CommonConstants.FULL_PERCENT)
      .justifyContent(FlexAlign.Start)
      .alignItems(HorizontalAlign.Start)
    }
    .backgroundColor(Color.Black)
    .width(CommonConstants.FULL_PERCENT)
    .height(CommonConstants.FULL_PERCENT)
  }

  // 初始化相机
  async initCamera(context: common.Context, surfaceId: string) {
    this.cameraManager = camera.getCameraManager(context);
    if (!this.cameraManager) {
      promptAction.showToast({ message: 'camera.getCameraManager error' })
      return;
    }

    this.cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => {
      promptAction.showToast({ message: `camera : ${cameraStatusInfo.camera.cameraId},status:  ${cameraStatusInfo.status}` })
    });

    let cameraArray: Array<camera.CameraDevice> = [];
    try {
      cameraArray = this.cameraManager.getSupportedCameras();
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `getSupportedCameras call failed. error code: ${err.code}` })
    }

    if (cameraArray.length <= 0) {
      promptAction.showToast({ message: 'cameraManager.getSupportedCameras error' })
      return;
    }

    let cameraOutputCap: camera.CameraOutputCapability =
      this.cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_VIDEO);
    if (!cameraOutputCap) {
      promptAction.showToast({ message: 'cameraManager.getSupportedOutputCapability error' })
      return;
    }
    promptAction.showToast({ message: 'outputCapability: ' + JSON.stringify(cameraOutputCap) })

    let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles;
    if (!previewProfilesArray) {
      promptAction.showToast({ message: 'createOutput previewProfilesArray === null || undefined' })
    }

    let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles;
    if (!photoProfilesArray) {
      promptAction.showToast({ message: 'createOutput photoProfilesArray === null || undefined' })
    }

    let videoProfilesArray: Array<camera.VideoProfile> = cameraOutputCap.videoProfiles;
    if (!videoProfilesArray) {
      promptAction.showToast({ message: 'createOutput videoProfilesArray === null || undefined' })
    }

    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) {
      promptAction.showToast({ message: '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_AVC,
      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,
      rotation: 0,
      location: {
        latitude: 30,
        longitude: 130
      }
    };

    try {
      this.avRecorder = await media.createAVRecorder();
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `createAVRecorder call failed. error code: ${err.code}` })
    }

    if (this.avRecorder === undefined) {
      return;
    }

    try {
      await this.avRecorder.prepare(aVRecorderConfig);
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `prepare call failed. error code: ${err.code}` })
    }

    let videoSurfaceId: string | undefined = undefined;
    try {
      videoSurfaceId = await this.avRecorder.getInputSurface();
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `getInputSurface call failed. error code: ${err.code}` })
    }
    if (videoSurfaceId === undefined) {
      return;
    }

    try {
      this.videoOutput = this.cameraManager.createVideoOutput(videoProfile, videoSurfaceId);
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `Failed to create the videoOutput instance. error: ${JSON.stringify(err)}` })
    }
    if (this.videoOutput === undefined) {
      return;
    }
    this.videoOutput.on('frameStart', () => {
      promptAction.showToast({ message: 'Video frame started' })
    });

    this.videoOutput.on('error', (error: BusinessError) => {
      promptAction.showToast({ message: `Video frame error code: ${error.code}` })
    });

    try {
      this.captureSession = this.cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession;
      ;
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `Failed to create the CaptureSession instance. errorCode = ${err.code}` })
    }
    if (this.captureSession === undefined) {
      return;
    }

    try {
      this.captureSession.beginConfig();
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `Failed to beginConfig. errorCode = ${err.code}` })
    }

    let cameraInput: camera.CameraInput | undefined = undefined;
    try {
      cameraInput = this.cameraManager.createCameraInput(cameraArray[0]);
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `Failed to createCameraInput. errorCode = ${err.code}` })
    }
    if (cameraInput === undefined) {
      return;
    }

    let cameraDevice: camera.CameraDevice = cameraArray[0];
    cameraInput.on('error', cameraDevice, (error: BusinessError) => {
      promptAction.showToast({ message: `Camera input error code: ${error.code}` })
    });

    try {
      await cameraInput.open();
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `Failed to open cameraInput. errorCode = ${err.code}` })
    }

    try {
      this.captureSession.addInput(cameraInput);
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `Failed to add cameraInput. errorCode = ${err.code}` })
    }

    let previewOutput: camera.PreviewOutput | undefined = undefined;
    try {
      previewOutput = this.cameraManager.createPreviewOutput(videoProfile, surfaceId);
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `Failed to create the previewOutput instance. error: ${JSON.stringify(err)}` })
    }

    if (previewOutput === undefined) {
      return;
    }

    try {
      this.captureSession.addOutput(previewOutput);
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `Failed to add previewOutput. errorCode = ${err.code}` })
    }

    try {
      this.captureSession.addOutput(this.videoOutput);
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `Failed to add videoOutput. errorCode = ${err.code}` })
    }

    try {
      await this.captureSession.commitConfig();
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `Failed to commitConfig. errorCode = ${err.code}` })
    }

    try {
      await this.captureSession.start();
    } catch (error) {
      let err = error as BusinessError;
      promptAction.showToast({ message: `Failed to start captureSession. errorCode = ${err.code}` })
    }

    this.videoOutput.start((err: BusinessError) => {
      if (err) {
        promptAction.showToast({ message: `Failed to start the video output. error: ${JSON.stringify(err)}` })
        return;
      }
      promptAction.showToast({message: 'Callback invoked to indicate the video output start success.'})
    });
  }

  // 开始录像
  async startRecord() {
    if (this.avRecorder) {
      try {
        await this.avRecorder.start();
      } catch (error) {
        let err = error as BusinessError;
        promptAction.showToast({ message: `avRecorder start error: ${JSON.stringify(err)}` })
      }
    }
  }

  // 停止录像
  async stopRecord() {
    if (this.avRecorder) {
      try {
        if (this.videoOutput) {
          this.videoOutput.stop((err: BusinessError) => {
            if (err) {
              promptAction.showToast({ message: `Failed to stop the video output. error: ${JSON.stringify(err)}` })
              return;
            }
            promptAction.showToast({message: 'Callback invoked to indicate the video output stop success.'})
          });
        }
        try {
          await this.avRecorder.stop();
          await this.avRecorder.release();
        } catch (error) {
          let err = error as BusinessError;
          promptAction.showToast({ message: `avRecorder stop error: ${JSON.stringify(err)}` })
        }
      } catch (error) {
        let err = error as BusinessError;
        promptAction.showToast({ message: `avRecorder stop error: ${JSON.stringify(err)}` })
      }
      try {
        if (this.captureSession) {
          this.captureSession.stop();
        }
        if (this.cameraInput) {
          this.cameraInput.close();
        }
        if (this.previewOutput) {
          this.previewOutput.release();
        }
        if (this.videoOutput) {
          this.videoOutput.release();
        }
        if (this.captureSession) {
          this.captureSession.release();
        }
        if (this.captureSession) {
          this.captureSession = undefined;
        }
      } catch (error) {
        let err = error as BusinessError;
        promptAction.showToast({ message: `avRecorder stop error: ${JSON.stringify(err)}` })
      }
    }
  }
}