跳转至

多媒体基础与 Demo 开发

本文档介绍 HiDiTing V100 多媒体软件的分层、模块职责、关键场景和应用开发入口。开发者可先阅读背景知识理解模块边界与状态约束,再按对应场景的接口参考和代码流程实现应用。

多媒体软件背景知识

本章说明多媒体软件的分层、模块职责、架构关系和功能边界。开发者先根据业务选择音频管理、音频采集、播放器或智能语音模块,再按对应场景的状态机和调用顺序实现应用。

多媒体软件架构图

多媒体软件概述

本章节介绍媒体子系统框架结构及支持的功能,详细见如下分解内容。

媒体子系统框架

媒体子系统框架如图1所示。

图 1 整体框图

整体框图

各层次职责概述:

  • APP:基于媒体框架提供的接口开发应用,同时向用户提供UI使用。
  • Framework:为上层开发提供媒体业务相关北向接口。
  • HAL(ardware Abstract Layer):基于SDK,提供设备管理、数据流控制等功能。
  • SDK:提供媒体采集、编解码、图像处理、视频显示等具体功能。

模块划分

图 1 模块划分

模块划分

本文针对媒体子系统主要介绍AudioManager(音频管理)、AudioCapture(音频采集)、Player(播放器)。

Framework层模块

  • AudioManager:音频管理,对APP侧提供音频资源管理接口。
  • AudioCapture:音频采集,对APP侧提供音频数据采集接口。
  • Player:播放器,对APP侧提供音视频片源播放接口。

HAL层模块

  • audio:提供音频设备管理、音频采集、渲染管理、音量调节等功能。
  • codec:提供音视频编解码功能。
  • camera:提供H264流预览显示功能。
  • formats:提供容器封装及解封装功能。

开发模块选择

多媒体软件由音频管理、输入输出流、音频采集、播放器和智能语音等模块组成。AudioManager 负责设备、音量和中断等全局策略;AudioStreamIn / AudioStreamOut 负责基础流收发;AudioCapturer 面向录音业务;Player 面向本地或流媒体播放;智能语音引擎负责语音识别和文本转语音。开发时先按业务选择模块类,再遵循该类的状态机和接口时序调用。

音频管理

基本概念

音频管理负责管理系统的音频资源,包括音频设备管理、音频策略管理、音频冲突管理、音频流管理、音量管理、音效管理。

图 1 音频管理总体框架

音频管理总体框架

  • AudioManager包括对输入输出设备的管理和输入输出流的管理。
  • AudioManager根据功能的不同,将音频设备分为输入和输出设备,使用DeviceManager模块基于南向Audio HDI(Hardware Device Interface)对音频设备进行抽象和统一管理。
  • 对应到输入设备上的采集数据流用AudioStreamIn实现。
  • 对应到输出设备上的播放数据流用AudioStreamOut实现。
  • StreamManager模块对不同应用的不同类型数据流进行统一管理。
  • PolicyManager模块根据不同产品音频配置策略决策音频流的冲突解决策略和路由设备选择。

功能描述

音频设备管理

AudioManager根据功能的不同,将音频设备分为输入和输出设备。当前支持Mic、Speaker、Modem以及蓝牙设备的管理,Mic、Speaker属于Primary device,是常驻设备;Modem和蓝牙设备的连接和断开状态,需要用户或者蓝牙服务在蓝牙对应的音频设备状态改变时,通过接口调用反馈给AudioManager:

  • Primary device:Mic、Speaker。
  • Bluetooth device:A2DP、SCO IN、SCO OUT。
  • Modem device:OUT_MODEM、OUT_MODEM_HEADSET、IN_MODEM、IN_MODEM_HEADSET。

音频中断策略管理

音频管理根据业务场景不同,涉及音频输入、输出策略决策和焦点管理,细分如下:

  • 支持如下音频类型:

    • 报警类:健康报警、设备告警。
    • 通话类:语音通话、蓝牙通话、4G VOLTE(Voice over LTE)通话。
    • 语音交互类:语音识别、语音助手、语音合成等。
    • 提示类:蓝牙电话提示音、系统提示音、闹钟。
    • 操作类:触屏音、按键音。
    • 媒体类:影音。
    • 蓝牙输入媒体音:手机蓝牙连接手表,然后从手机过来的媒体音。

    图 1 输出流打断策略关系图

    输出流打断策略关系图

    说明:

    • 目前不支持只能在本地播放的音频和蓝牙提示音的并发场景,用户需要在播放本地音频前,先将蓝牙输出设备断开。
    • 有蓝牙输入媒体音时,可以连接蓝牙耳机,但是手表本身的音频都从本地输出,不会从蓝牙耳机输出。
    • 如果手表连接手机,手表正在播放音乐,此时从手机侧过来的A2DP链路建立会把手表播放的音乐打断,A2DP链路断开后音乐恢复播放,可能手机只是创建A2DP链路,并没有发送数据,所以手表音乐播放过程中会出现短暂的无声,过一会就恢复的现象,实际上是正常的现象。

    图 2 输入流打断策略关系图

    输入流打断策略关系图

    目前不支持音频输入和输出并发场景的打断策略,典型场景:第一路媒体音,第二路语音采集录制、第一路语音采集录制,第二路媒体音等。

  • 支持音频输出分类分级处理策略:

    • 支持不同音频类型配置不同优先级,根据产品可以进行不同的配置。
    • 支持音频输出根据类型和优先级决策音频冲突的策略:打断退出、打断暂停、不播放、混音,延时。
  • 支持音频输入设备分优先级选择:

    支持音频输入设备分优先级选择,默认蓝牙优先于本地Mic。

  • 支持音频输出设备分优先级选择:

    支持音频输出设备分优先级选择,默认蓝牙优先于本地Speaker。

  • 支持异常焦点恢复:

    • 焦点申请成功后,一直不创建对应流会触发超时,进而删除该异常焦点。
    • 流销毁后,不去激活焦点会触发超时,进而删除该异常焦点。

音频流管理

音频管理根据流的方向不同,将流分为输入流和输出流。从流的生命周期、状态、倍速、信息、在线模式等角度实现流管理和控制,细分如下:

  • 支持音频流的生命周期管理:

    支持根据音频策略决策、参数创建和销毁音频流。

  • 支持音频流状态管理:

    • 支持根据用户行为和焦点状态启动、停止、暂停、刷新音频输出流。
    • 支持根据用户行为启动、停止音频输入流。
  • 支持音频输出流倍速:

    支持音频输出流的倍速播放(依赖SDK,当前SDK未支持)。

  • 支持音频流信息获取:

    • 支持获取音频输出流的播放位置信息。
    • 支持获取音频输出流的播放时间戳信息。
    • 支持获取音频输入流的采集时间戳信息。
    • 支持获取音频输出流的播放缓冲大小。
  • 支持音频流在线模式:

    • 支持获取音频输入流的数据通道ID,在线采集数据。
    • 支持获取音频输出流的数据通道ID,在线播放数据。

音量管理

音量管理涉及音量调节和音量记录,细分如下:

  • 支持音频流音量调节:

    • 支持增大音量、减少音量、静音、关闭静音、恢复音量。
    • 支持调节不同的音频类型的音量,包含不同的输出设备:本地speaker和蓝牙音量。
  • 支持音频流音量记录和管理:

    • 支持音量记录,播放时恢复上一次的音量。
    • 支持音量分类型记录管理,不同音频类型可以分别保存。
  • 支持多音频场景叠加时音量的淡入淡出效果:

    • 即将到来的音频流会把当前的流的音量压低为即将到来的音频流的一半,这个比例会一直存在,调整新的音频流音量,老的音频流音量也会变化,调整老的音频流音量,新的音频流音量也会变化,但是这个变化的音量值不会被一直记录,音频流停止以后音量就恢复为原来的值。
    • 即将到来的音频流停止以后会恢复当前音频流的音量。

    例如:现在正在播放提示音,此时又开始播放音乐,提示音和音乐应该混音,按照策略此时提示音音量会是音乐音量的一半,可能会出现提示音突然听不见或者声音很小,退出音乐又恢复的情况,就是这个策略导致的。

音效管理

音效按照音频场景对应生效,例如:音乐播放、语音通话场景对应不同音效,也可以没有音效,目前媒体配置音效具体效果依赖SDK。

音频管理DFX

音频管理DFX(Design For X)属于非功能属性,例如:性能、可靠性、可服务性、可测试性,细分如下:

  • 音频焦点信息(焦点SessionID、对应流类型、策略、对应流的创建销毁状态),请参见 AudioManager::DumpInfo
  • 支持前后台音频流信息查询(包括音频流类型、对应策略、音频格式、混音比例、流类型音量、流音量、流路由设备),请参见 AudioManager::DumpInfo
  • 支持音频设备音量查询。
  • 音频设备切换到耳机时,保持音量在安全范围内。
  • 可测试性支持CHR日志打点,通过调用公共CHR打点接口将打点数据通过串口打印或者保存到文件系统中,实现对音频管理模块关键行为节点的记录,具体枚举定义请参见“/src/middleware/services/media/dfx/chr/include/media_chr_log.h”文件。

音频采集

概述

音频采集模块提供完整的声音采集功能,根据用户配置,支持采集音频原始PCM和带有压缩的MP3数据、OPUS数据、SILK数据,仅OPUS数据支持OGG(OGG-Vorbis)格式封装。

备注:不支持多实例。

功能描述

机制原理

音频采集模块主要提供参数配置、参数获取、流程控制和状态获取四个功能。具体的流程框图和周边模块交互关系如图1所示。

图 1 音频采集模块上下文组件关系图

音频采集模块上下文组件关系图

概括来说,音频采集模块主要用来提供以下服务:

  • 提供采集参数配置和获取接口。

    例如:可配参数包含输入源类型、音频数据格式、采样率、声道数、采样位宽等。

  • 提供获取帧数接口。

    例如:外部根据该接口得到帧数进而计算存储音频数据缓冲区大小,详见 AudioCapturer::Read

  • 提供采集流程接口。

    例如:开始采集、开始读数据、停止采集、释放资源等。

  • 提供采集位置接口。

    例如:获取采集实时位置。

  • 提供采集状态获取接口。

    例如:采集状态包括初始状态、准备录制、正在录制、录制已停止、资源已释放。

音频采集状态机

图 1 音频采集调用和状态迁移

音频采集调用和状态迁移

为保证音频采集状态机正常运转,对上层调用约束如下:

  • Start不允许在INITIALIZED、RECORDING、RELEASED状态调用,正常是在SetCaptureInfo成功后调用。
  • Read和Stop只能在Start后调用。
  • GetFrameCount不允许在INITIALIZED和RELEASED状态调用,返回0表示失败。
  • GetCaptureInfo和GetAudioTime不允许在RELEASED状态调用。

音频采集DFX

音频采集DFX属于非功能属性,例如:性能、可靠性、可服务性、可测试性。细分如下:

  • 音频采集参数信息(帧数、采样率、声道数、码率、位宽、音频采集状态),请参见 AudioCapturer::DumpInfo
  • 支持获取采集音频时间。
  • 可测试性支持CHR日志打点,通过调用公共CHR打点接口将打点数据通过串口打印或者保存到文件系统中,实现对音频采集模块关键行为节点的记录,具体枚举定义请参见“/src/middleware/services/media/dfx/chr/include/media_chr_log.h”文件。

播放器模块

概述

播放器模块为用户提供本地文件播放功能、网络音频播放功能和StreamSource播放功能。播放器支持的音视频格式请参考“播放器模块支持音视频格式”小节。

播放器模块包含解封装功能与播控功能,解封装功能使用对媒体文件的解析,例如将MP3解析成ES(Elementary Stream)流;播控功能包括播放控制、内部数据管理等。用户可以根据demuxer_interface插件库头文件实现自己的解封装库。

此外,本模块提供了回调注册接口,用户可以选择注册回调接口以接收文件播放完成和拖动完成的消息。可以接收播放进度回调消息以及播放错误消息。

播放器模块在整个系统中的位置如图1所示。

图 1 播放器模块上下文

播放器模块上下文

说明: 本播放器不支持网络播放。

播放器模块层次结构

播放器模块的层次结构如图1所示。

图 1 播放器模块层次结构图

播放器模块层次结构图

Player主要由4大模块组成:player control(播放器控制模块)、player source(播放源处理模块)、player decoder(解码模块)、player sink(输出模块)。

  • player control模块主要负责接收响应APP侧命令、模块管理和上报事件处理。
  • player source对接Format模块,负责片源解析处理及输出数据帧处理。
  • player decoder模块对接codec模块,负责音视频帧解码处理。
  • player sink对接audio和显示模块,负责音视频流输出处理及同步处理。

播放器模块处理流程

播放器模块的处理流程如图1所示。

图 1 播放器模块处理流程

播放器模块处理流程

图1可以看出整个播放器的处理流程:

  1. 创建播放器实例。
  2. 设置播放源(文件路径uri或stream source)。
  3. 播放准备处理。
  4. 注册回调函数。
  5. 获取宽高属性(可选)。
  6. 设置视频显示区域。
  7. 设置音量(可选)。
  8. 执行播放直到文件结束。
  9. 播放过程中执行Pause/Play。
  10. 播放过程中执行Rewind。
  11. 播放过程中执行倍速播放(目前暂不支持)。
  12. 停止播放器。
  13. 释放播放器资源。
  14. 删除播放器实例。

播放器状态图

播放器状态如图1所示。

图 1 播放器模块状态图

播放器模块状态图

StreamSource源类型Buffer流转

针对StreamSource场景,Buffer运转方式示意图如图1所示,player内部持有Buffer实体,外部APP侧仅使用Available Buffer并填充后送到player侧,player从Buffer读取数据解析后送解码处理。

图 1 StreamSource buffer流转图

StreamSource-buffer流转图

播放器模块支持音视频格式

播放器在本地播放、网络播放、StreamSource播放场景支持的音视频格式如图1所示。

图 1 播放器模块支持音视频格式

播放器模块支持音视频格式

说明: 播放器不支持MSBC格式。 SILK格式播放不支持暂停恢复、播放中切换输出设备。

功能描述

播控模块

播控模块提供媒体播放、暂停、停止功能。用户在回调函数中不能有阻塞操作,也不能调用播放器接口。

播放器模块的播放状态对应接口如下:

  • 实例创建后对应PLAYER_IDLE状态。
  • SetSource对应PLAYER_INITIALIZED状态。
  • Prepare对应PLAYER_PREPARED状态。
  • Play对应PLAYER_STARTED状态。
  • Pause对应PLAYER_PAUSED状态。
  • Stop 对应PLAYER_STOPPED状态。
  • Reset对应PLAYER_IDLE状态。

播放器模块DFX

播放器模块DFX属于非功能属性,例如:性能、可靠性、可服务性、可测试性。细分如下:

  • 播放文件信息(流类型、文件路径、文件大小、开始播放时间、总播放时间、视频流索引、音频流索引等),请参见 Player::DumpInfo
  • 播放视频流信息(视频流索引号、视频解码类型、视频宽、视频高等),请参见 Player::DumpInfo
  • 播放音频流信息(音频流索引号、音频解码类型、采样率、声道数、位宽等),请参见 Player::DumpInfo
  • 播放控制信息(会话ID、输出设备、当前播放位置、倍速、播放状态、是否循环等),请参见 Player::DumpInfo
  • 支持获取播放状态、播放倍速、当前播放位置。
  • 支持获取视频宽、高信息。
  • 可测试性支持CHR日志打点,通过调用公共CHR打点接口将打点数据通过串口打印或者保存到文件系统中,实现对播放器模块关键行为节点的记录,具体枚举定义请参见“/src/middleware/services/media/dfx/chr/include/media_chr_log.h”文件。

智能语音服务

概述

媒体基于IntelligentVoiceService承载固定命令词、模糊命令词和文本转语音播放功能。其中需要在NPU部署算法的模糊命令词和文本转语音统一由IntelligentVoiceService和AI进行交互。整体架构如图1所示。

图 1 intelligent voice service 整体架构

语音服务整体架构

其中IntelligentVoiceService主要包括:

  • AsrEngine语音识别(Automatic Speech Recognition)引擎,包括固定命令词和模糊命令词的生命周期管理、焦点管理及命令词结果上报。
  • TtsEngine语音合成(Text-To-Speech)引擎,包括TTS的生命周期管理、焦点管理及语音播报。

功能描述

固定命令词

固定命令词和模糊命令词的架构统一通过AsrEngine实现,基于AudioManager和AudioStreamIn纳入统一的音频管理,解决命令词识别和其他音频场景的资源冲突问题,架构如图1所示。

图 1 intelligent voice service 固定命令词架构

语音服务固定命令词架构

说明:

  • 固定命令词使用的流类型是AUDIO_STREAM_VOICE_RECOGNITION(语音识别),与其他的流的交互策略,详见“音频中断策略管理”小节。
  • 固定命令词只支持19个固定的词语、人声必须要完全一致才能识别。
  • 固定命令词可以用来唤醒模糊命令词,详见“固定命令词唤醒模糊命令词”小节。

支持的固定命令词意图如表1所示。

表 1 支持的固定命令词意图

命令词ID

支持的中文命令词

对应的意图枚举,详见“数据类型及数据结构”小节

1

小艺小艺。

ASR_ENGINE_WAKEUP_KEYWORD_XIAO_YI

2

接听电话。

ASR_ENGINE_TELEPHONE_CTRL_ANSWER_CALL

3

挂断电话。

ASR_ENGINE_TELEPHONE_CTRL_REFUSE_CALL

4

上一首。

ASR_ENGINE_MUSIC_CTRL_PLAY_PRE

5

下一首。

ASR_ENGINE_MUSIC_CTRL_PLAY_NEXT

6

调大音量。

ASR_ENGINE_DEVICE_CTRL_INCREASE_VOLUME

7

调小音量。

ASR_ENGINE_DEVICE_CTRL_DECREASE_VOLUME

8

播放音乐。

ASR_ENGINE_MUSIC_CTRL_PLAY

9

停止播放。

ASR_ENGINE_MUSIC_CTRL_PAUSE

10

继续播放。

ASR_ENGINE_MUSIC_CTRL_RESUME

11

打开锻炼。

ASR_ENGINE_APP_SPORT

12

打开短信。

ASR_ENGINE_APP_MESSAGE

13

打开闹钟。

ASR_ENGINE_APP_ALARM_APP

14

支付宝支付。

ASR_ENGINE_APP_ALI_PAY

15

微信支付。

ASR_ENGINE_APP_WECHAT_PAY

16

支付宝扫一扫。

ASR_ENGINE_APP_ALI_PAY_SCAN

17

你好悠悠。

ASR_ENGINE_WAKEUP_KEYWORD_HELLO_YOYO

18

小微小微。

ASR_ENGINE_WAKEUP_KEYWORD_XIAO_WEI

19

嗨,塞莉亚。

ASR_ENGINE_WAKEUP_KEYWORD_HI_CELIA

模糊命令词

模糊命令词由AsrEngine向AudioManager请求音频焦点,并创建AudioStreamIn获取到的PCM音频流经过Fbank和CTC两个NPU推理后返回模糊命令词结果上报,具体加架构如图1所示。

图 1 intelligent voice service 模糊命令词架构

语音服务模糊命令词架构

说明:

  • 固定命令词使用的流类型是AUDIO_STREAM_VOICE_ASSISTANT(语音助手),与其他的流的交互策略,详见“音频中断策略管理”小节。
  • 模糊命令词可以识别不同表达但是意思相近的意图。
  • 模糊命令词最长可以识别5秒钟的人声数据,但是目前无法一次识别多个意图,如果有多个意图,会只上报第一个意图。
  • 使用模糊命令词必须上传相关算法文件到设备文件系统,具体路径请参考表2

具体的模糊命令词支持的意图如表1所示。

表 1 支持的模糊命令词意图

命令词ID

支持的模糊命令词意图

对应的意图枚举,详见“数据类型及数据结构”小节

1

打开心率

ASR_ENGINE_APP_HEART_RATE

3

打开运动记录

ASR_ENGINE_APP_SPORT_RECORD

5

打开训练状态

ASR_ENGINE_APP_TRANING_STATUS

6

打开血氧饱和度

ASR_ENGINE_APP_SPO2

7

打开睡眠

ASR_ENGINE_APP_SLEEP

8

打开压力

ASR_ENGINE_APP_PRESSURE

9

打开呼吸训练

ASR_ENGINE_APP_BREATH_TRAIN

10

打开通话记录

ASR_ENGINE_APP_CALL_RECORDS

14

打开天气

ASR_ENGINE_APP_WEATHER

15

打开音乐

ASR_ENGINE_APP_MUSIC_APP

16

打开遥控拍照

ASR_ENGINE_APP_REMOTE_PHOTO

17

打开通知

ASR_ENGINE_APP_NOTIFICATION

18

打开闹钟

ASR_ENGINE_APP_ALARM_APP

19

打开日程

ASR_ENGINE_APP_CALENDAR

21

打开指南针

ASR_ENGINE_APP_COMPASS

22

打开秒表

ASR_ENGINE_APP_STOPWATCH

23

打开倒计时

ASR_ENGINE_APP_TIMER

24

打开微信支付

ASR_ENGINE_APP_WECHAT_PAY

25

打开支付宝

ASR_ENGINE_APP_ALI_PAY

26

打开海拔气压计

ASR_ENGINE_APP_BAROMETER

27

打开气压高度计

ASR_ENGINE_APP_ALTIMETER

29

打开网易音乐

ASR_ENGINE_APP_NETEASY_MUSIC

30

打开电话

ASR_ENGINE_APP_TELEPHONE

31

打开短信

ASR_ENGINE_APP_MESSAGE

32

打开表盘

ASR_ENGINE_APP_WATCH_FACE

34

增大亮度

ASR_ENGINE_DEVICE_CTRL_INCREASE_BRIGHTNESS

35

减小亮度

ASR_ENGINE_DEVICE_CTRL_DECREASE_BRIGHTNESS

36

打开静音

ASR_ENGINE_DEVICE_CTRL_OPEN_MUTE

37

增大音量

ASR_ENGINE_DEVICE_CTRL_INCREASE_VOLUME

38

减小音量

ASR_ENGINE_DEVICE_CTRL_DECREASE_VOLUME

39

打开持续亮屏

ASR_ENGINE_DEVICE_CTRL_OPEN_STEADY_SCREEN

40

关闭持续亮屏

ASR_ENGINE_DEVICE_CTRL_CLOSE_STEADY_SCREEN

41

设置铃声

ASR_ENGINE_DEVICE_CTRL_SET_RING

45

打开勿扰

ASR_ENGINE_DEVICE_CTRL_OPEN_NODISTURB

46

关闭勿扰

ASR_ENGINE_DEVICE_CTRL_CLOSE_NODISTURB

49

打开省电

ASR_ENGINE_DEVICE_CTRL_OPEN_POWER_SAVE

50

关闭省电

ASR_ENGINE_DEVICE_CTRL_CLOSE_POWER_SAVE

55

暂停运动

ASR_ENGINE_MOTION_CTRL_PAUSE_SPORT

56

停止运动

ASR_ENGINE_MOTION_CTRL_STOP_SPORT

57

继续运动

ASR_ENGINE_MOTION_CTRL_RESUME_SPORT

75

查找手机

ASR_ENGINE_DEVICE_CTRL_FIND_PHONE

76

打开手电筒

ASR_ENGINE_DEVICE_CTRL_OPEN_FLASHLIGHT

77

关闭手电筒

ASR_ENGINE_DEVICE_CTRL_CLOSE_FLASHLIGHT

78

公交卡

ASR_ENGINE_APP_BUS_CARD

79

门禁卡

ASR_ENGINE_APP_ACCESS_CARD

82

设置闹钟

ASR_ENGINE_ALARM_CTRL_SET

83

设置倒计时

ASR_ENGINE_TIMER_CTRL_SET

85

开始运动

ASR_ENGINE_MOTION_CTRL_START_SPORT

86

开始户外跑

ASR_ENGINE_MOTION_CTRL_START_OUTDOOR_RUNNING

87

开始室内跑步

ASR_ENGINE_MOTION_CTRL_START_INDOOR_RUNNING

88

开始跑步课程

ASR_ENGINE_MOTION_CTRL_START_RUNNING_COURSE

89

开始健走

ASR_ENGINE_MOTION_CTRL_START_WALKING

90

开始户外骑行

ASR_ENGINE_MOTION_CTRL_START_OUTDOOR_CYCLING

91

开始跑步机

ASR_ENGINE_MOTION_CTRL_START_TREADMILL

92

开始室内骑行

ASR_ENGINE_MOTION_CTRL_START_INDOOR_CYCLING

93

开始室内步行

ASR_ENGINE_MOTION_CTRL_START_INDOOR_WALKING

94

开始公开水域游泳

ASR_ENGINE_MOTION_CTRL_START_OPENWATER_SWIMMING

95

开始泳池游泳

ASR_ENGINE_MOTION_CTRL_START_POOL_SWIMMING

96

开始椭圆机

ASR_ENGINE_MOTION_CTRL_START_ELLIPTICAL

97

开始登山

ASR_ENGINE_MOTION_CTRL_START_CLIMBING

98

开始越野跑

ASR_ENGINE_MOTION_CTRL_START_TRAIL_RUNNING

99

开始滑雪

ASR_ENGINE_MOTION_CTRL_START_SKIING

100

开始划船机

ASR_ENGINE_MOTION_CTRL_START_ROWING

101

开始铁人三项

ASR_ENGINE_MOTION_CTRL_START_TRIATHLON

102

开始高尔夫

ASR_ENGINE_MOTION_CTRL_START_GOLF

103

开始自由训练

ASR_ENGINE_MOTION_CTRL_START_FREE_TRAINING

表 2 算法文件上传路径

文件名/目录名

sdk目录

文件系统目录

new_ctc_final_fifo.exeom

/src/application/wearable/res/ai/ops/asr_ctc

/user/ai/ops/asr_ctc/new_ctc_final_fifo.exeom

ctc.exeom

/src/application/wearable/res/ai/ops/ctc

/user/ai/ops/ctc/ctc.exeom

NLU.exeom

/src/application/wearable/res/ai/ops/nlu/NLU.exeom

/user/ai/ops/nlu/NLU.exeom

save_posemb5000x64_uint16.bin

/src/application/wearable/res/ai

/user/ai/save_posemb5000x64_uint16.bin

offset.bin

/src/application/wearable/res/ai/ops/asr

/user/ai/ops/asr/offset.bin

文本转语音播放

文本转语音播放由TtsEngine模块进行文本前处理后送NPU推理,并把PCM数据片段合并后通过AudioStreamOut输出到本地的Speaker,具体架构如图1所示,

图 1 intelligent voice service TTS架构

语音服务TTS架构

说明:

  • TTS使用的流类型是AUDIO_STREAM_TTS(语音合成),与其他的流的交互策略,详见“音频中断策略管理”小节。
  • TTS目前支持识别3000多个常用汉字,以及阿拉伯数字,部分“,”“。”“!”等常用的标点符号,如果是无法识别的字符会进行跳过。
  • TTS支持多次设置文本,详见“TTS文本转语音”小节。
  • TTS支持的单次输入文本长度不超过1024个字节,总缓存文本空间不超过5×1024字节大小,详见 IntellVoiceTtsEngineTextSpeak

快速跑通多媒体软件 Demo

功能说明

本节以音频播放、音频采集和播放器三个最小场景说明多媒体接口的调用顺序、资源生命周期和错误定位入口。选择一个与目标业务最接近的场景跑通后,再进入“基于多媒体软件 Demo 开发自己的应用”扩展设备、格式和业务回调。

编译

说明: 本文示例命令以一站式 CLI 为主。实际开发可从以下三种环境中选择,推荐使用一站式 CLI。

开发环境 适用场景 使用指南
一站式 CLI(推荐) 快速完成目标选择、构建、烧录和串口监视 一站式 CLI 开发环境使用指南
HiSpark Studio for VS Code 图形化编辑、编译、烧录和调试 HiSpark Studio for VS Code 开发环境使用指南
WSL 与 Docker 在 Windows 上使用一致的 Linux 容器构建环境 WSL 与 Docker 环境使用指南

一站式 CLI 开发环境使用指南 准备环境、构建和烧录;构建前必须确保 fbb doctor 成功。开发前还应确认产品的设备路由、音频策略、权限和媒体资源路径已经就绪。

使用方式

音频播放场景

音频输出流必须遵循“管理器初始化 → 输出流初始化 → 开始播放 → 持续写入 → 停止 → 去初始化”的顺序。音频焦点、设备选择和音量策略由 AudioManager 管理,不能绕过管理器只依赖写数据成功判断播放成功。

音频播放流程

阶段 接口说明
初始化管理器 AudioManagerInit
创建输出流 AudioStreamOutInit
启动与送数据 AudioStreamOutPlayAudioStreamOutStreamWrite
停止与释放 AudioStreamOutStopAudioStreamOutDeinit

关键代码走读的重点是每一步返回值和状态约束:只有 AudioStreamOutInit() 成功后才能播放,只有 AudioStreamOutPlay() 成功后才能写入;停止后不得继续写数据。实际流参数、焦点策略、音量和设备选择见后文“音频管理”的“开发指导”。

音频采集场景

输入流遵循“管理器初始化 → 输入流初始化 → 启动 → 获取缓冲区 → 停止 → 去初始化”的顺序。获取到的缓冲区需要在当前采集状态和规定生命周期内消费;采样率、位宽和声道数应与下游编码、存储或算法要求一致。

阶段 接口说明
创建输入流 AudioStreamInInit
启动采集 AudioStreamInStart
获取数据 AudioStreamInObtainBuffer
停止与释放 AudioStreamInStopAudioStreamInDeinit

此场景的完整参数说明、数据类型和典型流程见后文“音频采集”的“典型场景开发指导”。出现无数据时,先检查输入设备路由和采集流状态;出现杂音时,再检查格式、增益和下游处理,不要直接把问题归因于编码器或播放器。

播放器场景

播放器场景适用于由容器、解封装和音视频编解码模块组成的本地或流媒体播放业务。创建播放器后应先设置数据源、准备、启动,再处理播放状态、错误、停止和释放;音视频流索引、时钟同步和资源释放的详细约束以播放器 API 参考和后文“播放器模块”为准。

对播放无画面、无声音或进度异常的问题,应分别验证数据源可读、容器/编解码支持、音频输出设备和视频显示链路。不要以“播放器创建成功”替代数据源已成功准备或流已正确输出。

预期结果

  • 音频播放场景中,输出流初始化、启动和写入均成功返回,停止后不再继续送数据。
  • 音频采集场景中,输入流启动后能够获取有效缓冲区,缓冲区在规定生命周期内完成消费并在停止后释放。
  • 播放器场景中,数据源经过 Prepare 后进入可播放状态,播放、停止和资源释放顺序符合状态机约束。

文件结构与代码走读

快速 Demo 的代码走读以三个业务场景的调用顺序为主:音频播放遵循 AudioManager 与输出流生命周期;音频采集遵循输入流与缓冲区生命周期;播放器遵循数据源、准备、播放、停止和释放的状态转换。以下代码均来自 SDK 现有工程,可作为场景实现时的直接参考。

Native 音频流参考

/samples/native_samples/voice_cloud_LLM/README.md 提供了麦克风 PCM 采集、云端音频下行和本地 PCM 播放的完整闭环。其中,采集和播放代码与本章的 AudioManager、AudioStreamIn/AudioStreamOut 场景接口一致。

文件 对应场景 走读重点
/samples/native_samples/voice_cloud_LLM/voice_cloud_llm_audio_capture.cpp 音频采集 初始化 AudioManager,创建会话和中断,初始化并启动 AudioStreamIn,在循环中获取 PCM 缓冲区,停止时按反序释放资源。
/samples/native_samples/voice_cloud_LLM/voice_cloud_llm_audio_player.cpp PCM 播放 初始化 AudioManager 和输出流,启动 AudioStreamOut,将队列中的 PCM 数据持续写入输出流,停止时清空队列并释放资源。
/samples/native_samples/voice_cloud_LLM/voice_cloud_llm.cpp 智能语音扩展 管理唤醒、ASR、网络会话、采集和播放之间的状态切换;适合参考多模块资源释放顺序。

采集初始化的核心顺序如下,后续的数据提取由 AudioStreamInObtainBuffer() 完成:

if (!AudioManagerInit()) {
    return ERRCODE_FAIL;
}
captureConfig.sessionID = g_audioManager.MakeSessionId();
g_audioManager.ActivateAudioInterrupt(g_captureCtx.interrupt);
AudioStreamInInit(captureConfig);
AudioStreamInStart();

播放初始化的关键在于先完成输出流初始化和启动,再通过 AudioStreamOutStreamWrite() 送入 PCM 数据:

if (!AudioManagerInit()) {
    return ERRCODE_FAIL;
}
g_audioManager.ActivateAudioInterrupt(g_playerCtx.interrupt);
AudioStreamOutInit(g_playerCtx.renderConfig);
AudioStreamOutPlay();

当前 diting-community 目标已在 /src/build/config/target_config/3322/config.py 中启用 CONFIG_VOICE_CLOUD_LLM_ENABLE 并接入 voice_cloud_LLM 组件,完整工程位于 /samples/native_samples/voice_cloud_LLM/README.md。它适合作为 Native 音频流和智能语音组合场景的代码参考,不作为本章最小播放或采集 Demo 的唯一构建入口。

JS 播放应用参考

JS 示例使用 @system.audio,与 Native 的 AudioManager/AudioStream 接口层不同,适合用于讲解应用 UI 与播放状态管理,不应与 Native 调用链混用。

文件 场景 走读重点
/samples/js_samples/music/entry/src/main/js/MainAbility/pages/play/play.js 音乐播放 srcsrcInnersrcList 的音源设置;onplayonendedontimeupdate 回调;播放、暂停、音量、循环和后台播放管理。
/samples/js_samples/woodenfish/entry/src/main/js/MainAbility/pages/woodenfish/woodenfish.js 最小内置音效播放 srcInner 设置内置 MP3,调用 play(),在 onended、页面退出和隐藏时调用 reset() 释放播放状态。

woodenfish 的最小播放流程为:设置 player.srcInner、设置流类型和音量、调用 player.play(),并在播放完成回调中 player.reset()music 在此基础上增加了列表、网络音源、进度和后台播放控制。

驱动与算法场景参考

/src/application/audio/CMakeLists.txt 将多个音频场景编入 audio_sample 组件。其中基础源码随组件参与构建,ASR、TTS、音效调整、唤醒等场景受产品配置开关控制。这些文件使用 SOUND、AI、AENC、ADEC 等驱动/SAP 接口,适合深入理解驱动能力和特性场景,不替代前述 Native 应用层接口示例。

代码路径 场景
/src/application/audio/sample_ao/sample_ao.c/src/application/audio/sample_ao/sample_aef.c SOUND/AO 播放、混音和 AEF 音效。
/src/application/audio/sample_ai/sample_ai.c/src/application/audio/sample_ai/sample_ai_aenc.c AI 采集和 AI→AENC 编码。
/src/application/audio/sample_decode/sample_adec.csample_encode/sample_encode.c ADEC 解码播放和 AENC 编码。
/src/application/audio/sample_asr/sample_asr.csample_tts/sample_tts.csample_sea/sample_sea.c ASR、TTS、SEA 语音算法场景。
/src/application/audio/sample_phone/sample_phone_apps.c 电话音频场景。
Demo 入口与场景分发

驱动与算法 Demo 不是单独复制到 samples 的工程,其代码统一归档在 /src/application/audio/CMakeLists.txt,由 audio_sample 组件组织。串口发送 AT^AUDIO=<场景名> <参数> 后,AT 层拆分参数并调用 audio_execute_function();该函数在 g_audio_func[] 中查找同名场景,再进入对应 Demo。

static functions g_audio_func[] = {
    SAMPLE_ITEM(sample_ai),
    SAMPLE_ITEM(sample_ao),
    SAMPLE_ITEM(sample_proc),
    /* 省略受产品配置控制的其他场景。 */
    SAMPLE_ITEM(sample_ai_aenc),
    SAMPLE_ITEM(sample_ai_ao),
    SAMPLE_ITEM(sample_ai_sea_ao),
    SAMPLE_ITEM(sample_sea),
    SAMPLE_ITEM(sample_aef),
};

for (i = 0; i < (sizeof(g_audio_func) / sizeof(g_audio_func[0])); i++) {
    if (strcmp(*argv, g_audio_func[i].name) != 0) {
        continue;
    }
    ret = g_audio_func[i].func(argc, argv);
    return;
}

这层分发使播放、采集、回环、编解码和算法场景共用同一个 AT 入口。新增场景时,应在自己的源文件中实现参数解析和资源生命周期,再把函数加入 g_audio_func[];不要把设备初始化逻辑堆到 AT 命令处理函数中。

直接跑通内置 PCM 播放

/src/application/audio/sample_ao/sample_ao.c 支持文件、固定地址和内置数组三类输入。使用 array 不需要先向文件系统部署 PCM 文件,适合先验证 SOUND、Track 和板级扬声器通路:

AT^AUDIO=sample_ao array -6 16000 16 1

参数依次表示“输入源、音量、采样率、位宽、声道数”。听到内置 PCM 后发送以下命令停止并释放资源:

AT^AUDIO=sample_ao q

sample_ao_entry() 保持“解析参数 → 打开输入 → 打开 SOUND/Track → 启动播放”的顺序;任一步失败都会跳转到对应的反向释放分支:

ret = sample_ao_open_file(inst);
if (ret != EXT_SUCCESS) {
    goto out0;
}
ret = sample_ao_open_inst(inst);
if (ret != EXT_SUCCESS) {
    goto out1;
}
ret = sample_ao_start_inst(inst);
if (ret != EXT_SUCCESS) {
    goto out2;
}

其中 sample_ao_open_inst() 先初始化音频子系统和 SOUND,再为每路 PCM 创建 Track;sample_ao_start_inst() 先启动 Track 送数,最后调用 uapi_snd_set_enable() 打开输出设备。sample_ao_exit() 则依次停止实例、关闭 SOUND/Track、关闭输入并释放实例,开发者扩展文件播放时应保留这一生命周期。

跑通麦克风到扬声器的实时回环

/src/application/audio/sample_ai/sample_ai.csample_ai_ao() 将 AI 采集帧经 ADP 送入播放端,可同时检查麦克风输入、PCM 缓冲和扬声器输出:

AT^AUDIO=sample_ai_ao -p adc0 -c 1 -s 16000

测试时降低扬声器音量并拉开麦克风与扬声器距离,避免声学啸叫。验证完成后发送:

AT^AUDIO=sample_ai_ao q

sample_ai_ao_entry() 先解析端口、声道和采样率,再打开播放端、AI 实例并启动回环;失败分支与退出命令都会按反向顺序释放资源:

ret = sample_ai_player_open(inst);
if (ret != EXT_SUCCESS) {
    goto out0;
}
ret = sample_ai_open_inst(inst);
if (ret != EXT_SUCCESS) {
    goto out1;
}
ret = sample_ai_ao_start_inst(inst);
if (ret != EXT_SUCCESS) {
    goto out2;
}

采集线程通过 uapi_adp_acquire_frame() 获取一帧,调用当前场景的 sample_ai_process 处理,再通过 uapi_adp_release_frame() 归还缓冲区。回环场景把处理函数设置为 sample_ai_ao_process(),后者调用 uapi_adp_send_frame() 将同一帧送入播放端。基于该 Demo 接入 AENC、SEA 或业务算法时,应在帧生命周期内完成处理,不能在释放后继续持有 uapi_audio_frame 中的数据地址。

从基础链路扩展算法场景

基础播放和回环通过后,再按目标能力选择现有实现:

目标 入口函数 扩展重点
AI 采集后编码 sample_ai_aenc() AI 与 AENC 的 PCM 格式必须一致,停止时先停输入再释放编码器。
解码并播放 sample_adec() 输入码流格式、ADEC 参数和播放端 PCM 属性必须匹配。
SEA 处理后回放 sample_ai_sea_ao() 在基础回环上启用 SEA,算法参数和资源需随实例一起释放。
AEF 音效 sample_aef() 先取得有效 SOUND 句柄,再加载与目标配置匹配的 AEF 能力。

建议先用 sample_ao array 验证输出,再用 sample_ai_ao 验证输入与回环,最后接入编解码或算法。这样可以把“无声、无数据、格式不匹配、算法初始化失败”分别定位到不同阶段。

需要展开接口参数、数据类型、错误码或完整调用流程时,可继续查阅音频管理开发指导音频采集典型场景开发指导播放器使用指导

开发自己的应用

选择与业务最接近的播放、采集或播放器场景,保持其初始化、启动、停止和释放的调用顺序,再替换数据源、设备路由、流参数或业务回调。新增场景应先用默认设备和已知格式跑通,再逐步接入蓝牙、音效、视频显示或智能语音等扩展能力。

开发前的接口选择

同一应用只选择一种主接口栈。Native、JS 和驱动/SAP 接口的对象、生命周期和构建方式不同,不能在同一个调用链中混用。

目标应用 推荐接口栈 代码参考 适用边界
C/C++ PCM 播放、采集或实时语音流 AudioManagerService(AudioStreamIn/AudioStreamOut) /samples/native_samples/voice_cloud_LLM/voice_cloud_llm_audio_capture.cpp 需要直接管理 PCM 帧、设备路由和音频中断。
C/C++ 文件或流媒体播放 Player C++ API 播放器使用指导 需要解封装、状态机、音视频同步或视频输出。
JS UI 播放应用 @system.audio /samples/js_samples/music/entry/src/main/js/MainAbility/pages/play/play.js 需要页面、播放列表、后台播放或交互状态管理。
驱动、编解码、音效和算法特性 SOUND/AI/AENC/ADEC 等驱动/SAP 接口 /src/application/audio/CMakeLists.txt 需要控制设备、编解码器、AEF、SEA、ASR、TTS 或电话链路。

关键配置和资源

开始编码前,确认以下条件。

项目 本章最小 Native Demo 的取值 影响
产品和构建配置 3322 目标启用 CONFIG_ENABLE_MEDIA_STREAM_DEMO 决定 media_stream_demo 组件是否参与构建和链接。
输出设备 默认设备路由至板级 Speaker 未正确配置输出设备时,接口成功返回也可能无声。
PCM 格式 16 kHz、单声道、16 bit、PCM AudioRendererConfig 和送入 AudioStreamOutStreamWrite() 的帧格式必须一致。
音频焦点 AUDIO_STREAM_MUSIC 与独立会话 ID 已有通话、语音助手等高优先级业务时,播放可能被打断。
媒体资源 本 Demo 运行时生成 1 kHz PCM 测试音 不依赖文件系统;使用文件或网络音源时,另行确认路径、格式和网络条件。

设备输入输出、TRACK MODE 和 AEF 的配置见音频配置;AT 场景的调用格式见AT命令使用

新建并接入 C 应用

以下以 media_stream_demo 为建议组件名,说明 C 应用播放 PCM 测试音所需的最小接入方式。

  1. /samples/native_samples 下新建组件目录,并在组件 CMakeLists.txt 中声明源文件、音频服务头文件目录和 WHOLE_LINK
  2. /samples/native_samples/CMakeLists.txt 中用独立配置宏包含该组件。
  3. 在目标配置的 defines 中启用组件宏,并把组件名加入 ram_component。本 Demo 的对应配置已在 /src/build/config/target_config/3322/config.py 中给出。
  4. 在应用入口注册可运行入口。本 Demo 使用 APP_FEATURE_INIT 注册 MSTREAM AT 命令;业务应用也可以在自己的任务、服务或 UI 回调中调用相同的初始化、播放和释放逻辑。

播放主流程必须保持以下顺序:

AudioManagerInit
  → AudioManagerMakeSessionId
  → AudioManagerActivateInterrupt
  → AudioStreamOutInit
  → AudioStreamOutPlay
  → AudioStreamOutStreamWrite
  → AudioStreamOutStop / AudioStreamOutDeinit
  → AudioManagerDeactivateInterrupt

应用应对每一步返回值进行检查;任一步失败时,按已成功的逆序释放输出流和音频中断。新应用应保留这一错误处理和资源释放结构。

核心代码走读

下表列出最小播放闭环涉及的接口;需要修改参数或扩展播放能力时,先查阅对应接口说明,再修改示例代码。

接口 用途
AudioManagerInit 初始化音频管理服务。
AudioManagerMakeSessionId 为本次播放申请独立会话 ID。
AudioManagerActivateInterruptAudioManagerDeactivateInterrupt 申请和释放音乐流的音频焦点。
AudioStreamOutInitAudioStreamOutPlay 按渲染参数创建输出流并启动播放。
AudioStreamOutStreamWrite 将 PCM 帧写入输出流。
AudioStreamOutStopAudioStreamOutDeinit 停止并销毁输出流。
  1. 创建会话并声明格式。 AudioRendererConfigsampleRatechannelCountbitWidthframeLength 必须与后续写入的 PCM 帧一致;streamType 用于申请对应的音频焦点。

    session_id = AudioManagerMakeSessionId();
    render_config.audioFormat = PCM;
    render_config.streamType = AUDIO_STREAM_MUSIC;
    render_config.sampleRate = 16000;
    render_config.sessionID = session_id;
    render_config.channelCount = 1;
    render_config.bitWidth = BIT_WIDTH_16;
    render_config.frameLength = 320;
    
  2. 按先后顺序申请资源。 只有在音频管理服务、会话和音频焦点均成功后,才初始化并启动输出流。示例对每一步的返回值进行判断,避免在未完成初始化的流上写数据。

    ret = AudioManagerActivateInterrupt(render_config.streamType, session_id);
    if (ret == ERRCODE_SUCC) {
        ret = AudioStreamOutInit(render_config);
    }
    if (ret == ERRCODE_SUCC) {
        ret = AudioStreamOutPlay();
    }
    
  3. 按帧送入 PCM 数据。 示例每次写入 320 × 2 字节,即 16 kHz 单声道 16 bit PCM 的 20 ms 数据,并按 20 ms 节拍送入。替换为文件、网络或算法输出时,仍要保持配置格式、字节数和数据节拍一致。

    ret = AudioStreamOutStreamWrite((const uint8_t *)pcm_frame,
        sizeof(pcm_frame));
    osal_msleep(20);
    
  4. 反向释放资源。 发生错误或播放完成时,依次停止输出、销毁输出流、释放音频焦点。该顺序避免输出设备仍被占用,影响下一次播放或其他音频业务。

    (void)AudioStreamOutStop();
    (void)AudioStreamOutDeinit();
    (void)AudioManagerDeactivateInterrupt();
    
  5. 提供可验证入口。 media_stream_demo_at_set() 只负责校验 PLAY 参数和创建任务;实际播放在独立任务中执行,避免 AT 命令处理函数被 1 秒的送帧过程阻塞。业务应用可保留这个入口用于板级验证,也可改为服务、按键或 UI 事件回调。

编译、验收与调试

一站式 CLI 开发环境使用指南完成构建和烧录。启动后执行:

AT+MSTREAM=PLAY

预期串口日志如下:

[media_stream_demo] playback started: 1 kHz, 16 kHz mono, 1 second
[media_stream_demo] playback completed

若无声或命令失败,按以下顺序检查:组件宏是否启用、MSTREAM 命令是否注册、Speaker 路由和音量、音频焦点是否被高优先级场景占用,以及 AudioStreamOutInitAudioStreamOutPlayAudioStreamOutStreamWrite 的返回值。