• 【Purple Pi OH RK3566鸿蒙开发板】OpenHarmony音频播放应用,真实体验感爆棚!


    本文转载于Purple Pi OH开发爱好者,作者ITMING 。

    原文链接:https://bbs.elecfans.com/jishu_2376383_1_1.html

    01注意事项

    • DevEco Studio 4.0 Beta2(Build Version: 4.0.0.400)

    • OpenHarmony SDK API 9

    • 创建工程类型选择Application

    • 修改entry/build-profile.json5配置文件中的targets>runtimeOS为OpenHarmony,然后进行Sync Now(同步)

    02工程概述

    PPI有声是一款基于OpenHarmony API 9 开发的,运行于Purple Pi 开发板(安装OpenHarmony标准系统)的音频播放应用程序。

    03场景化

    • 智慧家居类(电子门铃,温湿度显示仪,屏显灯控开关等)

    • 智慧办公类(打卡机,大屏显示等)

    • 智慧教育类(电子班牌,校园大屏,电子讲台等)

    04创建工程

    图片

    • Project name:工程名称

    • Bundle name:包名

    • Save location:工程存储路径

    • Compile SDK:编译API版本

    • Compatible SDK:兼容的最新API版本

    • Module name:模块名称

    • Model:模型

    • Enable Super Visual:是否启用低代码开发

    • Device Type:设备类型

    • Node:nodejs路径

    05媒体服务

    媒体子系统为开发者提供一套简单且易于理解的接口,使得开发者能够方便接入系统使用系统的媒体资源。

    媒体子系统包含了音视频相关媒体业务,提供以下常用共功能:

    • 音视频播放(AVPlayer)

    • 音视频录制(AVRecorder)

      5.1 AVPlayer概述

    AVPlayer主要工作是将Audio/Video媒体资源(比如mp4/mp3/mkv/mpeg-ts等)转码为可供渲染的图像或可听见的模拟信号,并通过输出设备进行播放。

    使用AVPlayer可以实现端到端播放原始媒体资源,播放对的全流程包含:创建AVPlayer,设置播放资源,设置播放参数 (音量/倍速/焦点模式),播放控制(播放/暂停/跳转/停止),重置,销毁资源。

    开发过程中开发者可以通过AVPlayer的state属性主动获取当前状态或使用on('stateChange')方法监听状态变化。若应用在音频播放器处于错误状态时执行操作,系统可能会抛出异常或生成其他未定义的行为。

    图片

    主:当播放处于prepared/playing/paused/completed状态时,播放引擎处于工作状态,需要占用系统较多的运行内容。当客户端暂时不适用播放器时,调用reset()或release()回收内存资源。

    5.2 开发步骤

    1. 导入media模块,调用createAVPlayer()方法创建AVPlayer实例,AVPlayer初始化idle状态。

    2. 设置业务监听事件,搭配全流程场景使用,如监听播放器state属性改变的stateChange;监听播放器错误信息的error;用于进度条,监听进度条长度,刷新资源时长的durationUpdate等。

    3. 设置资源:设置属性url,AVPlayer进入initialized状态

    4. 准备播放:调用prepare(),AVPlayer进入prepared状态,此时可以获取duration,设置音量。

    5. 音频播控:播放play(),暂停pause(),跳转seek(),停止stop()等操作。

    6. 调用reset()重置资源,AVPlayer重新进入idle状态,此时可更换播放源url。

    7. 调用release()销毁实例,AVPlayer进入released状态,退出播放。

    06构建PPI有声

    6.1 准备资源文件

    • 音频文件拷贝到resources/rawfile目录

    • 将拷贝到resources/base/mdiea目录

    • 音频播放背景图audio_bg.png

    • 音频播放旋转图audio.png

    • 暂停ic_pause.svg

    • 播放ic_play.svg

    图片

    6.2 构建UI页面

    整个UI以Flex弹性布局为主,子组件以列方式排列,分别为可旋转的音频播放控件,播放进度条以及播放控制按钮组成。

    6.2.1 可旋转的音频播放控件

    使用Stack堆叠布局容器为主,将旋转控件置于背景图之上。

    1. Stack({ alignContent: Alignment.Center }) {
    2.         Image($r('app.media.audio_bg'))
    3.           .width(200).height(200)
    4.         Image($r('app.media.audio'))
    5.           .width(100).height(100)
    6.           .backgroundColor(Color.White)
    7.           .borderRadius(50)
    8.           .rotate({ angle: this.angleNum })
    9.           .animation({
    10.             duration: this.duration,
    11.             tempo: 1,
    12.             curve: Curve.Linear,
    13.             iterations: -1,
    14.             playMode: PlayMode.Normal
    15.           })
    16.       }
    6.2.2 进度条

    播放进度由置于上部的播放时长和总时长,底部的播放进度条组成,包裹在Column列容器中。

    1. Column({ space4 }) {
    2.         Row() {
    3.           Text(this.msToS(this.currentProgress))
    4.             .fontSize(12)
    5.             .fontColor(0xc1c3c5)
    6.           Text(this.msToS(this.duration))
    7.             .fontSize(12)
    8.             .fontColor(0xc1c3c5)
    9.         }
    10.         .width('100%')
    11.         .justifyContent(FlexAlign.SpaceBetween)
    12.         // 播放进度条
    13.         Slider({
    14.           value: this.currentProgress,
    15.           min: 0,
    16.           max: this.duration,
    17.           style: SliderStyle.OutSet
    18.         })
    19.           .showTips(true)
    20.           .onChange((value: number, mode: SliderChangeMode) => {
    21.             this.currentProgress = value;
    22.             // 跳转到指定位置播放
    23.             this.avPlayer.seek(value);
    24.           })
    25.       }
    26.       .width('90%')
     
    

    6.2.3 播放控件

    播放控件通过当前AVPlayer的状态判断显示播放/暂停图标按钮。

    1. Row({ space10 }) {
    2.         if (this.state === 'playing') {
    3.           // 暂停
    4.           Image($r('app.media.ic_pause'))
    5.             .width(64).height(64)
    6.             .fillColor(0xff5722)
    7.             .onClick(() => {
    8.               // 暂停播放
    9.               this.avPlayer.pause().then(() => {
    10.                 this.angleNum = 0;
    11.               })
    12.             })
    13.         } else {
    14.           // 播放
    15.           Image($r('app.media.ic_play'))
    16.             .width(64).height(64)
    17.             .fillColor(0x00aaee)
    18.             .onClick(async () => {
    19.               if (this.avPlayer && this.avPlayer.state === "paused") {
    20.                 this.avPlayer.play().then(() => {
    21.                   this.angleNum = 360;
    22.                 })
    23.               } else {
    24.                 await this.initAVPlayer();
    25.               }
    26.             })
    27.         }
    28.       }
    29.       .width('100%')
    30.       .justifyContent(FlexAlign.Center)

    6.3 实现音频播放

    6.3.1 初始化AVPlayer

    1. // 播放音频AVPlayer实例
    2.   private avPlayer: media.AVPlayer = undefined;
    3.   // 初始化AVPlayer
    4.   async initAVPlayer() {
    5.     // 创建AVPlayer实例对象
    6.     this.avPlayerawait media.createAVPlayer();
    7.     // 创建状态机变化回调函数
    8.     this.setAVPlayerCallback();
    9.     await this.loadingResourceFile();
    10.   }
    6.3.2 加载HAP包资源文件
    1. // 加载HAP包资源文件
    2.   loadingResourceFile = async () => {
    3.     // 通过UIAbilityContext的resourceManager成员的getRawFd接口获取媒体资源播放地址
    4.     let context = getContext(this) as common.UIAbilityContext;
    5.     let fileDir = await context.resourceManager.getRawFd("audio.wav");
    6.     // 为fdSrc赋值触发initialized状态机上报
    7.     this.avPlayer.fdSrc = fileDir;
    8.   }
    6.3.3 注册AVPlayer回调函数
    1. // 注册AVPlayer回调函数
    2.   setAVPlayerCallback = () => {
    3.     // 状态机变化回调函数
    4.     // state:表示当前播放状态
    5.     // reason:表示当前播放状态的切换原因
    6.     this.avPlayer.on('stateChange', async (state, reason) => {
    7.       this.state = this.avPlayer.state;
    8.       switch (state) {
    9.         case 'initialized':
    10.           this.avPlayer.prepare().then(() => {
    11.             // 音频播放准备完毕后,获取音频总时长
    12.             this.duration = this.avPlayer.duration;
    13.           })
    14.           break;
    15.         case 'prepared':
    16.           // 开始播放
    17.           this.avPlayer.play().then(() => {
    18.             // 设置图标开始旋转
    19.             this.angleNum = 360;
    20.           })
    21.           break;
    22.       }
    23.     })
    24.     // 播放错误回调函数
    25.     this.avPlayer.on('error', (err) => {
    26.       console.error(`Error happened. Cause: ${JSON.stringify(err)}`);
    27.     })
    28.     // 监听资源播放当前时间回调函数
    29.     this.avPlayer.on('timeUpdate', (timenumber) => {
    30.       if (this.avPlayer.state === 'completed') {
    31.         this.currentProgress = 0;
    32.         this.duration = 0;
    33.         this.angleNum = 0;
    34.       } else {
    35.         this.currentProgress = time;
    36.       }
    37.     })
    38.   }
    07效果预览

    图片

    图片

  • 相关阅读:
    测试用到的测试工具总结一手
    mysql集群使用nginx配置负载均衡
    KT6368A蓝牙芯片的天线注意事项_倒F型-蛇形_陶瓷天线的区别
    linux top——COMMAND-LINE Options
    kafka教程
    记录下电脑windows安装Tina的过程
    SpringBoot整合RabbitMQ
    Python 潮流周刊#55:分享 9 个高质量的技术类信息源!
    如何提高社交产品的活跃度?
    [技术杂谈]几款常用的安装包制作工具
  • 原文地址:https://blog.csdn.net/Industio_CSDN/article/details/132870280