HarmonyOS鸿蒙Next中使用AVPlayer播放音频时,无法暂停

HarmonyOS鸿蒙Next中使用AVPlayer播放音频时,无法暂停

//音频播放器
async AVPlayer() {
  const avplayer = await media.createAVPlayer()
  const context = getContext(this) as common.UIAbilityContext;
  try {
    const fileDescriptor = await context.resourceManager.getRawFd(VideoSources[this.Params.id][2]);
    if (!fileDescriptor.fd) {
      throw new Error('Failed to load audio file descriptor');
    }
    avplayer.fdSrc = {
      fd: fileDescriptor.fd,
      offset: fileDescriptor.offset,
      length: -1
    };
    avplayer.on('stateChange', (state) => {
      switch (state) {
        case 'initialized':
          avplayer.prepare()
          break;
        case 'prepared':
            avplayer.play()
          break;
        case 'playing':
          if (this.runningData.Running_State === 'pause') {
            avplayer.pause()
          } else if (this.runningData.Running_State === 'end') {
            avplayer.stop()
            avplayer.release()
            avplayer.off('stateChange')
          }
          break;
        case 'paused':
          if (this.runningData.Running_State === 'start') {
            avplayer.play()
          } else if (this.runningData.Running_State === 'end') {
            avplayer.stop()
            avplayer.release()
            avplayer.off('stateChange')
          }
          break;
        case 'completed':
            avplayer.play()
          break;
        default:
          break;
      }
    })
    avplayer.on('error', (err) => {
      console.error(`播放错误: ${err.code}, ${err.message}`);
      avplayer.reset();
    });
  } catch (error) {
    console.log('初始化播放器失败', JSON.stringify(error))
  }
}

以上代码可以实现在进入页面后循环播放音乐,但是无法受到this.runningData.Running_State的控制而暂停,请问是什么原因?


更多关于HarmonyOS鸿蒙Next中使用AVPlayer播放音频时,无法暂停的实战教程也可以访问 https://www.itying.com/category-93-b0.html

10 回复

需要通过触发stateChange才能暂停播放,可以参考下这个示例:

import media from '@ohos.multimedia.media';
import { BusinessError } from '@ohos.base';
import common from '@ohos.app.ability.common';

// 音频播放
export class AVPlayerDemo {
  public flag: number = 1;
  public static avPlayerCom: media.AVPlayer;
  public static avFileDescriptorCom: media.AVFileDescriptor;
  public curAudioPath: string = '';
  public avFileDescriptor4: media.AVFileDescriptor | undefined = undefined;
  public isLoop: boolean = false;
  private avPlayer: media.AVPlayer | null = null;

  // 注册avplayer回调函数
  setAVPlayerCallback(avPlayer: media.AVPlayer): void {
    // seek操作结果回调函数
    avPlayer.on('seekDone', (seekDoneTime: number) => {
      console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
    })
    // error回调监听函数,当avPlayer在操作过程中出现错误时调用 reset接口触发重置流程
    avPlayer.on('error', (err: BusinessError) => {
      console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
      avPlayer.reset(); // 调用reset重置资源,触发idle状态
    })
    // 状态机变化回调函数
    avPlayer.on('stateChange', async (state: string, _: media.StateChangeReason) => {
      switch (state) {
        case 'idle': // 成功调用reset接口后触发该状态机上报
          console.info('AVPlayer state idle called.');
          avPlayer.release(); // 调用release接口销毁实例对象
          break;
        case 'initialized': // avplayer 设置播放源后触发该状态上报
          console.info('AVPlayer state initialized called.');
          avPlayer.prepare();
          break;
        case 'prepared': // prepare调用成功后上报该状态机
          console.info('AVPlayer state prepared called.');
          avPlayer.play(); // 调用播放接口开始播放
          break;
        case 'playing': // play成功调用后触发该状态机上报
          console.info('AVPlayer state playing called.');
          break;
        case 'paused': // pause成功调用后触发该状态机上报
          console.info('AVPlayer state paused called.');
          break;
        case 'completed': // 播放结束后触发该状态机上报
          console.info('AVPlayer state completed called.');
          if (this.isLoop) {
            avPlayer.play()
          } else {
            avPlayer.stop(); //调用播放结束接口
          }
          break;
        case 'stopped': // stop接口成功调用后触发该状态机上报
          console.info('AVPlayer state stopped called.');
          avPlayer.reset(); // 调用reset接口初始化avplayer状态
          break;
        case 'released':
          console.info('AVPlayer state released called.');
          break;
        default:
          console.info('AVPlayer state unknown called.');
          break;
      }
    })
  }
  async avPlayerUrlDemo(): Promise<void> {
    // 创建avPlayer实例对象
    try {
      this.avPlayer = await media.createAVPlayer();
      this.avPlayer.audioRendererInfo
      // 创建状态机变化回调函数
      await this.setAVPlayerCallback(this.avPlayer);
      // 通过UIAbilityContext获取沙箱地址filesDir,以Stage模型为例
      const context = getContext(this) as common.UIAbilityContext;
      let fileDescriptor = await context.resourceManager.getRawFd('AV_1739446770000.m4a');
      let avFileDescriptor: media.AVFileDescriptor =
        { fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length };
      // 测试可以播放
      this.avPlayer.fdSrc = avFileDescriptor;
      AVPlayerDemo.avFileDescriptorCom = avFileDescriptor;
    } catch (e) {
    }
  }

  async StopPlayer(): Promise<void> {
    try {
      this.avPlayer?.stop();
    } catch (e) {
    }
  }

  async PausePlayer(): Promise<void> {
    try {
      this.avPlayer?.pause();
    } catch (e) {
    }
  }

  async PauseedPlayer(): Promise<void> {
    try {
      this.avPlayer?.play();
    } catch (e) {
    }
  }
}
@Entry
@Component
export struct AVPlayerSimple {
  avPlayer = new AVPlayerDemo();

  build() {
    Column() {
      Button('播放音频')
        .onClick(() => {
          this.avPlayer.avPlayerUrlDemo().then(() => {
          });
        })
        .width('100')
        .height('100')
      Button('暂停播放')
        .onClick(() => {
          this.avPlayer.PausePlayer()
        })
        .width('100')
        .height('100')
      Button('继续播放')
        .onClick(() => {
          this.avPlayer.PauseedPlayer()
        })
        .width('100')
        .height('100')
      Button('停止播放')
        .onClick(() => {
          this.avPlayer.StopPlayer().then(() => {
          });
        })
        .width('100')
        .height('100')
      Button('循环播放')
        .onClick(() => {
          this.avPlayer.isLoop = true;
        })
        .width('100')
        .height('100')
    }
    .width('200')
    .height('200')
  }
}

更多关于HarmonyOS鸿蒙Next中使用AVPlayer播放音频时,无法暂停的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


import { media } from '@kit.MediaKit'
import { Context } from '@kit.AbilityKit'

export class AVPlayerClass {
  static player: media.AVPlayer
  static context: Context

  static async init(context: Context) {
    AVPlayerClass.context = context
    //   初始化
    //   1.1创建播放器 ->  idle
    AVPlayerClass.player = await media.createAVPlayer()
    //   1.2监听状态
    AVPlayerClass.player.on('stateChange', (state) => {
      if (state === 'initialized') {
        AVPlayerClass.player.prepare() // prepared
      } else if (state === 'prepared') {
        AVPlayerClass.player.play() //playing
      } else if (state === 'completed') {
        AVPlayerClass.player.reset()
      }
    })
    //   1.3音频文件的时长
    AVPlayerClass.player.on('durationUpdate', (duration) => {
      // duration就是音频文件的时长,每次设置音频源的时候进行更新
      //   10000ms
    })
    //   1.4播放器的实时进度
    AVPlayerClass.player.on('timeUpdate', (time) => {
      //   time就是播放的时长,播放过程中大概几十毫秒更新一次
      // 1000ms
    })
    //   1.5控制播放的进度
    //   从3S开始播放
    // AVPlayerClass.player.seek(3000)
    //   播放/暂停/停止
    // AVPlayerClass.player.play()
    // AVPlayerClass.player.pause()
    // AVPlayerClass.player.stop()


  }

  static async play(src:fdSrc ) {
    // 2.1设置播放源(在线) -> initialized
    // AVPlayerClass.player.url = 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/5.mp3'
    // 2.2设置播放源(本地)   -> initialized
    // 将之前没有播完的取消
    await AVPlayerClass.player.reset()
    // 重新播放新的
    // 宏任务  和  微任务 谁先执行?
    const fileDescriptor = AVPlayerClass.context.resourceManager.getRawFdSync(src + '.wav')
    AVPlayerClass.player.fdSrc = fileDescriptor
  }

  // static pause
  // static stop
  // static release
}
// 自定义类型
type fdSrc = 'camera' | 'information' | 'send'

你的暂停控制逻辑依赖stateChange事件触发,但该事件仅在播放器内部状态变化时才会回调。当外部变量this.runningData.Running_State改变时(例如用户点击暂停按钮),若未主动触发播放器的状态变化(如调用pause()或play()),则监听函数不会执行。

避免在stateChange回调中依赖外部变量,改为直接通过用户操作(如按钮点击)触发控制方法:

// 在UI事件中直接调用暂停
pauseAudio() {
  if (this.avPlayer && this.avPlayer.state === 'playing') {
    this.avPlayer.pause();
    this.runningData.Running_State = 'pause'; // 更新状态
  }
}

使用箭头函数或绑定this,确保回调中访问的是最新状态:

avplayer.on('stateChange', (state) => {
  // 使用箭头函数保留外层this作用域
  if (this.runningData.Running_State === 'pause' && state === 'playing') {
    avplayer.pause();
  }
});

在关键节点(如播放开始前)主动检查运行状态:

case 'prepared':
  if (this.runningData.Running_State === 'start') {
    avplayer.play();
  } else if (this.runningData.Running_State === 'pause') {
    avplayer.pause(); // 直接响应暂停状态
  }
  break;

修正completed状态处理,避免无效播放:

case 'completed':
  if (this.runningData.Running_State !== 'end') {
    avplayer.seek(0); // 回到开头
    avplayer.play();
  }
  break;

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

我问一下 runningData这是一个对象数据你有用@Trace修饰Running_state吗,不然UI监听不到的,也就不会改变状态

Running_state可以控制Video组件暂停,可以控制暂停弹窗,却无法控制音乐暂停,

在HarmonyOS Next中,AVPlayer暂停失败通常与状态管理有关。AVPlayer需处于prepared、started或paused状态才能响应pause()方法。若状态为stopped、idle或error,调用pause()将无效。检查播放器当前状态,确保在正确状态下执行暂停操作。同时确认音频资源已正确加载且未发生解码异常。可通过状态监听器实时追踪状态变更。

从代码分析,暂停功能失效的主要原因是状态管理逻辑存在时序问题。在playing状态中检查this.runningData.Running_State时,该状态可能尚未更新。

具体问题:

  1. 状态监听器仅在状态变更时触发,但this.runningData.Running_State可能在非状态变更期间被修改
  2. 当播放器进入playing状态后,除非再次触发状态变更,否则不会重新执行状态判断逻辑

建议优化方案:

  • 将状态控制逻辑从状态监听器中抽离,通过独立方法控制播放器
  • 在修改this.runningData.Running_State时直接调用对应的播放器控制方法

示例修改:

// 添加控制方法
controlPlayback() {
  if (this.runningData.Running_State === 'start' && this.avplayer.state === 'paused') {
    this.avplayer.play();
  } else if (this.runningData.Running_State === 'pause' && this.avplayer.state === 'playing') {
    this.avplayer.pause();
  } else if (this.runningData.Running_State === 'end') {
    this.avplayer.stop();
    this.avplayer.release();
  }
}

// 在runningData状态变更时调用此方法

同时简化状态监听器,仅处理必要的状态转换,避免在监听器中嵌入过多业务逻辑。这样可确保状态变更能及时响应外部控制。

回到顶部