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

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

参考文档

由于文件权限获取不太方便,现在使用的是 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%')
  }
}
相关推荐
anyup12 小时前
🔥2026最推荐的跨平台方案:H5/小程序/App/鸿蒙,一套代码搞定
前端·uni-app·harmonyos
Ranger092917 小时前
鸿蒙开发新范式:Gpui
rust·harmonyos
Huang兄17 小时前
鸿蒙-深色模式适配
harmonyos·arkts·arkui
SummerKaze3 天前
为鸿蒙开发者写一个 nvm:hmvm 的设计与实现
harmonyos
在人间耕耘4 天前
HarmonyOS Vision Kit 视觉AI实战:把官方 Demo 改造成一套能长期复用的组件库
人工智能·深度学习·harmonyos
王码码20354 天前
Flutter for OpenHarmony:socket_io_client 实时通信的事实标准(Node.js 后端的最佳拍档) 深度解析与鸿蒙适配指南
android·flutter·ui·华为·node.js·harmonyos
HarmonyOS_SDK4 天前
【FAQ】HarmonyOS SDK 闭源开放能力 — Ads Kit
harmonyos
Swift社区4 天前
如何利用 ArkUI 框架优化鸿蒙应用的渲染性能
华为·harmonyos
特立独行的猫a4 天前
uni-app x跨平台开发实战:开发鸿蒙HarmonyOS影视票房榜组件完整实现过程
华为·uni-app·harmonyos·轮播图·uniapp-x
盐焗西兰花5 天前
鸿蒙学习实战之路-STG系列(5/11)-守护策略管理-添加与修改策略
服务器·学习·harmonyos