鸿蒙选择本地视频文件,并获取首帧预览图

选择本地视频文件,并获取首帧预览图

参考文档

由于文件权限获取不太方便,现在使用的是 picker 的方式获取本地视频文件。文件位于我的手机/下载目录下。

操作分为几步:

  1. 获取文件地址;
  2. 获取视频信息;
  3. 首帧截图;
  4. 显示截图;

获取文件地址

typescript 复制代码
try {
  let context = CCAppContext.context.getHostContext()!
  let documentSelectOptions = new picker.DocumentSelectOptions();
  documentSelectOptions.fileSuffixFilters = ['视频|.mp4', '视频|.avi']
  let documentPicker = new picker.DocumentViewPicker(context);
  documentPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {
    console.info('DocumentViewPicker.select successfully, documentSelectResult uri: ' + JSON.stringify(documentSelectResult));
    if (documentSelectResult.length > 0) {
      let uri = documentSelectResult[0] // 这个是获取到的文件地址
  }).catch((err: BusinessError) => {
    console.error(`DocumentViewPicker.select failed with err, code is: ${err.code}, message is: ${err.message}`);
  });
} catch (error) {
  let err: BusinessError = error as BusinessError;
  console.error(`DocumentViewPicker failed with err, code is: ${err.code}, message is: ${err.message}`);
}

获取视频信息 & 获取首帧截图 & 显示图片

typescript 复制代码
try {
  // 打开视频文件获取文件描述符
  let fd = await fileIo.open(videoPath, fileIo.OpenMode.READ_ONLY)

  const extractor = await media.createAVMetadataExtractor()
  extractor.fdSrc = { fd: fd.fd }

  let metaData = await extractor.fetchMetadata()
  this.imageWidth = parseInt(metaData.videoWidth || '1') // 视频宽度
  this.imageHeight = parseInt(metaData.videoHeight || '1') // 视频高度
  let orientation = metaData.videoOrientation // 视频旋转,截图有可能有旋转角度

  const avImageGenerator = await media.createAVImageGenerator()
  avImageGenerator.fdSrc = { fd: fd.fd }

  // 配置缩略图参数
  const param: media.PixelMapParams = {
    width: this.imageWidth,
    height: this.imageHeight,
  }
  this.pixelMap = await avImageGenerator.fetchFrameByTime(
    0, // 0表示首帧(单位微秒)
    media.AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC,
    param
  )
  await avImageGenerator.release() // 释放资源
  fileIo.close(fd) // 关闭文件
} catch (error) {
  console.error(`Get thumbnail failed: ${error.code}, ${error.message}`)
}

完整代码

typescript 复制代码
import { fileIo, fileUri, picker } from '@kit.CoreFileKit';
import { BusinessError } from '@ohos.base'
import { media } from '@kit.MediaKit';

@Entry
struct test {
  @State pixelMap: PixelMap | undefined = undefined
  @State imageWidth: number = 1
  @State imageHeight: number = 1
  @State orientation: number = 0

  chooseFile() {
    try {
      let context = getContext()
      let documentSelectOptions = new picker.DocumentSelectOptions();
      documentSelectOptions.fileSuffixFilters = ['视频|.mp4', '视频|.avi']
      let documentPicker = new picker.DocumentViewPicker(context);
      documentPicker.select(documentSelectOptions).then((documentSelectResult: Array<string>) => {
        console.info('DocumentViewPicker.select successfully, documentSelectResult uri: ' + JSON.stringify(documentSelectResult));
        if (documentSelectResult.length > 0) {
          let uri = documentSelectResult[0]
          this.getFirstFrame(uri)
        }
      }).catch((err: BusinessError) => {
        console.error(`DocumentViewPicker.select failed with err, code is: ${err.code}, message is: ${err.message}`);
      });
    } catch (error) {
      let err: BusinessError = error as BusinessError;
      console.error(`DocumentViewPicker failed with err, code is: ${err.code}, message is: ${err.message}`);
    }
  }

  async getFirstFrame(videoPath: string) {
    try {
      // 打开视频文件获取文件描述符
      let fd = await fileIo.open(videoPath, fileIo.OpenMode.READ_ONLY);

      const extractor = await media.createAVMetadataExtractor()
      extractor.fdSrc = { fd: fd.fd };

      let metaData = await extractor.fetchMetadata()
      this.imageWidth = parseInt(metaData.videoWidth || '1')
      this.imageHeight = parseInt(metaData.videoHeight || '1')
      this.orientation = parseInt(metaData.videoOrientation || '0')

      const avImageGenerator = await media.createAVImageGenerator();
      avImageGenerator.fdSrc = { fd: fd.fd };

      // 配置缩略图参数
      const param: media.PixelMapParams = {
        width: this.imageWidth,
        height: this.imageHeight
      };
      this.pixelMap = await avImageGenerator.fetchFrameByTime(
        0, // 0表示首帧(单位微秒)
        media.AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC,
        param
      );
      await avImageGenerator.release(); // 释放资源
      fileIo.close(fd); // 关闭文件
    } catch (error) {
      console.error(`Get thumbnail failed: ${error.code}, ${error.message}`);
    }
  }

  build() {
    Column() {
      Text('截图')
        .fontSize('22fp')
        .fontColor(Color.Black)
        .onClick(() => {
          this.chooseFile()
        })

      Image(this.pixelMap)
        .objectFit(ImageFit.Cover)
        .width('30%')
        .aspectRatio(1)
        .orientation(this.orientation)
    }
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
    .width('100%')
    .height('100%')
  }
}
相关推荐
Georgewu2 小时前
【HarmonyOS 7】 HarmonyOS 应用 Skill创建和编排详解
harmonyos
Georgewu4 小时前
【HarmonyOS 7】 应用 Agent 接入云 A2A 与端 A2A 的实践详解
harmonyos
马剑威(威哥爱编程)10 小时前
【共创稿事节】HarmonyOS 7 Agent 开发实战 05:流式输出做打字机对话
华为·harmonyos
AI备忘录11 小时前
(二十五)华为华三锐捷迈普思科 ERPS 环网保护配置命令(G.8032 五厂商对照)
运维·服务器·网络·网络协议·华为
OH_TPC13 小时前
【鸿蒙优选三方库】@ohos/coap:让鸿蒙设备说上 IoT 的母语
物联网·华为·harmonyos·鸿蒙
程序猿追14 小时前
渐变中间那一格怎么算?用 colormath 做个鸿蒙调色器
华为·harmonyos
程序猿追14 小时前
在 HarmonyOS 6 上做了张 3D 翻转卡片,正面背面来回翻
3d·华为·harmonyos
小π军15 小时前
安卓、iOS、鸿蒙三端的理解
android·ios·harmonyos
m0_7381858215 小时前
Flutter 鸿蒙化实战:volume_controller 适配 OpenHarmony,系统音量控制
flutter·华为·harmonyos·鸿蒙
用户1179104883316 小时前
每个会话一个子进程:多会话真并行的架构取舍
开源·harmonyos