草庐IT

React学习笔记(番外一)——video.js视频播放组件的入门及排坑经历

IMplementist 2023-04-10 原文

React学习笔记(番外一)——video.js视频播放组件的入门及排坑经历

前言

很久没有静下心写博客了。近段时间接到一个任务,前端页面要加上视频播放功能。实现加排坑前后花了三天时间(别问我问什么这么久😂),觉得还是有必要记录一下的。


video.js的支持的视频格式及编码方式

这一部分有必要写在最前面,避免你看了一长串安装、引入、代码,然后发现自己想要播放的视频格式或编码video.js不支持。

支持的扩展名(格式)

从另一篇博客[1]中了解到,它支持mp4、webm、ogv、m3u8、flv、rtmp等扩展名的视频文件。

支持的视频编码

mp4为例,虽然扩展名都可以是mp4,但不同视频文件的编码可以是MPEG4、H.264等,这里要注意,video.js只支持H.264编码的mp4文件, 如果你要播放的文件编码不是H.264,需要先转换编码,或者在录制的时候设置为H.264

查看视频文件编码方式的方法:

  • Mac
    文件右键菜单 -> 显示简介 -> 更多信息 -> 编解码器
  • Windows
    文件右键菜单 -> 属性

video.js的安装

现在比较知名的前端库都支持node.js,video.js也不例外,所以安装只需要:

    npm install video.js

将video.js引入React

起初按照一些博文抄代码到自己的项目,发现会有一些报错,后来耗时很久没有解决,找到了官方给的示例代码[2],简洁且跑起来没有报错。
我这里把注释翻译一下,略作改动:

自定义播放器控件

import React from 'react';
import videojs from 'video.js';
// 记得引用css文件!
import 'video.js/dist/video-js.css';

export const VideoJS = (props) => {
  // video标签的引用Hook
  const videoRef = React.useRef(null);
  // 播放器实例的引用Hook
  const playerRef = React.useRef(null);
  const {options, onReady} = props;

  React.useEffect(() => {
	  // 确保video.js的播放器实例player仅被初始化一次,否则会报错
	  if (!playerRef.current) {
	      const videoElement = videoRef.current;
	      if (!videoElement) {
	          return;
	      }
	
	      const player = playerRef.current = videojs(videoElement, options, () => {
	          videojs.log('播放器准备就绪!');
	          onReady && onReady(player);
	      });
	  // 当props发生变化时,可以对已经存在的player实例作一些操作,如:
	  } else {
	     // const player = playerRef.current;
	     // player.autoplay(options.autoplay);
	     // player.src(options.sources);
	  }
  }, [options, videoRef]);

  // 控件被unmount卸载的时候,记得要对player实例执行反初始化dispose
  React.useEffect(() => {
    const player = playerRef.current;

    return () => {
      if (player) {
        player.dispose();
        playerRef.current = null;
      }
    };
  }, [playerRef]);

  return (
    <div data-vjs-player>
      <video ref={videoRef} className='video-js vjs-big-play-centered'/>
    </div>
  );
}

export default VideoJS;

引用自定义视频播放器控件

有了上述的视频播放器控件,我们可以在任意需要播放视频的页面中引用它:

import React from 'react';

// 从控件的路径引用它,这里默认它和页面在同一目录下
import VideoJS from './VideoJS'

const App = () => {
	const playerRef = React.useRef(null);

	const videoJsOptions = {
		// 自动播放:为true时,加载完毕自动播放
		autoplay: true,
		// 播放器子控件是否显示:为true时显示暂停、播放速率等按钮
		controls: true,
		// 响应性:为true时,播放视频进度条会自动移动
		responsive: true,
		// 流式布局:为true时尽可能大得填满父标签的剩余空间
		fluid: true,
		// 视频源
		sources: [{
			// 视频文件的路径,可以是一个前端相对路径、后台视频文件URL、直播源等
			src: '/path/to/video.mp4',
			// 视频源类型
			type: 'video/mp4'
		}]
	};

	// 播放器实例化完成后的事件动作,注意!不是视频加载成功
	const handlePlayerReady = (player) => {
		playerRef.current = player;
		// 播放器的子事件在这里定义

		player.on("canplaythrough", () => {
	        console.log("视频加载完成!")
        });
        
		player.on("error", () => {
            console.log(`视频文件加载失败,请稍后重试!`);
        });
        
        player.on("stalled", () => {
            console.log(`网络异常,请稍后重试!`);
        });
	};

	return (
		<>
			<!-- ... -->
			<VideoJS options={videoJsOptions} onReady={handlePlayerReady}/>
			<!-- ... -->
		</>
	);
}

更多的播放器事件定义可以参考另一篇博文[3]


排坑记录

报错VIDEOJS ERROR (CODE4 MEDIA_ERR_SRC_NOT_SUPPORTED) No compatible source was found for this media

我自己遇到两种情况引起的这个报错:

  1. 视频源的格式或编码方式video.js真的不支持 - 这种情况只能调整视频源,转码或者替换其他前端视频播放控件。

    我当时遇到的情况是: 起初不太懂同样是MP4文件,还有编码方式的区别。幸而有多个视频源,有的可以播放,有的不可以播放。点开文件属性进行对比,发现了编码方式的区别,即上述不支持MPEG4编码的问题。
  2. HTML DOM树中没有指定的video标签 - 从上述代码中可以看到,我们需要一个<video></video>标签去初始化video.js。如果因为一些代码逻辑问题,DOM树中没有video标签,此时初始化video.js对象也会报这个错。

    我当时遇到的情况是: 一个高阶组件通过props里的参数控制渲染一个<img></img>图片标签还是<video></video>视频标签。由于js代码逻辑有漏洞,渲染<img></img>图片标签的同时通过videojs(...)初始化了一个播放器对象。

重复初始化报错

严格意义上说是一个警告,原文不记得了,大致意思就是说player对象已经初始化过一次,所以不再接受options里的新参数。

这个是因为在组件上次unmount卸载的时候没有对player对象进行销毁dispose(),按上述的代码去写,就不会有这个问题。

React底层代码报错:要删除的标签video不存在

由于dispose()函数会删除<video></video>标签,之前借鉴其他博文里通过id初始化video.js对象,dispose()的时候就报了这个错。

原本的预期应该是React底层先unmount,这个时候就删除了<video></video>标签,dispose()在其后执行就不会报错。但是借鉴了那些代码之后不知道为什么dispose()执行在unmount之前了,React底层找不到要删除的<video></video>标签,就抛错了,并且导致了页面白屏。

解决方法同上,使用官方给出的示例代码去实现就好了,不会报这样的错。


后记

首先,感谢video.js视频播放组件的开发者及贡献者。这个组件很简单易用,为音视频的门外汉节省了很多时间和代码量。

实现过程中,一开始参考一些个人博文的代码,大多数是基于class组件实现的,和我的函数式组件及React Hook框架有些水土不服。所以花了些时间在踩坑和排坑上。如果一开始就看了官方的示例,就能省些时间。另外遇到及处理视频编码的问题,也额外学习到了一些相应的知识。


  1. video 、video.js、playease.js支持的视频格式 ↩︎

  2. React and Video.js ↩︎

  3. videojs的一些监听事件汇总 ↩︎

有关React学习笔记(番外一)——video.js视频播放组件的入门及排坑经历的更多相关文章

  1. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  2. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  3. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  4. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  5. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  6. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  7. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  8. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  9. ruby-on-rails - Ruby 长时间运行的进程对队列事件使用react - 2

    我有一个将某些事件写入队列的Rails3应用。现在我想在服务器上创建一个服务,每x秒轮询一次队列,并按计划执行其他任务。除了创建ruby​​脚本并通过cron作业运行它之外,还有其他稳定的替代方案吗? 最佳答案 尽管启动基于Rails的持久任务是一种选择,但您可能希望查看更有序的系统,例如delayed_job或Starling管理您的工作量。我建议不要在cron中运行某些东西,因为启动整个Rails堆栈的开销可能很大。每隔几秒运行一次它是不切实际的,因为Rails上的启动时间通常为5-15秒,具体取决于您的硬件。不过,每天这样做几

  10. ruby-on-rails - Assets 管道损坏 : Not compiling on the fly css and js files - 2

    我开始了一个新的Rails3.2.5项目,Assets管道不再工作了。CSS和Javascript文件不再编译。这是尝试生成Assets时日志的输出:StartedGET"/assets/application.css?body=1"for127.0.0.1at2012-06-1623:59:11-0700Servedasset/application.css-200OK(0ms)[2012-06-1623:59:11]ERRORNoMethodError:undefinedmethod`each'fornil:NilClass/Users/greg/.rbenv/versions/1

随机推荐