橄榄球世界杯_1990世界杯阿根廷 - liuweiqing95511.com

橄榄球世界杯_1990世界杯阿根廷 - liuweiqing95511.com

基于AVPlayer播放

Home 2025-05-13 16:13:18 基于AVPlayer播放

基于AVPlayer播放

基于AVPlayer视频播放 前言 AVPlayer:功能较完善的音视频播放Ark/JS API,集成了流媒体和本地资源解析,媒体资源解封装,视频解码和渲染功能,适

  • admin 男篮世界杯名单
  • 2025-05-13 16:13:18

基于AVPlayer视频播放

前言

AVPlayer:功能较完善的音视频播放Ark/JS API,集成了流媒体和本地资源解析,媒体资源解封装,视频解码和渲染功能,适用于媒体资源进行端到端播放的场景,可直接播放MP4,mkv等格式的视频文件在进行应用应用开发过程中,开发者可以通过AVPlayer的state属性主动获取当前状态或者使用on(‘stateChange’)方法监听变化。

开发步骤

调用createAVPlayer() 创建AVPlayer实例,初始化进入idle状态设置业务需要的监听事件

stateChange:监听播放器state属性变化videoSizeChange:用于视频播放,监听视频播放的宽高信息,可用于调整窗口大小,比例 设置资源:设置url,AVPayer进入initialized状态设置窗口:获取并设置属性&&,用于设置显示画面【从XComponent组件中获取surfaceID】准备播放:调用prepare( ),AVPlayer进入prepared状态视频播放控制:播放play( ) 暂停pause( ) 跳转seek( ) 停止stop等操作(可选)更换资源:调用reset( )重置资源,AVPlayer重新进入idle状态,允许更换资源url退出播放:调用release( )销毁实例,AVPayer进入released状态,退出播放

样例

utils

import { media } from "@kit.MediaKit"

import { common } from "@kit.AbilityKit"

//视频横竖屏切换的播放工具类

export class AVPlayerUtil{

//窗口id 为将来画布渲染视频做准备

private surfaceId:string = ''

//为媒体源提供播放API

private avPlayer: media.AVPlayer | undefined = undefined

setSurfaceId(surfaceId:string){

this.surfaceId = surfaceId

}

//用于在屏幕宽高改变时获取比值

private callBack:Function = () => {}

//设置播放器状态机

setAVPlayCallback(avPlayer:media.AVPlayer){

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

switch (state){

case 'idle':

console.log(`播放状态为空闲`)

avPlayer.release()

break

case 'initialized':

console.log(`播放状态为初始化`)

//将变量中的id给予给视频播放窗口id属性

//先给窗口id在进入准备状态

avPlayer.surfaceId = this.surfaceId

avPlayer.prepare()

break

case 'prepared':

console.log(`播放状态为准备`)

//播放

avPlayer.play()

break

case 'playing':

console.log(`播放状态为播放中`)

break

case 'paused':

console.log(`播放状态为暂停`)

break

case 'completed':

console.log(`播放状态为完成播放`)

//再次播放

avPlayer.play()

break

case 'stopped':

console.log(`播放状态为完成停止`)

//重置播放状态

avPlayer.reset()

break

case 'released':

console.log(`播放状态为释放`)

break

default:

break

}

})

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

console.log(`>>>>视频宽高被改变,宽为${width},高为${height}`)

//将变化后的宽高比值保存住

this.callBack(height/width)

})

}

//初始化播放器

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

this.avPlayer = await media.createAVPlayer()

this.callBack = callBack

//调用播放器状态函数,并传入状态实例

this.setAVPlayCallback(this.avPlayer)

//获取context

let context = getContext(this) as common.UIAbilityContext

//文件描述符, 用户获取rawfile/resource 目录下对应文件的信息

let fileDescriptor = await context.resourceManager.getRawFd(url)

//视频文件信息

let avFileDescriptor: media.AVFileDescriptor = {

//fd:资源句柄 通过resourceManager.getRawFd获取

fd:fileDescriptor.fd,

//资源偏移量

offset:fileDescriptor.offset,

//资源长度

length:fileDescriptor.length

}

//fdSrc 本地文件路径 此刻如果媒体资源路径正确,则状态机直接由闲置转为初始化

this.avPlayer.fdSrc = avFileDescriptor

console.log(`文件路径:${JSON.stringify(avFileDescriptor)}`)

}

}

pages

VideoPlayView

import { display, window } from "@kit.ArkUI"

import { AVPlayerUtil } from "../utils/AVPlayerUtil";

import { common } from "@kit.AbilityKit";

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

const context:Context = getContext(this)

@Component

export struct VideoPlayView{

//默认视频高度/宽度比值值

@State aspect:number = 9 / 16

//px2vp 将px单位的数值转换为以vp为单位的数值。

//display 屏幕属性提供管理显示设备的一些基础能力,包括获取默认显示设备的信息,获取所有显示设备的信息以及监听显示设备的插拔行为。

//获取当前默认的display对象。

//获取当前屏幕的宽度

@State xComponentWidth:number = px2vp(display.getDefaultDisplaySync().width)

//获取视频所需的高度 屏幕宽度 * 比例值

@State xComponentHeight: number = px2vp(display.getDefaultDisplaySync().width * this.aspect);

//是否处于全屏播放状态

@State isLandscape: boolean = false;

//录像回放是否被锁定

@State isVideoLock: boolean = false;

//控制中心开关旋转,值1:开启0:关闭

@State orientationLockState: string = '1';

//XComponent 提供用于图形绘制和媒体数据写入的外表,XComponent负责将其嵌入到视图中,支持应用自定义外表位置和大小。

//XComponent组件的控制器,可以将此对象绑定至XComponent组件,然后通过控制器来调用组件方法。

private xComponentController: XComponentController = new XComponentController();

//播放工具

private player?: AVPlayerUtil;

//获取该WindowStage实例下的主窗口。

private windowClass = (context as common.UIAbilityContext).windowStage.getMainWindowSync()

//是展开的还是半折叠的

isExpandedOrHalfFolded(): boolean {

//getFoldStatus 获取可折叠设备的当前折叠状态

//FOLD_STATUS_EXPANDED 表示设备当前折叠状态为完全展开

//FOLD_STATUS_HALF_FOLDED 表示设备当前折叠状态为半折叠。半折叠指完全展开和折叠之间的状态。

return display.getFoldStatus() === display.FoldStatus.FOLD_STATUS_EXPANDED ||

display.getFoldStatus() == display.FoldStatus.FOLD_STATUS_HALF_FOLDED

}

aboutToAppear(): void {

//应用窗口尺寸变化的监听

this.windowClass.on('windowSizeChange',(size) =>{

let viewWidth = px2vp(size.width);

let viewHeight = px2vp(size.height);

console.log(`>>>折叠状态${this.isExpandedOrHalfFolded()}`)

console.log(`>>>应用窗口宽度${viewWidth}`)

console.log(`>>>应用窗口高度${viewHeight}`)

if(this.isExpandedOrHalfFolded()){

this.xComponentWidth = viewWidth;

this.xComponentHeight = viewWidth * this.aspect;

}else {

if (viewWidth > viewHeight) {

this.xComponentWidth = viewHeight / this.aspect;

this.xComponentHeight = viewHeight;

this.isLandscape = true;

//隐藏底部导航栏

//setSpecificSystemBarEnabled 设置主窗口三键导航栏、状态栏、底部导航条的显示和隐藏

this.windowClass.setSpecificSystemBarEnabled('navigationIndicator', false);

}else{

this.xComponentHeight = viewWidth * this.aspect;

this.xComponentWidth = viewWidth;

this.windowClass.setSpecificSystemBarEnabled('navigationIndicator', true); // show bottom navigation bar

this.isLandscape = false;

}

}

})

}

//设置方向

setOrientation(orientation: number) {

//设置主窗口的显示方向属性,使用Promise异步回调

this.windowClass.setPreferredOrientation(orientation).then(() => {

console.log(`>>>>设置成功`)

}).catch((err: BusinessError) => {

console.log(`>>>>设置失败`)

});

}

build() {

Stack(){

//视频本体

Column(){

//提供用于图形绘制和媒体数据写入的表面,XComponent负责将其嵌入到视图中,支持应用自定义Surface位置和大小。

XComponent({

id: 'video_player_id',

//用于指定XComponent组件类型。 用于EGL/OpenGLES和媒体数据写入,开发者定制的绘制内容单独展示到屏幕上。背景色设置为黑色时会走显示子系统

type:XComponentType.SURFACE,

controller: this.xComponentController

})

//注册回调,加载完成后将会触发此回调。

.onLoad(() =>{

//创建视频播放工具对象

this.player = new AVPlayerUtil();

//getXComponentSurfaceId() : 获取XComponent对应Surface的ID,供@ohos接口使用

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

this.player.initPlayer('videoTest.mp4', (aspect: number) => {

this.aspect = aspect;

this.xComponentHeight = px2vp(display.getDefaultDisplaySync().width * aspect);

this.xComponentWidth = px2vp(display.getDefaultDisplaySync().width);

});

})

.width(this.xComponentWidth)

.height(this.xComponentHeight)

}

//视频图标

RelativeContainer(){

//是否被锁定

if (!this.isVideoLock){

Image($r('app.media.icon_back'))

.height(24)

.width(24)

.margin({

left: 16,

top: this.isLandscape ? 0 : 12

})

.onClick(() => {

if (this.isExpandedOrHalfFolded()) {

this.isLandscape = false;

} else {

//Orientation 窗口显示方向类型枚举。 USER_ROTATION_PORTRAIT 调用时临时旋转到竖屏,之后跟随传感器自动旋转,受控制中心的旋转开关控制,且可旋转方向受系统判定

this.setOrientation(window.Orientation.USER_ROTATION_PORTRAIT);

}

})

}

//如果为全屏

if (this.isLandscape){

//是否被锁定

Image(this.isVideoLock ? $r('app.media.icon_lock') : $r('app.media.icon_lock_open'))

.height(24)

.width(24)

.fillColor(Color.White)

.alignRules({

top: { anchor: '__container__', align: VerticalAlign.Center },

left: { anchor: '__container__', align: HorizontalAlign.Start }

})

.margin({ left: (AppStorage.get('statusBarHeight') || 0) })

.offset({ y: -12 })

.onClick(() => {

//将锁定进行切换

this.isVideoLock = !this.isVideoLock;

if (this.isExpandedOrHalfFolded() || this.orientationLockState === '0') {

return;

}

if (this.isVideoLock) {

//跟随传感器自动横向旋转,可以旋转到横屏、反向横屏,无法旋转到竖屏、反向竖屏。

this.setOrientation(window.Orientation.AUTO_ROTATION_LANDSCAPE);

} else {

//跟随传感器自动旋转,受控制中心的旋转开关控制,且可旋转方向受系统判定(如在某种设备,可以旋转到竖屏、横屏、反向横屏三个方向,无法旋转到反向竖屏)。

this.setOrientation(window.Orientation.AUTO_ROTATION_UNSPECIFIED);

}

})

}

//如果不为全屏

if(!this.isLandscape){

Image($r('app.media.icon_zoom_in'))

.height(24)

.width(24)

.alignRules({

bottom: { anchor: '__container__', align: VerticalAlign.Bottom },

right: { anchor: '__container__', align: HorizontalAlign.End }

})

.margin({

right: 16,

bottom: 8

})

.onClick(() => {

if (this.isExpandedOrHalfFolded()) {

this.isLandscape = true;

} else {

//调用时临时旋转到横屏,之后跟随传感器自动旋转,受控制中心的旋转开关控制,且可旋转方向受系统判定。

this.setOrientation(window.Orientation.USER_ROTATION_LANDSCAPE);

}

})

}

}

.width('100%')

.height('100%')

}

.width('100%')

.height(this.isLandscape ? '100%' : this.xComponentHeight)

.backgroundColor(Color.Black)

}

}

VideoDetail

//视频详情页

import { VideoPlayView } from './VideoPlayView';

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

@Entry

@Component

struct VideoDetail {

@State message: string = 'Hello World';

build() {

Column(){

//视频播放组件

VideoPlayView()

Scroll(){

Column(){

//视频相关推荐组件

//评论列表组件

}

}

.layoutWeight(1)

.scrollBar(BarState.Off)

.padding({ bottom: 16 })

//底部操作栏组件

}

.width('100%')

.height('100%')

.backgroundColor(Color.White)

}

}

EntryAbility

// 在onWindowStageCreate中设置

//视频横竖屏播放。获取屏幕安全区域,并将安全区域高度存入AppStorage中

let windowClass:window.Window = windowStage.getMainWindowSync()

//getWindowAvoidArea 获取当前窗口内容规避区域,系统栏、刘海屏、手势、软键盘等可能与窗口内容重叠需要内容避让的区域

//TYPE_SYSTEM 系统默认区域 包含状态栏、导航栏等

let area:window.AvoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM)

//将获取到的顶部避让区域的高度存入AppStorage

AppStorage.setOrCreate('statusBarHeight',px2vp(area.topRect.height))

结尾

功能实现思路

技术栈实现

VideoPlayView

获取windowStage实例下的主窗口

// 折叠屏相关

// 获取该windowStage实例下的主窗口

private windowClass = (context as common.UIAbilityContext).windowStage.getMainWindowSync()

应用窗口尺寸更新变化监听

this.windowClass.on('windowSizeChange',callBack)

设置主窗口三键导航栏,状态栏,底部导航条显示和隐藏

// false:不显示 true:显示

this.windowClass.setSpecificSystemBarEnabled('navigationIndicator', false)

AVplayerUtil

AVPlayer状态监听

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

AVPlayer视频尺寸监听

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

获取rawfile目录下的文件

// 获取context

let context = getContext(this) as common.UIAbilityContext

// 文件描述符,用户获取rawfile/resource 目录下对应文件的信息

// 找到该值是否存在,如果不存在使用网络路径 this.avPlayer.url

// context.resourceManager:访问应用资源的能力

let fileDescriptor = await context.resourceManager.getRawFd(url)

封装为视频文件信息

// 视频文件信息

// AVFileDescriptor音频文件资源描述

let avFileDescriptor:media.AVFileDescriptor = {

// 资源句柄,通过resourceManager.getRawFd获取

fd:fileDescriptor.fd,

// 资源偏移量

offset:fileDescriptor.offset,

// 资源长度

length:fileDescriptor.length,

}

this.avPlayer.fdSrc = avFileDescriptor

Descriptor = await context.resourceManager.getRawFd(url) ```

封装为视频文件信息

// 视频文件信息

// AVFileDescriptor音频文件资源描述

let avFileDescriptor:media.AVFileDescriptor = {

// 资源句柄,通过resourceManager.getRawFd获取

fd:fileDescriptor.fd,

// 资源偏移量

offset:fileDescriptor.offset,

// 资源长度

length:fileDescriptor.length,

}

this.avPlayer.fdSrc = avFileDescriptor

  • 朗逸1.5L加什么汽油,朗逸1.5L加92还是95汽油
Copyright © 2088 橄榄球世界杯_1990世界杯阿根廷 - liuweiqing95511.com All Rights Reserved.
友情链接