文档管理中心

AI字幕控件

适用场景

AI字幕控件应用广泛,例如在用户不熟悉音频源语言或者静音的时候,为用户提供字幕服务。

本章节将向您介绍如何使用AI字幕组件AICaptionComponentAICaptionController展示AI字幕,效果如下图所示。

接口说明

AI字幕功能主要由AICaptionComponent提供,更多接口及使用方法请参见API参考

展开
接口 描述
AICaptionComponent AI字幕组件。
AICaptionOptions AI字幕初始化参数。
AICaptionController AI字幕组件的控制器,是AI字幕组件的主要功能入口类,用来操作AI字幕。它所承载的工作包括:写音频数据、获取音频流信息等。

开发步骤

  1. 从项目根目录进入/src/main/ets/pages/Index.ets文件,在使用AI字幕控件前,将实现AI字幕控件和其他相关的类添加至工程。

    收起
    自动换行
    深色代码主题
    复制
    1. import { AICaptionComponent, AICaptionController, AICaptionOptions,AICaptionFontSize } from '@kit.SpeechKit';
  2. 简单配置页面的布局,加入AI字幕组件,判断设备是否支持(该查询接口仅支持26.0.0以上版本API),并在aboutToAppear中设置AI字幕组件的传入参数。

    收起
    自动换行
    深色代码主题
    复制
    1. import { hilog } from '@kit.PerformanceAnalysisKit';
    2. const TAG = 'AI_CAPTION_DEMO';
    3. class Logger {
    4. static info(...msg: string[]) {
    5. hilog.info(0x0000, TAG, msg.join());
    6. }
    7. static error(...msg: string[]) {
    8. hilog.error(0x0000, TAG, msg.join());
    9. }
    10. }
    11. @Entry
    12. @Component
    13. struct Index {
    14. private captionOption ?: AICaptionOptions;
    15. private controller = new AICaptionController();
    16. @State isSupport: boolean = false;
    17. @State isShown: boolean = false;
    18. aboutToAppear(): void {
    19. // 判断设备是否支持功能特性(仅支持26.0.0以上版本API)
    20. this.isSupport = this.controller.isCapabilitySupported()
    21. // AI字幕初始化参数,设置字幕的不透明度和回调函数
    22. this.captionOption = {
    23. initialOpacity: 1,
    24. onPrepared: () => {
    25. Logger.info('onPrepared')
    26. },
    27. onError: (error) => {
    28. Logger.error(`onError, code: ${error.code}, msg: ${error.message}`)
    29. },
    30. // 源语言
    31. sourceLanguage: 'zh',
    32. // 目标语言
    33. targetLanguage: 'zh',
    34. // 字体大小
    35. fontSize: AICaptionFontSize.NORMAL,
    36. // 字体颜色
    37. fontColor: Color.Black
    38. };
    39. }
    40. build() {
    41. Column({ space: 20 }) {
    42. if (this.isSupport) {
    43. // 调用AICaptionComponent组件初始化字幕
    44. AICaptionComponent({
    45. isShown: this.isShown,
    46. controller: this.controller,
    47. options: this.captionOption
    48. })
    49. .width('100%')
    50. .height(100)
    51. Divider()
    52. if (this.isShown) {
    53. Text('上面是字幕区域')
    54. .fontColor(Color.White)
    55. }
    56. }
    57. }
    58. .width('100%')
    59. .height('100%')
    60. .padding(10)
    61. .backgroundColor('#7A7D6A')
    62. }
    63. }
  3. 在布局中加入两个按钮以及点击事件的回调函数。

    • 第一个按钮的回调函数负责控制AI字幕组件的显示状态。
    • 第二个按钮的回调函数负责读取资源目录中的音频文件,将音频数据传给AI字幕组件。
    收起
    自动换行
    深色代码主题
    复制
    1. import { AudioData } from '@kit.SpeechKit';
    2. @Entry
    3. @Component
    4. struct Index {
    5. isReading: boolean = false;
    6. async readPcmAudio() {
    7. this.isReading = true;
    8. let fileData: Uint8Array | undefined = undefined;
    9. try {
    10. fileData =
    11. await this.getUIContext()?.getHostContext()?.resourceManager.getMediaContent($r('app.media.ChineseAudio').id);
    12. } catch (e) {
    13. Logger.info(`get fileData fail , msg ${e} `)
    14. }
    15. if (fileData === undefined) {
    16. return;
    17. }
    18. const bufferSize = 640;
    19. const byteLength = fileData.byteLength;
    20. let offset = 0;
    21. Logger.info('byteLength', byteLength.toString());
    22. let startTime = new Date().getTime();
    23. while (offset < byteLength) {
    24. // 模拟实际情况,读文件比录音机返回流快,所以要等待一段时间
    25. let nextOffset = offset + bufferSize;
    26. if (offset >= byteLength) {
    27. this.isReading = false;
    28. return;
    29. }
    30. const arrayBuffer = fileData.buffer.slice(offset, nextOffset);
    31. let data = new Uint8Array(arrayBuffer);
    32. Logger.info('data byteLength', data.byteLength.toString());
    33. const audioData: AudioData = {
    34. data: data
    35. };
    36. Logger.info(`offset: ${offset} | byteLength: ${byteLength} | bufferSize: ${bufferSize}`);
    37. if (this.controller) {
    38. Logger.info(`writeAudio: ${audioData.data.byteLength}`);
    39. try {
    40. this.controller.writeAudio(audioData);
    41. } catch (e) {
    42. Logger.error(`writeAudio exception`);
    43. }
    44. }
    45. offset = offset + bufferSize;
    46. const waitTime = bufferSize / 32;
    47. await this.sleep(waitTime);
    48. }
    49. let endTime = new Date().getTime();
    50. this.isReading = false;
    51. Logger.info('playtime', JSON.stringify(endTime - startTime));
    52. }
    53. async sleep(time: number): Promise<void> {
    54. return new Promise(resolve => setTimeout(resolve, time));
    55. }
    56. build() {
    57. Column({ space: 20 }) {
    58. // ...
    59. Button('切换字幕显示状态:' + (this.isShown ? '显示' : '隐藏'))
    60. .backgroundColor('#B8BDA0')
    61. .width(200)
    62. .onClick(() => {
    63. this.isShown = !this.isShown;
    64. })
    65. Button('读取PCM音频')
    66. .backgroundColor('#B8BDA0')
    67. .width(200)
    68. .onClick(() => {
    69. if (!this.isReading) {
    70. void this.readPcmAudio();
    71. }
    72. })
    73. // ...
    74. }
    75. }
    76. }

开发实例

Index.ets

收起
自动换行
深色代码主题
复制
  1. import { AICaptionComponent, AICaptionOptions, AICaptionController, AudioData,AICaptionFontSize } from '@kit.SpeechKit';
  2. import { BusinessError } from '@kit.BasicServicesKit';
  3. import { hilog } from '@kit.PerformanceAnalysisKit';
  4. const TAG = 'AI_CAPTION_DEMO';
  5. class Logger {
  6. static info(...msg: string[]) {
  7. hilog.info(0x0000, TAG, msg.join());
  8. }
  9. static error(...msg: string[]) {
  10. hilog.error(0x0000, TAG, msg.join());
  11. }
  12. }
  13. @Entry
  14. @Component
  15. struct Index {
  16. private captionOption?: AICaptionOptions;
  17. private controller: AICaptionController = new AICaptionController();
  18. @State isSupport: boolean = false;
  19. @State isShown: boolean = false;
  20. isReading: boolean = false;
  21. aboutToAppear(): void {
  22. // 判断设备是否支持功能特性
  23. this.isSupport = this.controller.isCapabilitySupported()
  24. // AI字幕初始化参数,设置字幕的不透明度和回调函数
  25. this.captionOption = {
  26. initialOpacity: 1,
  27. onPrepared: () => {
  28. Logger.info('onPrepared')
  29. },
  30. onError: (error: BusinessError) => {
  31. Logger.error(`AICaption component error. Error code: ${error.code}, message: ${error.message}`);
  32. },
  33. // 源语言
  34. sourceLanguage: 'zh',
  35. // 目标语言
  36. targetLanguage: 'zh',
  37. // 字体大小
  38. fontSize: AICaptionFontSize.NORMAL,
  39. // 字体颜色
  40. fontColor: Color.Black
  41. };
  42. }
  43. async readPcmAudio() {
  44. this.isReading = true;
  45. // ChineseAudio.pcm文件放在entry\src\main\resources\base\media路径下
  46. let fileData: Uint8Array | undefined = undefined;
  47. try {
  48. fileData =
  49. await this.getUIContext()?.getHostContext()?.resourceManager.getMediaContent($r('app.media.ChineseAudio').id);
  50. } catch (e) {
  51. Logger.info(`get fileData fail , msg ${e} `);
  52. }
  53. if (fileData === undefined) {
  54. return;
  55. }
  56. const bufferSize = 640;
  57. const byteLength = fileData.byteLength;
  58. let offset = 0;
  59. Logger.info(`Pcm data total bytes: ${byteLength.toString()}`);
  60. let startTime = new Date().getTime();
  61. while (offset < byteLength) {
  62. // 模拟实际情况,读文件比录音机返回流快,所以要等待一段时间
  63. let nextOffset = offset + bufferSize;
  64. if (offset >= byteLength) {
  65. this.isReading = false;
  66. return;
  67. }
  68. const arrayBuffer = fileData.buffer.slice(offset, nextOffset);
  69. let data = new Uint8Array(arrayBuffer);
  70. const audioData: AudioData = {
  71. data: data
  72. };
  73. if (this.controller) {
  74. try {
  75. this.controller.writeAudio(audioData);
  76. } catch (e) {
  77. Logger.error(`writeAudio exception`);
  78. }
  79. }
  80. offset = offset + bufferSize;
  81. const waitTime = bufferSize / 32;
  82. await this.sleep(waitTime);
  83. }
  84. let endTime = new Date().getTime();
  85. this.isReading = false;
  86. Logger.info(`Audio play time: ${JSON.stringify(endTime - startTime)}`);
  87. }
  88. async sleep(time: number): Promise<void> {
  89. return new Promise(resolve => setTimeout(resolve, time))
  90. }
  91. build() {
  92. Column({ space: 20 }) {
  93. if (this.isSupport ) {
  94. Button('切换字幕显示状态:' + (this.isShown ? '显示' : '隐藏'))
  95. .backgroundColor('#B8BDA0')
  96. .width(200)
  97. .onClick(() => {
  98. this.isShown = !this.isShown;
  99. })
  100. Button('读取PCM音频')
  101. .backgroundColor('#B8BDA0')
  102. .width(200)
  103. .onClick(() => {
  104. if (!this.isReading) {
  105. void this.readPcmAudio();
  106. }
  107. })
  108. Divider()
  109. // 调用AICaptionComponent组件初始化字幕
  110. AICaptionComponent({
  111. isShown: this.isShown,
  112. controller: this.controller,
  113. options: this.captionOption
  114. })
  115. .width('100%')
  116. .height(100)
  117. Divider()
  118. if (this.isShown) {
  119. Text('上面是字幕区域')
  120. .fontColor(Color.White)
  121. }
  122. }
  123. }
  124. .width('100%')
  125. .height('100%')
  126. .padding(10)
  127. .backgroundColor('#7A7D6A')
  128. }
  129. }
在 指南 中进行搜索
请输入您想要搜索的关键词