目录

setCurrentTime方法指定Video组件开始播放位置偶现其它帧闪烁的问题

问题现象

进入一个页面时想让Video组件从一个指定的时间开始播放,比如第10秒,设置了参数this.controller.setCurrentTime(AppStorage.get('currentTime'),SeekMode.ClosestKeyframe)偶尔会出现定位不准,展示了视频第一帧的情况。已知在onStart中调用setCurrentTime一定会出现第一帧,在onPrepared中开始调用setCurrentTime偶尔会出现第一帧。是什么原因造成的该情况,如何避免?

问题代码示例参考如下:

@Entry
@Component
struct VideoExamplePage {
  previewUri: Resource = $r('app.media.startIcon');
  curRate: PlaybackSpeed = PlaybackSpeed.Speed_Forward_1_00_X;
  isAutoPlay: boolean = true;
  showControls: boolean = true;
  controller: VideoController = new VideoController();
  @State totalTime: number = 0;

  build() {
    Column() {
      Video({
        src: $rawfile('videoTest.mp4'), // 替换已有视频资源
        currentProgressRate: this.curRate,
        controller: this.controller
      })
        .width('100%')
        .height(600)
        .autoPlay(this.isAutoPlay)
        .controls(this.showControls)
        .onPrepared((error?: DurationObject) => {
          if (error != undefined) {
            console.error(`onPrepared is ${error.duration}`);
            this.totalTime = error.duration;
            this.controller.setCurrentTime(10, SeekMode.Accurate); // 从第十秒开始播放
          }
        });
      Row() {
        Button('setTime')
          .onClick(() => {
            this.controller.setCurrentTime(10, SeekMode.Accurate); // 精准跳转到视频的10s位置
          })
          .margin(2);
      };
    };
  }
}

interface DurationObject {
  duration: number;
}

问题效果预览:

背景知识

问题定位

由于Video组件的跳转和播放都是在后台线程里进行,所以当视频自动播放时与跳转事件存在执行先后顺序的冲突。

分析结论

视频的play播放和seek跳转都是在后台线程里进行,由于冲突导致定位跳转过程中会偶现原位置的下一帧图片。

修改建议

取消自动播放,在onPrepared事件里先设置跳转,再进行播放,来代替autoPlay(true)自动播放。

  1. 设置autoPlay(true)修改为autoPlay(false)。
  2. onPrepared事件修改如下:
    .onPrepared((error?: DurationObject) => {
      if (error != undefined) {
        console.info(`onPrepared is ${error.duration}`);
        this.totalTime = error.duration;
        this.controller.setCurrentTime(10, SeekMode.Accurate); // 从第十秒开始播放
        this.controller.start();
      }
    })

完整代码如下:

@Entry
@Component
struct VideoExamplePage {
  previewUri: Resource = $r('app.media.startIcon');
  curRate: PlaybackSpeed = PlaybackSpeed.Speed_Forward_1_00_X;
  isAutoPlay: boolean = false;
  showControls: boolean = true;
  controller: VideoController = new VideoController();
  @State totalTime: number = 0;

  build() {
    Column() {
      Video({
        src: $rawfile('example.mp4'), // 替换已有视频资源
        currentProgressRate: this.curRate,
        controller: this.controller
      })
        .width('100%')
        .height(600)
        .controls(this.showControls)
        .onPrepared((error?: DurationObject) => {
          if (error != undefined) {
            console.info(`onPrepared is ${error.duration}`);
            this.totalTime = error.duration;
            this.controller.setCurrentTime(10, SeekMode.Accurate); // 从第十秒开始播放
            this.controller.start();
          }
        })
        .autoPlay(this.isAutoPlay);
      Row() {
        Button('setTime')
          .onClick(() => {
            this.controller.setCurrentTime(10, SeekMode.Accurate); // 精准跳转到视频的10s位置
          })
          .margin(2);
      };
    };
  }
}

interface DurationObject {
  duration: number;
}