• 作者:老汪软件技巧
  • 发表时间:2024-09-12 04:01
  • 浏览量:

鸿蒙视频封面获取前言

最近鸿蒙的功能做的差不多了,又有时间写写文章了,这篇文章要写的就是如何从鸿蒙本地视频里获取封面。

获取视频

我这是直接从相册获取获取视频,然后处理拿到封面的,前面也有写过获取视频的,这里再写一下吧:

  /**
   * 相册选取照片
   *
   * @param args 控制参数
   * @param callback H5侧回调函数
   */
  pickMedia(args: Array<number>): Promise<string> {
    return new Promise((resolve, reject) => {
      try {
        // 控制参数
        let type = args[0] as number;
        let num = args[1] as number;
        // 图片控制参数
        let fileType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
        if (type == 1) {
          fileType = photoAccessHelper.PhotoViewMIMETypes.VIDEO_TYPE;
        } else if (type == 2) {
          fileType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE;
        }
        let options = new photoAccessHelper.PhotoSelectOptions()
        options.MIMEType = fileType;
        options.maxSelectNumber = num
        // 通过系统相册选取
        this.openGallery(options, (urls) => {
          LogUtil.d('pickPhoto successful: ' + urls);
          resolve(JSON.stringify(urls))
        }, (error) => {
          LogUtil.e('pickPhoto error: ' + error);
          reject(error)
        })
      } catch (e) {
        LogUtil.e(e)
        reject(e)
      }
    });
  }
  
  /**
   * 打开相册选取图片、视频
   *
   * @param options 控制参数
   * @param onSuccess 成功回调
   * @param onFailed 失败回调
   */
  async openGallery(
    options: photoAccessHelper.PhotoSelectOptions,
    onSuccess: (uris: Array<string>)=> void,
    onFailed: (error: string)=>void
  ) {
    try {
      let photoPicker = new photoAccessHelper.PhotoViewPicker()
      photoPicker.select(options)
        .then((PhotoSelectResult: photoAccessHelper.PhotoSelectResult) => {
          // file://media/Photo/2/IMG_1713776063_001/screenshot_20240422_165243.jpg
          if (PhotoSelectResult.photoUris.length > 0) {
            onSuccess(PhotoSelectResult.photoUris)
          }
        }).catch((err: BusinessError) => {
          onFailed(err.message)
        })
    } catch (error) {
      let err: BusinessError = error as BusinessError
      onFailed(err.message)
    }
  }

复制到私有目录

获取视频封面这个功能调了好久,最后不知道怎么就好了,感觉还是得将数据复制到私有目录才能生效:

  /**
   * 复制文件到私有缓存目录
   *
   * @param path 原路径
   * @param type 类型
   * @param context 上下文
   * @returns 新路径
   */
  async copyFileToCache(path: string, type: string, context: Context): Promise<string>{
    return new Promise(async (resolve, reject) => {
      try {
        let file = await fs.open(path, fs.OpenMode.READ_ONLY);
        let fileName = file.name;
        if (file){
          let dir = context.cacheDir + '/' + type
          if (!fs.accessSync(dir)) {
            fs.mkdirSync(dir)
          }
          let newPath = context.cacheDir + `/${type}/${fileName}`
          await fs.copyFile(file.fd, newPath)
          LogUtil.d("copyFileToCache newPath: " + newPath)
          resolve(newPath)
        }
        reject("open file fail")
      } catch (err){
        LogUtil.d("copyFileToCache err: " + err.message)
        reject(err.message)
      }
    });
  }

取视频封面

视频封面怎么获取_视频封面获取鸿蒙怎么设置_

这里步骤有点长,一步一步来,我这先写了个入口,首先是复制到私有目录,再获取第一帧作为封面,如果没获取到再做个默认的:

  // 获取视频封面
  private async getVideoImage(uri: string): Promise<string> {
    let path = "";
    try {
      let mediaUtil = new MediaUtils();
      let transfer = new FileTransferUtil()
       
      // 需要先将文件复制到APP私有目录
      let localPath = await transfer.copyFileToCache(uriPath, 'video', this.context)
      LogUtil.d("getVideoImage localPath: " + localPath)
      // 先获取视频第一帧
      path = await mediaUtil.getVideoImage(this.context, localPath);
      LogUtil.d("getVideoImage path: " + path);
      // 默认图片
      if (path == "") {
        path = await this.getDefaultVideoImage();
        LogUtil.d("getVideoImage getDefaultVideoImage: " + path);
      }
    } catch (e) {
      LogUtil.d("getVideoImage error: " + e);
    }
    return path;
  }

下面着重看下如何获取第一帧:

  /**
   * 获取视频封面
   *
   * @param context 上下文
   * @param uri 视频路径
   */
  async getVideoImage(context: Context, localPath: string): Promise<PixelMap> {
    // 创建AVImageGenerator对象
    let avImageGenerator: media.AVImageGenerator = await media.createAVImageGenerator()
    // 设置fdSrc
    let file = fs.openSync(localPath, fs.OpenMode.READ_ONLY);
    // 设置fdSrc
    let avFileDescriptor: media.AVFileDescriptor = {
      fd: file.fd
    };
    avImageGenerator.fdSrc = avFileDescriptor;
    // 初始化入参
    let timeUs = 0
    let queryOption = media.AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC
    let param: media.PixelMapParams = {
      width : 300,
      height : 300
    }
    // 获取缩略图(promise模式)
    let pixelMap = await avImageGenerator.fetchFrameByTime(timeUs, queryOption, param);
    LogUtil.d("getVideoImage pixelMap: " + JSON.stringify(pixelMap))
    // 释放资源(promise模式)
    avImageGenerator.release()
    
    // 保存到文件
    return await this.savePixelMap(context, pixelMap);
    // 直接返回
    return pixelMap
  }

有点长,不过也就是从视频里面获取第一帧的PixelMap,实际就算Android的bitmap嘛,这里好坑官方demo只有resource资源的,说文件fd可以,但是没例子。

当然如果要把这个bitmap保存到文件,可以参考下面代码:


  /**
   * 保存pixelMap,返回路径
   * @param pm
   * @returns
   */
  private async savePixelMap(context: Context, pm: PixelMap): Promise<string> {
    if (pm === null) {
      LogUtil.e('传入的pm为空');
      return '';
    }
    try {
      return await this.packToFile(context, pm);
    } catch (err) {
      LogUtil.e("TAG", '保存文件失败,err=' + JSON.stringify(err));
      return '';
    }
  }
  private async packToFile(context: Context, pixelMap: PixelMap): Promise<string> {
    let fPath: string = context.cacheDir + '/image/' + this.getTimeStr() + '.jpg';
    let writeFd: fs.File = await fs.open(fPath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);
    let opts : image.PackingOption = { format: "image/jpeg", quality: 100};
    const imagePacker = image.createImagePacker();
    await imagePacker.packToFile(pixelMap, writeFd.fd, opts);
    fs.closeSync(writeFd.fd);
    return fPath;
  }
  private getTimeStr() {
    const now: Date = new Date();
    const year: number = now.getFullYear();
    const month: number = now.getMonth() + 1;
    const day: number = now.getDate();
    const hours: number = now.getHours();
    const minutes: number = now.getMinutes();
    const seconds: number = now.getSeconds();
    return `${year}${month}${day}_${hours}${minutes}${seconds}`;
  }

拿到bitmap或者文件路径,就可以在页面中显示,或者上传了。

小结

简单写了下从视频文件获取,视频第一帧图片获取,PixelMap(bitmap)保存到文件,方便页面显示或者封面文件上传。