音视频_V2

//1.定义接口数据

export interface SongItemType{

  img:string

  name:string

  author:string

  url:string

  id:string

}

//2.定义类型

import { SongItemType } from "./music"

@ObservedV2

export class GlobalMusic {

  @Trace "name": string = ''

  @Trace "img": string = ""

  @Trace "url": string = ''

  @Trace "time": number = 0

  @Trace "duration": number = 0

  //歌曲列表

  @Trace playIndex: number = 0

  @Trace playList: SongItemType[] = []

  //播放和暂停

  @Trace isPlay: boolean = false

}

//3定义工具类

import { media } from '@kit.MediaKit';

import { PlayStatus } from './PlayStatus';

import { BusinessError } from '@kit.BasicServicesKit';

import { VideoListener } from './VideoListener';

import { GlobalMusic } from '../models/GlobalMusic';

import { AppStorageV2 } from '@kit.ArkUI';

import { SongItemType } from '../models/music';

export class AVPlayerUtil {

  private durationTime: number = 0

  private currentTime: number = 0

  private surfaceId: string = '';

  private avPlayer: media.AVPlayer | undefined = undefined;

  //共享资源  修改

  currentSong: GlobalMusic = AppStorageV2.connect(GlobalMusic, 'SONG_KEY', () => new GlobalMusic())!

  private aspectCallBack: Function = () => {

  };

  setSurfaceId(surfaceId: string) {

    this.surfaceId = surfaceId;

  }

  setAVPlayerCallback(avPlayer: media.AVPlayer) {

    avPlayer.on('stateChange', async (state: string) => {

      switch (state) {

        case 'idle':

          avPlayer.release();

          break;

        case 'initialized':

          avPlayer.surfaceId = this.surfaceId;

          avPlayer.prepare();

          break;

        case 'prepared':

          console.log('----------  this.durationTime--', this.durationTime)

          avPlayer.play();

          this.currentSong.isPlay = true

          break;

        case 'playing':

          // this.videoListener?.onPlayStatus(PlayStatus.PLAY)

          break;

        case 'paused':

          // this.videoListener?.onPlayStatus(PlayStatus.PAUSE)

          break;

        case 'completed':

          avPlayer.pause()

          // this.videoListener?.onPlayStatus(PlayStatus.COMPLETE)

          break;

        case 'stopped':

          avPlayer.reset();

          break;

        case 'released':

          break;

        default:

          break;

      }

    })

    // 监听seek生效的事件

    this.avPlayer!.on('seekDone', (seekDoneTime: number) => {

      console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);

      avPlayer.play()

      this.currentSong.isPlay = true

    })

    // error回调监听函数,当avPlayer在操作过程中出现错误时调用 reset接口触发重置流程

    this.avPlayer!.on('error', (err: BusinessError) => {

      console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);

      avPlayer.reset(); // 调用reset重置资源,触发idle状态

    })

    // 用于进度条,监听进度条长度,刷新资源时长

    avPlayer.on('durationUpdate', (duration: number) => {

      console.info('-----AVPlayer state durationUpdate called. current time: ', duration);

      // 获取视频总时长

      this.currentSong.duration = duration

      // this.durationTime=duration

    })

    // 用于进度条,监听进度条当前位置,刷新当前时间

    avPlayer.on('timeUpdate', (time) => {

      console.info('---------AVPlayer state timeUpdate called. current time: ', time);

      // 获取当前播放时长

      this.currentSong.time = time

      // this.currentTime=time

    })

    // get video height and width

    avPlayer.on('videoSizeChange', (width: number, height: number) => {

      this.aspectCallBack(height / width);

    })

  }

  /**

  * 初始化* @param url

* @param callBack

*/

  async initPlayer(url: string, callBack: Function) {

    this.avPlayer = await media.createAVPlayer();

    this.aspectCallBack = callBack;

    this.setAVPlayerCallback(this.avPlayer);

    this.avPlayer.url = url

  }

  /**

  * 切换视频*/

  playChange(url: string) {

    if (this.avPlayer) {

      this.avPlayer.stop()

      this.avPlayer.release()

      this.initPlayer(url, this.aspectCallBack)

    }

}

  /**

  * 跳转播放*/

  seek(timeMs: number) {

    if (this.avPlayer) {

      this.avPlayer.seek(timeMs)

    }

}

  /**

  * 暂停播放*/

  pause() {

    if (this.avPlayer) {

      this.avPlayer.pause()

      this.currentSong.isPlay = false

    }

}

  /**

  * 播放*

  * 设置资源*/

  play(url: string) {

    if (this.avPlayer) {

      this.avPlayer.url = url

      this.currentSong.img = ''

      this.avPlayer.play()

      this.currentSong.isPlay = true

    }

}

  /**

  * 播放*/

// singPlay(song: SongItemType) {

//  this.avPlayer!.url = song.url

//  this.currentSong.img = song.img

//

// }

  singPlay2(song: SongItemType) {

    const inList = this.currentSong.playList.some(item => item.id === song.id)

    if (inList) {

      if (song.url === this.currentSong.url) {

        this.avPlayer?.play()

        this.currentSong.isPlay = true

      } else {

        //设置新的索引

        this.currentSong.playIndex = this.currentSong.playList.findIndex(item => item.id === song.id)

        //      切歌

        this.chSong()

      }

    } else {

      // add

      this.currentSong.playList.unshift(song)

      this.currentSong.playIndex = 0

      //      切歌

      this.chSong()

    }

}

  //

  async chSong() {

    await this.avPlayer?.reset()

    this.currentSong.duration = 0

    this.currentSong.time = 0

    this.currentSong.img = this.currentSong.playList[this.currentSong.playIndex].img

    this.currentSong.url = this.currentSong.playList[this.currentSong.playIndex].url

    this.currentSong.name = this.currentSong.playList[this.currentSong.playIndex].name

    this.avPlayer!.url = this.currentSong.url

  }

  /**

  * 停止播放*/

  stop() {

    if (this.avPlayer) {

      this.avPlayer.stop()

      this.avPlayer.release()

    }

}

  /**

  * 上一首* @param listener

*/

  prevPlay() {

    this.currentSong.playIndex--

    if (this.currentSong.playIndex < 0) {

      //去最后一首

      this.currentSong.playIndex = this.currentSong.playList.length - 1

    }

    this.singPlay2(this.currentSong.playList[this.currentSong.playIndex])

  }

  /**

  * 下一首* @param listener

*/

  nextPlay() {

    this.currentSong.playIndex++

    if (this.currentSong.playIndex >= this.currentSong.playList.length) {

      //去第一一首

      this.currentSong.playIndex = 0

    }

    this.singPlay2(this.currentSong.playList[this.currentSong.playIndex])

  }

  setListener(listener: VideoListener) {

    // this.videoListener = listener;

  }

  getDurationTime(): number {

    return this.durationTime;

  }

  getCurrentTime(): number {

    return this.currentTime;

  }

}

//5页面布局

import { media } from '@kit.MediaKit';

import { AppStorageV2, display } from '@kit.ArkUI';

import { BusinessError } from '@kit.BasicServicesKit';

import { AVPlayerUtil } from '../video/AVPlayerUtil';

import { PlayStatus } from '../video/PlayStatus';

import { timeConvert } from '../utils/TimeUtils';

import { VideoListener } from '../video/VideoListener';

import { SongItemType } from '../models/music';

import { GlobalMusic } from '../models/GlobalMusic';

@Entry

@ComponentV2

struct VideoPlayer {

  pathStack: NavPathStack = AppStorageV2.connect(NavPathStack, 'navStack', () => new NavPathStack())!

  // XComponent控制器

  // AVPlayer实例

  private avPlayer: media.AVPlayer | null = null;

  // 播放表面ID

  private surfaceID: string = '';

  // 播放状态相关变量

  @Local isPlaying: boolean = false;

  @Local currentTime: string = '11';

  @Local durationTime: string = '222';

  @Local progressValue: number = 0;

  private player?: AVPlayerUtil

  xComponentController: XComponentController = new XComponentController()

  @Local videoUrl: string = 'https://www.w3schools.com/html/movie.mp4'

  @Local timeFormat: string = '00:00'

  @Local duration: number = 0

  @Local time: number = 0

  @Local durationFormat: string = '00:00'

  @Local playStatus: PlayStatus = PlayStatus.INIT;

  songs: SongItemType[] = [{

    img: "http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimacloudMusic/0.jpg",

    name: '直到世界的尽头',

    author: "www",

    url: "http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/0.m4a",

    id: '0000'

  },

    {

      img: "http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimacloudMusic/1.jpg",

      name: '直到世界的尽头',

      author: "www",

      url: "http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/2.m4a",

      id: '0000'

    }

  ]

  // @Local playState:SongItemType=this.songs[0]

  @Local playState: GlobalMusic = AppStorageV2.connect(GlobalMusic, 'SONG_KEY', () => new GlobalMusic())!

  // @Local

// playState:

  // 数据共享变量

  // 数据共享变量@Provide videoData: { url: string, title: string } = {

//    url: 'http://example.com/video.mp4',

  //    title: '示例视频'

//  }

  number2time(number: number) {

}

  build() {

    // Navigation(this.pathStack) {

    Column() {

      Text(this.playState.img)

      // 视频显示区域

      XComponent({

        id: 'videoSurface',

        type: 'surface',

        controller: this.xComponentController

      }).height(200)

        .onLoad(() => {

          // this.initPlayer();

          this.player = new AVPlayerUtil();

          this.player.setSurfaceId(this.xComponentController.getXComponentSurfaceId());

          this.player.initPlayer(this.videoUrl, (aspect: number) => {

          })

        })

      // 控制面板

      Column() {

        Button(this.playState.isPlay ? '暂停' : '播放').onClick(() => {

          this.playState.isPlay ? this.avPlayer?.pause() :

          this.player?.singPlay2(this.playState.playList[this.playState.playIndex])

        })

        // .onClick(() => this.togglePlay())

        Flex({ justifyContent: FlexAlign.SpaceAround, alignItems: ItemAlign.Center }) {

          Row() {

            Text(this.formatDuration(this.playState.time))

              .fontWeight(400)

              .fontColor(Color.Red)

              .fontSize(14)

          }

          .width(100)

          .justifyContent(FlexAlign.End)

          Slider({

            value: this.playState.time,

            step: 0.1,

            min: 0,

            max: this.playState.duration,

            style: SliderStyle.OutSet

          })

            .margin({ left: 10, right: 10 })// .width('90%')

            .trackColor('rgba(0,0,0,0.1)')// 滑轨背景颜色

            .selectedColor('#3a8074')// 滑轨的已滑动部分颜色

            .showSteps(false)// 是否显示步长刻度

            // .showTips(true)

            .blockSize({ width: 12, height: 12 })// 滑块大小

            .blockColor('#E48E13')// 滑块颜色

            .trackThickness(3)// 滑轨粗细

            .trackBorderRadius(2)// 底板圆角半径

            .selectedBorderRadius(2)// 已滑动部分圆角半径

            // .margin({ bottom: 30 })

            .onChange((value: number, mode: SliderChangeMode) => {

              this.player?.seek(value);

              // if (mode == SliderChangeMode.Begin) {

//  // this.isSwiping = true;

//  this.player?.pause();

// }

// this.player?.seek(value);

// // this.currentTime = value;

// if (mode == SliderChangeMode.End) {

//  // this.isSwiping = false;

//  // this.flag = true;

//  this.player?.play('');

// }

            })

          Row() {

            Text(this.formatDuration(this.playState.duration))

              .fontWeight(400)

              .fontColor(Color.Red)

              .fontSize(14)

          }

          .width(100)

          .justifyContent(FlexAlign.Start)

        }

        //  Slider({

//    value: this.progressValue,

//    min: 0,

//    max: 100

//  })

//    .onChange((value: number) => {

//      this.seekVideo(value);

//    })

// }

      }

}

    // }.onAppear(() => {

//  this.pathStack.pushPathByName("Stark", null, null)

// }).hideNavBar(true)

//

  }

  /**

  * 视频时长转换,格式:totalFormat true: 00:00:00;false:00:00(如果超过60分钟,格式为 "hh:mm:ss")* @param milliseconds

* @returns

*/

  formatDuration(milliseconds: number, totalFormat: boolean = false): string {

    const totalSeconds = Math.floor(milliseconds / 1000); // 将毫秒转换为秒

    const hrs = Math.floor(totalSeconds / 3600);

    const mins = Math.floor((totalSeconds % 3600) / 60);

    const secs = totalSeconds % 60;

    if (!totalFormat) {

      // 如果超过60分钟,格式为"hh:mm:ss"

      if (hrs > 0) {

        const formattedHrs = String(hrs).padStart(2, '0');

        const formattedMins = String(mins).padStart(2, '0');

        const formattedSecs = String(secs).padStart(2, '0');

        return `${formattedHrs}:${formattedMins}:${formattedSecs}`;

      }

      // 如果少于60分钟,格式为"mm:ss"

      const formattedMins = String(mins).padStart(2, '0');

      const formattedSecs = String(secs).padStart(2, '0');

      return `${formattedMins}:${formattedSecs}`;

    } else {

      // 使用padStart来确保每个单位是两位数

      const formattedHrs = String(hrs).padStart(2, '0');

      const formattedMins = String(mins).padStart(2, '0');

      const formattedSecs = String(secs).padStart(2, '0');

      return `${formattedHrs}:${formattedMins}:${formattedSecs}`;

    }

}

}

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容