Video. Js is introduced

Video.js – open source HTML5 & Flash video player

As the front end of a high performance streaming media server, it is necessary to use streaming media player. In the selection of player, we selected the powerful and open source video.js. It can be used to play RTMP/HLS live streams.

In this article about integrating the Video.js player component into webpack, we will complete a small example of an HLS player. Let’s take a look at the image:



The installation video. Js

The HLS player we are developing needs to use an official plug-in for video.js: videojs-contrib-hls

Although video. Js webpack integration instructions are given in the official document (docs.videojs.com/tutorial-we…). However, in the actual development process, I still encountered as many pitfalls as anyone (github.com/videojs/vid…). Finally, after integrating video.js, I found that I couldn’t switch to Flash mode by inserting HLS stream. Finally, I decided to integrate video.js with external dependencies to familiarize myself with the use of Webpack externals. This is the “external dependency method”.

Since “external dependency method “, let’s first prepare the external dependency video.js file. Create the externals directory under the SRC directory and copy the downloaded video-js-5.19.2 directory file to this directory.

Modify template.html as follows:

<html> <head> <title><%= htmlWebpackPlugin.options.title %></title> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport"> <! -- video.js --> <link rel="stylesheet" href="/ video.js-5.19.2 / video.js.css "/> <script SRC ="/video-js-5.19.2/video.js"></script> <script SRC ="/video-js-5.19.2/videojs-contrib-hls4.js"></script> </head> <body  class="skin-green sidebar-mini"> <div id="app"></div> </body> </html>Copy the code

Modify webpack.dll.config.js as follows:

const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const webpack = require('webpack'); const path = require('path'); function resolve(dir) { return path.resolve(__dirname, dir) } module.exports = { entry: {// Extract the common component and package it as vendor.js vendor: ['jquery', 'vue', 'vuex', 'babel-polyfill', 'font-awesome/css/font-awesome.css', 'admin-lte/bootstrap/css/bootstrap.css', 'admin-lte/dist/css/AdminLTE.css', 'admin-lte/dist/css/skins/_all-skins.css', 'admin-lte/bootstrap/js/bootstrap.js', 'admin-lte/dist/js/app.js'] }, output: { path: resolve('dll'), filename: 'js/[name].[chunkhash:8].js', library: '[name]_library' }, resolve: { extensions: ['.js', '.vue', '.json'], alias: {' vue $' : 'vue/dist/vue.com mon. Js',' jquery $' : 'admin - lte/plugins/jquery/jquery - 2.2.3. Min. Js'}}, the module: {rules: [{ test: /\.css$/, loader: 'style-loader!css-loader' }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader?limit=10000&name=images/[name].[hash:8].[ext]' }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader?limit=10000&name=fonts/[name].[hash:8].[ext]' }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: 'url-loader?limit=10000&name=media/[name].[hash:8].[ext]' }] }, plugins: [ new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', "window.jQuery": 'jquery', "window.$": 'jquery' }), new CleanWebpackPlugin(['dll']), new CopyWebpackPlugin([ { from: 'src/externals' } ]), new webpack.DllPlugin({ path: resolve("dll/[name]-manifest.json"), name: "[name]_library", context: __dirname }), new HtmlWebpackPlugin({ filename: 'template.html', title: '<%= htmlWebpackPlugin.options.title %>', inject: 'head', chunks: ['vendor'], template: './src/template.html', minify: { removeComments: true, collapseWhitespace: false } }) ] };Copy the code

Import the CopyWebpackPlugin to copy external dependency files from the externals directory to the DLL directory. Finally, these external dependency files will be copied to the distribution directory

Modify webpack.config.js as follows:

const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const webpack = require('webpack'); const path = require('path'); require("babel-polyfill"); Function resolve(dir) {return path.resolve(__dirname, dir)} module.exports = {// define the entry of the page. {index: ['babel-polyfill', './ SRC /index.js'], player: ['babel-polyfill', './src/player.js'], about: ['babel-polyfill', './src/about.js'] }, output: { path: Resolve ('dist'), // specifies the publishing directory filename: 'js/[name].[chunkhash:8].js' // specifies the directory and filename of the generated page-entry js file, including the 8-bit hash value}, externals: {//video.js as an external resource to import 'video.js': 'videojs'}, // alias some common components and directories to import resolve: {extensions: ['.js', '.vue', '.json'], alias: { 'vue$': 'vue/dist/vue.common.js', 'jquery$': 'admin - lte/plugins/jQuery/jQuery - 2.2.3. Min. Js',' SRC ': resolve (' SRC'), 'assets' : resolve (' SRC/assets'),' components' : Resolve (' SRC /components')}}, module: {// configure webpack to load resources rules: [{test: /\.js$/, loader: 'babel-loader', include: [resolve('src')] }, { test: /\.vue$/, loader: 'vue-loader' }, { test: /\.css$/, loader: 'style-loader!css-loader' }, { test: /\.less$/, loader: "less-loader" }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader?limit=10000&name=images/[name].[hash:8].[ext]' }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url-loader?limit=10000&name=fonts/[name].[hash:8].[ext]' }, { test: /\.(swf|mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, loader: [hash:8].[ext]'}]}, plugins: [// new webpack.providePlugin ({$: 'jquery', jQuery: 'jquery', "window.jQuery": 'jquery', "window.$": 'jquery' }), new webpack.DllReferencePlugin({ context: __dirname, manifest: require('./dll/vendor-manifest.json') }), new CopyWebpackPlugin([ { from: 'dll', ignore: ['template.html', 'vendor-manifest.json']}]), new CleanWebpackPlugin(['dist']), Dist /js/index.[chunkhash:8].js // SRC /index.html as a template new HtmlWebpackPlugin({filename: 'index.html', title: 'video square ', inject: true, // head -> Cannot find Element: #app chunks: ['index'], template: './dll/template.html', minify: { removeComments: true, collapseWhitespace: False}}), new HtmlWebpackPlugin({filename: 'player.html', title: 'HLS player ', inject: true, chunks: ['player'], template: './dll/template.html', minify: { removeComments: true, collapseWhitespace: False}}), // generate version information page, Dist /js/about.[chunkhash:8].js // SRC /index.html as a template new HtmlWebpackPlugin({filename: 'about.html', title: 'version info ', Inject: true, chunks: ['about'], template: './ DLL /template.html', minify: { removeComments: true, collapseWhitespace: false } }) ] };Copy the code

The point is to declare VideoJS to be used as an external resource under the externals block and then add a new static page configuration that will be used as an entry point for the HLS player

Add the left menu item

Open SRC /store/index.js and modify as follows:

import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex); const store = new Vuex.Store({ state: { logoText: "EasyDSS", logoMiniText: "DSS", menus: [ { path: "/index.html", icon: "Mouse -pointer", text:" video square "}, {path: "/player. HTML ", icon: "play", text: "HLS player "}, {path: "/about.html", icon: Mutations: {}, mutations: {}, actions: {}}) export default store;Copy the code

Write the HLS player page

Will video. Js simple encapsulated into components, the newly built/SRC/compontents VideoJS. Vue

<template> <div class="player-wrapper"> <div class="video-wrapper" style="padding-bottom:55%; position:relative; margin:0 auto; overflow:hidden;" > <div class="video-inner" style="position:absolute; top:0; bottom:0; left:0; right:0;" > < / div > < / div > < / div > < / template > < script > videojs. Options. The flash. The SWF = '/ video - js - 5.19.2 / video - js - fixed. SWF'; videojs.options.techOrder = ['html5', 'flash']; if (videojs.browser.IE_VERSION) { // if IE use flash first videojs.options.techOrder = ['flash', 'html5']; } export default { data() { return { player: null } }, props: { videoUrl: { default: "" }, autoplay: { default: true } }, beforeDestroy() { this.destroyVideoJS(); }, deactivated() { this.destroyVideoJS(); }, watch: { videoUrl: function(val) { this.destroyVideoJS(); this.initVideoJS(); } }, mounted() { this.initVideoJS(); }, computed: { type() { let _type = "application/x-mpegURL"; if (this.rtmp) { _type = "rtmp/mp4"; } return _type; }, rtmp() { return (this.src || "").indexOf("rtmp") == 0; }, src() { if (! this.videoUrl) { return ""; } if (this.videoUrl.indexOf("/") === 0) { return location.protocol + "//" + location.host + this.videoUrl; } return this.videoUrl; }, videoHtml() { return ` <video class="video-js vjs-default-skin vjs-big-play-centered" style="width: 100%; height: 100%;" controls preload="none"> <source src="${this.src}" type="${this.type}"></source> <p class="vjs-no-js"> To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank"> supports  HTML5 video </a> </p> </video> `; } }, methods: { destroyVideoJS() { if (this.player) { this.player.dispose(); this.player = null; } }, initVideoJS() { $(this.$el).find(".video-inner").empty().append(this.videoHtml); if (! this.src) { return; } if (this.rtmp) { this.player = videojs($(this.$el).find("video")[0], { notSupportedMessage: 'Flash is not installed or enabled in your browser ', tech: [' Flash'], Autoplay: this.autoplay}); this.player.on("error", e => { var $e = $(this.$el).find(".vjs-error .vjs-error-display .vjs-modal-dialog-content"); var $a = $("<a href='http://www.adobe.com/go/getflashplayer' target='_blank'></a>").text($e.text()); $e.empty().append($a); }) } else { this.player = videojs($(this.$el).find("video")[0], { autoplay: this.autoplay }); } } } } </script>Copy the code

Encapsulate video. Js API

Write player pop-up components, new SRC/components/VideoDlg vue

<template> <div class="modal fade" data-keyboard="false" data-backdrop="static"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">&times; </span> </button> <h4 class="modal-title text-success text-center">{{videoTitle}}</h4> </div> <div class="modal-body"> <VideoJS v-if="bShow" :videoUrl="videoUrl"></VideoJS> </div> <div class="modal-footer"> <button type="button" class="btn Btn-default "data-dismiss="modal"> close </button> </div> </div> </div> </template> <script> import VideoJS from './VideoJS.vue' export default { data() { return { videoUrl: "", videoTitle: "", bShow: false } }, mounted() { $(document).on("hide.bs.modal", this.$el, () => { this.bShow = false; }).on("show.bs.modal", this.$el, () => { this.bShow = true; }) }, components: { VideoJS }, methods: { play(src,title) { this.videoUrl = src||""; this.videoTitle = title||""; $(this.$el).modal("show"); } } } </script>Copy the code

Encapsulate the Bootstrap modal box

Write HLS Player page content, new SRC/components/Player. The vue

<template> <div class="container-fluid no-padding"> <br> <div class="col-sm-8 col-sm-offset-2"> <form role="form" class="form-horizontal" id="url-form"> <div class="form-group"> <div class="input-group" id="input-url-group"> <input Type ="text" class="form-control" id="input-url" name="url" placeholder=" input address "V-model. trim="url" @keydown.enter.prevent="play"> <span class="input-group-btn"> <a class="btn btn-primary" role="button" @ click. Prevent = "play" > < I class = "fa fa - play" > < / I > play < / a > < / span > < / div > < / div > < / form > < / div > < / div > < / template > <script> import Vue from 'vue' import { Message } from 'element-ui' Vue.prototype.$message = Message; export default { data() { return { url: "" } }, methods: { play() { if (! This. Url) {this.$message({type: 'error', message: "play address cannot be empty"}); return; } this.$emit("play", { videoUrl: this.url, videoTitle: this.url}); } } } </script>Copy the code

Here, in passing, is an example of the Message usage of Element-UI: click the play button, and the Message is passed to the parent, along with the play address as an argument

Create an entry js and create a new SRC /player.js

import Vue from 'vue' import store from "./store"; import AdminLTE from './components/AdminLTE.vue' import Player from './components/Player.vue' import VideoDlg from './components/VideoDlg.vue' new Vue({ el: '#app', store, template: ` <AdminLTE> <VideoDlg ref="videoDlg"></VideoDlg> <Player @play="play"></Player> </AdminLTE>`, components: { AdminLTE, Player, VideoDlg }, methods: { play(video){ this.$refs.videoDlg.play(video.videoUrl, video.videoTitle); }}})Copy the code

Receive the playback message from the Player component, open the pop-up box of the Player, and complete the video playback

run

We changed template.html and webpack.dll.config.js, so we need to rebuild the common component library first

npm run dll
Copy the code

then

npm run start
Copy the code

WEB:www.liveqing.com