HarmonyOS鸿蒙Next中请问你们的ijkplayer组件里timeUpdate怎么写,需要检测到秒的回调

HarmonyOS鸿蒙Next中请问你们的ijkplayer组件里timeUpdate怎么写,需要检测到秒的回调 最近用了ijkplayer播放器,需要做一个秒的回调,然后在做一个进度条,目前有一个,就是不知道它回调的都代表什么

setOnInfoListener:设置播放器的各种状态回调监听
6 回复

开发者您好,如果您这边想实现进度条逻辑,可以使用ijkplayer提供的接口:

// 获取当前播放位置
let position = this.mIjkMediaPlayer.getCurrentPosition();
// 获取视频时长
let duration = this.mIjkMediaPlayer.getDuration();

通过定时任务定时刷新当前视频播放进度:

this.updateProgressTimer = setInterval(() => {
  if (!this..mDestroyPage) {
    that.setProgress();
  }
}, 1000);

private setProgress() {
    let position = this.mIjkMediaPlayer.getCurrentPosition();
    let duration = this.mIjkMediaPlayer.getDuration();
    let pos = 0;
    if (duration > 0) {
      this.slideEnable = true;
      let curPercent = position / duration;
      pos = curPercent * 100;
      if (pos > this.PROGRESS_MAX_VALUE) {
        this.progressValue = this.PROGRESS_MAX_VALUE;
      } else {
        this.progressValue = pos;
      }
    }
    this.totalTime = this.stringForTime(duration);
    if (position > duration) {
      position = duration;
    }
    this.isCurrentTime = true;
    this.currentTime = this.stringForTime(position);
    this.isCurrentTime = false;
  }

完整示例代码如下:

import { Callback } from '[@ohos](/user/ohos).base';
import { IjkMediaPlayer, InterruptEvent, InterruptHintType } from '[@ohos](/user/ohos)/ijkplayer';
import { OnVideoSizeChangedListener } from '[@ohos](/user/ohos)/ijkplayer';
import { OnCompletionListener } from '[@ohos](/user/ohos)/ijkplayer';
import { OnErrorListener } from '[@ohos](/user/ohos)/ijkplayer';
import { OnInfoListener } from '[@ohos](/user/ohos)/ijkplayer';
import { OnSeekCompleteListener } from '[@ohos](/user/ohos)/ijkplayer';
import { LogUtils } from '[@ohos](/user/ohos)/ijkplayer';
import { window } from '[@kit](/user/kit).ArkUI';
import { BusinessError } from '[@kit](/user/kit).BasicServicesKit';

export enum PlayStatus {
  INIT = 0,
  PAUSE = 1,
  PLAY = 2,
};

[@Entry](/user/Entry)
[@Component](/user/Component)
struct PageDemo {
  [@State](/user/State) progressValue: number = 0;
  [@State](/user/State) currentTime: string = '00:00';
  [@State](/user/State) totalTime: string = '00:00';

  [@State](/user/State)  replayVisible: Visibility = Visibility.None;
  [@State](/user/State) slideEnable: boolean = false;
  private mContext: object | undefined = undefined;
  private mFirst: boolean = true;
  private mDestroyPage: boolean = false;
  private playSpeed: string = '1f';
  [@State](/user/State) volume: number = 1.0;
  private oldSeconds: number = 0;
  private isSeekTo: boolean = false;
  private isCurrentTime: boolean = false;
  [@State](/user/State) videoWidth: string = '100%';
  private initAspectRatio: number = 1;
  [@State](/user/State) videoAspectRatio: number = this.initAspectRatio;

  private last: number = 0;
  [@State](/user/State) loadingVisible: Visibility = Visibility.None;
  private videoParentAspectRatio: number = this.initAspectRatio;
  private mIjkMediaPlayer = IjkMediaPlayer.getInstance();
  private controlPlayStatus = PlayStatus.INIT;
  private PROGRESS_MAX_VALUE: number = 100;
  private updateProgressTimer: number = 0;
  private curIndex: number = 0;

  // 此处设置rtsp播放源链接即可
  private videoUrls: string[] = [
    'https://1252866558.vod2.myqcloud.com/9eadadb7vodtransgzp1252866558/2f7e1372243791580925650614/v.f1425023.mp4',
    'https://1251542705.vod2.myqcloud.com/4a8d9c67vodtransgzp1251542705/203109c63270835013529449619/v.f1419907.mp4'];

  private videoUrl = this.videoUrls[0]

  [@State](/user/State)  isRecord: boolean = false
  controller: TextClockController = new TextClockController();

  [@State](/user/State) light: boolean = true;

  aboutToAppear() {
    let event: Callback<InterruptEvent> = (event) => {
      LogUtils.getInstance().LOGI(`event: ${JSON.stringify(event)}`);
      if (event.hintType === InterruptHintType.INTERRUPT_HINT_PAUSE) {
        this.pause();
      } else if (event.hintType === InterruptHintType.INTERRUPT_HINT_RESUME) {
        this.startPlayOrResumePlay();
      } else if (event.hintType === InterruptHintType.INTERRUPT_HINT_STOP) {
        this.stop();
      }
    };
    this.mIjkMediaPlayer.on('audioInterrupt', event);
    this.controller.start()
  }

  aboutToDisappear() {
    // 退出
    this.mDestroyPage = true;
    this.mIjkMediaPlayer.setScreenOnWhilePlaying(false);
    if (this.controlPlayStatus !== PlayStatus.INIT) {
      this.stop();
    }
    this.mIjkMediaPlayer.off('audioInterrupt');
    this.controller.stop()
  }

  onPageShow() {
    // 开始播放
    if (this.mContext && !this.mFirst) {
      this.startPlayOrResumePlay();
    }
  }

  onPageHide() {
    // 暂停播放
    LogUtils.getInstance().LOGI('onPageHide-->pause');
    this.pause();
  }

  xcomponentController: XComponentController = new XComponentController();

  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Auto, justifyContent: FlexAlign.Start }) {
      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
        Text('ijkplayer播放器').fontSize('30px').fontColor(Color.White).margin('10px').fontWeight(FontWeight.Bold)
      }.height('100px').width('100%').backgroundColor(Color.Black)

      Divider().vertical(false).strokeWidth('20px').color(Color.White).lineCap(LineCapStyle.Round)
      Stack({ alignContent: Alignment.Center }) {
        Column() {
          XComponent({
            id: 'xcomponentId',
            type: 'surface',
            libraryname: 'ijkplayer_napi'
          }).onLoad((event?: object) => {
            if (!!event) {
              this.initDelayPlay(event);
            }
          })
            .onDestroy(() => {
            })
            .width('100%')
            .width(this.videoWidth)
            .aspectRatio(this.videoAspectRatio)
        }.aspectRatio(this.videoAspectRatio)

        // 此处添加网络加载loading效果
        Image($r('app.media.startIcon'))
          .objectFit(ImageFit.Auto)
          .width('120px')
          .height('120px')
          .visibility(this.replayVisible)
          .border({ width: 0 })
          .borderStyle(BorderStyle.Dashed)
          .onClick(() => {
            this.startPlayOrResumePlay();
          })

        // 视频播放完成后重新播放
        Image($r('app.media.startIcon'))
          .objectFit(ImageFit.Auto)
          .width('120px')
          .height('120px')
          .visibility(this.loadingVisible)
          .border({ width: 0 })
          .borderStyle(BorderStyle.Dashed)

      }.width('100%').backgroundColor('#000000').clip(true)

      Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
        Text(this.currentTime).width('100px').fontSize('20px').margin('20px');
        Slider({
          value: this.progressValue,
          min: 0,
          max: this.PROGRESS_MAX_VALUE,
          step: 1,
          style: SliderStyle.OutSet
        })
          .width('600px')
          .blockColor(Color.Blue)
          .trackColor(Color.Gray)
          .selectedColor(Color.Blue)
          .showSteps(true)
          .showTips(true)
          .enabled(this.slideEnable)
          .onChange((value: number, mode: SliderChangeMode) => {
            if (mode === 2) {
              this.isSeekTo = true;
              this.mDestroyPage = false;
              LogUtils.getInstance().LOGI('slider-->seekValue start:' + value);
              let seekValue = value * (this.mIjkMediaPlayer.getDuration() / 100);
              this.seekTo(seekValue + '');
              this.setProgress();
              LogUtils.getInstance().LOGI('slider-->seekValue end:' + seekValue);
              this.isSeekTo = false;
            }
          });
        Text(this.totalTime).width('100px').fontSize('20px').margin('10px');
      }

      Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
        Button('播放')
          .onClick(() => {
            this.startPlayOrResumePlay();
          }).width('400px').height('80px').margin('15px');
        Button('暂停')
          .onClick(() => {
            this.pause();
          }).width('400px').height('80px').margin('15px');
        Button('切换')
          .onClick(() => {
            this.playNext();
          }).width('400px').height('80px').margin('15px');

        Button('切换常亮').onClick(() => {
          this.light = !this.light
          console.info('testTag this.light:' + this.light)
          this.mIjkMediaPlayer.setScreenOnWhilePlaying(this.light)
        })
          .width('400px').height('80px').margin('15px');
      }

      Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
        Button('1倍速')
          .onClick(() => {
            if (!this.debounce()) {
              return;
            }
            this.playSpeed = '1f';
            this.mIjkMediaPlayer.setSpeed('1f');
          })
          .width('400px')
          .height('80px')
          .margin('15px')

        Button('1.5倍速')
          .onClick(() => {
            window.getLastWindow(this.getUIContext().getHostContext()).then((topWindow) => {
              const item: window.Window = topWindow;
              item.setWindowSystemBarEnable([])
              console.info(`Succeeded in obtaining the top window. Window id: ${topWindow.getWindowProperties().id}`);
            }).catch((err: BusinessError) => {
              console.error(`Failed to obtain the top window. Cause code: ${err.code}, message: ${err.message}`);
            });

            if (!this.debounce()) {
              return;
            }
            this.playSpeed = '1.5f';
            this.mIjkMediaPlayer.setSpeed('1.5f');
          })
          .width('400px')
          .height('80px')
          .margin('15px')

        Button('切换视频并2 倍速')
          .onClick(() => {
            this.playSpeed = '2f';
            this.mIjkMediaPlayer.setSpeed('2f');
          })
          .width('400px')
          .height('80px')
          .margin('15px')
      }

      Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) {
        Text('视频音量').width('120px').fontSize('30px').margin('15px')
        Slider({
          value: this.volume,
          min: 0.0,
          max: 1.0,
          step: 0.1,
          style: SliderStyle.OutSet
        })
          .width('600px')
          .blockColor(Color.Blue)
          .trackColor(Color.Gray)
          .selectedColor(Color.Blue)
          .showSteps(true)
          .showTips(true)
          .onChange((value: number, mode: SliderChangeMode) => {
            if (mode === SliderChangeMode.End) {
              this.volume = value;
              this.mIjkMediaPlayer.setVolume(this.volume.toString(), this.volume.toString());
            }
          })
      }
    }
  }

  // initDelayPlay
  private initDelayPlay(context: object) {
    this.mContext = context;
    let that = this;
    setTimeout(() => {
      that.startPlayOrResumePlay();
      that.mFirst = false;
    }, 300);
  }

  // startPlayOrResumePlay
  private startPlayOrResumePlay() {
    this.mDestroyPage = false;
    LogUtils.getInstance().LOGI('startPlayOrResumePlay start this.controlPlayStatus:' + this.controlPlayStatus);
    if (this.controlPlayStatus === PlayStatus.INIT) {
      this.stopProgressTask();
      this.startProgressTask();
      this.play(this.videoUrl.toString());
    }
    if (this.controlPlayStatus === PlayStatus.PAUSE) {
      this.mIjkMediaPlayer.start();
      this.setProgress();
    }
  }

  // completionNum
  private completionNum(num: number): string | number {
    if (num < 10) {
      return '0' + num;
    } else {
      return num;
    }
  }

  // stringForTime
  private stringForTime(timeMs: number): string {
    let totalSeconds: number | string = (timeMs / 1000);
    let newSeconds: number | string = totalSeconds % 60;
    let minutes: number | string = (totalSeconds / 60) % 60;
    let hours: number | string = totalSeconds / 3600;
    hours = this.completionNum(Math.floor(Math.floor(hours * 100) / 100));
    minutes = this.completionNum(Math.floor(Math.floor(minutes * 100) / 100));
    newSeconds = Math.floor(Math.floor(newSeconds * 100) / 100);
    if (this.isCurrentTime) {
      if (this.oldSeconds < newSeconds || newSeconds === 0 || this.isSeekTo) {
        this.oldSeconds = newSeconds;
      } else {
        newSeconds = this.oldSeconds;
      }
    }
    newSeconds = this.completionNum(newSeconds);
    if (hours > 0) {
      return hours + ':' + minutes + ':' + newSeconds;
    } else {
      return minutes + ':' + newSeconds;
    }
  }

  // setProgress
  private setProgress() {
    let position = this.mIjkMediaPlayer.getCurrentPosition();
    let duration = this.mIjkMediaPlayer.getDuration();
    let pos = 0;
    if (duration > 0) {
      this.slideEnable = true;
      let curPercent = position / duration;
      pos = curPercent * 100;
      if (pos > this.PROGRESS_MAX_VALUE) {
        this.progressValue = this.PROGRESS_MAX_VALUE;
      } else {
        this.progressValue = pos;
      }
    }
    this.totalTime = this.stringForTime(duration);
    if (position > duration) {
      position = duration;
    }
    this.isCurrentTime = true;
    this.currentTime = this.stringForTime(position);
    this.isCurrentTime = false;
  }

  // startProgressTask
  private startProgressTask() {
    let that = this;
    this.updateProgressTimer = setInterval(() => {
      if (!that.mDestroyPage) {
        that.setProgress();
      }
    }, 1000);
  }

  // stopProgressTask
  private stopProgressTask() {
    clearInterval(this.updateProgressTimer);
  }

  private play(url: string) {
    let that = this;
    // 设置XComponent回调的context
    if (!!this.mContext) {
      this.mIjkMediaPlayer.setContext(this.mContext, 'xcomponentId');
    }
    if (this.controlPlayStatus === PlayStatus.INIT) {
      this.mIjkMediaPlayer.reset();
    }
    this.controlPlayStatus = PlayStatus.PLAY;
    // 设置debug模式
    this.mIjkMediaPlayer.setDebug(true);
    // 初始化配置
    this.mIjkMediaPlayer.native_setup();
    // 初始化配置后需要重新设置音频流音量,否则音量为默认值1.0
    this.mIjkMediaPlayer.setVolume(this.volume.toString(), this.volume.toString());
    // 设置视频源
    this.mIjkMediaPlayer.setDataSource(url);
    // 设置视频源http请求头
    let headers = new Map([
      ["user_agent", "Mozilla/5.0 BiliDroid/7.30.0 (xxxx.com)"],
      ["referer", "xxx.xxx.xxxx.com"]
    ]);
    this.mIjkMediaPlayer.setDataSourceHeader(headers);

    this.mIjkMediaPlayer.setScreenOnWhilePlaying(true)
    this.mIjkMediaPlayer.setSpeed(this.playSpeed);
    let speed = this.mIjkMediaPlayer.getSpeed();
    // 是否开启循环播放
    let mOnVideoSizeChangedListener: OnVideoSizeChangedListener = {
      onVideoSizeChanged: (width: number, height: number, sarNum: number, sarDen: number) => {
        if (height === 0) {
          return;
        }
        const va = width / height;
        const vpa = that.videoParentAspectRatio;
        if (vpa > va) {
          this.videoWidth = (width / (height * vpa)) * 100 + '%';
        } else {
          this.videoWidth = '100%';
        }
        if (width && height) {
          this.videoAspectRatio = width / height;
        }
      }
    };
    this.mIjkMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);

    let mOnCompletionListener: OnCompletionListener = {
      onCompletion: () => {
        that.currentTime = that.stringForTime(this.mIjkMediaPlayer.getDuration());
        that.progressValue = this.PROGRESS_MAX_VALUE;
        that.slideEnable = false;
        that.stop();
      }
    };
    this.mIjkMediaPlayer.setOnCompletionListener(mOnCompletionListener);

    let mOnSeekCompleteListener: OnSeekCompleteListener = {
      onSeekComplete: () => {
        that.startPlayOrResumePlay();
      }
    };
    this.mIjkMediaPlayer.setOnSeekCompleteListener(mOnSeekCompleteListener);
    let mOnInfoListener: OnInfoListener = {
      onInfo: (what: number, extra: number) => {
      }
    };

更多关于HarmonyOS鸿蒙Next中请问你们的ijkplayer组件里timeUpdate怎么写,需要检测到秒的回调的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


按照ijkPleyer文档接口编写代码。

这是个三方库嘛?去看一下三方库的md文件或者教程吧:OpenHarmony三方库中心仓 不行就联系一下这个三方组件的作者

是啊,第三方库,你有咩有做过m3u8的播放,有推荐吗

在HarmonyOS鸿蒙Next中,ijkplayer组件的timeUpdate回调可通过监听PlayerEvent.TIME_UPDATE事件实现。使用on('timeUpdate')方法注册监听器,回调函数参数包含当前播放时间(单位:毫秒)。若需检测到秒的回调,可在回调函数内将毫秒转换为秒,并执行相应逻辑。示例代码片段如下:

player.on('timeUpdate', (time) => {
  const currentSecond = Math.floor(time / 1000);
  // 处理每秒回调逻辑
});

在HarmonyOS Next的ijkplayer中,要实现秒级回调并用于进度条,关键在于正确使用setOnInfoListenersetOnTimeUpdateListener

对于你的需求,建议优先使用setOnTimeUpdateListener,因为它专门用于时间更新。其回调参数currentPosition(当前播放位置,单位毫秒)和duration(总时长,单位毫秒)正是你构建进度条所需的。

核心实现步骤:

  1. 设置时间更新监听器:通过setOnTimeUpdateListener注册回调。
  2. 在回调中进行秒级判断:在监听器的onTimeUpdate方法中,将currentPosition(毫秒)转换为秒。你可以通过判断当前秒数是否变化(例如,currentSecond != lastSecond)来触发你的“秒回调”逻辑。
  3. 更新进度条:在同一回调中,使用currentPositionduration计算播放进度百分比((currentPosition / duration) * 100),并据此更新UI进度条。

关于setOnInfoListener:它主要用于播放器状态、缓冲进度等信息的回调(如开始渲染、缓冲开始/结束、解码器信息等),虽然其中也可能包含时间信息,但**setOnTimeUpdateListener是更直接、更专用于时间与进度更新的接口**。

简单代码示意:

// 假设 player 是你的ijkplayer实例
player.setOnTimeUpdateListener({
  onTimeUpdate: (currentPosition: number, duration: number) => {
    // 1. 秒级回调判断
    let currentSec = Math.floor(currentPosition / 1000);
    if (currentSec !== this.lastSec) {
      this.lastSec = currentSec;
      // 在这里执行你需要每秒执行一次的业务逻辑
      console.log(`每秒回调: ${currentSec}s`);
    }

    // 2. 更新进度条
    if (duration > 0) {
      let progressPercent = (currentPosition / duration) * 100;
      // 更新你的UI进度条组件,例如:
      // this.uiProgressBar.setValue(progressPercent);
    }
  }
});

这样,你便可以在一个回调中同时满足“秒回调”和“进度条更新”两个需求。setOnTimeUpdateListener的回调频率通常很高(约每秒多次),通过上述的秒数取整与比较,可以高效地过滤出每秒一次的事件。

回到顶部