HarmonyOS 鸿蒙Next中avPlayer播放视频

HarmonyOS 鸿蒙Next中avPlayer播放视频

一:配置网络权限

module.json5文件中配置网络权限

"requestPermissions": [
  {"name": "ohos.permission.INTERNET"}
],

二: 创建 avPlayer 函数

async createAVPlayer() {
  if (this.avPlayer) {
    return;
  }
  await media.createAVPlayer().then((player: media.AVPlayer) => {
    this.avPlayer = player
    console.log(TAG + ' avPlayer创建成功! ');
  })
    .catch((err: BusinessError) => {
      console.error(TAG + ` avPlayer创建异常: errCode:${err.code}  errMessage:${err.message}`);
    })
}

三:创建状态机

如果播放音频,不传surfaciID参数
如果播放视频,传入surfaceID参数,并在initalized状态中,将surfaceID赋值给avPlayer.surfaceID
理解为: 将 视频流 放入 surfaceID的值为 ‘xx’ 的盒子中播放

//创建状态机
setAVPlayerCallback(avPlayer: media.AVPlayer, surfaceID?: string | undefined) {
  // 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}`);
    if (err.code === 5400106) {
      showToast('音频地址异常')
    }
    avPlayer.reset(); // 调用reset重置资源,触发idle状态
  });

  // 状态机变化回调函数
  avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
    switch (state) {
      case 'idle': // 闲置状态, avplayer刚被创建或者调用reset方法之后进入idle状态
        //此状态可设置 url 或者 fdSrc属性,然后自动进入initialized状态
        this.setPlayState()
        break;
      case 'initialized': // 资源初始化,
        //此时可以配置 窗口、音频、等静态属性
        console.info('AVPlayer state initialized called.');
        this.setPlayState()
        if (surfaceID) {
          avPlayer.surfaceId = surfaceID
        }
        avPlayer.prepare();
        break;
      case 'prepared': // 准备完毕状态,此时播放引擎的资源已准备就绪
        console.info('AVPlayer state prepared called.');
        this.setPlayState()
        avPlayer.play(); // 调用播放接口开始播放
        break;
      case 'playing': // 正在播放状态,可在prepared/paused/completed状态调用play方法,avplayer会进入此状态
        this.setPlayState()
        break;
      case 'paused': // 暂停状态,在playing状态中执行pause方法,会进入paused状态
        console.info('AVPlayer state paused called.');
        this.setPlayState()
        avPlayer.pause()
        break;
      case 'completed': // 播放至结尾状态, 可调用play方法进入playing状态,调用stop方法进入stopped状态
        console.info('AVPlayer state completed called.');
        this.setPlayState()
        avPlayer.stop(); //调用播放结束接口
        break;
      case 'stopped': // 停止状态, 可调用prepared/playing/paused/completed状态调用stop方法进入此状态
        //此状态只保留属性,但会释放内存资源
        //可调用prepared重新准备
        //可调用reset重置
        //可调用release彻底销毁
        console.info('AVPlayer state stopped called.');
        this.setPlayState()
        avPlayer.reset(); // 调用reset接口初始化avplayer状态
        break;
      case 'released': //销毁状态
        console.info('AVPlayer state released called.');
        this.setPlayState()
        avPlayer.reset()
        break;
      default:
        console.info('AVPlayer state unknown called.');
        break;
    }
  });

  //监听进度条长度
  avPlayer.on('durationUpdate', (data: number) => {
    this.audioDuration = data
    this.setPlayState()
    console.log('durationUpdate: ' + JSON.stringify(this.audioDuration));
  })

  //监听进度条当前位置
  avPlayer.on('timeUpdate', (data: number) => {
    this.currTimeInAudio = data
    console.log('timeUpdate: ' + JSON.stringify(this.currTimeInAudio));
  })
}

四: 播放视频函数

创建avPlayer函数
注册状态机
设置视频地址 和 surfaceID

//播放
async play(src: string,surfaceID?:string|undefined) {
  //如果avplayer不存在,创建,此时播放状态为 idle: 空闲状态
  await this.createAVPlayer()
  //设置状态机
  this.setAVPlayerCallback(this.avPlayer!,surfaceID);
  //设置 音频url,状态自动跳至 initialized
  this.avPlayer!.url = src;
}

五: 暂停播放

//暂停播放
pause() {
  if (this.avPlayer) {
    this.avPlayer.pause()
  }
}

六: 停止播放

//停止播放
stop() {
  if (this.avPlayer) {
    this.avPlayer.stop()
  }
}

七: 重置 avPlayer状态

//重置 avplayer 状态
reset() {
  if (this.avPlayer) {
    this.avPlayer.reset()
  }
}

八: 释放 avPlayer

//释放avPlayer
release() {
  if (this.avPlayer) {
    this.avPlayer.release()
  }
}

九:调用方式

//播放音频
AvPlayerUtil._instance.play('视频地址',surfaceID)
//停止音频
AvPlayerUtil._instance.stop()
//暂停播放
AvPlayerUtil._instance.pause()
// 重置 avPlayer状态
AvPlayerUtil._instance.reset()
//释放 avPlayer
AvPlayerUtil._instance.release()
//继续播放
AvPlayerUtil._instance.play()

十: 创建视频播放容器

XComponent({
  type: XComponentType.SURFACE,//固定语法
  controller: this.xcpController,//XComponentController控制器
})
  .width('80%')
  .height(200)
  .onLoad(() => {
    this.surfaceID = this.xcpController.getXComponentSurfaceId()//当 XComponentController 加载完毕之后,获取surfaceID
    AvPlayerUtil._instance.play('https://media.w3.org/2010/05/sintel/trailer.mp4', this.surfaceID)//调用播放函数,并传入 视频地址 和 surfaceID 参数

  })
  .onDestroy(() => {
    AvPlayerUtil._instance.release()
  })

十一: 工具类完整代码

import { media } from '@kit.MediaKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { audio } from '@kit.AudioKit';
import { showToast } from '../../logic/CommonLogic';

const TAG = '[AvPlayerUtil] '

export class AvPlayerUtil {
  private playState?: media.AVPlayerState
  private avPlayer?: media.AVPlayer
  private audioDuration?: number
  private currTimeInAudio?: number
  private surfaceID: string | undefined = undefined
  public static _instance: AvPlayerUtil = new AvPlayerUtil()

  async createAVPlayer() {
    if (this.avPlayer) {
      return;
    }
    await media.createAVPlayer().then((player: media.AVPlayer) => {
      this.avPlayer = player
      console.log(TAG + ' avPlayer创建成功! ');
    })
      .catch((err: BusinessError) => {
        console.error(TAG + ` avPlayer创建异常: errCode:${err.code}  errMessage:${err.message}`);
      })
  }

  setPlayState() {
    this.playState = this.avPlayer?.state
    console.log('PlayState: ' + JSON.stringify(this.playState));
  }

  // 创建状态机
  setAVPlayerCallback(avPlayer: media.AVPlayer, surfaceID?: string | undefined) {
    // 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}`);
      if (err.code === 5400106) {
        showToast('音频地址异常')
      }
      avPlayer.reset(); // 调用reset重置资源,触发idle状态
    });

    // 状态机变化回调函数
    avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
      switch (state) {
        case 'idle': // 闲置状态, avplayer刚被创建或者调用reset方法之后进入idle状态
          //此状态可设置 url 或者 fdSrc属性,然后自动进入initialized状态
          this.setPlayState()
          break;
        case 'initialized': // 资源初始化,
          //此时可以配置 窗口、音频、等静态属性
          console.info('AVPlayer state initialized called.');
          this.setPlayState()
          if (surfaceID) {
            avPlayer.surfaceId = surfaceID
          }
          avPlayer.prepare();
          break;
        case 'prepared': // 准备完毕状态,此时播放引擎的资源已准备就绪
          console.info('AVPlayer state prepared called.');
          this.setPlayState()
          avPlayer.play(); // 调用播放接口开始播放
          break;
        case 'playing': // 正在播放状态,可在prepared/paused/completed状态调用play方法,avplayer会进入此状态
          this.setPlayState()
          break;
        case 'paused': // 暂停状态,在playing状态中执行pause方法,会进入paused状态
          console.info('AVPlayer state paused called.');
          this.setPlayState()
          avPlayer.pause()
          break;
        case 'completed': // 播放至结尾状态, 可调用play方法进入playing状态,调用stop方法进入stopped状态
          console.info('AVPlayer state completed called.');
          this.setPlayState()
          avPlayer.stop(); //调用播放结束接口
          break;
        case 'stopped': // 停止状态, 可调用prepared/playing/paused/completed状态调用stop方法进入此状态
          //此状态只保留属性,但会释放内存资源
          //可调用prepared重新准备
          //可调用reset重置
          //可调用release彻底销毁
          console.info('AVPlayer state stopped called.');
          this.setPlayState()
          avPlayer.reset(); // 调用reset接口初始化avplayer状态
          break;
        case 'released': //销毁状态
          console.info('AVPlayer state released called.');
          this.setPlayState()
          avPlayer.reset()
          break;
        default:
          console.info('AVPlayer state unknown called.');
          break;
      }
    });

    //监听进度条长度
    avPlayer.on('durationUpdate', (data: number) => {
      this.audioDuration = data
      this.setPlayState()
      console.log('durationUpdate: ' + JSON.stringify(this.audioDuration));
    })

    //监听进度条当前位置
    avPlayer.on('timeUpdate', (data: number) => {
      this.currTimeInAudio = data
      console.log('timeUpdate: ' + JSON.stringify(this.currTimeInAudio));
    })
  }

  //播放和暂停切换
  TogglePlayOrPause() {
    if (!this.avPlayer) {
      return false;
    }
    if (this.playState === 'playing') {
      this.avPlayer!.pause()
      return true;
    } else if (this.playState === 'paused') {
      this.avPlayer!.play()
      return true;
    }
    return false;
  }

  //暂停播放
  pause() {
    if (this.avPlayer) {
      this.avPlayer.pause()
    }
  }

  //停止播放
  stop() {
    if (this.avPlayer) {
      this.avPlayer.stop()
    }
  }

  //释放avPlayer
  release() {
    if (this.avPlayer) {
      this.avPlayer.release()
    }
  }

  //重置 avplayer 状态
  reset() {
    if (this.avPlayer) {
      this.avPlayer.reset()
    }
  }

  //播放
  async play(audioSrc: string, surfaceID?: string | undefined) {
    //如果播放状态为playing或者paused,返回true,调用指定方法,直接return;
    let res = this.TogglePlayOrPause();
    if (res) {
      return;
    }
    //如果avplayer不存在,创建,此时播放状态为 idle: 空闲状态
    await this.createAVPlayer()
    //设置状态机
    this.setAVPlayerCallback(this.avPlayer!, surfaceID);
    //设置 音频url,状态自动跳至 initialized
    this.avPlayer!.url = audioSrc;
  }
}

十二:UI完整代码

import { media } from '@kit.MediaKit'
import { AvPlayerUtil } from './AvPlayerUtil'

@Component
struct AvPlayer_VideoPage {
  xcpController: XComponentController = new XComponentController()
  @State surfaceID: string | undefined = undefined
  private avPlayer: media.AVPlayer | undefined = undefined

  build() {
    NavDestination() {
      Column() {
        XComponent({
          id: 'player',
          type: XComponentType.SURFACE,
          controller: this.xcpController,
        })
          .width('80%')
          .height(200)
          .onLoad(() => {
            this.surfaceID = this.xcpController.getXComponentSurfaceId()
            AvPlayerUtil._instance.play('https://media.w3.org/2010/05/sintel/trailer.mp4', this.surfaceID)
          })
          .onDestroy(() => {
            AvPlayerUtil._instance.release()
          })
      }
      .width('100%')
      .height('100%')
    }
    .hideTitleBar(true)
  }
}

@Builder
function AvPlayer_VideoPageBuilder() {
  AvPlayer_VideoPage()
}

更多关于HarmonyOS 鸿蒙Next中avPlayer播放视频的实战教程也可以访问 https://www.itying.com/category-93-b0.html

7 回复

点赞

更多关于HarmonyOS 鸿蒙Next中avPlayer播放视频的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


感谢分享

点赞!

感谢分享

写的真不错,学习到了

在HarmonyOS Next中,avPlayer是视频播放的核心组件。使用步骤如下:

  1. 创建AvPlayer实例:
let avPlayer = new media.AvPlayer()
  1. 设置播放源:
  • 网络视频:
avPlayer.url = 'http://example.com/video.mp4'
  • 本地视频:
avPlayer.fdSrc = {fd:xxx, offset:0, length:xxx}
  1. 设置监听事件:
avPlayer.on('stateChange', (state) => {})
  1. 准备并播放:
avPlayer.prepare()
avPlayer.play()

支持播放控制:

  • pause() 暂停
  • stop() 停止
  • seek() 跳转
  • setVolume() 音量调节

注意:需申请ohos.permission.INTERNET权限(网络播放时),

您的代码实现基本正确,展示了HarmonyOS Next中AVPlayer的完整使用流程。以下是关键点说明:

  1. 网络权限配置正确,需要在module.json5中声明ohos.permission.INTERNET权限。

  2. AVPlayer状态机处理完善,涵盖了所有关键状态:

    • idle → initialized → prepared → playing 是正常播放流程
    • 正确处理了pause/stop/reset/release等操作
  3. 视频播放需要特别注意:

    • 必须使用XComponent作为视频渲染容器
    • 需要在initialized状态设置surfaceId
    • 示例中正确获取了XComponent的surfaceId
  4. 工具类封装合理,提供了完整的播放控制方法。

建议改进点:

  1. 增加错误处理,特别是网络异常时的重试机制
  2. 考虑添加缓冲状态监听(bufferUpdate事件)
  3. 视频尺寸可能需要根据内容自适应调整

当前实现已能满足基本视频播放需求,代码结构清晰,状态处理完整,是标准的HarmonyOS媒体播放实现方式。

回到顶部