9afce6f6创建于 2025年5月7日历史提交
/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import window from '@ohos.window';
import {
  AvVideoPlayer,
  GlobalContext,
  IVideoPlayer,
  PlayerType,
  PlayStatus,
  StandardGSYVideoModel
} from '@ohos/gsyvideoplayer';
import emitter from '@ohos.events.emitter';
import settings from '@ohos.settings';
import {
  BaseDanmaku,
  BaseDanmakuParser,
  Callback,
  DANMAKU_STYLE_STROKEN,
  DanmakuContext,
  DanmakuTimer,
  DanmakuView,
  IDanmakus,
  IDanmakuView,
  JSONSource,
  OnDanmakuClickListener,
  Proxy,
  SpannedCacheStuffer,
  SystemClock
} from '@ohos/danmakuflamemaster';
import { SegmentButton, SegmentButtonOptions, SegmentButtonItemTuple } from '@kit.ArkUI';
import { DanmakuParser } from './DanmakuParser';
import { sourceData } from './DanmakuData';
import { logger } from 'utils';
import { Directions } from '../view/VideoView';
import { Constants } from '../common/Constants';

let updateProgressTimer: number = 0;
let changeWidth: number = 0;
let changeHeight: number = 0;
let uiTime: number = 0;
const SPEED_POPUP_LIST_SPACE: number = 20;
const PERCENT_LOW: string = '30%';
const PERCENT_MIDDLE: string = '60%';
const PERCENT_HIGH: string = '100%';
const videoPlayEvent: emitter.InnerEvent = {
  eventId: 1
};
const videoInitEvent: emitter.InnerEvent = {
  eventId: 2
};
const videoPauseEvent: emitter.InnerEvent = {
  eventId: 3
};

let touchStartX: number = 0;
let touchStartY: number = 0;

// 当前视频中心图标显示状态,包括播放、暂停、加载和隐藏
export enum CenterUiShowStatus { PLAY, PAUSE, LOADING };

@Component
export struct DanmakuVideoPlayer {
  iVideoPlayer: IVideoPlayer | undefined = undefined;
  @State settingsShow: boolean = false;
  @State @Watch('changeDanmukuHeight') danmukuHeightSelectedIndexes: number[] = [2];
  @State danmukuHeightCapsuleOptions: SegmentButtonOptions = SegmentButtonOptions.capsule({
    buttons: [{ text: PERCENT_LOW }, { text: PERCENT_MIDDLE }, { text: PERCENT_HIGH }] as SegmentButtonItemTuple,
    multiply: false,
    selectedBackgroundColor: $r('sys.color.ohos_id_color_text_secondary'),
    selectedFontColor: Color.Orange,
    fontColor: $r('sys.color.ohos_id_color_foreground_contrary'),
    fontSize: $r('app.string.danmaku_player_send_danmaku_fontsize'),
    selectedFontSize: $r('app.string.danmaku_player_send_danmaku_fontsize'),
    buttonPadding: { top: $r('app.integer.danmaku_player_buttonPadding_flex'), right: $r('app.integer.danmaku_player_buttonPadding_horizontal'), bottom: $r('app.integer.danmaku_player_buttonPadding_flex'), left: $r('app.integer.danmaku_player_buttonPadding_horizontal') },
    backgroundBlurStyle: BlurStyle.BACKGROUND_THICK
  });
  @Provide currentTime: string = '00:00';
  @Provide totalTime: string = '00:00';
  @State progressValue: number = 0;
  @State progressMaxValue: number = Constants.HUNDRED_PERCENT;
  @State fullShowTop: boolean = false; // 是否显示视频标题栏
  @State showBottomUi: boolean = false; // 是否显示底部装饰器
  @State fullShowLock: boolean = false; // 是否显示UI锁定控制按钮
  @State lock: boolean = false; // 是否锁定视频UI控制界面
  // 当前视频中心图标状态
  @State centerUiShowStatus: CenterUiShowStatus = CenterUiShowStatus.PLAY;
  @State isShowCenterUi: boolean = true;
  // 当前视频方向
  @Link curDirection: Directions;
  // 弹幕开关文本
  @State danmakuText: Resource = $r('app.string.danmaku_player_danmaku_toggle_text_show');
  // 是否显示亮度数值的布局
  @State showBrightnessUi: boolean = false;
  // 是否显示横向滑动的进度布局
  @State showSeekProgressUi: boolean = false;
  // 当前亮度 [0-255]
  @State currentBrightness: number = Constants.DEFAULT_BRIGHTNESS;
  // 拖拽的时间
  @State seekCurrentTime: string = this.currentTime;
  // 当前进度
  @State seekTimePosition: number = 0;
  @Link windowClass: window.Window | undefined;
  // 是否拖动进度条
  private isSeek: boolean = false;
  // 开始拖动进度条时是否正在播放
  private isPlaying: boolean = false;
  // 是否正在加载手势滑动禁止
  private isLoadNotSeek: boolean = false;
  // 当前拖拽进度的方向资源
  @State seekDirectionRes: Resource = $r('app.media.danmaku_player_video_forward_icon');
  private isVerticalDrag: boolean = false;
  private isHorizontalDrag: boolean = false;
  // 控制封面图显隐
  @State coverVisible: Visibility = Visibility.Visible;
  private panOptionBright: PanGestureOptions = new PanGestureOptions({
    direction: PanDirection.Vertical
  });
  private panOptionSeek: PanGestureOptions = new PanGestureOptions({
    direction: PanDirection.Horizontal
  });
  public title: string = '';
  private xComponentId: string = '';
  private danmakuShow: boolean = true;
  second: number = Constants.COUNT_DOWN_TIME;
  // 构建StandardGSYVideoModel对象
  public videoModel: StandardGSYVideoModel = new StandardGSYVideoModel();
  @State model: DanmakuView.Model = new DanmakuView.Model();
  private context: DanmakuContext | undefined = undefined;
  private parser: BaseDanmakuParser | undefined = undefined;
  private cacheStufferAdapter: Proxy = new Pro();
  @State curRate: PlaybackSpeed = PlaybackSpeed.Speed_Forward_1_00_X;
  controller: VideoController = new VideoController();
  private getDirection: ()=> number = () => { return 0};


  /*
   * TODO: 知识点:解析弹幕
   */
  private createParser(): BaseDanmakuParser {
    const parser: BaseDanmakuParser = new DanmakuParser();
    const jsonSource = new JSONSource(sourceData);
    parser.load(jsonSource);
    return parser;
  }

  /*
   * 初始化播放器
   */
  videoInit: (iVideoPlayer: IVideoPlayer, xid: string) => void = (iVideoPlayer: IVideoPlayer, xid: string) => {
    logger.info('VideoInit');
    this.iVideoPlayer = iVideoPlayer;
    this.xComponentId = xid;
    this.iVideoPlayer.setUp(this.videoModel.getUrl(), this.videoModel.getCacheWithPlay());
  }

  changeDanmukuHeight() {
    this.model.setHeight(vp2px(changeHeight - 1) * (this.danmukuHeightSelectedIndexes[0] + 1) / 3);
  }

  /*
   * TODO: 知识点:添加弹幕,设置该条弹幕的相关参数,示例中为定值
   */
  private addDanmaku(isLive: Boolean) {
    if (!this.context) {
      return;
    }
    let danmaku: BaseDanmaku = this.context.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
    danmaku.text = '这是一条弹幕' + SystemClock.uptimeMillis();
    danmaku.padding = getContext(this).resourceManager.getNumber($r('app.integer.danmaku_player_danmaku_padding').id);
    danmaku.priority = 0; // 可能会被各种过滤器过滤并隐藏显示
    danmaku.isLive = isLive.valueOf();
    danmaku.setTime(this.model.getCurrentTime() +
    getContext(this).resourceManager.getNumber($r('app.integer.danmaku_player_time_range').id));
    if (this.parser) {
      danmaku.textSize =
        getContext(this).resourceManager.getNumber($r('app.integer.danmaku_player_new_danmaku_fontsize').id) *
          (this.parser.getDisplayer().getDensity() * 0.5);
    }
    danmaku.textColor = 0xffff0000;
    danmaku.textShadowColor = 0xffffffff;
    danmaku.borderColor = 0xff00ff00;
    this.model.addDanmaku(danmaku);
  }

  /*
   * 上侧返回装饰器
   */
  @Builder
  topTitle() {
    Row() {
      Image($r('app.media.danmaku_player_video_back'))
        .width($r('app.integer.danmaku_player_back_icon_width'))
        .height($r('app.integer.danmaku_player_back_icon_height'))
        .margin({
          top: $r('app.integer.danmaku_player_back_icon_margin_top'),
          left: $r('app.integer.danmaku_player_back_icon_margin_left')
        })
        .onClick(() => {
          this.videoModel.ExecuteBackClickListener();
        })
      Text(this.videoModel.getTitle())
        .fontSize($r('app.string.danmaku_player_video_title_fontsize'))
        .margin({
          top: $r('app.integer.danmaku_player_title_margin_top'),
          left: $r('app.integer.danmaku_player_title_margin_left')
        })
        .fontColor($r('app.color.danmaku_player_title_color'))
    }
    .alignItems(VerticalAlign.Center)
    .justifyContent(FlexAlign.Start)
    .backgroundColor($r('app.color.danmaku_player_bottom_controls_color'))
    .width($r('app.string.danmaku_player_full_size'))
    .padding($r('app.integer.danmaku_player_top_title_padding'))
  }

  /*
   * 播放暂停控制装饰器
   */
  @Builder
  middleControls() {
    Row() {
      Row() {
        if (this.isShowCenterUi) {
          if (this.centerUiShowStatus === CenterUiShowStatus.PLAY) {
            Image($r('app.media.danmaku_player_video_play_pressed'))
              .width($r('app.integer.danmaku_player_play_icon_width'))
              .height($r('app.integer.danmaku_player_play_icon_width'))
              .onClick(() => {
                this.videoToPlay();
              })
          } else if (this.centerUiShowStatus === CenterUiShowStatus.PAUSE) {
            Image($r('app.media.danmaku_player_video_pause_normal'))
              .width($r('app.integer.danmaku_player_play_icon_width'))
              .height($r('app.integer.danmaku_player_play_icon_width'))
              .onClick(() => {
                this.videoToPause();
              })
          } else {
            Image($r('app.media.danmaku_player_icon_load'))
              .objectFit(ImageFit.Auto)
              .width($r('app.integer.danmaku_player_play_icon_width'))
              .height($r('app.integer.danmaku_player_play_icon_width'))
          }
        }
      }
      .justifyContent(FlexAlign.Center)
      .alignItems(VerticalAlign.Center)
      .align(Alignment.BottomEnd)
      .width($r('app.string.danmaku_player_empty_width'))
      .height($r('app.string.danmaku_player_full_size'))

      Row() {
        if (this.curDirection === Directions.VERTICAL && this.fullShowLock) {
          Image(this.lock ? $r('app.media.danmaku_player_lock') : $r('app.media.danmaku_player_unlock'))
            .width($r('app.integer.danmaku_player_lock_icon_width'))
            .height($r('app.integer.danmaku_player_lock_icon_width'))
            .objectFit(ImageFit.Contain)
            .margin({
              right: $r('app.integer.danmaku_player_lock_margin')
            })
            .onClick(() => {
              this.lock = !this.lock;
              if (this.lock) {
                this.showBottomUi = false;
                this.fullShowTop = false;
                this.isShowCenterUi = false;
              } else {
                this.showBottomUi = true;
                this.fullShowTop = true;
                this.isShowCenterUi = true;
              }
            })
        }
      }
      .justifyContent(FlexAlign.End)
      .alignItems(VerticalAlign.Center)
      .width($r('app.string.danmaku_player_empty_width'))
      .height($r('app.string.danmaku_player_full_size'))
    }
    .width($r('app.string.danmaku_player_full_size'))
    .height($r('app.string.danmaku_player_full_size'))
    .justifyContent(FlexAlign.End)
  }

  // 弹幕高度弹框
  @Builder
  settingPopup() {
    List({ space: SPEED_POPUP_LIST_SPACE }) {
      ListItem() {
        Column() {
          Text($r('app.string.danmaku_player_height'))
            .fontColor(Color.White)
            .fontSize($r('app.string.danmaku_player_send_danmaku_fontsize'))
            .margin({ left: $r('app.integer.danmaku_player_back_icon_margin_top'), top: $r('app.integer.danmaku_player_back_icon_margin_top') })
          SegmentButton({
            options: this.danmukuHeightCapsuleOptions,
            selectedIndexes: this.danmukuHeightSelectedIndexes
          })
            .margin({ left: $r('app.integer.danmaku_player_back_icon_margin_top'), right: $r('app.integer.danmaku_player_back_icon_margin_top'), top: $r('app.integer.danmaku_player_back_icon_margin_top') })
        }
        .justifyContent(FlexAlign.SpaceBetween)
      }
    }
    .width($r('app.string.danmaku_player_setting_popup_width'))
    .height(this.getDirection() === 1 ? $r('app.string.danmaku_player_setting_popup_big_height') : $r('app.string.danmaku_player_setting_popup_little_height'))
  }

  /*
   * 暂停播放
   */
  private videoToPause() {
    if (!this.iVideoPlayer) {
      return;
    }
    logger.info('standardGSYVideoPlayer onPause1 click');
    this.iVideoPlayer.pause();
    this.showBottomUi = true;
    this.fullShowTop = true;
    this.fullShowLock = true;
    this.timeCountdown();
  }

  /*
   * 开始播放
   */
  private videoToPlay() {
    if (!this.iVideoPlayer) {
      return;
    }
    logger.info('standardGSYVideoPlayer onPlay click');
    this.centerUiShowStatus = CenterUiShowStatus.LOADING;
    this.showBottomUi = true;
    this.fullShowTop = true;
    this.fullShowLock = true;
    this.timeCountdown();
    this.videoJudgeToPlay();
  }

  /*
   * 判断是否播放逻辑
   */
  private videoJudgeToPlay() {
    logger.info('videoJudgeToPlay');
    if (!this.iVideoPlayer) {
      return;
    }
    if (GlobalContext.getContext().getObject('xid') !== this.iVideoPlayer.xComponentId) {
      // 首次播放
      logger.info('videoJudgeToPlay1');
      this.iVideoPlayer.play();
      this.iVideoPlayer.firstOrSeek = true;
    } else {
      logger.info('videoJudgeToPlay2');
      if (this.iVideoPlayer.playStatus === PlayStatus.PAUSE) {
        // 暂停后重新播放
        logger.info('standardGSYVideoPlayer videoJudgeToPlay ResumePlay');
        if (this.isSeek) {
          this.iVideoPlayer.seekTo(this.progressValue);
        } else {
          this.model.resume();
          logger.info('this.model.resume');
          this.iVideoPlayer.resumePlay();
        }
      } else {
        // 播放完成后重新播放
        logger.info('standardGSYVideoPlayer videoJudgeToPlay play: ' + this.iVideoPlayer.videoUrl);
        if (this.isSeek) {
          this.iVideoPlayer.seekTo(this.progressValue);
        } else {
          this.iVideoPlayer.play();
        }
      }
    }
  }

  /*
   * 底部装饰器:包括进度条、全屏按钮、发送弹幕按钮、弹幕开关
   */
  @Builder
  bottomControls() {
    Column() {
      Row() {
        Text(this.currentTime)
          .fontSize($r('app.string.danmaku_player_total_time_fontsize'))
          .margin({
            left: $r('app.integer.danmaku_player_time_margin')
          })
          .fontColor($r('app.color.danmaku_player_time_color'))

        Slider({
          value: this.progressValue,
          min: 0,
          max: this.progressMaxValue,
          step: 1,
          style: SliderStyle.OutSet
        })
          .width(this.curDirection === Directions.VERTICAL ?
          $r('app.string.danmaku_player_slider_length_vertical') :
          $r('app.string.danmaku_player_slider_length_horizontal'))
          .blockColor(Color.Blue)
          .trackColor(Color.White)
          .selectedColor(Color.Blue)
          .showSteps(true)
          .onChange((value: number, mode: SliderChangeMode) => {
            clearInterval(uiTime);
            if (!this.iVideoPlayer) {
              return;
            }
            switch (mode) {
              case SliderChangeMode.Begin:
                // 暂停通知的回调会到的慢一点,使用isSeek参数去控制pause通知里面的状态操作
                this.stopProgressTask();
                this.isSeek = true;
                if (this.centerUiShowStatus === CenterUiShowStatus.PAUSE) {
                  this.isPlaying = true;
                } else {
                  this.isPlaying = false;
                }
                this.iVideoPlayer.pause();
                break;
              case SliderChangeMode.Moving:
                this.currentTime = this.stringForTime(value);
                break;
              case SliderChangeMode.End:
                // 没有改变进度时,设置isSeek为false
                if (this.progressValue === value) {
                  this.isSeek = false;
                }
                this.progressValue = value;
                if (this.isPlaying) {
                  this.centerUiShowStatus = CenterUiShowStatus.LOADING;
                  this.iVideoPlayer.seekTo(value);
                }
                logger.info('slider-->seekValue end: ' + value);
                break;
            }
          })
        Text(this.totalTime)
          .fontSize($r('app.string.danmaku_player_total_time_fontsize'))
          .margin({
            right: $r('app.integer.danmaku_player_total_time_right_margin')
          })
          .fontColor($r('app.color.danmaku_player_time_color'))

        Image(this.curDirection === Directions.VERTICAL ? $r('app.media.danmaku_player_video_shrink') :
        $r('app.media.danmaku_player_video_enlarge'))
          .width($r('app.integer.danmaku_player_enlarge_icon_width'))
          .height($r('app.integer.danmaku_player_enlarge_icon_width'))
          .margin({
            right: $r('app.integer.danmaku_player_enlarge_icon_margin')
          })
          .onClick(() => {
            this.timeCountdown();
            if (this.curDirection === Directions.VERTICAL) {
              this.videoModel.ExecuteBackClickListener();
            } else {
              this.videoModel.ExecuteFullClickListener();
            }
          })
      }
      .width($r('app.string.danmaku_player_full_size'))
      .backgroundColor($r('app.color.danmaku_player_bottom_controls_color'))

      Row() {
        Text($r('app.string.danmaku_player_send_danmu'))
          .backgroundColor($r('app.color.danmaku_player_color_add_danmaku'))
          .width($r('app.string.danmaku_player_send_danmaku_width'))
          .textAlign(TextAlign.Center)
          .fontColor($r('app.color.danmaku_player_color_add_text_danmaku'))
          .borderRadius($r('app.string.danmaku_player_send_danmaku_border_radius'))
          .onClick(() => {
            this.timeCountdown();
            this.addDanmaku(false);
          })
          .layoutWeight(1)
          .fontSize($r('app.string.danmaku_player_send_danmaku_fontsize'))
          .margin($r('app.string.danmaku_player_send_danmaku_margin'))

        Text(this.danmakuText)
          .textAlign(TextAlign.Center)
          .fontColor($r('app.color.danmaku_player_color_show_danmaku'))
          .fontSize($r('app.string.danmaku_player_send_danmaku_fontsize'))
        Toggle({
          type: ToggleType.Switch,
          isOn: this.danmakuShow
        })
          .size({
            width: $r('app.string.danmaku_player_toggle_width'),
            height: $r('app.string.danmaku_player_toggle_height')
          })
          .selectedColor($r('app.color.danmaku_player_toggle_color'))
          .switchPointColor($r('app.color.danmaku_player_switchPoint_color'))
          .onChange((isOn: boolean) => {
            this.timeCountdown();
            if (!isOn) {
              this.model.hide();
              this.danmakuText = $r('app.string.danmaku_player_danmaku_toggle_text_hide');
            } else {
              this.model.setWidth(vp2px(changeWidth));
              this.model.setHeight(vp2px(changeHeight - 1) * (this.danmukuHeightSelectedIndexes[0] + 1) / 3);
              this.model.hide();
              this.model.show();
              this.danmakuText = $r('app.string.danmaku_player_danmaku_toggle_text_show');
            }
            this.danmakuShow = !this.danmakuShow;
          })
        Image($r('app.media.danmaku_player_gearshape'))
          .fillColor(Color.White)
          .width($r('app.integer.danmaku_player_lock_icon_width'))
          .height($r('app.integer.danmaku_player_lock_icon_width'))
          .onClick(() => {
            this.timeCountdown();
            this.settingsShow = !this.settingsShow;
          })
          .bindPopup(this.settingsShow, {
            enableArrow: false,
            radius: $r('app.integer.danmaku_player_current_brightness'),
            builder: this.settingPopup(),
            placement: Placement.Top,
            popupColor: $r('app.string.danmaku_player_popup_color'),
            backgroundBlurStyle: BlurStyle.NONE,
            onStateChange: (e) => {
              this.timeCountdown();
              this.settingsShow = e.isVisible;
            }
          })
      }
      .backgroundColor($r('app.color.danmaku_player_bottom_controls_color'))
      // 横屏时避让设备边缘
      .padding({
        bottom: this.curDirection === Directions.VERTICAL ? $r('app.integer.danmaku_player_send_danmu_padding') : 0,
        left: this.curDirection === Directions.VERTICAL ? $r('app.integer.danmaku_player_send_danmu_padding') : 0,
        right: this.curDirection === Directions.VERTICAL ? $r('app.integer.danmaku_player_send_danmu_padding') : 0,
      })
    }
  }

  /*
   * 亮度调节控件
   */
  @Builder
  brightnessUI() {
    Column() {
      Image($r('app.media.danmaku_player_video_brightness_6_white_36dp'))
        .objectFit(ImageFit.Auto)
        .width($r('app.integer.danmaku_player_brightness_icon_width'))
        .height($r('app.integer.danmaku_player_brightness_icon_height'))
        .alignSelf(ItemAlign.Center)

      Text((this.currentBrightness / Constants.MAX_BRIGHTNESS * Constants.HUNDRED_PERCENT).toFixed(0) + '%')
        .fontColor(Color.White)
        .textAlign(TextAlign.Center)
        .width($r('app.integer.danmaku_player_brightness_text_width'))
    }
    .visibility(this.showBrightnessUi ? Visibility.Visible : Visibility.None)
    .width($r('app.integer.danmaku_player_brightness_UI_width'))
    .height($r('app.string.danmaku_player_full_size'))
    .justifyContent(FlexAlign.Center)
    .position({ right: $r('app.string.danmaku_player_brightness_position_right') })
  }

  /*
   * 进度条控件
   */
  @Builder
  seekProgressUI() {
    Column() {
      Image(this.seekDirectionRes)
        .objectFit(ImageFit.Auto)
        .width($r('app.integer.danmaku_player_seek_icon_width'))
        .height($r('app.integer.danmaku_player_seek_icon_height'))
        .margin({
          top: $r('app.integer.danmaku_player_seek_progress_margin_top')
        })
        .alignSelf(ItemAlign.Center)

      Row() {
        Text(`${this.seekCurrentTime}/${this.totalTime}`)
      }
      .margin({
        top: $r('app.integer.danmaku_player_seek_progress_margin_top')
      })

      Progress({
        value: this.seekTimePosition,
        total: this.progressMaxValue ? this.progressMaxValue :
        getContext(this).resourceManager.getNumber($r('app.integer.danmaku_player_progress_total').id),
        type: ProgressType.Linear
      })
        .style({
          strokeWidth: $r('app.integer.danmaku_player_progress_stroke_width'),
          enableSmoothEffect: false
        })
        .padding({
          left: $r('app.integer.danmaku_player_seek_padding_Left'),
          right: $r('app.integer.danmaku_player_seek_padding_right')
        })
        .margin({
          top: $r('app.integer.danmaku_player_seek_margin_top'),
          bottom: $r('app.integer.danmaku_player_seek_margin_bottom')
        })
        .backgroundColor(Color.White);
    }
    .visibility(this.showSeekProgressUi ? Visibility.Visible : Visibility.Hidden)
    .width($r('app.integer.danmaku_player_seek_progress_width'))
    .backgroundColor($r('app.color.danmaku_player_seek_to_dialog_background'))
    .alignItems(HorizontalAlign.Center)
    .alignSelf(ItemAlign.Center)
  }

  build() {
    Stack({ alignContent: Alignment.Bottom }) {
      // 使用gsyvideoplayer库中的AvVideoPlayer播放器
      AvVideoPlayer({
        videoInit: this.videoInit
      })

      Row() {
        this.seekProgressUI()
      }
      .justifyContent(FlexAlign.Center)
      .alignItems(VerticalAlign.Center)
      .width($r('app.string.danmaku_player_full_size'))
      .height($r('app.string.danmaku_player_full_size'))

      this.brightnessUI()

      DanmakuView({
        model: this.model
      })
        .backgroundColor(Color.Transparent)
        .position({
          x: 0,
          y: 0
        })
        .enabled(false);

      Stack() {
        if (this.videoModel.getCoverImage() && this.coverVisible === Visibility.Visible) {
          Image(this.videoModel.getCoverImage())
            .width($r('app.string.danmaku_player_full_size'))
            .height($r('app.string.danmaku_player_full_size'))
        }
      }

      this.middleControls()

      Stack() {
        if (this.curDirection === Directions.VERTICAL && this.fullShowTop) {
          this.topTitle()
        }
      }
      .position({
        x: 0,
        y: 0
      })

      Stack() {
        if (this.showBottomUi) {
          this.bottomControls()
        }
      }
    }
    .gesture(
      GestureGroup(GestureMode.Parallel,
        // 单击播放区域控制UI显示
        TapGesture()
          .onAction((event: GestureEvent | undefined) => {
            if (event) {
              this.timeCountdown();
              this.fullShowLock = true;
              if (!this.lock) {
                this.showBottomUi = true;
                this.fullShowTop = true;
                this.isShowCenterUi = true;
              }
              if (event && event.fingerList && event.fingerList[0]) {
                logger.info('TapGesture event.onActionStart start x: ' + event.fingerList[0].localX +
                  ' ---event.onActionStart start y: ' + event.fingerList[0].localY);
              }
            }
          }),
        /*
         * 绑定屏幕亮度拖动手势
         */
        PanGesture(this.panOptionBright)
          .onActionStart((event: GestureEvent | undefined) => {
            logger.info('Vertical Pan Start');
            if (this.lock || this.isHorizontalDrag) {
              return;
            }
            if (event && event.fingerList && event.fingerList[0]) {
              touchStartX = event.fingerList[0].localX;
              touchStartY = event.fingerList[0].localY;
              logger.info('VerticalPanBright event.onActionStart start x: ' + touchStartX +
                ' event.onActionStart start y: ' + touchStartY);
            }
          })
          .onActionUpdate((event: GestureEvent | undefined) => {
            if (this.lock || this.isHorizontalDrag) {
              return;
            }
            this.isVerticalDrag = true;
            if (event && event.fingerList && event.fingerList[0]) {
              const touchY = event.fingerList[0].localY;
              const deltaY = touchY - touchStartY;
              // 移动距离占播放器高度的比例(deltaY取反是因为值的正负与滑动方向相反)
              const percent = (-deltaY / changeHeight);
              const width = changeWidth as number;
              if (touchStartX <= (width / 2)) {
                this.showBrightnessUi = true;
                let brightness = this.currentBrightness;
                brightness += Constants.MAX_BRIGHTNESS * percent;
                if (brightness < 0) {
                  brightness = 0;
                }
                if (brightness > Constants.MAX_BRIGHTNESS) {
                  brightness = Constants.MAX_BRIGHTNESS;
                }
                const finalValue = brightness / Constants.MAX_BRIGHTNESS;
                if (this.windowClass) {
                  this.windowClass.setWindowBrightness(finalValue);
                }
                this.currentBrightness = brightness;
              }
              touchStartY = touchY;
            }
          })
          .onActionEnd((event: GestureEvent | undefined) => {
            logger.info('Vertical Pan End');
            this.showBrightnessUi = false;
            this.isVerticalDrag = false;
          }),
        PanGesture(this.panOptionSeek)
          .onActionStart((event: GestureEvent | undefined) => {
            logger.info('Horizontal Pan Start');
            if (!this.iVideoPlayer || this.lock || this.coverVisible === Visibility.Visible) {
              return;
            }
            if (this.centerUiShowStatus === CenterUiShowStatus.LOADING) {
              this.isLoadNotSeek = true;
              return;
            }
            if (this.isVerticalDrag) {
              return;
            }
            if (event && event.fingerList && event.fingerList[0]) {
              this.stopProgressTask();
              this.isSeek = true;
              if (this.centerUiShowStatus === CenterUiShowStatus.PAUSE) {
                this.isPlaying = true;
              } else {
                this.isPlaying = false;
              }
              this.iVideoPlayer.pause();
              touchStartX = event.fingerList[0].localX;
              this.seekTimePosition = this.progressValue;
              logger.info('HorizontalSeekProgress event.onActionStart start x: ' + event.fingerList[0].localX +
                ' event.onActionStart start y: ' + event.fingerList[0].localY);
            }
          })
          .onActionUpdate((event: GestureEvent | undefined) => {
            if (!this.iVideoPlayer || this.lock || this.isLoadNotSeek || this.isVerticalDrag ||
              this.coverVisible === Visibility.Visible) {
              return;
            }
            this.isHorizontalDrag = true;
            if (event && event.fingerList && event.fingerList[0]) {
              this.showSeekProgressUi = true;
              const touchX = event.fingerList[0].localX;
              const deltaX = touchX - touchStartX;
              if (deltaX > 0) {
                this.seekDirectionRes = $r('app.media.danmaku_player_video_forward_icon');
              } else {
                this.seekDirectionRes = $r('app.media.danmaku_player_video_backward_icon');
              }
              let seekTimePosition: number =
                Number((this.progressValue + (deltaX * this.progressMaxValue / changeWidth)).toFixed(0));
              if (seekTimePosition < 0) {
                seekTimePosition = 0;
              }
              if (seekTimePosition > this.progressMaxValue) {
                seekTimePosition = this.progressMaxValue;
              }
              // 当前调整的进度
              this.seekCurrentTime = this.stringForTime(seekTimePosition);
              // 设置当前进度
              if (this.progressMaxValue !== 0) {
                this.seekTimePosition = seekTimePosition;
              }
            }
          })
          .onActionEnd((event: GestureEvent | undefined) => {
            if (!this.iVideoPlayer || this.coverVisible === Visibility.Visible) {
              return;
            }
            if (this.isLoadNotSeek) {
              this.isLoadNotSeek = false;
              return;
            }
            if (this.isHorizontalDrag && this.iVideoPlayer) {
              if (this.progressValue === this.seekTimePosition) {
                this.isSeek = false;
                return;
              }
              this.progressValue = this.seekTimePosition;
              this.currentTime = this.stringForTime(this.progressValue);
              if (this.isPlaying) {
                this.centerUiShowStatus = CenterUiShowStatus.LOADING;
                this.iVideoPlayer.seekTo(this.seekTimePosition);
              }
            }
            this.isHorizontalDrag = false;
            this.showSeekProgressUi = false;
          }),
        // 双击播放或暂停
        TapGesture({ count: 2 })
          .onAction((event: GestureEvent | undefined) => {
            if (this.lock || this.centerUiShowStatus === CenterUiShowStatus.LOADING || !this.iVideoPlayer) {
              return;
            }
            if (this.iVideoPlayer.xComponentId !== GlobalContext.getContext().getObject('xid')) {
              this.videoToPlay();
            } else {
              if (this.iVideoPlayer.isPlaying()) {
                this.videoToPause();
              } else {
                this.videoToPlay();
              }
            }
          })
      ))
    .onAreaChange((oldValue: Area, newValue: Area) => {
      if (newValue.width !== oldValue.width || newValue.height !== oldValue.height) {
        changeWidth = newValue.width as number;
        changeHeight = newValue.height as number;
        // 全屏时显示上方标题栏和锁定按钮
        if (this.curDirection === Directions.VERTICAL) {
          this.fullShowTop = true;
          this.fullShowLock = true;
          this.timeCountdown();
        }
        this.model.setWidth(vp2px(changeWidth));
        this.model.setHeight(vp2px(changeHeight) * (this.danmukuHeightSelectedIndexes[0] + 1) / 3);
        if (this.danmakuShow) {
          setTimeout(() => {
            if (!this.iVideoPlayer?.isPlaying()) {
              this.model.hide();
            }
            this.model.show();
          }, Constants.DANMU_RESIZE_TIME)
        }
      }
    })
    .backgroundColor(Color.Black)
  }

  /*
   * 注册的emitter消息回调,在list列表中,每个item都可以收到,只能通过xid区分当前是哪个才是真正的被点击的item
   * 去做相应的事件处理,不是当前选中的,一律变为初始状态(goInit)
   */
  private emitterInit() {
    emitter.on(videoPlayEvent, (data: emitter.EventData) => {
      if (data && data.data && typeof data.data.xid === 'string') {
        if (this.xComponentId === data.data.xid) {
          this.goPlaying();
        }
      }
    });
    emitter.on(videoInitEvent, (data: emitter.EventData) => {
      if (data && data.data && typeof data.data.xid === 'string') {
        if (this.xComponentId === data.data.xid) {
          this.goInit();
        }
      }
    });
    emitter.on(videoPauseEvent, (data: emitter.EventData) => {
      if (data && data.data && typeof data.data.xid === 'string') {
        if (this.xComponentId === data.data.xid) {
          if (GlobalContext.getContext().getObject('playType') === PlayerType.SYSTEM_AVPLAYER) {
            if (this.iVideoPlayer && this.iVideoPlayer.isPlaying()) {
              this.goPause();
            }
          } else {
            this.goPause();
          }
        }
      }
    })
  }

  /*
   * 监听播放
   */
  private goPlaying() {
    logger.info('standardGSYVideoPlayer onPlayingListener');
    this.coverVisible = Visibility.None;
    this.centerUiShowStatus = CenterUiShowStatus.PAUSE;
    this.timeCountdown();
    if (!this.iVideoPlayer) {
      return;
    }
    if (this.danmakuShow) {
      this.model.show();
    }
    if (this.isSeek) {
      this.model.seekTo(this.progressValue);
      this.isSeek = false;
    } else {
      // 视频从头开始播放时,重置弹幕到开始位置
      if (this.progressValue === 0) {
        this.model.seekTo(0);
      }
    }
    this.progressMaxValue = this.iVideoPlayer.getDuration();
    this.totalTime = this.stringForTime(this.progressMaxValue);
    this.stopProgressTask();
    this.startProgressTask();
    logger.info('standardGSYVideoPlayer onPlayingListener end.' + this.progressMaxValue);
  }

  /*
   * 监听初始化
   */
  private goInit() {
    logger.info('standardGSYVideoPlayer play finished');
    if (this.iVideoPlayer && GlobalContext.getContext().getObject('playType') === PlayerType.SYSTEM_AVPLAYER) {
      logger.info('PlayerType.SYSTEM_AVPLAYER need to stop');
      this.iVideoPlayer.stop();
    }
    this.model.pause();
    this.stopProgressTask();
    this.progressValue = this.progressMaxValue;
    this.currentTime = this.stringForTime(this.progressValue);
    setTimeout(() => {
      this.coverVisible = Visibility.Visible;
      this.centerUiShowStatus = CenterUiShowStatus.PLAY;
      this.isShowCenterUi = true;
      this.showBottomUi = false;
      this.fullShowTop = false;
      this.fullShowLock = false;
      this.progressValue = 0;
      this.currentTime = '00:00';
      this.lock = false;
    }, 0)
  }

  /*
   * 监听暂停
   */
  private goPause() {
    logger.info('standardGSYVideoPlayer onPauseListener');
    this.stopProgressTask();
    if (!this.isSeek) {
      this.centerUiShowStatus = CenterUiShowStatus.PLAY;
    }
    this.model.pause();
  }

  aboutToAppear() {
    this.emitterInit();
    this.currentBrightness =
      Number(settings.getValueSync(getContext(this), settings.display.SCREEN_BRIGHTNESS_STATUS,
        Constants.DEFAULT_BRIGHTNESS.toString()));
    setTimeout(() => {
      this.danmakuInit();
    }, 0)

  }

  /*
   * TODO: 知识点:初始化弹幕,设置弹幕相关参数
   */
  danmakuInit() {
    const maxLinesPair: Map<number, number> = new Map();
    // 滚动弹幕最大显示5行
    maxLinesPair.set(BaseDanmaku.TYPE_SCROLL_RL, Constants.MAX_DANMAKU_LINES);
    // 设置是否禁止重叠
    const overlappingEnablePair: Map<number, boolean> = new Map();
    overlappingEnablePair.set(BaseDanmaku.TYPE_SCROLL_RL, true);
    overlappingEnablePair.set(BaseDanmaku.TYPE_FIX_TOP, true);
    this.context = DanmakuContext.create();
    const danmakuStyle = Constants.DANMAKU_STYLE;
    this.context.setDanmakuStyle(DANMAKU_STYLE_STROKEN, danmakuStyle); // 设置描边样式
    this.context.setDuplicateMergingEnabled(false); // 设置是否启用合并重复弹幕
    this.context.setScrollSpeedFactor(Constants.DANMAKU_SCROLL_SPEED); // 设置滚动弹幕速率
    this.context.setScaleTextSize(Constants.DANMAKU_SCALE_TEXT_SIZE); // 设置缩放字体大小
    this.context.setCacheStuffer(new SpannedCacheStuffer(), this.cacheStufferAdapter); // 设置弹幕缓存填充器
    this.context.setMaximumLines(maxLinesPair).preventOverlapping(overlappingEnablePair); // 设置最大弹幕显示行数
    this.context.setDanmakuMargin(Constants.DANMAKU_MARGIN); // 设置弹幕显示外边距
    if (this.model === null) {
      return;
    }
    this.parser = this.createParser();
    this.model.setCallback(new Call(this));
    this.model.setOnDanmakuClickListener(new OnDanMaku(this));
    this.model.prepare(this.parser, this.context);
    this.model.showFPS(false);
  }

  aboutToDisappear() {
    logger.info('standardGSYVideoPlayer aboutToDisappear');
    this.emitterOff();
    this.stop();
    this.model.stop();
    this.model.release();
  }

  /*
   * 关闭事件监听
   */
  private emitterOff() {
    emitter.off(1);
    emitter.off(2);
    emitter.off(3);
  }

  /*
   * 停止播放
   */
  stop() {
    logger.info('standardGSYVideoPlayer stop');
    this.stopProgressTask();
    clearInterval(uiTime);
    if (!this.iVideoPlayer) {
      return;
    }
    this.iVideoPlayer.stop();
    this.iVideoPlayer.release();
  }

  /*
   * 时间格式化
   */
  private completionNum(num: number): string | number {
    if (num < getContext(this).resourceManager.getNumber($r('app.integer.danmaku_player_completion_time'))) {
      return '0' + num;
    } else {
      return num;
    }
  }

  /*
   * 获取时间字符串
   */
  private stringForTime(timeMs: number): string {
    const totalSeconds: number = Math.floor(timeMs / Constants.MS_IN_SECOND);
    let seconds: number = totalSeconds % Constants.SECOND_IN_MINUTE;
    let minutes: number = Math.floor((totalSeconds / Constants.SECOND_IN_MINUTE) % Constants.MINUTE_IN_HOUR);
    let hours: number = Math.floor(totalSeconds / Constants.SECOND_IN_HOUR);

    if (hours > 0) {
      return this.completionNum(hours) + ':' + this.completionNum(minutes) + ':' + this.completionNum(seconds);
    } else {
      return this.completionNum(minutes) + ':' + this.completionNum(seconds);
    }
  }

  private stopProgressTask() {
    clearInterval(updateProgressTimer);
  }

  private startProgressTask() {
    updateProgressTimer = setInterval(() => {
      this.setProgress();
    }, Constants.MS_IN_SECOND);
  }

  /*
   * 设置进度
   */
  private setProgress() {
    if (!this.iVideoPlayer) {
      return;
    }
    this.progressValue = this.iVideoPlayer.getCurrentPosition();
    this.currentTime = this.stringForTime(this.progressValue);
  }

  /*
   * 时间倒数
   */
  private timeCountdown() {
    clearInterval(uiTime);
    this.second = Constants.COUNT_DOWN_TIME;
    uiTime = setInterval(() => {
      if (this.second <= 0) {
        clearInterval(uiTime);
        this.showBottomUi = false;
        this.fullShowLock = false;
        this.fullShowTop = false;
        if (this.centerUiShowStatus === CenterUiShowStatus.PAUSE) {
          this.isShowCenterUi = false;
        }
      } else {
        this.second--;
      }
    }, Constants.MS_IN_SECOND);
  }
}

class Call implements Callback {
  private that: ESObject;

  constructor(that: ESObject) {
    this.that = that;
  }

  public updateTimer(timer: DanmakuTimer): void {
  }

  public drawingFinished(): void {

  }

  public danmakuShown(danmaku: BaseDanmaku): void {
  }

  public prepared(): void {
  }
}

class OnDanMaku implements OnDanmakuClickListener {
  private that: ESObject;

  constructor(that: ESObject) {
    this.that = that;
  }

  /*
   * 弹幕点击逻辑
   */
  onDanmakuClick(danmakus: IDanmakus): boolean {
    console.log('DFM onDanmakuClick: danmakus size:' + danmakus.size());
    const latest: BaseDanmaku = danmakus.last();
    if (null !== latest) {
      console.log('DFM onDanmakuClick: text of latest danmaku:' + latest.text);
      return true;
    }
    return false;
  };

  /*
   * 弹幕长按逻辑
   */
  onDanmakuLongClick(danmakus: IDanmakus): boolean {
    return false;
  };

  /*
   * 点击视窗逻辑
   */
  onViewClick(view: IDanmakuView): boolean {
    this.that.isVisible = true;
    return false;
  };
}

class Pro extends Proxy {
  public prepareDrawing(danmaku: BaseDanmaku, fromWorkerThread: boolean): void {
  }

  public releaseResource(danmaku: BaseDanmaku): void {

    // TODO:知识点:重要:清理含有ImageSpan的text中的一些占用内存的资源 例如drawable
  }
}