一、AVPlayer 是什么?

AVPlayer 是鸿蒙系统 @kit.MediaLibraryKit 包中提供的一个功能强大的音视频播放器组件。它是一个端到端的播放解决方案,集成了流媒体协议解析、媒体资源解封装、音视频解码和渲染功能。你可以用它来播放本地文件(如 mp4, mkv, mp3)或网络流媒体(如 HLS, DASH)。

核心特点:

  • 功能全面:支持播放、暂停、跳转、倍速、音量调节、音轨切换等。
  • 多格式支持:支持广泛的容器格式和编码格式。
  • 状态机驱动:通过清晰的状态变化来管理播放生命周期,开发逻辑更清晰。
  • 事件监听:提供丰富的事件回调,用于更新UI和处理异常。
二、核心概念:状态机 (State Machine)

理解AVPlayer的状态机是正确使用它的关键!任何操作都需要在合适的状态下调用。

  1. idle (闲置状态): 调用 createAVPlayer() 后的初始状态。
  2. initialized (初始化状态): 成功设置播放资源(urlfdSrc)后的状态。
  3. prepared (准备状态): 调用 prepare() 成功后的状态。此时可以获取视频时长(duration)、设置音量、倍速等。
  4. playing (播放状态): 调用 play() 后的状态。
  5. paused (暂停状态): 调用 pause() 后的状态。
  6. completed (播放完成状态): 播放自然结束的状态。
  7. stopped (停止状态): 调用 stop() 后的状态。
  8. released (释放状态): 调用 release() 后的状态。此时播放器实例已销毁,不可再用。
  9. error (错误状态): 在任何阶段发生错误时进入此状态。

重要提示:prepared, playing, paused, completed 状态时,播放器引擎处于工作状态,会占用较多系统资源。在不使用时,请及时调用 reset()release() 进行资源回收。

三、开发步骤详解 (ArkTS版)

下面我们以播放一个网络视频为例,分步讲解。

步骤 1:导入模块并声明权限

// 1. 导入必要的模块
import { media } from '@kit.MediaLibraryKit';
import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit'; // 如果涉及本地文件操作

// 2. 在 module.json5 中声明网络权限
// "requestPermissions": [
//   {
//     "name": "ohos.permission.INTERNET"
//   }
// ]
步骤 2:创建 AVPlayer 实例

// 在您的组件或类中定义
private avPlayer: media.AVPlayer | null = null;

// 创建播放器的方法
async createAVPlayerInstance() {
  try {
    this.avPlayer = await media.createAVPlayer();
    console.info('AVPlayer 创建成功,当前状态:idle');
    // 创建成功后,立即设置监听器!
    this.setupAVPlayerListeners();
  } catch (error) {
    console.error(`创建AVPlayer失败: ${error.code}, ${error.message}`);
  }
}
步骤 3:设置事件监听器 (核心)

这是与播放器交互并更新UI的关键。


private setupAVPlayerListeners() {
  if (this.avPlayer == null) {
    return;
  }

  // 1. 监听状态变化 (最重要的事件)
  this.avPlayer.on('stateChange', (state: media.AVPlayerState) => {
    console.info(`状态发生变化: ${state}`);
    switch (state) {
      case 'prepared':
        // 可以在这里获取总时长并更新UI
        let duration = this.avPlayer?.duration;
        console.info(`视频总时长: ${duration} ms`);
        break;
      case 'completed':
        // 播放完成,可以重置或进行下一步操作
        console.info('播放完成');
        break;
      case 'error':
        // 发生错误,需要进行处理
        console.error('播放器进入错误状态');
        break;
    }
  });

  // 2. 监听错误事件
  this.avPlayer.on('error', (err: BusinessError) => {
    console.error(`发生错误, code: ${err.code}, message: ${err.message}`);
  });

  // 3. 监听时间更新 (用于更新进度条)
  this.avPlayer.on('timeUpdate', (currentTime: number) => {
    // 每秒会触发多次,用于更新UI上的当前播放时间
    // this.currentTime = currentTime; // 更新@State变量
  });

  // 4. 监听时长更新
  this.avPlayer.on('durationUpdate', (duration: number) => {
    // this.totalDuration = duration; // 更新@State变量
  });

  // 5. 监听跳转完成
  this.avPlayer.on('seekDone', (seekTime: number) => {
    console.info(`跳转完成到: ${seekTime} ms`);
    // 跳转完成后可以恢复播放
  });

  // 还有其他事件,如 volumeChange, speedDone, bufferingUpdate 等
}

最佳实践: 监听器应在 idle 状态、设置资源之前就设置好,以确保能捕获到所有状态变化。

步骤 4:设置播放资源 (URL)

async setVideoSource() {
  if (this.avPlayer == null) {
    return;
  }

  try {
    // 设置网络视频地址
    let url = ' https://www.example.com/sample.mp4 '; // 替换为有效地址
    this.avPlayer.url = url; // 赋值后,状态会变为 'initialized'
    console.info('资源设置成功');

  } catch (error) {
    console.error(`设置资源失败: ${error.code}, ${error.message}`);
  }
}
  • 播放本地沙箱文件:使用 fdSrc 属性。
    
      
    // 获取应用沙箱内文件的文件描述符
    let file = await fs.open('path/in/sandbox/video.mp4');
    let avFileDescriptor: media.AVFileDescriptor = {
      fd: file.fd,
      offset: 0,   // 偏移量
      length: 1024 // 文件长度,不知道可设为-1
    };
    this.avPlayer.fdSrc = avFileDescriptor;
    
步骤 5:设置显示窗口 (仅视频)

视频播放需要绑定一个 XComponent 提供的 surface

  1. 在布局文件中添加 XComponent:
    
      
    XComponent()
      .id('xComponentId') // 设置ID
      .type('surface')    // 类型必须为surface
      .width('100%')
      .height(300)
    
  2. 在代码中获取 surfaceID 并设置:
    
      
    import { UIContext } from '@ohos.arkui.UIContext';
    
    // 在合适的生命周期函数中(如aboutToAppear)或点击事件中
    private setSurface() {
      // 通过ID获取XComponent的UIContext,然后获取surfaceID
      let uiContext = this.getUIContext('xComponentId') as UIContext;
      let surfaceID = uiContext.getXComponentSurfaceId();
    
      if (this.avPlayer != null) {
        this.avPlayer.surfaceId = surfaceID; // 设置显示窗口
      }
    }
    
步骤 6:准备播放

async preparePlayback() {
  if (this.avPlayer == null) {
    return;
  }
  // 确保状态是 initialized
  if (this.avPlayer.state === 'initialized') {
    try {
      await this.avPlayer.prepare();
      console.info('Prepare调用成功,状态变为prepared');
    } catch (error) {
      console.error(`Prepare失败: ${error.code}, ${error.message}`);
    }
  }
}
步骤 7:播放控制

prepared, playing, paused 等状态下进行控制。


// 播放
play() {
  this.avPlayer?.play();
}

// 暂停
pause() {
  this.avPlayer?.pause();
}

// 跳转 (单位: ms)
seekTo(time: number) {
  this.avPlayer?.seek(time);
}

// 停止 (停止后如需再次播放,需重新调用prepare())
stop() {
  this.avPlayer?.stop();
}

// 设置音量 (范围 0.0 ~ 1.0)
setVolume(volume: number) {
  this.avPlayer?.setVolume(volume);
}

// 设置倍速 (如 0.75, 1.0, 1.5, 2.0)
setSpeed(speed: number) {
  this.avPlayer?.setSpeed(speed);
}
步骤 8:资源释放 (非常重要!)

在页面销毁或不再需要播放器时,必须释放资源。


releaseAVPlayer() {
  if (this.avPlayer != null) {
    // 先停止播放
    this.avPlayer.stop();
    // 释放资源,实例不可再用
    this.avPlayer.release();
    this.avPlayer = null;
    console.info('AVPlayer 已释放');
  }
}

// 在ArkUI组件的 aboutToDisappear 生命周期中调用
aboutToDisappear() {
  this.releaseAVPlayer();
}
四、完整代码示例

// 一个极简的播放组件示例
@Entry
@Component
struct SimpleVideoPlayer {
  private avPlayer: media.AVPlayer | null = null;
  @State currentState: string = '未开始';

  aboutToAppear() {
    this.initPlayer();
  }

  async initPlayer() {
    this.avPlayer = await media.createAVPlayer();
    this.setupListeners();
    this.avPlayer.url = ' https://example.com/sample.mp4 ';
  }

  setupListeners() {
    this.avPlayer?.on('stateChange', (state: media.AVPlayerState) => {
      this.currentState = state;
      console.info(state);
    });
  }

  build() {
    Column() {
      Text(`当前状态: ${this.currentState}`)
      XComponent()
        .id('videoSurface')
        .type('surface')
        .width('100%')
        .height(300)
        .onLoad(() => {
          let uiContext = this.getUIContext('videoSurface') as UIContext;
          let surfaceId = uiContext.getXComponentSurfaceId();
          this.avPlayer && (this.avPlayer.surfaceId = surfaceId);
        })

      Row() {
        Button('准备').onClick(() => { this.avPlayer?.prepare(); })
        Button('播放').onClick(() => { this.avPlayer?.play(); })
        Button('暂停').onClick(() => { this.avPlayer?.pause(); })
        Button('停止').onClick(() => { this.avPlayer?.stop(); })
      }
    }.width('100%').height('100%')
  }

  aboutToDisappear() {
    this.avPlayer?.release();
  }
}
五、总结与最佳实践
  1. 状态驱动:任何操作前,先检查 avPlayer.state 是否处于合适的状态。
  2. 提前监听:在 idle 状态就设置好所有事件监听器。
  3. 资源释放:务必在组件销毁时调用 release(),防止内存泄漏。
  4. 错误处理:一定要监听 error 事件,并做好用户提示。
  5. 网络权限:播放网络视频别忘了在 module.json5 中声明 ohos.permission.INTERNET 权限。
  6. 后台播放:如果需要应用退到后台后继续播放音频,需要申请长时任务权限并接入 AVSession

通过本教程,你应该对如何使用鸿蒙的 AVPlayer 有了全面的了解。现在就可以动手实践,在你的应用中集成多媒体播放功能了!

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐