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…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
