HarmonyOS鸿蒙Next中视频没有关键帧,怎么获取封面/缩略图?

HarmonyOS鸿蒙Next中视频没有关键帧,怎么获取封面/缩略图? 用 AVImageGenerator.fetchFrameByTime 获取的。

视频缩略图获取类,用于从视频资源中获取缩略图。

3 回复

使用 AV_IMAGE_QUERY_CLOSEST 来获取封面/缩略图就好了。

cke_7469.png

示例代码:

import { media } from '@kit.MediaKit';
import { fileIo as fs } from '@kit.CoreFileKit';

/**
 * 从视频中获取指定时间的缩略图/封面
 * @param uri 视频文件路径
 * @param size 目标图片尺寸 [宽度, 高度]
 * @param time 要截取的时间点(单位:秒)
 */
async function getVideoImage(uri: string, size: number[], time: number) {
  try {
    // 创建AVImageGenerator实例
    let avImageGenerator: media.AVImageGenerator = await media.createAVImageGenerator();

    // 打开视频文件
    let file = fs.openSync(uri, fs.OpenMode.READ_ONLY)
    let avFileDescriptor: media.AVFileDescriptor = { fd: file.fd };

    // 设置视频源
    avImageGenerator.fdSrc = avFileDescriptor

    // 将秒转换为微秒(1秒 = 1,000,000微秒)
    let timeUs = (time > 0) ? time * 1000000 : 0

    // 设置查询选项(选取离传入时间点最近的帧,该帧不一定是关键帧)
    let queryOption = media.AVImageQueryOptions.AV_IMAGE_QUERY_CLOSEST

    // 设置图片参数
    let param: media.PixelMapParams = {
      width: size[0],
      height: size[1],
    }

    // 异步获取指定时间的视频帧
    avImageGenerator.fetchFrameByTime(timeUs, queryOption, param)
      .then((pixelMap: PixelMap) => {
        // pixelMap 就是获取到的视频缩略图
        // 可以在这里处理pixelMap,例如显示或保存

        // 释放资源
        avImageGenerator.release()
          .catch(() => {
            console.error('资源释放失败')
          })

        // 关闭文件
        fs.closeSync(file)
      })
      .catch(() => {
        console.error('获取视频帧失败')
      })

  } catch (error) {
    console.error('获取视频图片失败:', error)
  }
}

更多关于HarmonyOS鸿蒙Next中视频没有关键帧,怎么获取封面/缩略图?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,若视频无关键帧,可通过VideoControllergetThumbnail方法获取封面。该方法支持指定时间点生成缩略图。也可使用PixelMap处理视频帧数据,结合Image组件展示。

在HarmonyOS Next中,如果视频文件本身缺少关键帧,使用AVImageGenerator.fetchFrameByTime方法获取封面或缩略图时,可能会遇到无法解码或返回黑帧/花屏的问题。这是因为该方法依赖于可解码的帧数据。

针对此情况,可以采取以下替代方案来获取可用的缩略图:

  1. 尝试多个时间点:由于缺少关键帧,特定时间点可能无法解码。可以循环尝试多个不同的时间点(例如0ms, 100ms, 500ms, 1000ms…),直到fetchFrameByTime成功返回一个有效的PixelMap。这是最直接的方法。

    let avImageGenerator = new media.AVImageGenerator();
    // ... 配置avImageGenerator (设置Source等)
    let timeArray = [0, 100, 500, 1000]; // 尝试的时间点数组(单位:ms)
    for (let time of timeArray) {
      try {
        let pixelMap = await avImageGenerator.fetchFrameByTime(time, media.AVImageGeneratorOptions);
        if (pixelMap != null) {
          // 成功获取到缩略图
          break;
        }
      } catch (err) {
        // 该时间点获取失败,继续尝试下一个
        console.error(`Fetch frame at time ${time} failed: ${err.message}`);
      }
    }
    
  2. 使用fetchFrameByIndex:如果视频有完整的帧索引,可以尝试使用fetchFrameByIndex方法,从0帧开始尝试获取。但注意,如果关键帧缺失,靠后的帧同样可能失败。

  3. 后端预处理:对于重要的视频资源,最可靠的方式是在视频上传或存储阶段,由服务端或后台进程统一生成并存储一张可靠的封面图(例如,强制截取第一帧或指定时间点的帧),应用端直接获取该预生成的图片。这避免了终端解码的兼容性问题。

  4. 设置解码参数:检查AVImageGeneratorOptions中的配置,确保widthheight设置合理,避免缩放比例过大导致解码负担增加。但这对缺少关键帧的问题帮助有限。

核心要点:当视频内部编码数据不完整(缺少关键帧)时,在终端侧无法保证能从任意时间点解码出画面。采用“多次尝试不同时间点”是最常用的客户端降级方案。如果对封面图有强一致性要求,建议采用方案3,即服务端预生成。

回到顶部