cocosCreator视频web模式播放踩坑解决

/*** 对外输出接口*/
export interface VideoPlayerManageInterface {//初始化视频播放器init(list: VideoPlayerManageInitListType[],options?: VideoPlayerManageInitOptionsType): Promise<void>;//播放视频play(url: string, currentTime?: number): Promise<void>;//销毁视频destroyUrl(url: string, currentTag: string): void;//销毁标签的所有视频destroyTag(targetTag: string): void;
}export type VideoPlayerManageInitListType = {//标签,分组tag: string;//视频地址url: string;//视频宽度width: number;//视频高度height: number;//是否循环播放isLoop: boolean;//视频播放结束回调(非循环下生效)endCallback?: () => void;
};export type VideoPlayerManageInitOptionsType = {//视频封面地址poster?: string;//播放按钮地址playerStartButtonUrl?: string;//是否已经点击过播放按钮hasClicked?: boolean;//是否是苹果设备检测函数checkIsAppleFun?: () => boolean;
};/*** 鉴于cocos对于视频播放的支持不够完善,在自带的VideoPlayer组件上进行了封装* 使用VideoPlayerManage进行视频的提前装载,播放,销毁等操作* 解决视频切换时的黑屏问题、视频默认样式修改、ISO自动播放、播放时未就绪报错的问题* web环境使用*/cc.macro.ENABLE_TRANSPARENT_CANVAS = true;export default class VideoPlayerManage implements VideoPlayerManageInterface {private static V: VideoPlayerManageInterface = null;public static get instance(): VideoPlayerManageInterface {if (!this.V) {this.V = new VideoPlayerManage();}return this.V;}private readonly CLASS_NAME = "cocosVideo";private poster = ""; //这里是一个视频封面的图片地址private playerStartButtonUrl = ""; //这里是一个播放按钮的图片地址private hasClicked = false;constructor() {cc.Canvas.instance.node.getComponentInChildren(cc.Camera).backgroundColor = new cc.Color(0, 0, 0, 0);}private map: Map<string,{url: string;videoPlayer: cc.VideoPlayer;tag: string[];}> = new Map();async init(list: VideoPlayerManageInitListType[],options?: VideoPlayerManageInitOptionsType): Promise<void> {Object.keys(options).forEach((key) => {this[key] = options[key];});await Promise.all(list.map((listItem) => this.initVideoPlayerCore(listItem)));await this.initVideos();}async play(url: string, currentTime = 0) {const videoPlayer = this.map.get(url).videoPlayer;this.map.forEach((value) => {value.videoPlayer.node.active = url === value.url;});videoPlayer.currentTime = currentTime;if (videoPlayer.isPlaying()) {videoPlayer.pause();}videoPlayer.node.off("ready-to-play");videoPlayer.node.on("ready-to-play",() => {if (!videoPlayer.isPlaying()) {videoPlayer.play();}},this);await this.waitPlayerClick();if (!videoPlayer.isPlaying()) {videoPlayer.play();} else {videoPlayer.resume();}}destroyUrl(url: string, currentTag: string) {const item = this.map.get(url);if (!item) return;if (item.tag.length > 1) {item.tag = item.tag.filter((tagItem) => {return tagItem !== currentTag;});return;}this.delOneVideo(item);}destroyTag(targetTag: string) {this.map.forEach((item) => {if (item.tag.includes(targetTag)) {item.tag = item.tag.filter((tagItem) => {return tagItem !== targetTag;});if (item.tag.length === 0) {this.delOneVideo(item);}}});}private delOneVideo(item) {item.videoPlayer.node.destroy();const videoDom = this.getVideoDom(item.url);if (videoDom) {videoDom.parentNode.removeChild(videoDom);}this.map.delete(item.url);}private async initVideos() {await this.delayOneFrame();const list = document.getElementsByClassName(this.CLASS_NAME);for (let i = 0; i < list.length; i++) {const video: Element = list[i];video["poster"] = this.poster;video["autoplay"] = true;}}private async initVideoPlayerCore(listItem) {const videoMapItem = this.map.get(listItem.url);if (videoMapItem) {!videoMapItem.tag.includes(listItem.tag) &&videoMapItem.tag.push(listItem.tag);return;}const videoPlayer = await this.createVideoPlayerForUrl(listItem);this.map.set(listItem.url, {url: listItem.url,videoPlayer,tag: [listItem.tag],});}private async createVideoPlayerForUrl(listItem): Promise<cc.VideoPlayer> {const videoNode: cc.Node = new cc.Node();videoNode.active = false;const videoPlayer = videoNode.addComponent(cc.VideoPlayer);videoPlayer.mute = true;videoPlayer.resourceType = cc.VideoPlayer.ResourceType.LOCAL;videoNode.width = listItem.width;videoNode.height = listItem.height;videoPlayer.stayOnBottom = true;cc.Canvas.instance.node.addChild(videoNode);const asset = await this.loadVideo(listItem.url);videoPlayer.clip = asset as unknown as string;this.setLoopAndEndCallBack(videoPlayer,listItem.isLoop,listItem.endCallback);return videoPlayer;}private loadVideo(url: string) {return new Promise((resolve, reject) => {cc.assetManager.loadRemote(url,{ ext: ".mp4" },(err, asset: cc.Asset) => {if (err) {reject(err);} else {resolve(asset);}});});}private loadPng(url: string) {return new Promise((resolve, reject) => {cc.assetManager.loadRemote(url,{ ext: ".png" },(err, asset: cc.Asset) => {if (err) {reject(err);} else {resolve(asset);}});});}private delayOneFrame(): Promise<void> {return new Promise((resole) => {cc.Canvas.instance.scheduleOnce(() => {resole();});});}private setLoopAndEndCallBack(videoPlayer: cc.VideoPlayer,isLoop: boolean,endCallback: () => void) {videoPlayer.node.off("completed");videoPlayer.node.on("completed",() => {if (isLoop) {videoPlayer.currentTime = 0;videoPlayer.play();} else {endCallback && endCallback();}},this);}private async waitPlayerClick(): Promise<void> {return new Promise((resolve) => {if (this.hasClicked || !this.checkIsApple()) {resolve();return;}const node = new cc.Node();node.addComponent(cc.BlockInputEvents);const sprite = node.addComponent(cc.Sprite);this.loadPng(this.playerStartButtonUrl).then((asset) => {sprite.spriteFrame = new cc.SpriteFrame(asset as unknown as cc.Texture2D);node.setPosition(cc.v2(0, 0));cc.Canvas.instance.node.addChild(node);node.once(cc.Node.EventType.TOUCH_END, () => {node.destroy();this.hasClicked = true;resolve();});});});}private checkIsApple() {return /iphone|ipad|ios|mac/gi.test(navigator.userAgent.toLowerCase());}private getVideoDom(url: string) {const list = document.getElementsByClassName(this.CLASS_NAME);for (let i = 0; i < list.length; i++) {const video = list[i];if (url == video["src"]) {return video;}}return null;}
}

使用样例:

import VideoPlayerManage from "./VideoPlayerManage";const { ccclass } = cc._decorator;@ccclass
export default class Index extends cc.Component {private list = ["http://localhost:3000/light1.mp4","http://localhost:3000/light2.mp4",];private index = 0;protected async onLoad(): Promise<void> {await VideoPlayerManage.instance.init(this.list.map((url) => {return {tag: url,url,width: this.node.width,height: this.node.height,isLoop: true,endCallback: () => {console.log("end");},};}),{poster: "",playerStartButtonUrl: "http://localhost:3000/head1.png",});this.playByIndex();this.node.on(cc.Node.EventType.TOUCH_END, () => {this.playByIndex();});}playByIndex() {this.index++;if (this.index >= this.list.length) {this.index = 0;}VideoPlayerManage.instance.play(this.list[this.index]);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/473166.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前后端请求响应

引入 在之前的例子中&#xff0c;我们编写了一个简单的web类&#xff0c;我们运行启动类&#xff0c;启动内嵌的tomcat后就可以在浏览器通过特定的路径访问tomcat中的应用程序。 但之前编写的程序仅仅是个简单的java类&#xff0c;其并未实现某个接口或继承某个类&…

Ubuntu24 上安装搜狗输入法

link 首先在终端中依次输入以下代码 sudo apt update sudo apt install fcitx 找到语言支持 在终端中依次输入 sudo cp /usr/share/applications/fcitx.desktop /etc/xdg/autostart/ sudo apt purge ibus 进入网页 搜狗输入法linux-首页​ shurufa.sogou.com/linux 找到刚才下…

Linux从0——1之shell编程4

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…

嵌入式硬件杂谈(二)-芯片输入接入0.1uf电容的本质(退耦电容)

引言&#xff1a;对于嵌入式硬件这个庞大的知识体系而言&#xff0c;太多离散的知识点很容易疏漏&#xff0c;因此对于这些容易忘记甚至不明白的知识点做成一个梳理&#xff0c;供大家参考以及学习&#xff0c;本文主要针对芯片输入接入0.1uf电容的本质的知识点的进行学习。 目…

树莓派4B Qt+FFMPEG 多线程录制USB相机mjpeg数据流“h264_omx“硬件编码的MP4文件

文章目录 1 前言2 一些问题说明2.0 树莓派4b系统版本2.1 Qt2.2 FFMPEG2.3 图像格式 3 核心代码3.0 代码逻辑3.1 pro文件3.2 avframequeue.cpp3.3 decodethread.cpp 4 资源下载 1 前言 本项目为在树莓派4B开发板上&#xff0c;通过QtFFMPEG以多线程分别解码、编码USB摄像头视频数…

Cartographer激光雷达slam -20241116

Cartographer Cartographer代码结构 cartographer&#xff1a;负责处理来自雷达、IMU和里程计的数据并基于这些数据进行地图的构建&#xff0c;是cartographer理论的底层实现cartographer_ros&#xff1a;基于ros的通信机制获取传感器的数据并将它们转换成cartographer中定义…

Scratch 014生日贺卡(上)

知识回顾&#xff1a; 1、“面向鼠标指针”积木块 2、“重复执行直到”积木块 本次分享制作生日贺卡引入广播模块 案列效果&#xff1a; 生日贺卡上案例效果-CSDN直播 步骤拆解&#xff1a; 1、添加背景和角色 2、编辑贺卡造型添加名字 3、流程图的组成和画法 4、…

外网访问 WebDav 服务

从外部网络环境&#xff08;比如异地和家中网络&#xff09;来访问公司内网的 WebDav 服务&#xff08;基于 IIS &#xff09;并映射成本地虚拟磁盘。 步骤如下 第一步 在公司内网的电脑上设置 webDav。 1&#xff0c;找到【控制面板】&#xff0c;双击进入。 2&#xff0c…

渑池县中药材产业党委莅临河南广宇企业管理集团有限公司参观交流

11月14日&#xff0c;渑池县人大副主任、工商联主席杨航率县中药材产业党委代表团一行13人&#xff0c;莅临河南广宇集团参观交流。河南广宇集团总经理王峰、副总经理王培等领导热情接待并陪同参观、座谈。 代表团一行首先参观了集团旗下郑州美信中医院&#xff08;庚贤堂中医药…

WP网站如何增加文章/页面的自定义模板

通过Wordpress我们后台在发布文章或者页面的时候其实可以看到有些主题 他有选择使用的页面模板&#xff0c;可以自定义模板&#xff0c;但是有些主题却没有选择主题这个功能&#xff0c;那这个自定义模板的功能是如何实现的呢&#xff1f;以下分两种情况&#xff1a;Page页面和…

FFmpeg 4.3 音视频-多路H265监控录放C++开发十四,总结编码过程,从摄像头获得数据后,转成AVFrame,然后再次转成AVPacket,

也就是将摄像头采集到的YUV 的数据换成 AVFrame&#xff0c;然后再次转成 AVPacket&#xff0c;那么这AVPakcet数据要怎么办呢&#xff1f;分为三种情况&#xff1a; 一种是将AVPacket存储成h264文件&#xff0c;由于h264编码器在将avframe变成avpacket的时候就是按照h264的格…

SQL Server 查询设置 - LIKE/DISTINCT/HAVING/排序

目录 背景 一、LIKE - 模糊查询 1. 通配符 % 2. 占位符 _ 3. 指定集合 [] 3.1 表示否定 ^ 3.2 表示范围 - 4. 否定 NOT 二、DISTINCT - 去重查询 三、HAVING - 过滤查询 四、小的查询设置 1. ASC|DESC - 排序 2. TOP - 限制 3. 子查询 4. not in - 取补集&…

动态规划-完全背包问题——322.零钱兑换

1.题目解析 题目来源 322.零钱兑换——力扣 测试用例 2.算法原理 1.状态表示 这里需要寻找硬币使总面值等于一个值求出所需硬币的最小个数&#xff0c;所以不妨设置一个二维dp表&#xff0c;即dp[i][j]&#xff1a;在[1,i]个硬币中选择的硬币总面值完全等于j时所需要的最小硬…

从零到一:利用 AI 开发 iOS App 《震感》的编程之旅

在网上看到一篇关于使用AI开发的编程经历&#xff0c;分享给大家 作者是如何在没有 iOS 开发经验的情况下&#xff0c;借助 AI&#xff08;如 Claude 3 模型&#xff09;成功开发并发布《震感》iOS 应用。 正文开始 2022 年 11 月&#xff0c;ChatGPT 诞生并迅速引发全球关注。…

【Linux庖丁解牛】—Linux基本指令(下)!

目录 1、grep指令 2、zip/unzip指令 3、sz/rz指令 4、tar指令 ​编辑 5、scp指令 6、bc指令 7、uname –r指令 8、重要的几个热键 9、关机 10、完结撒花 1、grep指令 grep是文本过滤器&#xff0c;其作用是在指定的文件中过滤出包含你指定字符串的内容&#xff0c;…

小程序19-微信小程序的样式和组件介绍

在小程序中不能使用 HTML 标签&#xff0c;也就没有 DOM 和 BOM&#xff0c;CSS 也仅支持部分选择器 小程序提供了 WXML 进行页面结构的编写&#xff0c;WXSS 进行页面的样式编写 WXML 提供了 view、text、image、navigator等标签构建页面结构&#xff0c;小程序中标签称为组件…

VMD + CEEMDAN 二次分解,CNN-LSTM预测模型

往期精彩内容&#xff1a; 时序预测&#xff1a;LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较 全是干货 | 数据集、学习资料、建模资源分享&#xff01; EMD变体分解效果最好算法——CEEMDAN&#xff08;五&#xff09;-CSDN博客 拒绝信息泄露&#xff01;VMD滚动分…

《生成式 AI》课程 第3講 CODE TASK 任务3:自定义任务的机器人

课程 《生成式 AI》课程 第3講&#xff1a;訓練不了人工智慧嗎&#xff1f;你可以訓練你自己-CSDN博客 我们希望你创建一个定制的服务机器人。 您可以想出任何您希望机器人执行的任务&#xff0c;例如&#xff0c;一个可以解决简单的数学问题的机器人0 一个机器人&#xff0c…

SOLIDWORKS Toolbox:一键自动化,让紧固件与零部件管理更高效

紧固件广泛应用于从手机到火箭的各种产品中。在SOLIDWORKS设计时&#xff0c;通过使用实际的CAD模型来包含和跟踪紧固件是最简便和全面的方法&#xff0c;这有助于理解设计的整体&#xff0c;并自动管理零件数据和设计文档&#xff0c;如工程图和物料清单(BOM)。 在SOLIDWORKS…

串口DMA接收不定长数据

STM32F767—&#xff1e;串口通信接收不定长数据的处理方法_stm32串口超时中断-CSDN博客 STM32-HAL库串口DMA空闲中断的正确使用方式解析SBUS信号_stm32 hal usart2 dma-CSDN博客 #define USART1_RxBuffSize 100 extern DMA_HandleTypeDef hdma_usart1_rx; //此处声明的变量在…