DevEco Studio版本:4.0.0.600
File-->New-->Module,选择Static Library
PlayControl:视频播放控制类
PlayProgress:视频播放器进度条
VideoConstant:视频播放状态配置类
VideoPlayer:视频播放器管理类
然后在VideoLibrary的Index.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
参考链接:静态har共享包
在Entry目录下的oh-package.json5文件中添加对VideoLibaray的引用,详细操作参考之前文章。
"dependencies": {
"@app/videoLibrary": "file:../VideoLibrary"
}
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 视频播放