鸿蒙开发笔记-MediaKit的使用(视频播放)

0.常量定义

在视频播放工具类中,大部分使用到的常量都被提前定义。

class CommonConstants {
  /**
   * Video aspect ratio.
   * 视频横纵比
   */
  static readonly VIDEO_ASPECT_RATIO: number = 1.78;
  /**
   * Video detail.
   * 视频细节
   */
  static readonly VIDEO_DETAIL: string = 'videoDetail';
  /**
   * Full percent.
   * 百分之百
   */
  static readonly FULL_PERCENT: string = '100%';
  /**
   * Idle state of avPlayer.
   * avPlayer 的空闲状态
   */
  static readonly AV_PLAYER_IDLE_STATE: string = 'idle';
  /**
   * Initialized state of avPlayer.
   * avPlayer 的初始化状态
   */
  static readonly AV_PLAYER_INITIALIZED_STATE: string = 'initialized';
  /**
   * Prepared state of avPlayer.
   * avPlayer 的准备状态
   */
  static readonly AV_PLAYER_PREPARED_STATE: string = 'prepared';
  /**
   * Playing state of avPlayer.
   * avPlayer 的播放状态
   */
  static readonly AV_PLAYER_PLAYING_STATE: string = 'playing';
  /**
   * Pause state of avPlayer.
   */
  static readonly AV_PLAYER_PAUSED_STATE: string = 'paused';
  /**
   * Completed state of avPlayer.
   * avPlayer 的完成状态
   */
  static readonly AV_PLAYER_COMPLETED_STATE: string = 'completed';
  /**
   * Stopped state of avPlayer.
   * avPlayer 的停止状态
   */
  static readonly AV_PLAYER_STOPPED_STATE: string = 'stopped';
  /**
   * Release state of avPlayer.
   * avPlayer 的发布状态
   */
  static readonly AV_PLAYER_RELEASE_STATE: string = 'released';
  /**
   * Error state of avPlayer.
   * avPlayer 的错误状态
   */
  static readonly AV_PLAYER_ERROR_STATE: string = 'error';
  /**
   * 视频组件的hsp包名.
   */
  static readonly VIDEO_DETAIL_HSP_NAME: string = 'entry';
  /**
   * Current time of avPlayer.
   * avPlayer的当前时间
   */
  static readonly AV_PLAYER_CURRENT_TIME: string = 'currentTime';
  /**
   * Progress of av player.
   * avPlayer的进度
   */
  static readonly AV_PLAYER_PROGRESS: string = 'progress';
  /**
   * Total time of av player.
   * avPlayer的总时间
   */
  static readonly AV_PLAYER_TOTAL_TIME: string = 'totalTime';
  /**
   * Update time of av player.
   * avPlayer的更新的时间
   */
  static readonly AV_PLAYER_UPDATE_TIME: string = 'updateTime';
  /**
   * Initial time.
   * 初始化时间
   */
  static readonly INITIAL_TIME: string = '00:00:00';
  /**
   * One hundred for progress.
   * 进度条:100
   */
  static readonly PROGRESS_HUNDRED: number = 100;
  /**
   * One thousand for progress.
   * 进度条:1000
   */
  static readonly PROGRESS_THOUSAND: number = 1000;
  /**
   * Product video name.
   * 视频名
   */
  static readonly PRODUCT_VIDEO_NAME: string = 'product.mp4';
  /**
   * Second in hour.
   * 一小时的秒数
   */
  static readonly SECOND_IN_HOUR: number = 3600;
  /**
   * Second in minute.
   * 一分钟的秒数
   */
  static readonly SECOND_IN_MINUTE: number = 60;
  /**
   * colon.
   * 冒号
   */
  static readonly COLON: string = ':';
  /**
   * Time prefix.
   * 时间前缀
   */
  static readonly TIME_PREFIX: string = '0';
  /**
   * Empty time.
   * 空时间
   */
  static readonly EMPTY_TIME: string = '00';
  /**
   * Zero.
   * 数字:0
   */
  static readonly ZERO: number = 0;
  /**
   * One.
   * 数字:1
   */
  static readonly ONE: number = 1;
  /**
   * Ten.
   * 数字:10
   */
  static readonly TEN: number = 10;
}

1.需要创建的对象

 // 创建mediaKIT中的AVPlayer实体,用于音视频的播放
  private avPlayer ?: media.AVPlayer 
  // 创建一个AVImageGenerator实体,用于获取缩略图
  private avImageGenerator ?: media.AVImageGenerator
  
  
  // 获取当前页面的UIAbility的上下文信息
  private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
  
  // 视频播放的路径
  private url: resourceManager.RawFileDescriptor | null = null
  private pixel_map : image.PixelMap | undefined = undefined;

  private playState: boolean = true
  private surfaceId: string = ''
  private sliderBegin: number = 0
  private startTime: number = 0
  private isFullScreen: boolean = false
  private updateTime: number = 0

2.私有函数

2.1 报错进行日志输出

private onError: (err: BusinessError) => void = (err: BusinessError) => {
    Logger.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`)
    if (this.avPlayer === undefined) {
      Logger.error(`AvPlayer is undefined`)
      return
    }
    this.avPlayer.reset()
  }

2.2 视频播放时的时间更新

private onTimeUpdateFunction: (updateTime: number) => void = (updateTime: number) => {
    if (this.avPlayer === undefined) {
      Logger.error(`AvPlayer is undefined`)
      return
    }
    this.updateTime = updateTime
    AppStorage.setOrCreate<string>(CommonConstants.AV_PLAYER_CURRENT_TIME, this.formatTime(updateTime)) // 当前播放时间
    AppStorage.setOrCreate<number>(CommonConstants.AV_PLAYER_UPDATE_TIME, updateTime) // 更新的时间
    AppStorage.setOrCreate<number>(CommonConstants.AV_PLAYER_PROGRESS, 
      updateTime / this.avPlayer.duration * CommonConstants.PROGRESS_HUNDRED) // 当前进度
  }

2.3 AVPlayer的状态切换

// avPlayer 状态切换
  private onStateChange: (state: media.AVPlayerState) => void = async (state: media.AVPlayerState) => {
    if (this.avPlayer === undefined) {
      Logger.error(`AvPlayer is undefined`);
      return;
    }
    switch (state) {
      case CommonConstants.AV_PLAYER_IDLE_STATE:
        this.url = await this.context.createModuleContext(CommonConstants.VIDEO_DETAIL_HSP_NAME).resourceManager
          .getRawFd(CommonConstants.PRODUCT_VIDEO_NAME);
        this.avPlayer.fdSrc = this.url;
        Logger.info('AVPlayer state idle called.');
        break;
      case CommonConstants.AV_PLAYER_INITIALIZED_STATE:
        Logger.info('AVPlayer initialized called.');
        this.avPlayer.surfaceId = this.surfaceId;
        this.avPlayer.prepare().then(() => {
          Logger.info('AVPlayer prepare succeeded.');
        }, (err: BusinessError) => {
          Logger.error(`Invoke prepare failed, code is ${err.code}, message is ${err.message}`);
          if (this.avPlayer === undefined) {
            Logger.error(`AvPlayer is undefined`);
            return;
          }
          this.avPlayer.reset();
        });
        break;
      case CommonConstants.AV_PLAYER_PREPARED_STATE:
        this.avPlayer.videoScaleType = media.VideoScaleType.VIDEO_SCALE_TYPE_FIT;
        Logger.info('AVPlayer state prepared called.');
        this.seekToStart();
        this.avPlayer.play();
        AppStorage.setOrCreate<string>(CommonConstants.AV_PLAYER_TOTAL_TIME, this.formatTime(this.avPlayer.duration));
        break;
      case CommonConstants.AV_PLAYER_PLAYING_STATE:
        Logger.info('AVPlayer state playing called.');
        this.playState = true;
        if (this.isFullScreen) {
          AppStorage.setOrCreate<boolean>('fullScreenPlayState', this.playState);
        }
        this.seekToStart();
        break;
      case CommonConstants.AV_PLAYER_PAUSED_STATE:
        Logger.info('AVPlayer state paused called.');
        this.playState = false;
        if (this.isFullScreen) {
          AppStorage.setOrCreate<boolean>('fullScreenPlayState', this.playState);
        }
        this.seekToStart();
        break;
      case CommonConstants.AV_PLAYER_COMPLETED_STATE:
        Logger.info('AVPlayer state completed called.');
        this.playState = false;
        if (this.isFullScreen) {
          AppStorage.setOrCreate<boolean>('fullScreenPlayState', this.playState);
        }
        this.avPlayer.stop();
        break;
      case CommonConstants.AV_PLAYER_STOPPED_STATE:
        Logger.info('AVPlayer state stopped called.');
        break;
      case CommonConstants.AV_PLAYER_RELEASE_STATE:
        Logger.info('AVPlayer state released called.');
        break;
      case CommonConstants.AV_PLAYER_ERROR_STATE:
        Logger.error('AVPlayer state error called.');
        break;
      default:
        Logger.info('AVPlayer state unknown called.');
        break;
    }
  }

3. 暴露的函数,可供外部使用

3.1 创建AVPlayer对象

async createAvPlayer(surfaceId: string, isFullScreen: boolean) {
    this.isFullScreen = isFullScreen
    if (this.avPlayer === undefined || this.avPlayer.state === CommonConstants.AV_PLAYER_RELEASE_STATE) {
      this.avPlayer = await media.createAVPlayer()
      this.avImageGenerator = await media.createAVImageGenerator()
      this.surfaceId = surfaceId
      Logger.info('Created AvPlayer successfully.');
      this.url = await this.context.createModuleContext(CommonConstants.VIDEO_DETAIL_HSP_NAME).resourceManager
        .getRawFd(CommonConstants.PRODUCT_VIDEO_NAME);
      this.avPlayer.fdSrc = this.url;
      this.avImageGenerator.fdSrc = this.url
      this.setAVPlayerCallback();
    } else {
      Logger.info(`AvPlayer has been created`);
    }
  }

 3.2 获取某一时刻的视频图片

async getPixelImage(time: number){
    
    let queryOption = media.AVImageQueryOptions.AV_IMAGE_QUERY_NEXT_SYNC
    let param: media.PixelMapParams = {
      width : 300,
      height : 300
    }
    this.pixel_map = await this.avImageGenerator?.fetchFrameByTime(time,queryOption,param)
    return this.pixel_map
  }

3.3 获取当前视频播放的时间

getCurrentTime(){
    return this.updateTime
  }

3.4 设置AVPlayer回调

setAVPlayerCallback(): void {
    if (this.avPlayer === undefined) {
      Logger.error(`AvPlayer is undefined`);
      return;
    }
    this.avPlayer.on('error', this.onError);
    this.onTimeUpdate();
    this.setStateChange();
  }

3.5 AVPlayer的时间更新开关

onTimeUpdate(): void {
    if (this.avPlayer === undefined) {
      Logger.error(`AvPlayer is undefined`);
      return;
    }
    this.avPlayer.on('timeUpdate', this.onTimeUpdateFunction);
  }

  offTimeUpdate(): void {
    if (this.avPlayer === undefined) {
      Logger.error(`AvPlayer is undefined`);
      return;
    }
    try {
      this.avPlayer.off('timeUpdate');
    } catch (exception) {
      Logger.error('Failed to unregister callback. Code: ' + JSON.stringify(exception));
    }
  }

3.6 获取视频的总时长

getDuration(): number|undefined{
    return this.avPlayer?.duration
  }

3.7 设置状态改变

setStateChange(): void {
    if (this.avPlayer === undefined) {
      Logger.error(`AvPlayer is undefined`);
      return;
    }
    this.avPlayer.on('stateChange', this.onStateChange)
  }

3.8 设置开始时间

setStartTime(startTime: number): void {
    this.startTime = startTime;
  }

3.9 从开始位置进行播放

seekToStart(): void {
    if (this.startTime != 0 && this.avPlayer !== undefined) {
      this.avPlayer.seek(this.startTime, media.SeekMode.SEEK_PREV_SYNC);
      this.startTime = 0;
    } else {
      Logger.info(`Video is played from the beginning`);
    }
  }

3.10 释放资源

release(): void {
    if (this.avPlayer !== undefined && this.avPlayer.state !== CommonConstants.AV_PLAYER_RELEASE_STATE) {
      try {
        this.avPlayer.off('error');
        this.avPlayer.off('stateChange');
      } catch (exception) {
        Logger.error('Failed to unregister callback. Code: ' + JSON.stringify(exception));
      }
      this.avImageGenerator?.release()
      this.avPlayer.release();
    } else {
      Logger.info(`AvPlayer release failed`);
    }
  }

3.11 设置视频播放的位置

sliderChange(value: number, mode: SliderChangeMode): void {
    let seekType: media.SeekMode = value > this.sliderBegin ? media.SeekMode.SEEK_PREV_SYNC :
    media.SeekMode.SEEK_NEXT_SYNC;
    if (this.avPlayer === undefined) {
      Logger.error(`AvPlayer is undefined`);
      return;
    }
    switch (mode) {
      case SliderChangeMode.Begin:
        Logger.info(`AvPlayer SliderChangeMode Begin`);
        this.sliderBegin = value;
        this.avPlayer.pause();
        break;
      case SliderChangeMode.Moving:
        Logger.info(`AvPlayer SliderChangeMode Moving`);
        this.avPlayer.seek(value / CommonConstants.PROGRESS_HUNDRED * this.avPlayer.duration, seekType);
        break;
      case SliderChangeMode.End:
        Logger.info(`AvPlayer SliderChangeMode End`);
        this.avPlayer.play();
        break;
      case SliderChangeMode.Click:
        Logger.info(`AvPlayer SliderChangeMode Click`);
        this.avPlayer.seek(this.sliderBegin / CommonConstants.PROGRESS_HUNDRED * this.avPlayer.duration, seekType);
        break;
      default:
        break;
    }
  }

3.12 视频播放状态修改(播放/暂停)

playerStateControl(): void {
    if (this.avPlayer === undefined) {
      Logger.error(`AvPlayer is undefined`);
      return;
    }
    if (this.avPlayer.state === CommonConstants.AV_PLAYER_STOPPED_STATE) {
      this.avPlayer.prepare();
      return;
    }
    if (!this.playState) {
      this.avPlayer.play();
    } else {
      this.avPlayer.pause();
    }
  }

3.13 视频播放

play(): void {
    if (this.avPlayer !== undefined && !this.playState) {
      this.avPlayer.play();
    } else {
      Logger.info(`AvPlayer play failed`);
    }
  }

3.14 视频暂停

pause(): void {
    if (this.avPlayer !== undefined && this.playState) {
      this.avPlayer.pause();
    } else {
      Logger.info(`AvPlayer pause failed`);
    }
  }

3.15 设置视频源路径

setUrl(url: resourceManager.RawFileDescriptor | null) {
    this.url = url
  }

3.16 时间格式化

formatTime(duration: number): string {
    let totalSecond: number = Math.round(duration / CommonConstants.PROGRESS_THOUSAND);
    let hourNum: number = Math.floor(totalSecond / CommonConstants.SECOND_IN_HOUR);
    let minNum: number = Math.floor((totalSecond % CommonConstants.SECOND_IN_HOUR) / CommonConstants.SECOND_IN_MINUTE);
    let secNum: number = (totalSecond % CommonConstants.SECOND_IN_HOUR) % CommonConstants.SECOND_IN_MINUTE;
    return this.formatUnitTime(hourNum) + CommonConstants.COLON + this.formatUnitTime(minNum) + CommonConstants.COLON +
    this.formatUnitTime(secNum);
  }

  formatUnitTime(time: number): string {
    if (time >= CommonConstants.ONE && time < CommonConstants.TEN) {
      let zero: string = CommonConstants.TIME_PREFIX;
      return zero.concat(time.toString());
    } else if (time >= CommonConstants.ZERO && time < CommonConstants.ONE) {
      return CommonConstants.EMPTY_TIME;
    }
    return time.toString();
  }

4.如何使用

// TODO 待更新。。。。。

  • 17
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值