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

2023-09-14 10:09:06

本文转载于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堆叠布局容器为主,将旋转控件置于背景图之上。

Stack({ alignContent: Alignment.Center }) {
        Image($r('app.media.audio_bg'))
          .width(200).height(200)
        Image($r('app.media.audio'))
          .width(100).height(100)
          .backgroundColor(Color.White)
          .borderRadius(50)
          .rotate({ angle: this.angleNum })
          .animation({
            duration: this.duration,
            tempo: 1,
            curve: Curve.Linear,
            iterations: -1,
            playMode: PlayMode.Normal
          })
      }
6.2.2 进度条

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

Column({ space: 4 }) {
        Row() {
          Text(this.msToS(this.currentProgress))
            .fontSize(12)
            .fontColor(0xc1c3c5)
          Text(this.msToS(this.duration))
            .fontSize(12)
            .fontColor(0xc1c3c5)
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
        // 播放进度条
        Slider({
          value: this.currentProgress,
          min: 0,
          max: this.duration,
          style: SliderStyle.OutSet
        })
          .showTips(true)
          .onChange((value: number, mode: SliderChangeMode) => {
            this.currentProgress = value;
            // 跳转到指定位置播放
            this.avPlayer.seek(value);
          })
      }
      .width('90%')
 

6.2.3 播放控件

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

Row({ space: 10 }) {
        if (this.state === 'playing') {
          // 暂停
          Image($r('app.media.ic_pause'))
            .width(64).height(64)
            .fillColor(0xff5722)
            .onClick(() => {
              // 暂停播放
              this.avPlayer.pause().then(() => {
                this.angleNum = 0;
              })
            })
        } else {
          // 播放
          Image($r('app.media.ic_play'))
            .width(64).height(64)
            .fillColor(0x00aaee)
            .onClick(async () => {
              if (this.avPlayer && this.avPlayer.state === "paused") {
                this.avPlayer.play().then(() => {
                  this.angleNum = 360;
                })
              } else {
                await this.initAVPlayer();
              }
            })
        }
      }
      .width('100%')
      .justifyContent(FlexAlign.Center)

6.3 实现音频播放

6.3.1 初始化AVPlayer

// 播放音频AVPlayer实例
  private avPlayer: media.AVPlayer = undefined;

  // 初始化AVPlayer
  async initAVPlayer() {
    // 创建AVPlayer实例对象
    this.avPlayer = await media.createAVPlayer();
    // 创建状态机变化回调函数
    this.setAVPlayerCallback();

    await this.loadingResourceFile();
  }
6.3.2 加载HAP包资源文件
// 加载HAP包资源文件
  loadingResourceFile = async () => {
    // 通过UIAbilityContext的resourceManager成员的getRawFd接口获取媒体资源播放地址
    let context = getContext(this) as common.UIAbilityContext;
    let fileDir = await context.resourceManager.getRawFd("audio.wav");
    // 为fdSrc赋值触发initialized状态机上报
    this.avPlayer.fdSrc = fileDir;
  }
6.3.3 注册AVPlayer回调函数
// 注册AVPlayer回调函数
  setAVPlayerCallback = () => {
    // 状态机变化回调函数
    // state:表示当前播放状态
    // reason:表示当前播放状态的切换原因
    this.avPlayer.on('stateChange', async (state, reason) => {
      this.state = this.avPlayer.state;
      switch (state) {
        case 'initialized':
          this.avPlayer.prepare().then(() => {
            // 音频播放准备完毕后,获取音频总时长
            this.duration = this.avPlayer.duration;
          })
          break;
        case 'prepared':
          // 开始播放
          this.avPlayer.play().then(() => {
            // 设置图标开始旋转
            this.angleNum = 360;
          })
          break;
      }
    })

    // 播放错误回调函数
    this.avPlayer.on('error', (err) => {
      console.error(`Error happened. Cause: ${JSON.stringify(err)}`);
    })
    // 监听资源播放当前时间回调函数
    this.avPlayer.on('timeUpdate', (time: number) => {
      if (this.avPlayer.state === 'completed') {
        this.currentProgress = 0;
        this.duration = 0;
        this.angleNum = 0;
      } else {
        this.currentProgress = time;
      }
    })
  }
07效果预览

图片

图片

更多推荐

视屏点播项目

项目背景大家应该在电脑上刷过视频吧,这个项目就是模拟一下我们刷视频的整个流程,我们要做的是一个类似B站的网页,这里面包含视频的上传修改和观看以及删除,注意我这个是一个简易版本的,在后面我会做一个升级,增加其他的功能.基本原理下面我们说一下我们项目的基本原理.我们这里做的是服务器客户端类型的项目.当客户端发起请求之后,我

PyTorch中的pyi檔案生成機制

PyTorch中的pyi檔案生成機制前言pyi檔由py生成pyi.in由pyi.in生成pyitorch/CMakeLists.txttools/pyi/gen_pyi.pygen_pyinative_functionsrand.names&rand.names_outrand.generator_with_names

Linux文件编程(lseek函数和stat函数)

文章目录前言一、lseek函数二、stat函数总结前言本篇文章来讲解lseek函数和stat函数,lseek函数主要用来设置文件偏移量,stat函数主要用来获取文件属性。一、lseek函数lseek函数用于在打开的文件中移动文件指针的位置。它可以用于设置文件的读写位置或查找特定位置的数据。函数原型如下:#include

Linux 系统下 CMake 示 例

CMake是一个开源的跨平台工具,可以构建、测试和打包软件。它具有如下特性:自动搜索可能需要的程序、库和头文件的能力;独立的构建目录(如build),可以安全清理;支持复杂的自定义命令(下载、生成各种文件);自定义配置可选组件;从简单的文本文件(CMakeLists.txt)自动生成工作区和项目的能力;在主流平台上自动

Linux之Shell进阶(变量和条件判定语句)

文章目录变量变量的含义变量的定义与使用(重点)只读变量接收用户输入删除变量条件判断语句变量变量的含义什么是量?量就是数据.什么是变量?数据可以发生改变就是变量。在一个脚本周期内,其值可以发生改变的量就是变量。什么叫做一个脚本周期?一个脚本周期我们可以简单的理解为当前的shell文件。变量是shell中不可或缺的一部分,

C#中的方法

引言在C#编程语言中,方法是一种封装了一系列可执行代码的重要构建块。通过方法,我们可以将代码逻辑进行模块化和复用,提高代码的可读性和可维护性。本文将深入探讨C#中的方法的定义、参数传递、返回值、重载、递归等方面的知识,并结合实际案例介绍方法的应用。方法的定义和调用:1.1方法是什么:方法是一个包含一组语句的代码块,用于

学习python和anaconda的经验

PYTHON1常用命令1.11.1注释Python注释多行的方法有以下三种:使用ctrl+/实现多行注释、在每一行的开头使用shift+#键、输入’‘’‘’'或者"“”“”",将要注释的代码插在中间1.2definit():函数区分两个函数:1.definit(self):这种形式在__init__方法中,只有一个se

【LeetCode-面试经典150题-day24】

目录35.搜索插入位置74.搜索二维矩阵162.寻找峰值33.搜索旋转排序数组35.搜索插入位置题意:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。请必须使用时间复杂度为O(logn)的算法。【输入样例】nums=[1,3,5,6],target

Linux性能调优 —— 内存篇

Linux性能调优——内存篇Linux内存的工作原理内存映射的概念虚存空间分布内存分配与回收分配回收内存查看与分析查看内存使用情况命令:free命令:vmstat命令:top分析单个进程命令:ps-pLinux内存的工作原理内存映射的概念大多数计算机用的主存都是动态随机访问内存(DRAM),只有内核才可以直接访问物理内

Android ANR问题触发机制

1Anr类型​Anr一般有四种类型。1.1inputdispatchingtimeout​主要时按键或触摸屏事件在5s内没有响应。这个时间在ActivityManagerService中定义。C:\Users\wangjie\AppData\Local\Android\Sdk\sources\android-32\co

设计模式:中介者模式

目录组件代码示例源码中的使用优缺点总结:中介者模式(MediatorPattern)是一种行为型设计模式,它通过封装一组对象之间的交互,来减少对象之间的直接耦合。中介者模式通过引入一个中介者对象,将对象之间的通信转化为与中介者的交互,从而降低了对象之间的依赖关系。在中介者模式中,对象之间的通信通过中介者进行,而不是直接

热文推荐