仓颉开发 Media Kit 媒体服务实战:AVImageGenerator 视频缩略图提取与 AVCodec 格式支持
仓颉开发 Media Kit 媒体服务实战:AVImageGenerator 视频缩略图提取与 AVCodec 格式支持
本篇技术指南聚焦 HarmonyOS 仓颉应用中的媒体服务(Media Kit,当前为 Beta 特性)开发,围绕"媒体信息查询"与"媒体格式支持"两大主题展开:一方面详细讲解如何使用 AVImageGenerator 从原始媒体资源中提取视频指定时间的缩略图,覆盖从创建实例、设置数据源、获取帧图像到释放资源的完整流程,并给出可在仓颉模板工程中直接运行的完整示例;另一方面系统梳理 AVCodec 当前支持的编解码能力与媒体数据封装、解析格式,帮助开发者在选型与排障时快速对齐能力边界。读完本文,你将能够独立实现"视频缩略图预览"类功能,并准确判断视频/音频文件在设备上的硬解、软解与封装兼容性。
相关入口文档位于 README_zh.md,其下包含两个核心子文档:使用AVImageGenerator提取视频指定时间图像(仓颉) 与 AVCodec支持的格式。
Media Kit 概览:媒体服务能做什么
Media Kit 属于仓颉开发框架中的媒体子系统,ohos.multimedia.media 模块为开发者提供一套简单且易于理解的接口,方便开发者接入系统并使用系统的媒体资源。从 media 模块 API 参考 可以看到,该模块当前提供的核心能力之一即为获取视频缩略图(AVImageGenerator)。
在仓颉工程中接入该模块,导入方式如下:
import kit.MediaKit.*
模块级的权限列表中声明了 ohos.permission.MICROPHONE,但 AVImageGenerator 的缩略图提取链路本身不涉及麦克风权限,开发者在实际使用时按业务场景申请所需权限即可。
从源码结构看,MediaKit 参考目录 由三部分构成:模块总览 README_zh.md、接口参考 cj-apis-multimedia_media.md 以及错误码参考 cj-errorcode-multimedia-media.md,后两者是下文排障与参数校验的主要依据。
使用 AVImageGenerator 提取视频指定时间图像
AVImageGenerator 是 Media Kit 提供的视频缩略图获取类,用于从原始媒体资源中获取指定时间点的缩略图。典型应用场景包括:视频列表页生成封面预览、视频播放器进度条缩略图、视频元数据可视化等。获取视频资源缩略图的完整流程包含四步:创建 AVImageGenerator 对象 → 设置资源 → 获取缩略图 → 销毁资源。
第一步:创建 AVImageGenerator 实例
调用顶层函数 createAVImageGenerator() 创建实例:
public func createAVImageGenerator(): AVImageGenerator
该函数返回一个 AVImageGenerator 实例,具备系统能力 SystemCapability.Multimedia.Media.AVImageGenerator,起始版本为 22。创建实例时可能抛出 BusinessException 业务异常,对应的模块错误码为 5400101(No memory),即内存分配失败。
实例数量限制: 根据 Media 错误码文档 的说明,错误码 5400101 的常见原因之一是实例数量超过 16 个。因此在用完资源后务必及时销毁实例,避免频繁创建却不释放导致内存分配失败。
第二步:设置资源(fdSrc 数据源)
AVImageGenerator 通过可读写属性 fdSrc 设置媒体数据源,其类型为 AVFileDescriptor:
public class AVFileDescriptor {
public var fd: Int32
public var offset: Int64
public var length: Int64
public init(...)
}
AVFileDescriptor 三个字段的含义与取值约束如下表:
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| fd | Int32 | 是 | - | 资源句柄,可通过 ResourceManager.getRawFd 获取,也可通过 open() 获取。 |
| offset | Int64 | 否 | 0 | 命名参数。资源偏移量,需要基于预置资源的信息输入,非法值会造成媒体资源解析错误。 |
| length | Int64 | 否 | -1 | 命名参数。资源长度,默认值为文件中从偏移量开始的剩余字节,需要基于预置资源的信息输入。 |
开发者需根据实际情况确认资源有效性并设置 fdSrc,官方提供了两种资源来源:
- HAP 内置资源:使用
ResourceManager.getRawFd打开 HAP 资源文件描述符。该方法用于获取resources/rawfile目录下 rawfile 文件所在 HAP 的文件描述符,接口签名为public func getRawFd(path: String): RawFileDescriptor(详见 cj-apis-resource_manager.md)。 - 应用沙箱路径:使用应用沙箱路径访问对应资源,前提是必须确认资源文件可用。应用文件路径的获取方式参见 获取应用文件路径,其中
files目录(filesDirectory,应用通用文件路径)可用于保存图片、媒体文件等私有数据,适合存放待提取缩略图的视频文件;应用沙箱的介绍及如何向应用沙箱推送文件,参见 文件管理。
文件描述符独占原则: 不同 AVImageGenerator 或 AVMetadataExtractor 如果操作同一资源,需要多次打开文件描述符,不要共用同一文件描述符。API 参考文档进一步强调:将资源句柄(fd)传递给 AVImageGenerator 实例后,不允许再通过该资源句柄做其他读写操作,包括同时传递给多个 AVPlayer / AVMetadataExtractor / AVImageGenerator / AVTranscoder。同一时间通过同一资源句柄读写文件存在竞争关系,会导致视频缩略图数据获取异常。
第三步:调用 fetchFrameByTime() 获取指定时间图像
调用 fetchFrameByTime() 获取指定时间点的视频帧,返回一个 PixelMap 对象,该对象可直接用于 Image 控件显示(例如将返回结果绑定到 ArkUI 的 Image 组件):
public func fetchFrameByTime(timeUs: Int64, options: AVImageQueryOptions, param: PixelMapParams): PixelMap
参数说明:
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| timeUs | Int64 | 是 | 需要获取的缩略图在视频中的时间点,单位为微秒(μs)。 |
| options | AVImageQueryOptions | 是 | 需要获取的缩略图时间点与视频帧的对应关系。 |
| param | PixelMapParams | 是 | 需要获取的缩略图的格式参数。 |
AVImageQueryOptions 枚举: 在获取视频缩略图时,传入的时间点与实际取得的视频帧所在时间点不一定相等,必须通过该枚举指定两者的时间关系。枚举成员及语义如下:
| 枚举成员 | 语义 |
|---|---|
| AvImageQueryNextSync | 选取传入时间点或之后的关键帧。 |
| AvImageQueryPreviousSync | 选取传入时间点或之前的关键帧。 |
| AvImageQueryClosestSync | 选取离传入时间点最近的关键帧。 |
| AvImageQueryClosest | 选取离传入时间点最近的帧,该帧不一定是关键帧。 |
该枚举实现了 Equatable<AVImageQueryOptions> 与 ToString 父类型,并重载了 == / != 运算符用于比较。注意:开发指导示例中写作 AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC,而 API 参考文档中的枚举成员名为 AvImageQueryNextSync,两处文档命名风格略有差异,编写代码时以 cj-apis-multimedia_media.md 中的声明为准。
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 且不大于原始视频高度,否则返回的缩略图不会进行缩放。 |
fetchFrameByTime() 可能抛出的业务异常包括:5400101(No memory)、5400102(Operation not allowed)以及 5400106(Unsupported format)。
第四步:调用 release() 释放资源
缩略图获取完成后,调用 release() 销毁实例、释放底层资源:
public func release(): Unit
该操作具备系统能力 SystemCapability.Multimedia.Media.AVImageGenerator,是完整流程中的收尾环节,建议配合错误码文档中"销毁当前实例并重新创建"的处理策略使用,防止实例泄漏触发 16 个实例上限。
完整示例:在仓颉模板工程中运行
开发指导提供了可直接在仓颉模板工程中编译运行的完整示例,分为 index.cj(UI 与缩略图获取逻辑)与 main_ability.cj(Ability 生命周期与 Context 注入)两个文件。
index.cj:声明 UI 并触发缩略图获取
// index.cj
import kit.MediaKit.*
import kit.ImageKit.PixelMap
import kit.AbilityKit.*
var ctx = Option<UIAbilityContext>.None
@Entry
@Component
class EntryView {
@State
var message: String = 'Hello World'
// pixelMap对象声明,用于图片显示
@State
var pixelMap: ?PixelMap = Option<PixelMap>.None
func build() {
Row() {
Column() {
Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)
Button() {
Text('TestButton')
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.margin(top: 20.px)
.backgroundColor(0x0D9FFB)
.width(60.percent)
.height(5.percent)
.onClick ({
evt =>
// 设置fdSrc, 获取视频的缩略图
testFetchFrameByTime()
})
Image(this.pixelMap).width(300).height(300)
.margin(top: 20.px)
}
.width(100.percent)
}
.height(100.percent)
}
// 使用资源管理接口获取打包在HAP内的视频文件,通过设置fdSrc属性,
// 获取视频指定时间的缩略图,并通过Image控件显示在屏幕上。
func testFetchFrameByTime() {
// 创建AVImageGenerator对象
let avImageGenerator = createAVImageGenerator()
// 设置fdSrc
avImageGenerator.fdSrc = AVFileDescriptor(ctx.getOrThrow().resourceManager.getRawFd('demo.mp4').fd)
// 初始化入参
let timeUs = 0
let queryOption = AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC
let param = PixelMapParams(
width : 300,
height : 300
)
// 获取缩略图
pixelMap = avImageGenerator.fetchFrameByTime(timeUs, queryOption, param)
// 释放资源
avImageGenerator.release()
Hilog.info(1, "info", "release success.")
}
}
代码要点逐段解读:
- UI 结构:
build()中构建了Row>Column布局,包含一个Text标签、一个触发按钮以及一个用于显示缩略图的Image控件。Image(this.pixelMap)直接绑定@State声明的?PixelMap类型状态变量,fetchFrameByTime()返回的 PixelMap 赋值后即触发界面刷新。 - 按钮事件:
.onClick回调中调用testFetchFrameByTime(),将缩略图获取逻辑与用户交互解耦。 - 数据源:
ctx.getOrThrow().resourceManager.getRawFd('demo.mp4').fd通过全局保存的 UIAbilityContext 打开打包在 HAP 内的demo.mp4资源,取其 fd 构造AVFileDescriptor。实际使用时请将'demo.mp4'替换为 rawfile 目录下真实存在的资源文件名。 - 查询参数:
timeUs = 0表示取 0 微秒(即视频起始位置)处的帧;PixelMapParams(width: 300, height: 300)限定输出尺寸为 300×300。 - 资源生命周期:
fetchFrameByTime()后立即调用release(),并用Hilog.info打印释放成功的日志。
main_ability.cj:注入 UIAbilityContext
示例需要通过 ctx = this.context 将 UIAbility 的上下文注入到全局变量 ctx,供 index.cj 中的 getRawFd 调用使用:
// main_ability.cj
import ohos.ability.AbilityStage
import ohos.ability.LaunchReason
class MainAbility <: UIAbility {
public init() {
super()
registerSelf()
}
public override func onCreate(want: Want, launchParam: LaunchParam): Unit {
Hilog.info(1, "info", "MainAbility OnCreated.${want.abilityName}")
match (launchParam.launchReason) {
case LaunchReason.START_ABILITY => Hilog.info(1, "info", "START_ABILITY")
case _ => ()
}
}
public override func onWindowStageCreate(windowStage: WindowStage): Unit {
Hilog.info(1, "info", "MainAbility onWindowStageCreate.")
windowStage.loadContent("EntryView")
// declared in index.cj
ctx = this.context
}
}
要点说明:onWindowStageCreate 中先通过 windowStage.loadContent("EntryView") 加载声明在 index.cj 中的页面,再将 this.context 赋值给 index.cj 中声明的全局变量 ctx。如果示例需获取 Context 应用上下文,都需在仓颉模板工程的 main_ability.cj 文件中进行此类配置;获取当前应用沙箱所在路径则可通过 UIAbilityContext.filesDir 属性。
此外,API 参考中还给出了带异常捕获的紧凑写法,可作为无 UI 场景下的参考:
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.error(0, "AppLogCj", e.message)
}
常见错误码与处理建议
AVImageGenerator 相关接口抛出的业务异常可在 Media错误码 中查阅,与缩略图提取强相关的错误码整理如下:
| 错误码 | 错误信息 | 含义 | 处理建议 |
|---|---|---|---|
| 5400101 | No memory. | 内存分配失败 | 常见原因是实例数量超过 16 个或 new/malloc 失败。销毁当前实例并重新创建,若仍失败则停止相关操作。 |
| 5400102 | Operation not allowed. | 当前操作不允许 | 当前状态机不支持该操作。确认实例状态,将实例切换到正确的状态后再执行操作。 |
| 5400103 | I/O error. | 出现 IO 错误 | 媒体与其他模块(图形、音频、网络、HDI、相机)的数据交互出现问题。确认网络通畅,销毁并重建实例。 |
| 5400106 | Unsupported format. | 不支持的格式 | 资源格式不在 AVCodec 支持范围内,参见下文的格式能力表核验源文件。 |
AVCodec 支持的媒体格式
在接入视频缩略图能力之前,务必确认源文件的编解码与封装格式在支持范围内。AVCodec 的能力边界详见 cj-avcodec-support-formats.md,本节完整汇总。
媒体编解码能力
视频解码: 当前支持的解码能力如下:
| 视频硬解类型 | 视频软解类型 |
|---|---|
| AVC(H.264)、HEVC(H.265) | MPEG2、MPEG4、H.263、AVC(H.264) |
视频编码: 当前支持的编码能力如下:
| 视频编码类型 |
|---|
| HEVC(H.265)、 AVC(H.264) |
音频解码: 当前支持的解码能力如下:
AAC、MPEG(MP3)、Flac、Vorbis、AMR(amrnb、amrwb)、G711mu、APE。
音频编码: 当前支持的编码能力如下:
AAC、Flac、MP3、G711mu。
媒体数据解析(解封装)格式
媒体数据解析(解封装)支持的能力如下表,涵盖了音视频、纯音频以及外挂字幕三类场景:
| 媒体格式 | 封装格式 | 码流格式 |
|---|---|---|
| 音视频 | mp4 | 视频码流:AVC(H.264)、MPEG4,音频码流:AAC、MPEG(MP3),字幕流:WEBVTT |
| 音视频 | fmp4 | 视频码流:AVC(H.264),音频码流:AAC、MPEG(MP3) |
| 音视频 | mkv | 视频码流:AVC(H.264),音频码流:AAC、MPEG(MP3)、OPUS |
| 音视频 | mpeg-ts | 视频码流:AVC(H.264)、MPEG2、MPEG4,音频码流:AAC、MPEG(MP3) |
| 音视频 | flv | 视频码流:AVC(H.264),音频码流:AAC |
| 音视频 | mpeg-ps | 视频码流:AVC(H.264)、MPEG2,音频码流:MPEG(MP2、MP3) |
| 音视频 | avi | 视频码流:H.263、AVC(H.264)、MPEG2、MPEG4,音频码流:AAC、MPEG(MP2、MP3)、PCM |
| 音频 | m4a | 音频码流:AAC |
| 音频 | aac | 音频码流:AAC |
| 音频 | mp3 | 音频码流:MPEG(MP3) |
| 音频 | ogg | 音频码流:Vorbis |
| 音频 | flac | 音频码流:Flac |
| 音频 | wav | 音频码流:PCM、G711mu |
| 音频 | amr | 音频码流:AMR(amrnb、amrwb) |
| 音频 | ape | 音频码流:APE |
| 外挂字幕 | srt | 字幕流:SRT |
| 外挂字幕 | webvtt | 字幕流:WEBVTT |
媒体数据封装格式
当前支持的封装(Muxer)能力如下:
| 封装格式 | 视频编解码类型 | 音频编解码类型 | 封面类型 |
|---|---|---|---|
| mp4 | AVC(H.264) | AAC、MPEG(MP3) | jpeg、png、bmp |
| m4a | - | AAC | jpeg、png、bmp |
| mp3 | - | MPEG(MP3) | - |
| amr | - | AMR(amrnb、amrwb) | - |
| wav | - | G711mu(pcm-mulaw) | - |
| aac | - | AAC | - |
封装注意事项:
- 封装格式为 mp4、音频编解码类型为 MPEG(MP3)时,采样率需大于等于 16000Hz。
- 封装格式为 mp4/m4a、音频编解码类型为 AAC 时,声道数范围为 1~7。
小结
本文围绕 Media Kit 在仓颉开发中的媒体信息查询能力展开。AVImageGenerator 为视频缩略图提取提供了简洁的四步流程(创建 → 设置 fdSrc → fetchFrameByTime → release),返回的 PixelMap 可直接驱动 ArkUI 的 Image 控件,配合 AVImageQueryOptions 的四种帧选取策略与 PixelMapParams 的尺寸控制,可灵活实现封面预览、进度条缩略图等常见交互;同时,AVCodec 的编解码与封装解析能力表为资源选型和错误排查(如 5400106 Unsupported format)提供了明确的依据。若需进一步查阅接口细节,可参考 cj-apis-multimedia_media.md 与 cj-errorcode-multimedia-media.md。