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]);}
}
相关文章:
cocosCreator视频web模式播放踩坑解决
/*** 对外输出接口*/ export interface VideoPlayerManageInterface {//初始化视频播放器init(list: VideoPlayerManageInitListType[],options?: VideoPlayerManageInitOptionsType): Promise<void>;//播放视频play(url: string, currentTime?: number): Promise<v…...
c++头文件中 #ifndef的作用
避免文件重复处理、变量等重定义 //c1.hpp #ifndef C1_HPP #define C1_HPP int a 0; #endif // LFU_CACHE_HPP#include"c1.hpp" #ifndef C2_HPP #define C2_HPP int b1; #endif#include"c1.hpp" #include"c2.hpp" #include<iostream> in…...
Xcode 项目内 OC 混编 Python,调用 Python 函数,并获取返回值(基于 python 的 c函数库)
1:新建 Xcode 工程 2:工程添加 Python.framework 1597052861430.jpg 3:在当前工程下新建一个名字为 googleT 的 python 文件(googleT.py) 1597052584962.jpg 在 googleT.py 文件内写入一个测试 python 函数 def lgf_translate( str ):var1 Hello World!print (str var1)retu…...
每日计划-1117
1. 完成 169. 多数元素 class Solution { public:int majorityElement(vector<int>& nums) {// 使用哈希表来统计每个元素出现的次数unordered_map<int, int> countMap;int n nums.size();for (int num : nums) {// 如果元素已经在哈希表中,增加其…...
如何用GPT-4o解读视频
OpenAI在去年推出的GPT-4V已经支持了多模态识别,但一直仅限于图片输入,不支持视频。相比之下,Google的Gemini早已支持视频识别。最近,我司业务场景中出现了一个需要识别视频的需求,而我们只采购了GPT-4o模型。这就引发…...
[ACTF2020]Upload 1--详细解析
信息收集 题目告诉我们是一道upload,也就是文件上传漏洞题目。 进入界面,是一个灯泡,将鼠标放在图标上就会出现文件上传的相应位置: 思路 文件上传漏洞,先看看有没有前端校验。 在js源码中找到了前端校验ÿ…...
【微软:多模态基础模型】(3)视觉生成
欢迎关注【youcans的AGI学习笔记】原创作品 【微软:多模态基础模型】(1)从专家到通用助手 【微软:多模态基础模型】(2)视觉理解 【微软:多模态基础模型】(3)视觉生成 【微…...
整合Druid
添加依赖 配置数据源信息...
基于Python空气质量可视化及预测
摘 要 随着社会的发展和工业化进程的加速,环境问题日益凸显,尤其是空气质量问题对人们的生活和健康产生了重大影响。为了更好地了解和预测空气质量,本文设计并实现了一个基于Python爬虫、Flask框架和ECharts的天气质量预测及可视化系统。该系统通过爬取网络上的空气质量数据…...
第1章-PostgreSQL(PG)介绍
第1章-PostgreSQL(PG)介绍 1、简介2、排名3、发展4、应用5、优势6、对比 1、简介 PostgreSQL是一种特性非常齐全的自由软件的对象-关系型数据库管理系统(ORDBMS),是以加州大学计算机系开发的POSTGRES,4.2版…...
moduo之阻塞队列BlockingQueue和BoundedBlockingQueue
简介 moduo中的队列与java线程池中的队列类似, 有无界阻塞队列和有界阻塞队列 结构 #mermaid-svg-Gf8nET825tZgzVRM {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Gf8nET825tZgzVRM .error-icon{fill…...
大模型Tuning方法详解
1. 引言 大模型与Tuning的重要性 随着人工智能和深度学习技术的快速发展,大规模预训练模型(Large Pre-trained Models,简称大模型)在自然语言处理、计算机视觉等领域取得了显著的效果。大模型如GPT-4、BERT、T5和DALL-E等具备强…...
爬虫策略与反爬机制——爬虫常见策略
随着网络爬虫技术的日益发展,反爬机制也变得越来越复杂,网站和服务商不断加强对爬虫行为的监控和限制,开发者需要采取一系列有效的爬虫策略来提高爬虫的效率并规避反爬措施。本章将介绍一些常见的爬虫策略,帮助开发者应对不同情况…...
Linux基础(十七)——Linux 帐号管理与 ACL 权限设置
Linux 帐号管理与 ACL 权限设置 1.UID与GID2./etc/passwd3./etc/shadow4./etc/group5./etc/gshadow6.有效群组和初始群组7.账号管理7.1 增加、修改、删除账户7.2 增加、修改、删除群组7.3 实例 8.ACL使用8.1 ACL定义8.2 查询与设置ACL 9.用户切换9.1 su9.2 .sudo 10. 使用者的特…...
【HarmonyOS】鸿蒙系统在租房项目中的项目实战(二)
从今天开始,博主将开设一门新的专栏用来讲解市面上比较热门的技术 “鸿蒙开发”,对于刚接触这项技术的小伙伴在学习鸿蒙开发之前,有必要先了解一下鸿蒙,从你的角度来讲,你认为什么是鸿蒙呢?它出现的意义又是…...
11.16 Vue element
Ajax 概念:Asynchronous JavaScript Anderson XML,异步的JavaScript和XML。 作用: 数据交换:通过Ajax 可以给服务器发送请求,并收取服务器相应的数据。异步交互:可以在不重新加载整个页面的情况下&#…...
Gin 框架中的路由
1、路由概述 路由(Routing)是由一个 URI(或者叫路径)和一个特定的 HTTP 方法(GET、POST 等) 组成的,涉及到应用如何响应客户端对某个网站节点的访问。 RESTful API 是目前比较成熟的一套互联网应用程序的 API 设计理论,所以我们设计我们的路 由的时候建议参考 …...
在MATLAB中实现自适应滤波算法
自适应滤波算法是一种根据信号特性自动调整滤波参数的数字信号处理方法,其可以有效处理噪声干扰和信号畸变问题。在许多实时数据处理系统中,自适应滤波算法得到了广泛应用。在MATLAB中,可以使用多种方法实现自适应滤波算法。本文将介绍自适应…...
linux文件与重定向
目录 一、共识原理 二、回顾C语言文件函数 1.fopen 2.fwrite 3.fclose 三、文件系统调用 1.open 2.write 3.访问文件的本质 4.stdin&&stdout&&stderror 5.文件的引用计数 四、重定向 1.文件描述符的分配规则 2. 输出重定向 3.重定向系统调用 4.…...
基于Python的仓库管理系统设计与实现
背景: 基于Python的仓库管理系统功能介绍 本仓库管理系统采用Python语言开发,利用Django框架和MySQL数据库,实现了高效、便捷的仓库管理功能。 用户管理: 支持员工和管理员角色的管理。 用户注册、登录和权限分配功能&#x…...
Trigger.dev任务重试退避策略:指数退避配置完整指南 [特殊字符]
Trigger.dev任务重试退避策略:指数退避配置完整指南 🚀 【免费下载链接】trigger.dev Trigger.dev – build and deploy fully‑managed AI agents and workflows 项目地址: https://gitcode.com/gh_mirrors/tr/trigger.dev 构建可靠的后台任务和…...
MiGPT小爱音箱AI升级终极指南:5步快速接入ChatGPT和豆包大模型
MiGPT小爱音箱AI升级终极指南:5步快速接入ChatGPT和豆包大模型 【免费下载链接】mi-gpt 🏠 将小爱音箱接入 ChatGPT 和豆包,改造成你的专属语音助手。 项目地址: https://gitcode.com/GitHub_Trending/mi/mi-gpt 你是否曾希望家中的小…...
Cursor编辑器深度解析:AI驱动的智能编程助手如何重塑开发工作流
1. 项目概述:一个为开发者而生的“智能副驾”如果你是一名开发者,最近一定在某个技术社区、朋友圈或者同事的聊天里,听到过“Cursor”这个名字。它不是某个新的编程语言,也不是一个框架,而是一个被许多一线工程师私下称…...
2026年DLL修复工具深度测评:免费解决DLL缺失的可行方案
电脑运行办公软件、打开大型游戏时,经常弹出XXX.dll 缺失、无法找到入口点、无法加载动态链接库等报错窗口?相信绝大多数 Windows 用户都遇到过这种糟心情况:好好的程序突然打不开,游戏双击没任何反应,重装软件不起作用…...
S32K3 FlexCAN实战:从MCAL配置到DMA接收,手把手教你避开那些手册里没写的坑
S32K3 FlexCAN深度实战:从寄存器配置到DMA优化全链路解析 在车载电子架构快速迭代的今天,S32K3系列MCU凭借其强大的FlexCAN模块成为汽车电子开发者的首选。但官方文档往往只勾勒出理想状态下的功能框架,当工程师真正着手实现CAN FD通信时&…...
【花雕学编程】Arduino动手做(252)---ESP32-S3-RGB-LED矩阵开发板之全屏循环显示七种颜色
37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里准备逐一动手试试多做实验,不管成功与否,都会记录下来——小小的…...
告别JSON臃肿:手把手教你用MessagePack为C++微服务瘦身(附性能对比)
告别JSON臃肿:手把手教你用MessagePack为C微服务瘦身(附性能对比) 在当今高性能后端服务开发中,微服务架构已成为主流选择。然而,随着服务规模的扩大,服务间通信的数据量急剧增长,传统的JSON序列…...
TrollInstallerX终极指南:iOS 14-16.6.1越狱工具一键部署全解析
TrollInstallerX终极指南:iOS 14-16.6.1越狱工具一键部署全解析 【免费下载链接】TrollInstallerX A TrollStore installer for iOS 14.0 - 16.6.1 项目地址: https://gitcode.com/gh_mirrors/tr/TrollInstallerX 想要在iOS 14.0到16.6.1系统上轻松安装Troll…...
Windows进程内存操作实战:ClawMem库核心原理与应用指南
1. 项目概述:一个内存操作工具箱的诞生在软件开发和逆向工程领域,对进程内存进行安全、高效、可控的读写操作,是一个既基础又充满挑战的需求。无论是为了调试、分析程序行为,还是为了实现特定的功能扩展,直接与内存打交…...
Sprout OS:为创意工作者打造的Linux开源操作系统部署与优化指南
1. 项目概述:一个为创意工作者量身定制的操作系统如果你是一名设计师、视频剪辑师、音乐制作人或者任何需要高性能计算和稳定创作环境的创意专业人士,那么你肯定对“创作环境”这四个字又爱又恨。爱的是,它是你挥洒才华的舞台;恨的…...
