• 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
    
    • 1

    将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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

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

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

    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}/>
    			<!-- ... -->
    		</>
    	);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    更多的播放器事件定义可以参考另一篇博文[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.js。如果因为一些代码逻辑问题,DOM树中没有video标签,此时初始化video.js对象也会报这个错。

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

    重复初始化报错

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

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

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

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

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

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


    后记

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

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


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

    2. React and Video.js ↩︎

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

  • 相关阅读:
    原生Hadoop3.X高可用配置方式
    【嵌入式开源库】MultiButton的使用,简单易用的事件驱动型按键驱动模块
    内省机制(操作javaBean的信息)
    网络安全(黑客)自学
    java aspose cells 读取名称管理器
    ETLCloud制造业轻量级数据中台解决方案
    Node.js躬行记(21)——花10分钟入门Node.js
    什么是过期域名?做网站用过期域名好不好?
    sqlserver sql语句优化
    Elasticsearch安装
  • 原文地址:https://blog.csdn.net/Mr_Megamind/article/details/127705263