当前位置: 首页 > news >正文

【HarmonyOS AI赋能】AI字幕AICaption详解

【HarmonyOS AI赋能】AI字幕AICaption详解

一、前言

最近在做 HarmonyOS 应用开发时,需要集成 AI 字幕功能。
鸿蒙系统使用AI能力赋能,生成音频字幕的效果。简单说就是把音频流实时转成文字显示在界面上。
翻了官方文档,结合实际写的 demo 代码,踩了几个小坑后终于跑通了。今天就把整个过程拆解开,讲讲 AI 字幕相关的控件、接口怎么用,以及关键流程里要注意的细节,希望能帮到有同样需求的同学。

二、如何使用AI字幕控件

// 导入AI字幕相关组件和服务
import { AICaptionComponent, AICaptionOptions, AICaptionController, AudioData } from '@kit.SpeechKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { hilog } from '@kit.PerformanceAnalysisKit';const TAG = 'AI_CAPTION_DEMO'// 自定义日志工具类,方便记录不同级别的日志
class Logger {static info(...msg: string[]) {hilog.info(0x0000, TAG, msg.join())}static error(...msg: string[]) {hilog.error(0x0000, TAG, msg.join())}
}@Entry
@Component
struct Index {// AI字幕组件的配置选项private captionOption?: AICaptionOptions;// AI字幕组件的控制器,用于与组件交互private controller: AICaptionController = new AICaptionController();// 控制字幕组件是否显示的状态变量@State isShown: boolean = false;// 标记音频是否正在读取和处理中isReading: boolean = false;// 组件即将显示时调用的生命周期函数aboutToAppear(): void {// 初始化AI字幕的配置参数this.captionOption = {// 设置字幕初始透明度initialOpacity: 1,// 当字幕组件准备就绪时的回调函数onPrepared: () => {Logger.info('onPrepared')},// 当字幕组件发生错误时的回调函数onError: (error: BusinessError) => {Logger.error(`AICaption component error. Error code: ${error.code}, message: ${error.message}`)}}}// 读取PCM音频文件并发送到AI字幕组件的方法async readPcmAudio() {this.isReading = true;// 从资源管理器获取音频文件数据let fileData: Uint8Array | undefined;try {fileData = await this.getUIContext()?.getHostContext()?.resourceManager.getMediaContent($r('app.media.langhua').id);} catch (e) {Logger.info(`get fileData fail , msg ${e} `)}if (fileData === undefined) {return;}// 分块处理音频数据const bufferSize = 640;const byteLength = fileData.byteLength;let offset = 0;Logger.info(`Pcm data total bytes: ${byteLength.toString()}`)let startTime = new Date().getTime();// 循环读取音频数据块while (offset < byteLength) {let nextOffset = offset + bufferSize// 检查是否超出文件长度if (offset > byteLength) {this.isReading = false;return}// 截取当前块的音频数据const arrayBuffer = fileData.buffer.slice(offset, nextOffset);let data = new Uint8Array(arrayBuffer);const audioData: AudioData = {data: data}// 将音频数据写入AI字幕控制器if (this.controller) {try {this.controller.writeAudio(audioData)} catch (e) {Logger.error(`writeAudio exception`)}}// 移动偏移量并等待一段时间,模拟真实音频流offset = offset + bufferSize;const waitTime = bufferSize / 32await this.sleep(waitTime)}// 处理完成,记录总耗时let endTime = new Date().getTime()this.isReading = false;Logger.info(`Audio play time: ${JSON.stringify(endTime - startTime)}`)}// 辅助函数,用于暂停指定时间sleep(time: number): Promise<void> {return new Promise(resolve => setTimeout(resolve, time))}// 构建UI界面build() {Column({ space: 20 }) {// 切换字幕显示状态的按钮Button('切换字幕显示状态:' + (this.isShown ? '显示' : '隐藏')).backgroundColor('#B8BDA0').width(200).onClick(() => {this.isShown = !this.isShown;})// 读取并处理PCM音频的按钮Button('读取PCM音频').backgroundColor('#B8BDA0').width(200).onClick(() => {if (!this.isReading) {this.readPcmAudio()}})Divider()// AI字幕组件AICaptionComponent({isShown: this.isShown,controller: this.controller,options: this.captionOption}).width('100%').height(100)Divider()// 仅在字幕显示时显示提示文本if (this.isShown) {Text('上面是字幕区域').fontColor(Color.White)}}.width('100%').height('100%').padding(10).backgroundColor('#7A7D6A')}
}

首先会从本地资源加载PCM格式的音频文件,分块传给AI字幕控制器,最后通过AICaptionComponent把识别出的字幕显示在界面上。整个过程依赖HarmonyOS的SpeechKit(提供AI字幕核心能力)

AI字幕功能的核心就是三个“主角”:AICaptionComponent(字幕显示控件)、AICaptionController(字幕控制器,负责交互)、AICaptionOptions(字幕配置项),再加上AudioData(音频数据格式)。先一个个说清楚它们的作用和用法。

1、 字幕配置项:AICaptionOptions

这个是用来给AICaptionComponent设置初始参数和回调的,必须在组件渲染前初始化(demo里放在aboutToAppear生命周期里,很合理)。里面主要配置三个东西:

  • initialOpacity:字幕区域的初始透明度,取值0~1。demo里设为1,就是完全不透明;如果想让字幕淡一点,比如0.8也可以。
  • onPrepared:字幕控件准备就绪的回调。控件初始化完成后会触发这个方法,我们可以在这里打印日志,或者做一些“准备好后的操作”(比如启用音频读取按钮)。
  • onError:错误回调。AI字幕识别过程中如果出问题(比如权限不够、音频格式不对),会通过这个接口返回BusinessError,里面有错误码和错误信息——这个一定要加,不然出了问题都不知道在哪排查。

demo里的配置代码很标准,直接套着用就行,重点是错误回调里要把error.codeerror.message打出来,方便调试。

2、字幕控制器:AICaptionController

这是整个AI字幕功能的“桥梁”——一边连音频数据,一边连显示控件。我们需要手动实例化它(demo里用private controller: AICaptionController = new AICaptionController()),然后传给AICaptionComponent

它最核心的方法是writeAudio(audioData: AudioData):把音频数据(必须是AudioData格式)传给AI引擎,引擎识别后会自动把字幕传给AICaptionComponent显示。这里要注意两点:

  1. 控制器必须和AICaptionComponent绑定(通过组件的controller参数),不然数据传过去也没法显示;
  2. 调用writeAudio时要加try-catch,避免音频数据格式不对导致程序崩溃(demo里已经加了,这点很重要)。

3、字幕显示控件:AICaptionComponent

这是界面上实际显示字幕的“容器”,直接在build方法里用就行。它需要三个参数:

  • isShown:控制字幕是否显示(布尔值),demo里用@State修饰的isShown变量绑定,点击按钮切换状态;
  • controller:就是上面说的AICaptionController,必须传,不然控件没法接收字幕数据;
  • options:前面配置的AICaptionOptions,传初始化好的配置项就行。

另外要给控件设宽高(demo里width('100%')height(100)),不然可能显示不出来。我一开始没设高度,结果字幕区域缩成一条线,后来调了高度才正常——这个细节别忽略。

4、 音频数据格式:AudioData

AI字幕只认AudioData格式的音频数据,它的结构很简单,就一个data字段,类型是Uint8Array(二进制数据)。我们从本地读的PCM文件、或者从麦克风实时获取的音频流,都要转成这种格式才能传给控制器。

5、加载本地PCM音频文件

demo里是从entry\src\main\resources\base\media路径加载PCM文件,核心是用resourceManager.getMediaContent()方法。这里要注意两个点:

  1. 资源引用要正确:demo里写的是$r('app.media.startIcon'),这明显是个图标资源,实际开发中要改成自己的PCM文件,比如$r('app.media.test_pcm')(前提是把test_pcm.pcm放在media文件夹里);
  2. 异步处理:getMediaContent()是异步方法,必须用await,而且要包在try-catch里——万一文件没找到、或者权限不够,能捕获到错误,避免程序卡死。

加载成功后,会得到Uint8Array类型的fileData,这就是音频的二进制数据。

6、分块处理音频数据

为什么要分块?因为PCM文件可能很大,如果一次性把所有数据传给控制器,会导致两个问题:一是内存占用太高,二是AI识别跟不上(实际场景中音频是实时流,不是一次性加载的)。

demo里的处理逻辑很经典:

  • 设一个bufferSize(640字节),每次取640字节的数据;
  • offset记录当前读取到的位置,循环截取fileData的一部分(从offsetoffset+bufferSize);
  • 把截取的部分转成AudioData格式,调用controller.writeAudio()传给控制器;
  • 每次传完后,用sleep方法等一段时间(bufferSize/32毫秒)——这个等待时间是模拟“实时音频流”的速度,不然读取速度比识别速度快,字幕会乱跳。

这里的bufferSizewaitTime不是固定的,要根据实际音频的采样率调整。比如采样率16kHz、16位单声道的PCM,每毫秒的字节数是32(16000Hz * 16bit / 8bit),所以640字节刚好是20毫秒的音频,waitTime设20毫秒更准确——demo里用bufferSize/32,算下来也是20毫秒,刚好匹配这个场景。

7、 第三步:字幕显示与状态控制

当控制器把音频数据传给AI引擎后,识别出的字幕会自动送到AICaptionComponent——我们不需要手动处理字幕文本,控件会自己显示。

界面上的“切换字幕显示状态”按钮,本质是修改isShown变量:isShown=true时,控件显示字幕;isShown=false时,控件隐藏。另外还加了isReading变量,防止用户重复点击“读取PCM音频”按钮(点击后isReading设为true,直到音频处理完才设为false),这个细节能提升用户体验,避免并发问题。

http://www.dtcms.com/a/478068.html

相关文章:

  • 极海APM32F107V6 + 合宙Air780E
  • 做欧洲电商看哪个网站网站建设开发案例教程视频教程
  • C++11(可变参数模板、新的类功能和STL中的一些变化)
  • 医疗运营管理系统编程可靠性、安全性与动态性融合路径
  • 昂瑞微——以创新驱动未来,用芯连接世界
  • 网站承建商有哪些济南seo外贸网站建设
  • Flink1.20 CEP【水位线异常原因深度分析】
  • 30个酷炫HTML+CSS特效源码
  • vtkGaussianBlurPass代码解析
  • 网站制作过程合理的步骤是福州网站推广优化
  • 牛客算法基础noob71 学生综合评估系统
  • 如何清除 Yarn 缓存 ?
  • 做听书网站怎么做用动易建设网站教程
  • 东丽开发区做网站公司响应式网站源码下载
  • RabbitMQ为什么使用AMQP协议
  • 阜新本地网站建设平台百度竞价推广价格
  • Linux 系统启动过程
  • 多制式基站综合测试线的架构与验证实践 (2)
  • 如何阿里巴巴网站做推广方案沈阳妇科哪个医院比较专业
  • 合肥网站seo优化排名手机端网站首页怎么做
  • AI人工智能-机器学习-第一周(小白)
  • 【开题答辩过程】以《基于SpringBoot和Vue框架的智能宠物之家系统的设计与实现》为例,不会开题答辩的可以进来看看
  • 告别“手绘序列帧”:Substance Designer中的程序化VFX材质工作流
  • 网站策划与建设阶段的推广的目标办公空间设计网站
  • Ubuntu 24.04.3 LTS 设置静态IP
  • Spring 框架@Transactional注解,事务的各个传播行为的逻辑以及使用场景。
  • 福建巢网站建设chinacd小说wordpress
  • 轻松搭建RTMP推流、WebRTC拉流服务器SRS服务,源码编译安装
  • Linux内核架构浅谈26-Linux实时进程调度:优先级反转与解决方案
  • 企业官方网站建设目的网站基础知识