仓颉媒体服务 Media Kit(Beta)开发指南:AVImageGenerator 视频缩略图获取 API 全解析
仓颉媒体服务 Media Kit(Beta)开发指南:AVImageGenerator 视频缩略图获取 API 全解析
Media Kit(媒体服务)是 OpenHarmony-仓颉 SDK 提供的音视频媒体能力套件,其中 ohos.multimedia.media 模块为开发者提供了一套简单且易于理解的接口,用于接入系统并获取视频资源的关键帧画面。本文以 Media Kit 参考文档 为主线,围绕 AVImageGenerator(视频缩略图获取) 这一核心功能,系统讲解从实例创建、数据源描述、帧查询选项到错误码排查的完整链路,并结合仓库内 API 参考 与 Media 错误码 文档给出可直接运行的仓颉代码示例。读完本文,你将能够在仓颉应用模板工程中实现“按时间点提取视频任意帧作为缩略图”的完整流程。
Media Kit 与 ohos.multimedia.media 模块概览
ohos.multimedia.media 是 Media Kit 中面向仓颉语言的媒体服务接口模块,归属于媒体子系统,涵盖音视频相关的媒体业务。当前版本(起始版本 22)下,该模块提供的常用功能为:
- 获取视频缩略图(AVImageGenerator)
其能力分层清晰:createAVImageGenerator() 负责创建实例,AVImageGenerator 负责帧抓取与资源释放,AVFileDescriptor 描述数据源,PixelMapParams 描述输出图像规格,AVImageQueryOptions 决定取帧策略。各接口的系统能力归属如下:
| 接口 / 类 | 系统能力(SysCap) |
|---|---|
| createAVImageGenerator()、AVImageGenerator、PixelMapParams、AVImageQueryOptions | SystemCapability.Multimedia.Media.AVImageGenerator |
| AVFileDescriptor | SystemCapability.Multimedia.Media.Core |
说明: Media Kit 当前标注为 Beta 特性,接口以“起始版本:22”为准。系统能力(SystemCapability)是判断接口在当前设备上是否可用的依据,具体可参考 系统能力使用指南。
开发前置准备
导入模块
所有相关接口均通过 Kit 导入语句引入:
import kit.MediaKit.*
示例代码中还会用到 kit.LocalizationKit.*(获取资源文件描述符)、kit.PerformanceAnalysisKit.*(Hilog 日志)以及 ohos.business_exception.BusinessException(业务异常捕获)。
权限声明
调用本模块接口需要申请以下权限(在 module.json5 中配置):
ohos.permission.MICROPHONE
若不需要访问麦克风场景,开发者应在配置文件中按实际需求声明;权限申请方式详见 访问控制开发指导。
示例工程与运行前提
API 文档中的仓颉示例并非完整程序,而是对应 API 的主要代码。若希望编译运行,需要将示例拷贝到仓颉模板工程中,具体流程参见 仓颉示例代码说明:
- 示例代码首行带有
// index.cj注释时,表示该示例可在仓颉模板工程的index.cj文件中编译运行; - 示例需要获取 Context 应用上下文时,需在仓颉模板工程的
main_ability.cj文件中定义Global类并赋值,即通过Global.abilityContext访问上下文; - 获取当前应用沙箱路径可通过 UIAbilityContext 的 filesDir 属性。
main_ability.cj 中需要补充的 Global 类模板如下(节选自 开发说明):
// main_ability.cj 中定义
public class Global {
public static var _abilityContext: Option<UIAbilityContext> = None
public static var _windowStage: Option<WindowStage> = None
public static prop abilityContext: UIAbilityContext {
get() {
match (_abilityContext) {
case Some(context) => context
case None => throw Exception("Global.abilityContext is not set")
}
}
}
}
同时,仓颉 API 目前仅可在 Stage 模型下使用。
createAVImageGenerator():创建缩略图生成器
一切视频缩略图操作都从创建 AVImageGenerator 实例开始。接口签名如下:
public func createAVImageGenerator(): AVImageGenerator
- 系统能力: SystemCapability.Multimedia.Media.AVImageGenerator
- 起始版本: 22
- 返回值: AVImageGenerator 实例
- 异常:
BusinessException,对应 Media 错误码,详见 Media 错误码:
| 错误码 ID | 错误信息 |
|---|---|
| 5400101 | No memory. |
最小创建示例:
// index.cj
import kit.MediaKit.*
import kit.PerformanceAnalysisKit.*
import ohos.business_exception.BusinessException
try {
let generator = createAVImageGenerator()
} catch (e: BusinessException) {
Hilog.error(0, "AppLogCj", e.message)
}
AVFileDescriptor:音视频文件资源描述
AVFileDescriptor 用于描述音视频文件资源,其典型使用场景是:应用中的音频资源被连续存储在同一个文件中,需要根据偏移量(offset)和长度(length)进行定位播放或抓帧。
public class AVFileDescriptor {
public var fd: Int32
public var offset: Int64
public var length: Int64
public init(
fd: Int32,
offset!: Int64 = 0,
length!: Int64 = -1
)
}
字段说明
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| fd | Int32 | 无 | 资源句柄,通过 getRawFd 获取,也可以通过 open 获取。 |
| offset | Int64 | 0 | 资源偏移量,默认值为 0,需基于预置资源的信息输入,非法值会造成音视频资源解析错误。 |
| length | Int64 | -1 | 资源长度,默认值为文件中从偏移量开始的剩余字节,需基于预置资源的信息输入,非法值会造成音视频资源解析错误。 |
其中 offset 与 length 均为命名参数(!: 语法),可省略:
// 等价于 offset = 0, length = 文件中从偏移量开始的剩余字节
let desc = AVFileDescriptor(rawFd.fd)
// 或显式指定
let desc = AVFileDescriptor(rawFd.fd, offset: rawFd.offset, length: rawFd.length)
fd 的来源:getRawFd 与 FileIo.open
- 预置资源(rawfile):通过
ResourceManager的 getRawFd(path) 获取resources/rawfile目录下文件所在 HAP 的文件描述符,返回 RawFileDescriptor(内含fd、offset、length三个字段,与AVFileDescriptor一一对应)。注意:getRawFd获取的 fd 使用完毕后需调用 closeRawFd 关闭,避免资源泄露。 - 沙箱文件:通过 FileIo.open(path, mode) 打开应用沙箱路径或 URI,默认以
OpenMode.READ_ONLY只读方式打开,返回值File对象提供句柄。
AVImageGenerator:视频缩略图获取类
AVImageGenerator 是本文的核心类,用于从视频资源中获取缩略图。调用其方法前,必须先通过 createAVImageGenerator() 构建实例。
public class AVImageGenerator {}
prop fdSrc:设置数据源
通过 fdSrc 属性将 AVFileDescriptor 设置为数据源:
public mut prop fdSrc: AVFileDescriptor
假设一个连续存储的媒体文件,地址偏移为 0、字节长度为 100,其文件描述为 AVFileDescriptor { fd = 资源句柄; offset = 0; length = 100; }。
重要约束: 将资源句柄(fd)传递给
AVImageGenerator实例之后,不允许通过该资源句柄再做其他读写操作,包括但不限于将同一个资源句柄传递给多个 AVPlayer / AVMetadataExtractor / AVImageGenerator / AVTranscoder。同一时间通过同一个资源句柄读写文件时存在竞争关系,将导致视频缩略图数据获取异常。
该属性可能抛出 BusinessException,错误码包括:
| 错误码 ID | 错误信息 |
|---|---|
| 5400101 | No memory. |
| 5400102 | Operation not allowed. |
func fetchFrameByTime():按时间点抓取视频帧
这是获取缩略图的核心方法:
public func fetchFrameByTime(timeUs: Int64, options: AVImageQueryOptions, param: PixelMapParams): PixelMap
参数说明:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| timeUs | Int64 | 是 | 需要获取的缩略图在视频中的时间点,单位为微秒(μs)。 |
| options | AVImageQueryOptions | 是 | 需要获取的缩略图时间点与视频帧的对应关系。 |
| param | PixelMapParams | 是 | 需要获取的缩略图的格式参数。 |
返回值: PixelMap,即视频缩略图。PixelMap 是图像像素类,用于读取或写入图像数据以及获取图像信息,可将结果直接用于界面展示或后续图像处理。
异常(BusinessException):
| 错误码 ID | 错误信息 |
|---|---|
| 5400101 | No memory. |
| 5400102 | Operation not allowed. |
| 5400106 | Unsupported format. |
调用示例(抓取第 0 微秒处的下一关键帧):
let timeUs = 0
let queryOption = AVImageQueryOptions.AvImageQueryNextSync
let param = PixelMapParams(width: 300, height: 300)
let pic = generator.fetchFrameByTime(timeUs, queryOption, param)
func release():释放资源
缩略图获取完成后,应显式释放 AVImageGenerator 占用的资源:
public func release(): Unit
该方法可能抛出 BusinessException(错误码 5400101 No memory.)。
PixelMapParams:缩略图输出格式参数
PixelMapParams 用于描述输出缩略图的尺寸规格:
public class PixelMapParams {
public var width: Int32
public var height: Int32
public init(width!: Int32 = -1, height!: Int32 = -1)
}
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| width | Int32 | -1 | 输出的缩略图宽度。应保证大于 0 且不大于原始视频宽度,否则返回的缩略图不会进行缩放。 |
| height | Int32 | -1 | 输出的缩略图高度。应保证大于 0 且不大于原始视频高度,否则返回的缩略图不会进行缩放。 |
width 与 height 均为命名参数,可只指定其一:
// 仅指定宽度
let param = PixelMapParams(width: 320)
// 指定宽高
let param = PixelMapParams(width: 300, height: 300)
AVImageQueryOptions:时间点与视频帧的对应关系
在获取视频缩略图时,传入的时间点与实际取得的视频帧所在时间点不一定相等,因此需要指定二者的对应关系。该枚举实现了 Equatable<AVImageQueryOptions> 与 ToString 父类型,并重载了 ==、!= 运算符及 toString() 方法:
public enum AVImageQueryOptions <: Equatable<AVImageQueryOptions> & ToString {
| AvImageQueryNextSync
| AvImageQueryPreviousSync
| AvImageQueryClosestSync
| AvImageQueryClosest
| ...
}
| 枚举值 | 语义 |
|---|---|
| AvImageQueryNextSync | 选取传入时间点或之后的关键帧。 |
| AvImageQueryPreviousSync | 选取传入时间点或之前的关键帧。 |
| AvImageQueryClosestSync | 选取离传入时间点最近的关键帧。 |
| AvImageQueryClosest | 选取离传入时间点最近的帧,该帧不一定是关键帧。 |
选帧策略建议: 若追求解码效率与画面稳定,优先使用带 Sync(关键帧)语义的选项;若追求时间点精确对齐,可选用 AvImageQueryClosest(非关键帧)。实际编码中,视频关键帧间隔(GOP)直接决定取帧结果与请求时间点的偏差,开发者应根据视频源特性选择策略。
完整实战示例:从预置视频中抓取缩略图
将以下代码放入仓颉模板工程的 index.cj(资源文件 trailer.mp4 放置于 resources/rawfile 目录,并替换为实际路径):
// index.cj
import kit.MediaKit.*
import kit.LocalizationKit.*
import ohos.business_exception.BusinessException
import kit.PerformanceAnalysisKit.Hilog
try {
let timeUs = 0
let queryOption = AVImageQueryOptions.AvImageQueryNextSync
let param = PixelMapParams(width: 300, height: 300)
let generator = createAVImageGenerator()
let abilityContext = Global.abilityContext // 此处需手动配置模板,获取Context上下文。上下文获取方式请参见使用说明。
let rawFd = abilityContext.resourceManager.getRawFd("trailer.mp4") // 请替换您的资源路径,获取文件路径参考本文使用说明
generator.fdSrc = AVFileDescriptor(rawFd.fd, offset: rawFd.offset, length: rawFd.length)
let pic = generator.fetchFrameByTime(timeUs, queryOption, param)
generator.release()
} catch (e: BusinessException) {
Hilog.info(0, "test", "${e.message}")
}
执行流程拆解:
- 通过
createAVImageGenerator()创建实例; - 通过
Global.abilityContext.resourceManager.getRawFd("trailer.mp4")获取预置资源的RawFileDescriptor(fd / offset / length); - 将其转换为
AVFileDescriptor并赋值给generator.fdSrc设置数据源; - 指定时间点(
timeUs = 0)、查询策略(AvImageQueryNextSync)与输出规格(300 × 300),调用fetchFrameByTime得到PixelMap缩略图; - 调用
release()释放资源。
若数据源为应用沙箱文件,可将第 2~3 步替换为:
// 沙箱路径可通过 UIAbilityContext.filesDir 获取
let file = FileIo.open(path, mode: FileIo.OpenMode.READ_ONLY)
let desc = AVFileDescriptor(file.fd)
generator.fdSrc = desc
Media 错误码排查指南
本模块错误码均为 BusinessException 抛出,下表汇总了 Media 错误码 中的特有错误码(通用错误码请参考 通用错误码说明):
| 错误码 ID | 错误信息 | 错误描述 | 可能原因 | 处理步骤 |
|---|---|---|---|---|
| 5400101 | No memory. | 分配内存失败 | 实例数量超过 16 个;new 或 malloc 流程失败导致空指针 | 销毁当前实例并重新创建,若重新创建失败则停止相关操作 |
| 5400102 | Operation not allowed. | 当前操作不允许 | 当前状态机不支持此操作 | 确认当前状态是否支持该操作,将实例切换到正确状态 |
| 5400103 | I/O error. | 出现 IO 错误 | 媒体与其他模块(图形、音频、网络、HDI、相机)的数据交互发生问题 | 确认网络是否通畅,销毁并重新创建实例 |
| 5400104 | Operation timeout. | 操作超时 | 网络超时(默认 15 秒,从开始缓存的事件上报后计时);访问其他模块超时 | 确认网络通畅;销毁并重新创建实例 |
| 5400105 | Service died. | 播放服务死亡 | 播放服务死亡 | 销毁当前实例并重新创建 |
| 5400106 | Unsupported format. | 不支持的规格 | 不支持的文件或格式 | 切换为支持的规格 |
| 5400107 | Audio interrupted. | 音频焦点冲突导致录制失败 | 有其他进程占用音频焦点 | 停止其他进程占用后重新创建 |
| 5411001 | Can not find host. | 解析或链接服务端地址错误 | 链接地址错误;地址解析失败 | 更换正确的服务端地址 |
| 5411002 | Connection time out. | 网络连接超时 | 网络异常 | 确认网络通畅;销毁并重新创建实例 |
| 5411003 | NetWork abnormal. | 网络异常导致的数据或链路异常 | 网络异常 | 销毁并重新创建实例 |
| 5411004 | NetWork unavailable. | 网络被禁用 | 网络被禁用 | 确认网络是否被禁用;销毁并重新创建实例 |
| 5411005 | No permission. | 无访问权限 | 无访问权限 | 确认访问权限;销毁并重新创建实例 |
| 5411006 | Network access denied. | 客户端请求参数错误或超出处理能力 | 请求参数错误或超出处理能力 | 确认请求参数是否正常;销毁并重新创建实例 |
| 5411007 | Cannot find available network resources. | 无可用网络资源 | 链接服务端地址异常 | 确认链接地址正常;销毁并重新创建实例 |
| 5411008 | SSL client cert needed. | SSL 客户端不可信,服务端校验客户端证书失败 | 未携带证书、证书不合法、证书过期 | 确认 SSL 证书正常;销毁并重新创建实例 |
| 5411009 | SSL connection failed. | SSL 链接失败 | SSL 链接失败 | 确认 SSL 链接是否失效;销毁并重新创建实例 |
| 5411010 | SSL server cert needed. | SSL 服务端不可信,客户端校验服务端证书失败 | 未携带证书、证书不合法、证书过期 | 确认 SSL 证书正常;销毁并重新创建实例 |
| 5411011 | Unsupportted request. | 客户端请求参数错误或超出处理能力 | 请求参数错误或超出处理能力 | 确认请求参数正常;销毁并重新创建实例 |
其中与视频缩略图获取直接相关的错误码为 5400101、5400102、5400106:内存不足时检查实例数量(上限 16 个);Operation not allowed 时检查状态机与操作时序;Unsupported format 时确认视频文件格式与编码规格是否受支持。
最佳实践与注意事项
- 资源句柄独占: fd 传给
AVImageGenerator后,禁止再通过该句柄做读写,也不要将同一句柄同时传给多个 AVPlayer / AVMetadataExtractor / AVImageGenerator / AVTranscoder,避免竞争导致缩略图数据异常。 - 及时释放: 抓帧完成后调用
release();通过getRawFd获取的 fd 在不再使用时调用closeRawFd(path)关闭,防止资源泄露。 - 合理设置输出尺寸:
PixelMapParams的宽高应大于 0 且不大于原始视频宽高,否则不会缩放;同时留意PixelMap序列化大小限制(最大 128MB,大小约为宽 × 高 × 每像素字节数),过大尺寸可能导致送显失败。 - 选帧策略按需取舍: 需要快速返回且对时间精度要求不高时优先选择关键帧选项(
AvImageQueryNextSync等);需要精确对齐指定时间点时选择AvImageQueryClosest。 - 异常兜底: 所有媒体接口调用都应使用
try-catch (e: BusinessException)包裹,并结合错误码进行差异化处理(如内存不足时销毁重建、格式不支持时切换规格)。 - 示例工程校验: 需要 Context 的示例必须在
main_ability.cj中配置Global类;沙箱文件路径通过filesDir获取,具体模板参见 仓颉示例代码说明。
至此,从 createAVImageGenerator() 创建实例、AVFileDescriptor 描述数据源、AVImageQueryOptions 确定取帧策略、fetchFrameByTime 抓取帧画面,到 release() 释放资源与错误码排查,一条完整的视频缩略图获取链路已经全部打通,可直接应用于仓颉应用的媒体预览、视频列表封面等场景。