仓颉开发 Media Kit 媒体服务实战:AVImageGenerator 视频缩略图提取与 AVCodec 格式支持

原创2026-10-15 14:44:401,388 阅读
文章标签:文档知识库

仓颉开发 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,官方提供了两种资源来源:

  1. HAP 内置资源:使用 ResourceManager.getRawFd 打开 HAP 资源文件描述符。该方法用于获取 resources/rawfile 目录下 rawfile 文件所在 HAP 的文件描述符,接口签名为 public func getRawFd(path: String): RawFileDescriptor(详见 cj-apis-resource_manager.md)。
  2. 应用沙箱路径:使用应用沙箱路径访问对应资源,前提是必须确认资源文件可用。应用文件路径的获取方式参见 获取应用文件路径,其中 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。

登录后查看全文
CangjieCorpus