ios中video标签兼容问题

news2024/11/18 22:42:56

案例

这是在实际开发中遇到的问题,产品给出的效果图如下:
在这里插入图片描述

其实就是一个h5页面中有很多视频, 但是我为什么都画着预览图呢? 因为在ios中video标签中尽管有src属性, 但是在没有预览图的情况下, 是一个灰蒙蒙的图层的, 很丑, 效果如下:

在这里插入图片描述

看到这里是不是很崩溃, 在PC端, 在安卓端都是正常显示的, 但是在ios系统不行, 也就是说在ipad和iphone中不行.

究竟是如何产生这个问题的呢?

在ios系统中有个保护机制, 如果video标签未开始播放, 是不会去加载视频的.

所以也就是说, video标签还未去加载视频, 就显示不出来视频首帧画面.

把video标签加上autoplay属性, 就正常看到视频的画面了.

但是又出现了新的问题, 这样的话, 页面上很多视频, 导致视频声音重叠了

把video标签加上muted属性, 表示静音播放.

要想在video标签中显示首帧画面, 需要添加poster属性, 属性值应该是图片的url

接下来就是怎么获取到视频首帧的图片的url

前端获取视频首帧画面

loadedmetadata事件监听+canvas渲染首帧
在网上找了少相关事例, 同时也问了一下chatGPT如何处理, 得到的结果都差不多, 监听video标签的两个事件
一个是加载元数据loadedmetadata
一个是浏览器估计它可以在不停止内容缓冲的情况下播放媒体直到结束, canplaythrough事件
还有一个canplay, 浏览器可以播放媒体文件了,但估计没有足够的数据来支撑播放到结束,不必停下来进一步缓冲内容。

具体属性和方法可以查看MDN

video.addEventListener(
   'loadedmetadata',
   () => {
     console.log('loadedmetadata')
     const canvas = document.createElement('canvas');
     video.addEventListener('canplaythrough', () => {
       console.log('canplaythrough')
       canvas.width = video.videoWidth;
       canvas.height = video.videoHeight;
       canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
       const firstFrame = canvas.toDataURL();
       // console.log(firstFrame); // 输出第一帧画面的Base64编码字符串
       
     });
   },
   { once: true }
 );

问题:
但是只是这样处理, 在谷歌浏览器端, 图片的不能正常展示出来
排查: 把canvas渲染出来, 新增img标签, 看一下base64的图片是什么情况

结果竟然连canvas画出来都是一张透明图层
img标签展示

在这里插入图片描述

1.第一帧显示不出来, 获取第二帧

指定video.currentTime = 2, 来获取第二帧

const canvas = document.getElementById('canvas');

const video = document.createElement('video');
video.src = "视频链接";

let firstFrame = true; 
video.addEventListener('seeked', function() {
  firstFrame = true;   
});

video.addEventListener(
  'loadedmetadata',
  () => {
    video.currentTime = 2;
    video.addEventListener('canplay', () => {
      canvas.width = video.videoWidth;
      canvas.height = video.videoHeight;
      canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
      // const firstFrame = canvas.toDataURL('image/png');
      document.querySelector("#testImg").src = canvas.toDataURL();
      // console.log(firstFrame); // 输出第一帧画面的Base64编码字符串
    });

  },
  { once: true }
);

这个时候canvas显示是正常的了, 但是img还是不正常, 而且控制台报错了
原因:

videoFirstFrame.html:37 Uncaught DOMException: Failed to execute ‘toDataURL’ on ‘HTMLCanvasElement’: Tainted canvases may not be exported.
at HTMLVideoElement.

在这里插入图片描述

This error occurs when you try to use the toDataURL() method on an HTML canvas element that has been tainted. A tainted canvas is one that has been affected by a security feature that prevents scripts from accessing pixel data from certain sources, such as images loaded from a different domain.

当您尝试在已被污染的 HTML 画布元素上使用 toDataURL() 方法时,会发生此错误。受污染的画布受到安全功能的影响,该功能阻止脚本访问来自特定来源的像素数据,例如从不同域加载的图像。

To resolve this error, you need to make sure that all the images used in your canvas are hosted on the same domain as your webpage, or that they have the appropriate cross-origin resource sharing (CORS) headers set. If you cannot control the source of the images, you may need to proxy them through your own server to avoid the tainting issue.
要解决此错误,您需要确保画布中使用的所有图像都托管在与您的网页相同的域中,或者它们具有适当的跨域资源共享 (CORS) 标头集。如果您无法控制图像的来源,您可能需要通过自己的服务器代理它们以避免污染问题。

2.视频链接跨域

导致渲染出来的base64图片, 浏览器认为不安全
给video新增属性crossOrigin为anonymous

const video = document.createElement('video');
video.crossOrigin = 'anonymous';
video.src = url;
return new Promise((resolve, reject) => {
  try {
    video.addEventListener(
      'loadedmetadata',
      () => {
        video.currentTime = 2;
        const canvas = document.querySelector('#canvas');
        video.addEventListener('canplay', () => {
          canvas.width = video.videoWidth;
          canvas.height = video.videoHeight;
          canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
          const firstFrame = canvas.toDataURL();
          // document.querySelector("#testImg").src = firstFrame;
          this.testImg = firstFrame;
          // console.log(firstFrame); // 输出第一帧画面的Base64编码字符串
          resolve(firstFrame);
        });
      },
      { once: true }
    );
  } catch (err) {
    console.error(err);
    reject('');
  }
});

可以设置video允许视频url跨域,这样调用canvas.toDataURL()就能够看到图片正常展示在谷歌浏览器上

3.ios显示还是白屏

本以为将firstFrame赋值给video的poster属性上,就能解决ios白屏的问题了,但是效果却仍然不行,原因是video.canplay事件未执行

换成video.canplaythrough事件也还是未执行

原因还是ios中,视频只有在播放的情况下会加载

所以就只能给video标签加上autoplay属性和muted属性,这样就能在ios中获取到视频首帧了。

在这里插入图片描述

所以最后在ios上,前端负责渲染第一帧的处理,最后逻辑是


getFirstImg(url) {
  const video = document.createElement('video');
  video.crossOrigin = 'anonymous'; // 允许url跨域
  video.autoplay = true; // 自动播放
  video.muted = false; // 静音
  video.src = url;

  return new Promise((resolve, reject) => {
    try {
      video.addEventListener(
        'loadedmetadata',
        () => {
          console.log('loadedmetadata')
          video.currentTime = 2;
          const canvas = document.createElement('canvas');
          video.addEventListener('canplaythrough', () => {
            console.log('canplaythrough')
            canvas.width = video.videoWidth;
            canvas.height = video.videoHeight;
            canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
            const firstFrame = canvas.toDataURL();
            // console.log(firstFrame); // 输出第一帧画面的Base64编码字符串
            resolve(firstFrame);
          });
        },
        { once: true }
      );
    } catch (err) {
      console.error(err);
      reject('');
    }
  });
},

async handleImg() {
  for (let i = 0; i < this.videoList.length; i++) {
    this.videoList[i].img = await this.getFirstImg(this.videoList[i].url);
    console.log(i, 11111);
  }
},

mounted() {
    this.handleImg();
},


4.当视频数量很多时,加载时间过长

前端来获取视频首帧终究不是上上策,因为前端处理视频,就需要视频加载这个过程,如果页面上有很多视频,那访问这样页面,岂不是要偷偷浪费用户流量来加载,最理想的情况是:用户点击播放,需要看哪个视频,就加载哪个视频,这样既能保证页面的响应速度,也能够保证不浪费用户流量,最重要的是用户体验感会增强很多。

所以还是后端直接返回视频首帧的图片,最理想

后端返回视频首帧图片

但是技术评审时,后端就需要排查,如果上传视频的时候,没有保存视频首帧图片,那他怎么去拿这个数据返回呢?

其实上传文件,现在最流行的不是oss就是cos
OSS:阿里云存储
COS:腾讯云存储

oss获取视频首帧

https://help.aliyun.com/document_detail/64555.html

阿里云获取视频首帧很简单,直接在url后面拼接参数就行了

这样都不需要后端怎么处理,前端这样就能够拼接
参数 描述 取值范围
t 指定截图时间。如果设置的截图时间t超过了视频时长,则返回视频的最后一帧关键帧。 [0,视频时长]
单位:ms

w 指定截图宽度,如果指定为0,则自动计算。 [0,视频宽度]
单位:像素(px)

h 指定截图高度,如果指定为0,则自动计算;如果w和h都为0,则输出为原视频宽高。 [0,视频高度]
单位:像素(px)

m 指定截图模式,不指定则为默认模式,根据时间精确截图。如果指定为fast,则截取该时间点之前的最近的一个关键帧。 枚举值:fast

f 指定输出图片的格式。 枚举值:jpg和png

// oss获取视频的第一帧
getVideoCover(url, time, format, width, height) {
  const image = `${url}?x-oss-process=video/snapshot,t_${time},f_${format},w_${width},h_${height}`;
  return image;
},

cos获取视频首帧

https://cloud.tencent.com/document/product/460/47505
发现需要收费,还有用量限制

cos是提供了接口,还没有oss这么方便
在这里插入图片描述

这里又有两种情况,一种是后端去请求cos接口,这样能够一起返回给前端页面
一种是前端去请求接口,但是如果在视频很多的情况下,会导致这个页面要请求很多接口

个人建议:还是后端去请求接口更可靠

好了,今天的分享到这里就结束了,如果大家有什么好的方法能够解决这个问题,也欢迎大家在评论区参与讨论~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RK3568平台开发系列讲解(Camera篇)快速了解RK Camera的使用

🚀返回专栏总目录 文章目录 一、MIPI CSI1.1 Full Mode特点1.2 Split Mode特点二、Full Mode配置2.1 配置sensor端2.2 csi2_dphy0相关配置2.3 isp相关配置三、Split Mode配置3.1 配置sensor端3.2 csi2_dphy1/csi2_dphy2相关配置3.3 isp相关配置四、软件相关目录

自然语言处理实战项目7-利用层次聚类方法做文本的排重,从大量的文本中找出相似文本

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下自然语言处理实战项目7-利用层次聚类方法做文本的排重&#xff0c;从大量的文本中找出相似文本。随着互联网技术的不断发展&#xff0c;越来越多的数据被广泛地应用在各个领域中。而文本数据是其中之一&#xff0c;…

ar在汽车维修行业的应用场景

由于AR增强现实技术的易用性&#xff0c;在汽车产业链中&#xff0c;已处处可见AR技术的踪影&#xff0c;像汽车设计AR远程协同&#xff0c;汽车装配AR远程指导&#xff0c;汽车维修AR远程协助等等&#xff0c;那么下面为详细介绍AR增强现实技术在汽车制造领域的应用。 环境/物…

实力在线 | 赛宁网安连续三年入选《中国网络安全市场全景图》

​​近日&#xff0c;国内专业安全机构数说安全正式发布了《2023年中国网络安全市场全景图》&#xff08;以下简称全景图&#xff09;。此次全景图分类架构涵盖了产品、解决方案、应用场景、服务4个维度&#xff0c;覆盖了目前我国网络安全行业所有成熟的细分市场。 赛宁网安凭…

Postman实现数字签名,Session依赖, 接口依赖, 异步接口结果轮询

Script(JS)为Postman赋予无限可能 基于Postman 6.1.4 Mac Native版 演示结合user_api_demo实现 PS 最近接到任务, 要把几种基本下单接口调试和持续集成一下, 做个常规功能验证, 研究了下发现, 不同的下单途径, 有的需要登录(Session依赖), 有的需要验签(使用数字签名的微服务),…

用本地机做跳板使服务器连接外网【mac】

用自己的电脑做跳板使服务器连接外网 前提整体流程连接服务器配置服务器配置自己的电脑 前提 很多时候我们的服务器只能联内网&#xff0c;但是没法登外网&#xff0c;这样pip&#xff0c;conda 啥的都没法用&#xff0c;很麻烦。 一个简单的解决方法就是用自己的电脑作为跳板…

补充点【数据分析统计学知识点】

数据分析所需的统计学笔记 0.数据分析师拿到数据 第一利器&#xff1a;描述性统计信息 第二利器&#xff1a;对比分析法 第三利器&#xff1a;指标体系-要素拆解&#xff0c;维度拆解 第四利器&#xff1a;相关分析 第五利器&#xff1a;趋势预测 1.均值 2.中位数 3.众数…

基于Java+SpringBoot+vue的口腔管家平台设计与实现

博主介绍&#xff1a;擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案例…

Hadoop 3.0.0的集群搭建和Hive 3.1.3的安装

Hi,朋友们&#xff0c;我们又见面了&#xff0c;这一次我给大家带来的是Hadoop集群搭建及Hive的安装。 说明一下&#xff0c;网上有很多的教程&#xff0c;为什么我还要水&#xff1f; 第一&#xff0c;大多数的安装都是很顺利的&#xff0c;没有疑难解答。 第二&#xff0c…

分布式项目08 redis的扩容,预备redis宕机的哨兵处理 和 最后集大成redis集群

01.redis扩容 由于redis的容量也是有限的&#xff0c;所以&#xff0c;就会有扩容的操作。也就好像内存中扩容一样。redis有分片这种说法&#xff0c;具体实现操作如下&#xff1a; 第一步&#xff1a;首先在/usr/local/src中去找到redis文件夹。cd /usr/local/src 而后进入re…

瑞吉外卖 - 菜品信息分页查询功能(17)

某马瑞吉外卖单体架构项目完整开发文档&#xff0c;基于 Spring Boot 2.7.11 JDK 11。预计 5 月 20 日前更新完成&#xff0c;有需要的胖友记得一键三连&#xff0c;关注主页 “瑞吉外卖” 专栏获取最新文章。 相关资料&#xff1a;https://pan.baidu.com/s/1rO1Vytcp67mcw-PD…

开源大语言模型完整列表

Large Language Model (LLM) 即大规模语言模型&#xff0c;是一种基于深度学习的自然语言处理模型&#xff0c;它能够学习到自然语言的语法和语义&#xff0c;从而可以生成人类可读的文本。 LLM 通常基于神经网络模型&#xff0c;使用大规模的语料库进行训练&#xff0c;比如使…

【Linux进阶之路】基本指令(下)

文章目录 一. 日志 date指令——查看日期基本语法1基本语法2cal指令——查看日历常见选项 二 .find——查找文件常用选项-name显示所有文件显示指定类型的文件 三.grep——行文本过滤工具语法常见的用法补充知识——APP与服务器的联系 四.打包压缩与解压解包zip与unzipzipunzip…

又一高速AD/DA数据采集卡问世,何不了解一下?

FMC190 子卡集成 2 通道 3Gsps 采样率&#xff0c;14 位 AD&#xff0c;2 通道12.8Gsps 16 位DA。板载时钟芯片 HMC7044&#xff0c;可以提供JESD204B所需要的各种时钟。具有同步/触发功能&#xff0c;模拟信号采用 SSMA 射频连接器输入和输出。板载时钟芯片为 HMC7044&#xf…

Unity UI -- (3)管理屏幕大小和锚点

在前面我们探索了一些基本的文本格式。我们需要考虑一个问题&#xff0c;这个文本在屏幕大小发生变化时该如何适应呢&#xff1f; 在Unity中&#xff0c;我们可以使用Canvas和Anchor Point&#xff08;锚点&#xff09;系统来确保UI元素总是出现在正确的位置&#xff0c;不管它…

Arduino ESP32的串口

Arduino的ESP32的三个串口 ESP32的三个串口 在Arduino的核心中有三个串口&#xff0c;分别是Serial&#xff0c;Serial1&#xff0c;和Serial2. 当然ESP32也可以使用软串口。但硬件串口更稳健&#xff08;我这样理解&#xff09;。但Serial1这个串口不能使用&#xff0c;因为…

腾讯云轻量应用服务器公网带宽速度如何?

腾讯云轻量应用服务器网速怎么样&#xff1f;轻量应用公网IP带宽速度与地域有关&#xff0c;中国大陆地域如北京、上海、广州等地域采用多线BGP网络&#xff0c;保障轻量应用服务器的速度&#xff0c;中国香港国际CN2高速网络。中国内地的地域大家是完全不用担心&#xff0c;大…

Eureka实战入门

spring cloud的版本 spring cloud的版本是根据springboot版本确定的 Spring Cloud Dalston, Edgware, Finchley, and Greenwich 版本不再更新了 版本 SNAPSHOT&#xff1a;开发版本&#xff0c;最新版本 Milestone&#xff1a;大版本在发布前的里程碑的版本 Release candi…

usb摄像头驱动-core层hub.c

usb摄像头驱动-core层hub.c 文章目录 usb摄像头驱动-core层hub.cusb_hub_inithub_probehub_eventport_eventhub_port_connect_changehub_port_connectusb_new_deviceannounce_device 在USB摄像头驱动中&#xff0c;hub.c文件扮演着USB集线器&#xff08;Hub&#xff09;驱动的角…

<数据结构>顺序表和链表的比较|缓存命中率

&#x1f4ad;前言&#xff1a;通过之前对顺序表和链表的实现&#xff0c;我们可以发现在增删查改某些操作上两者的效率前言有所差异&#xff0c;本篇文章将总结二者的异同。 顺序表的实现http://t.csdn.cn/Lxyg2单链表的实现http://t.csdn.cn/rHgjG双链表的实现http://t.csdn.…