如何进行视频压缩
========

#### 问题现象

视频压缩处理是影音娱乐类应用中的典型场景之一，如上传、发送或保存视频时存在大小限制，需要对视频进行压缩，保证视频质量及大小满足用户需要。

#### 解决方案

*   **方案一**
    1.  利用三方库videocompressor的视频压缩功能来实现，具体步骤如下：
        
        static async selectVideo(selectNumber: number): Promise<photoAccessHelper.PhotoSelectResult> {
          let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
         _ // 设置要选择的媒体文件类型_
          photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.VIDEO\_TYPE;
         _ // 设置选择文件最大数量_
          photoSelectOptions.maxSelectNumber = selectNumber;
          let photoPicker = new photoAccessHelper.PhotoViewPicker();
         _ // PhotoViewPicker的select方法不需要特殊权限即可读取到图片或者视频文件_
          return await photoPicker.select(photoSelectOptions);
        }
        
        _// 获取视频路径_
        async selectAndCompressVideo() {
          let selectResult: photoAccessHelper.PhotoSelectResult = await PictureUtils.selectVideo(1);
          _// 得到选中视频的uri_
          this.videoPath = selectResult.photoUris\[0\];
          hilog.info(0x0000, TAG, 'videoCompressor select selectFilePath:' + this.videoPath)
        }
        
    2.  调用compressVideo方法并指定相应质量进行压缩。
        
        compressVideo(quality: CompressQuality, qualityTag: string) {
          this.quantity = qualityTag;
          let videoCompressor = new VideoCompressor();
          _// 调用compressVideo方法并指定相应质量进行压缩_
          videoCompressor.compressVideo(this.uiContext.getHostContext()!, this.videoPath, quality)
            .then((data) => {
              if (data.code == CompressorResponseCode.SUCCESS) {
                hilog.info(0x0000, TAG,
                  'videoCompressor ' + qualityTag + ' message:' + data.message + '--outputPath:' + data.outputPath);
               _ // 获取存在应用沙箱路径下的压缩后的视频文件，点击保存按钮后将其写入相册_
                this.buffer = PictureUtils.compressVideo(data.outputPath);
                hilog.info(0x0000, TAG, \`The buffer: ${JSON.stringify(this.buffer)}\`);
                this.uiContext.showAlertDialog({ message: '已压缩完成，可执行下载!' });
              } else {
                hilog.info(0x0000, TAG,
                  'videoCompressor ' + qualityTag + ' code:' + data.code + '--error message:' + data.message);
              }
            }).catch((err: Error) => {
            hilog.info(0x0000, TAG, 'videoCompressor ' + qualityTag + ' get error message' + err.message);
          })
        }
        
    3.  压缩完成后，视频保存至沙箱。
        
        try {
          let parentDir = this.uiContext.getHostContext()!.filesDir;
          let fileName: string = Date.now() + '-' + this.quantity + '.mp4';
          this.fileDir = parentDir + '/' + fileName;
          let file = await fileIo.open(this.fileDir, fileIo.OpenMode.READ\_WRITE | fileIo.OpenMode.CREATE);
         _ // 写入文件_
          await fileIo.write(file.fd, this.buffer);
          this.compressedSize = (await fileIo.stat(file.fd)).size;
          _// 关闭文件_
          await fileIo.close(file.fd);
          this.uiContext.showAlertDialog({ message: '保存成功!' });
        } catch (error) {
          hilog.error(0x0000, TAG, 'error is' + JSON.stringify(error));
          this.uiContext.showAlertDialog({ message: '保存失败!' });
        }
        
*   **方案二**
    
    使用AVCodec Kit视频编码功能，将未经压缩的视频数据传输至视频编码器进行编码处理，通过设定所需的编码格式、比特率、帧率等参数，并对编码输出进行控制，从而实现视频文件的压缩目的，编码器参数配置范围请参考：[获取支持的编解码能力](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/obtain-supported-codecs)。
    
    int32\_t VideoEncoder::Configure(const SampleInfo &sampleInfo) {
        OH\_AVFormat \*format = OH\_AVFormat\_Create();
        CHECK\_AND\_RETURN\_RET\_LOG(format != nullptr, AVCODEC\_SAMPLE\_ERR\_ERROR, "AVFormat create failed");
    
        OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_WIDTH, sampleInfo.videoWidth);
        OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_HEIGHT, sampleInfo.videoHeight);
        OH\_AVFormat\_SetDoubleValue(format, OH\_MD\_KEY\_FRAME\_RATE, sampleInfo.frameRate);
        OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_PIXEL\_FORMAT, sampleInfo.pixelFormat);
        OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_VIDEO\_ENCODE\_BITRATE\_MODE, sampleInfo.bitrateMode);
        OH\_AVFormat\_SetLongValue(format, OH\_MD\_KEY\_BITRATE, sampleInfo.bitrate);
        OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_PROFILE, sampleInfo.hevcProfile);
       _ // Setting HDRVivid-related parameters_
        if (sampleInfo.isHDRVivid) {
            OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_I\_FRAME\_INTERVAL, sampleInfo.iFrameInterval);
            OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_RANGE\_FLAG, sampleInfo.rangFlag);
            OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_COLOR\_PRIMARIES, sampleInfo.primary);
            OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_TRANSFER\_CHARACTERISTICS, sampleInfo.transfer);
            OH\_AVFormat\_SetIntValue(format, OH\_MD\_KEY\_MATRIX\_COEFFICIENTS, sampleInfo.matrix);
        }
       _ // ..._
    
       _ // Setting the Encoder_
        int ret = OH\_VideoEncoder\_Configure(encoder\_, format);
        OH\_AVFormat\_Destroy(format);
        format = nullptr;
        CHECK\_AND\_RETURN\_RET\_LOG(ret == AV\_ERR\_OK, AVCODEC\_SAMPLE\_ERR\_ERROR, "Config failed, ret: %{public}d", ret);
        return AVCODEC\_SAMPLE\_ERR\_OK;
    }
    
    示例代码参考：[基于AVCodec能力的视频编解码](https://gitcode.com/HarmonyOS_Samples/AVCodecVideo)。
    
*   **方案三**
    
    如果是视频拍摄过程中的场景，可通过AVRecorder设立录像输出流并调整自定义录像参数，从而调控视频文件的比特率与分辨率等特性，进而实现对视频文件大小的有效管理。
    
    let videoProfilesArray: Array<camera.VideoProfile> = cameraOutputCap.videoProfiles;
    if (!videoProfilesArray) {
      Logger.error(TAG, 'createOutput videoProfilesArray === null || undefined');
    }
    _// ..._
    
    let aVRecorderProfile: media.AVRecorderProfile = {
      _// ..._
      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 = {
      _// ..._
      videoSourceType: media.VideoSourceType.VIDEO\_SOURCE\_TYPE\_SURFACE\_YUV,
      profile: aVRecorderProfile,
      url: this.url,
      rotation: 90,
      _// ..._
    };
    
    示例代码参考：[基于CameraKit通过AVRecorder录像](https://gitcode.com/harmonyos_samples/camera-kit-avrecorder)。
    

*   **方案四**
    
    ArkTS提供了AVTranscoder接口用于视频转码。视频的码率和分辨率是影响视频画质和文件大小的关键因素，通过AVTranscoder进行视频转码，可以实现节省视频存储空间和提高视频文件的设备兼容性。
    
    AVTranscoder使用方式及常见问题请参考文档：[视频转码](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/using-avtranscoder-for-transcodering)。