鸿蒙自定义Video播放器

发布时间:2024年01月24日

前言

DevEco Studio版本:4.0.0.600

使用效果

如何使用

参考文档:OpenHarmony Video使用说明

1、module创建

File-->New-->Module,选择Static Library

2、相关类创建

PlayControl:视频播放控制类

PlayProgress:视频播放器进度条

VideoConstant:视频播放状态配置类

VideoPlayer:视频播放器管理类

然后在VideoLibraryIndex.ets类中添加对外输出的引用

export { VideoPlayer } from './src/main/ets/VideoPlayer'

PlayControl类:

import { VideoConstant } from './VideoConstant';

/**
 * 播放控制器
 */
@Component
export struct PlayControl {
   private playVideoModel?: VideoController;
   @Link videoStatus: number

   build() {
      Row() {
         Image(this.videoStatus == VideoConstant.STATUS_START ? $r('app.media.start_press') : $r('app.media.video_play_press'))
            .width('92px')
            .height('92px')
            .margin({ left: '156px', right: '156px' })
            .onClick(() => {
               if (this.videoStatus == VideoConstant.STATUS_START) {
                  this.videoStatus = VideoConstant.STATUS_PAUSE
                  if (this.playVideoModel != undefined) {
                     this.playVideoModel.pause()
                  }
               } else {
                  this.videoStatus = VideoConstant.STATUS_START
                  if (this.playVideoModel != undefined) {
                     this.playVideoModel.start()
                  }
               }
            })
      }
   }
}

PlayProgress类:

/**
 * 播放进度条
 */
@Component
export struct PlayProgress {
   @Prop currentTime: number
   @Prop totalTime: number
   private playVideoModel?: VideoController;

   build() {
      Row() {
         Text(this.formatTime(this.currentTime))
            .fontSize('14px')
            .fontColor(Color.White)
            .margin({ left: '16px', right: '12px' })
         Slider({
            value: this.currentTime, //当前进度值
            max: this.totalTime, //最大值,默认值100
            step: 1, //设置Slider滑动步长
            style: SliderStyle.OutSet //设置Slider的滑块与滑轨显示样式。
         })
            .blockColor(Color.White)//设置滑块的颜色。
            .trackColor($r('app.color.track_color'))//设置滑轨的背景颜色
            .selectedColor(Color.White)//设置滑轨的已滑动部分颜色
            .trackThickness(2)//设置滑轨的粗细
            .layoutWeight(1)
            .height('60px')
            .onChange((value: number, mode: SliderChangeMode) => {
               if (this.playVideoModel != undefined) {
                  this.playVideoModel.setCurrentTime(value, SeekMode.Accurate) // 精准跳转到视频的10s位置
               }
            })
         Text(this.formatTime(this.totalTime))
            .fontSize('14px')
            .fontColor(Color.White)
            .margin({ left: '12px', right: '16px' })
      }
      .width('100%')
      .backgroundColor('#88000000')
   }

   /**
    * 格式化时间
    */
   private formatTime(time: number): string {
      if (time < 60) {
         if (time > 9) {
            return '00:' + time
         } else {
            return '00:0' + time
         }
      } else {
         let timeStr = ''
         let hours = Number.parseInt((time / 60).toString())
         let seconds = time % 60
         if (hours > 9) {
            timeStr = hours.toString()
         } else {
            timeStr = '0' + hours
         }
         if (seconds > 9) {
            timeStr = timeStr + ':' + seconds
         } else {
            timeStr = timeStr + ':0' + seconds
         }
         return timeStr
      }
   }
}

VideoConstant类:

export class VideoConstant {
   /**
    * 开始播放
    */
   static readonly STATUS_START: number = 1;

   /**
    * 暂停播放
    */
   static readonly STATUS_PAUSE: number = 2;

   /**
    * 停止播放
    */
   static readonly STATUS_STOP: number = 3;
}

VideoPlayer类:

import router from '@ohos.router'
import { PlayControl } from './video/PlayControl'
import { PlayProgress } from './video/PlayProgress'
import { VideoConstant } from './video/VideoConstant'

@Preview
@Component
export struct VideoPlayer {
   private videoWidth: Length = '1024px'
   private videoHeight: Length = '550px'
   //视频未播放时的预览图片路径,默认不显示图片
   private previewUris?: Resource
   //视频播放源的路径,支持本地视频路径和网络路径
   @Prop innerResource: Resource
   //设置是否循环播放
   private isLoop: boolean = false
   //设置是否静音
   private isMuted: boolean = false
   //设置是否自动播放
   private isAutoPlay: boolean = true
   //视频控制器,可以控制视频的播放状态
   private controller: VideoController = new VideoController()
   //是否显示控制视图
   @State isShowController: boolean = true
   //视频播放状态
   @State videoStatus: number = VideoConstant.STATUS_STOP
   //视频时长,单位:秒(S)
   @State duration: number = 0
   //视频播放当前时间,单位:秒(S)
   @State currentTime: number = 0

   build() {
      Stack() {
         Video({
            src: this.innerResource,
            previewUri: this.previewUris,
            controller: this.controller
         })
            .muted(this.isMuted)//设置是否静音
            .loop(this.isLoop)//设置是否循环播放
            .autoPlay(this.isAutoPlay)//设置是否自动播放
            .controls(false)//设置是否显示默认控制条
            .objectFit(ImageFit.Contain)//设置视频适配模式
            .width('100%')
            .height('100%')
            .onTouch((event) => {
               if (event.type == TouchType.Up) {
                  this.isShowController = true
               }
            })
            .onStart(() => { //播放时触发该事件。
               console.info('VideoCreateComponent---   onStart')
               this.videoStatus = VideoConstant.STATUS_START
            })
            .onPause(() => { //播放时触发该事件。
               console.info('VideoCreateComponent---   onPause')
               this.videoStatus = VideoConstant.STATUS_PAUSE
            })
            .onFinish(() => { //播放结束时触发该事件。
               console.info('VideoCreateComponent---   onFinish')
               this.videoStatus = VideoConstant.STATUS_STOP
            })
            .onError(() => { //播放失败时触发该事件。
               console.info('VideoCreateComponent---   onError')
            })
            .onPrepared((e) => { //视频准备完成时触发该事件,通过duration可以获取视频时长,单位为秒(s)
               console.info('VideoCreateComponent---   onPrepared is ' + e.duration)
               this.duration = e.duration
            })
            .onSeeking((e) => { //操作进度条过程时上报时间信息,单位为s。
               console.info('VideoCreateComponent---   onSeeking is ' + e.time)
            })
            .onSeeked((e) => { //操作进度条完成后,上报播放时间信息,单位为s
               console.info('VideoCreateComponent---   onSeeked is ' + e.time)
            })
            .onUpdate((e) => { //播放进度变化时触发该事件,单位为s。
               console.info('VideoCreateComponent---   onUpdate is ' + e.time)
               this.currentTime = e.time
            })

         RelativeContainer() {
            Image($r('app.media.video_back'))
               .width('48px')
               .height('48px')
               .alignRules({
                  top: { anchor: '__container__', align: VerticalAlign.Top },
                  left: { anchor: '__container__', align: HorizontalAlign.Start }
               })
               .margin({ left: '48px', top: '24px' })
               .id('imageBack')
               .onClick(() => {
                  router.back()
               })

            PlayControl({ playVideoModel: this.controller, videoStatus: $videoStatus })
               .id('playControl')
               .alignRules({
                  middle: { anchor: '__container__', align: HorizontalAlign.Center },
                  center: { anchor: '__container__', align: VerticalAlign.Center }
               })

            PlayProgress({ playVideoModel: this.controller, totalTime: this.duration, currentTime: this.currentTime })
               .id('playProgress')
               .alignRules({
                  middle: { anchor: '__container__', align: HorizontalAlign.Center },
                  bottom: { anchor: '__container__', align: VerticalAlign.Bottom }
               })

         }.width('100%')
         .height('100%')
         .visibility(this.isShowController ? Visibility.Visible : Visibility.None)
         .onTouch((event) => {
            if (event.type == TouchType.Up) {
               this.isShowController = false
            }
         })
      }
      .width(this.videoWidth)
      .height(this.videoHeight)
   }
}

资源引用:

start_press.png

video_back.png

video_play_press.png

3、在Entry中引用HttpLibaray

参考链接:静态har共享包

Entry目录下的oh-package.json5文件中添加对VideoLibaray的引用,详细操作参考之前文章。

"dependencies": {
  "@app/videoLibrary": "file:../VideoLibrary"
}

4、代码调用

import { VideoPlayer } from "@app/videoLibrary"

@Entry
@Component
struct Index {
   build() {
      Stack() {
         VideoPlayer({
            // previewUris: $r('app.media.preview'), //未播放时的封面
            innerResource: $rawfile('adv_test_video.mp4')
         })
      }
      .width('100%')
      .height('100%')
   }
}
属性是否必须描述
videoWidth非必须视频播放器宽度
videoHeight非必须视频播放器高度
previewUris非必须视频未播放时的预览图片路径,默认不显示图片
innerResource必须视频播放源的路径,支持本地视频路径和网络路径
isLoop非必须设置是否循环播放
isMuted非必须设置是否静音
isAutoPlay非必须设置是否自动播放

因为我们的视频是横屏播放的,所以需要配置下界面横屏显示

可以参考之前的文章OpenHarmony 实现屏幕横竖屏_openharmony 屏幕旋转-CSDN博客

横屏配置:在Entry目录下src/main/module.json5中的abilities添加orientation属性

其实鸿蒙官方提供了两种方式播放视频:Video组件、AVPlayer,上面的示例是以Video组件进行封装的,感兴趣的同学可以尝试用AVPlayer进行封装下。

我在尝试用AVPlayer进行视频播放时发现,其seek方法定位不准,大家在调用时也可以关注下这个问题。如果你这边解决了,可以在下方留言说明下如何解决的,感谢!!!

参考文档OpenHarmony 视频播放

文章来源:https://blog.csdn.net/Abner_Crazy/article/details/135814363
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。