HarmonyOS 鸿蒙Next 分享一个封装画中画视频组件

发布于 1周前 作者 itying888 来自 鸿蒙OS

HarmonyOS 鸿蒙Next 分享一个封装画中画视频组件

import { media } from ‘@kit.MediaKit’
import { PiPWindow } from ‘@kit.ArkUI’

@Entry @Component export struct XComponentCase { // 视频源 mediaUrl: string = https://video19.ifeng.com/video09/2024/05/23/p7199260608686989961-0-122707.mp4 // 自动播放 autoPlay: boolean = false // 自动浮窗 autoPip: boolean = true // avplayer控制器 private videoPlayer?: media.AVPlayer // 视频时长 @State private duration: number = 0 // 播放时长 @State private time: number = 0 // 播放器状态 @State private playState: media.AVPlayerState = ‘idle’ // 是否画中画 @State pipWindow: boolean = false // 控制栏位置 @State private sliderY: string | number = 0 // 防抖定时器 private timer: number = -1 // XComponent控制器 private controller: XComponentController = new XComponentController() // 画中画控制器 private pipController?: PiPWindow.PiPController

// 进度条的展示隐藏 changeSlider() { // 如果显示,防抖倒计时关闭 // 否则先显示,倒计时关闭 if (this.sliderY === 0) { clearInterval(this.timer) this.timer = setTimeout(() => { this.sliderY = ‘100%’ }, 3000) } else { this.sliderY = 0 this.timer = setTimeout(() => { this.sliderY = ‘100%’ }, 3000) } }

// 切换浮窗 async changePipWindow() { this.pipWindow = !this.pipWindow if (!this.pipController) { return } if (this.pipWindow) { await this.pipController.startPiP() } else { await this.pipController.stopPiP() }

<span class="hljs-keyword">this</span>.changeSlider()

}

// 时长数字(ms)转字符串 number2time(number: number) { const ms: number = number % 1000 const second = (number - ms) / 1000 const s: number = second % 60 if (second > 60) { const m: number = (second - s) / 60 % 60 return m.toString().padStart(2, ‘0’) + ‘:’ + s.toString().padStart(2, ‘0’) } return ‘00:’ + s.toString().padStart(2, ‘0’) }

// 初始化 async init(){ this.videoPlayer = await media.createAVPlayer() this.videoPlayer.url = this.mediaUrl this.videoPlayer.on(“stateChange”, async (state) => { if (state === ‘initialized’) { if (this.videoPlayer) { // 只有设置了src才能给播放器设置输出源的id否则无效 this.videoPlayer.surfaceId = this.controller.getXComponentSurfaceId() this.videoPlayer.prepare() } } else if (state === ‘prepared’) { if (this.videoPlayer && this.autoPlay) { this.videoPlayer.play() } } else if (state === ‘completed’) { if (this.videoPlayer) { this.videoPlayer.play() } } this.playState = state }) this.videoPlayer.on(“timeUpdate”, time => { this.time = time }) this.videoPlayer.on(“durationUpdate”, duration => { this.duration = duration }) PiPWindow.create({ context: getContext(this), componentController: this.controller, templateType: PiPWindow.PiPTemplateType.VIDEO_PLAY, // 对于视频通话、视频会议等场景,需要设置相应的模板类型 }).then(control => { this.pipController = control this.pipController.on(“stateChange”,(state)=>{ if(state===PiPWindow.PiPState.STARTED){ this.pipWindow = true }else if(state === PiPWindow.PiPState.STOPPED){ this.pipWindow = false } }) // 自动开启 control.setAutoStartEnabled(this.autoPip) }) } build() { Column() { Row() { Row() { XComponent({ id: ‘xComponent’, type: XComponentType.SURFACE, controller: this.controller }) .onLoad(() => { // 初始化 this.init() }) .id(‘xComponent’) .renderFit(RenderFit.RESIZE_CONTAIN) .onClick(() => { this.changeSlider() }) // 进度条 Row({ space: 12 }) { Image(this.playState === ‘playing’ ? $r(‘sys.media.ohos_ic_public_pause’) : $r(‘sys.media.ohos_ic_public_play’)) .width(20) .aspectRatio(1) .fillColor(Color.White) .onClick(() => { this.playState === ‘playing’ ? this.videoPlayer?.pause() : this.videoPlayer?.play() this.changeSlider() }) Text(this.number2time(this.time)) .fontSize(12) .fontColor(Color.White) Slider({ value: this.time, min: 0, max: this.duration }) .layoutWeight(1) .blockColor(Color.White) .selectedColor(Color.White) .trackColor(’#ccc5c5c5’) .trackThickness(2) .onChange((val) => { this.videoPlayer?.seek(val, 2) this.changeSlider() }) Text(this.number2time(this.duration)) .fontSize(12) .fontColor(Color.White) Image($r(‘sys.media.arrowshape_3_triangle_path’)) .width(20) .aspectRatio(1) .fillColor(Color.White) .rotate({ angle: this.pipWindow ? 0 : 180 }) .animation({ duration: 300 }) .onClick(() => { this.changePipWindow() }) } .width(‘100%’) .padding({ left: 12, right: 12 }) .linearGradient({ direction: GradientDirection.Bottom, colors: [[’#33000000’, 0], [’#77000000’, 0.15], [’#aa000000’, 0.75], [’#aa000000’, 1]] }) .position({ bottom: 0 }) .translate({ y: this.sliderY }) .animation({ duration: 300 }) } .width(‘100%’) .height(‘100%’) .clip(true) }.width(‘100%’) } .height(‘100%’) .width(‘100%’) } }<button style="position: absolute; padding: 4px 8px 0px; cursor: pointer; top: 8px; right: 8px; font-size: 14px;">复制</button>

基于xcomponent+avplayer封装的视频组件,支持画中画格式,有视频开发需求的小伙伴可以做个参考~

cke_144204.png


更多关于HarmonyOS 鸿蒙Next 分享一个封装画中画视频组件的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS 鸿蒙Next 分享一个封装画中画视频组件的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS 鸿蒙Next中,封装画中画(Picture-in-Picture,PiP)视频组件涉及使用系统的多媒体框架和UI布局技术。以下是一个简要的封装过程:

  1. 创建PiP服务: 定义一个PiP服务,该服务负责处理视频的播放和PiP窗口的管理。在服务的onCreate方法中,初始化媒体播放器并设置视频源。

  2. 实现PiP窗口: 使用鸿蒙提供的窗口管理API创建并配置PiP窗口。设置窗口大小、位置以及视频内容的显示。

  3. UI布局: 在应用的布局文件中,定义一个容器用于放置PiP窗口。确保容器可以响应触摸事件,以便用户可以拖动或调整PiP窗口的位置和大小。

  4. 生命周期管理: 在应用的生命周期回调中,管理PiP服务的启动和停止。确保在应用进入后台或用户离开当前页面时,PiP窗口能够正确显示或隐藏。

  5. 事件处理: 处理用户与PiP窗口的交互,如拖动、缩放以及播放控制(播放/暂停、音量调节等)。

  6. 权限管理: 确保应用具有必要的权限,如悬浮窗权限、多媒体播放权限等。

通过上述步骤,可以在HarmonyOS 鸿蒙Next中封装一个功能完善的画中画视频组件。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部