video.js 视频截图录制自定义全屏,hlsflvmp4视频播放
Posted 肖某人2077
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了video.js 视频截图录制自定义全屏,hlsflvmp4视频播放相关的知识,希望对你有一定的参考价值。
功能
- video.js内嵌 截图、录制功能 (图片、视频会下载到本地)
- 自定义全屏
- 播放hls、flv、mp4
- 功能集合成Vue组件
参考
播放hls、flv、mp4
安装
// video.js
npm install video.js
// 播放hls
npm install videojs-contrib-hls
// 播放flv
npm install videojs-flvjs-es6
npm install flv.js
引入
import "videojs-contrib-hls";
import "videojs-flvjs-es6";
import videojs from "video.js";
import video_zhCN from "video.js/dist/lang/zh-CN.json";
videojs.addLanguage("zh-CN", video_zhCN);
export default
props:
name:
type: String,
default: "my-video",
,
// 视频地址
videoUrl:
type: String,
default: "",
,
//视频宽度
videoWidth:
type: String,
default: "100%",
,
//视频高度
videoHeight:
type: String,
default: "100%",
,
,
data()
const options =
// 封面图
poster: '',
language: "zh-CN",
// 设置自动播放
autoplay: true,
// 设置了它为true,才可实现自动播放,同时视频也被静音 (Chrome66及以上版本,禁止音视频的自动播放)
muted: true,
// 预加载
preload: "none",
// 显示播放的控件
controls: true,
// 进度条
liveui: true,
notSupportedMessage: "此视频暂无法播放,请稍后再试", // 允许覆盖Video.js无法播放媒体源时显示的默认信息。
// 播放速率
playbackRates: [0.5, 1, 2, 4, 8, 16],
controlBar:
// 画中画
pictureInPictureToggle: false,
// 当前时间和持续时间的分隔符
timeDivider: true,
// 显示持续时间
durationDisplay: true,
// 是否显示剩余时间功能
remainingTimeDisplay: true,
// 是否显示全屏按钮
fullscreenToggle: true,
,
return
options,
// videoUrl 从无到有时会显示一会视频错误信息
// 使用该字段判断 no-video 来避免显示错误信息
hasVideoUrl: false,
// 视频流类型
videoTypeObj:
mp4: 'video/mp4',
flv: 'video/x-flv',
m3u8: 'application/x-mpegURL'
;
,
methods:
getVideo(nowPlayVideoUrl)
if (!nowPlayVideoUrl) this.hasVideoUrl = false;
if (!this.player)
this.player = videojs(this.$refs.player, this.options);
this.player.src([
src: nowPlayVideoUrl,
type: this.videoUrl
?
this.videoTypeObj[this.videoUrl.split('.').slice(-1)]
:
''
,
]);
setTimeout(() =>
this.hasVideoUrl = !!nowPlayVideoUrl;
, 100);
,
watch:
//监听视频地址、video的id值
videoUrl:
deep: true,
immediate: true,
handler(val)
this.$nextTick(() =>
this.getVideo(val);
);
,
,
,
beforeDestroy()
// 组件销毁时,清除播放器
if (this.player) this.player.dispose();
,
;
<div
:style=" width: videoWidth, height: videoHeight, position: 'relative' "
class="display"
>
<video
style="width: 100%; height: 100%"
class="video-js"
ref="player"
v-show="videoUrl"
>
</video>
<!-- 视频路径为空时 -->
<div
v-show="!hasVideoUrl"
:style="
height: videoWidth,
width: videoHeight,
position: 'absolute',
left: 0,
top: 0,
zIndex: 2,
background: '#000',
"
>
no video
</div>
</div>
引入组件(视频地址使用的时西瓜的测试视频)
import Player from './components/Player.vue'
export default
name: 'App',
components:
Player
,
data:
// flv
nowPlayVideoUrl: "//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv",
// mp4
// nowPlayVideoUrl: "//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4",
// hls
// nowPlayVideoUrl: "//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/hls/xgplayer-demo.m3u8",
...
<template>
<div style="height:400px;width:600px">
<Player :videoUrl="nowPlayVideoUrl"/>
</div>
</template>
加入自定义全屏
在props中加入 name 、fullscreenType、fullscreenChange属性
props:
...
// 当存在多个直播时,用于判断是哪个直播需要全屏
name:
type: String,
default: "my-video",
,
// 全屏类型
fullscreenType:
type: String,
default: "initial",
validator: function (value)
// 这个值必须匹配下列字符串中的一个
return ["none", "DIY", "initial"].indexOf(value) !== -1;
,
// 全屏状态改变 当fullscreenType为DIY时有效
fullscreenChange:
type: Function,
default: () => ,
,
修改options
const options=
...
controlBar:
// 画中画
pictureInPictureToggle: false,
// 当前时间和持续时间的分隔符
timeDivider: true,
// 显示持续时间
durationDisplay: true,
// 是否显示剩余时间功能
remainingTimeDisplay: true,
// 是否显示全屏按钮
fullscreenToggle: this.fullscreenType === "initial" ? true : false,
,
修改getVideo方法
if (!nowPlayVideoUrl) this.hasVideoUrl = false;
if (!this.player)
this.player = videojs(this.$refs.player, this.options);
// 自定义全屏功能
if (this.fullscreenType === "DIY") this.setDIYFullscreen();
...
编写setDIYFullscreen方法
setDIYFullscreen()
const fullscreenButton = this.player.controlBar.addChild("button");
fullscreenButton.controlText("全屏");
fullscreenButton.addClass("vjs-fullscreen-control");
const fullscreenButtonDom = fullscreenButton.el();
fullscreenButtonDom.onclick = () =>
this.isDIYFullscreen = !this.isDIYFullscreen;
if (this.isDIYFullscreen)
this.player.addClass("vjs-fullscreen");
fullscreenButton.controlText("退出全屏");
console.log('全屏');
else
this.player.removeClass("vjs-fullscreen");
fullscreenButton.controlText("全屏");
console.log('退出全屏');
// 父组件回调
this.fullscreenChange(this.isDIYFullscreen, this.name);
;
修改父组件引入
<template>
<div style="height:400px;width:600px">
<Player fullscreenType="DIY" :videoUrl="nowPlayVideoUrl"/>
</div>
</template>
效果
自定义全屏
加入截图、录制功能
安装
// 录制所需插件
npm i recordrtc
创建video.js文件,将video.js相关引入、逻辑都放整合放在这里(自定义全屏除外)
import videojs from "video.js";
import video_zhCN from "video.js/dist/lang/zh-CN.json";
import RecordRTC from "recordrtc";
import "videojs-contrib-hls";
import "videojs-flvjs-es6";
// 截图图片
import cameraImg from '../assets/images/camera.png'
// 录像图片
import monitorImg from '../assets/images/monitor.png'
videojs.addLanguage("zh-CN", video_zhCN);
// 创建 截图、录像
var Component = videojs.getComponent("Component");
var CustomBar = videojs.extend(Component,
constructor: function (player, options) ,
createEl: function ()
return videojs.dom.createEl('div',
innerHTML: `这是一个自定义组件`
)
)
// 注册 截图、录像组件
videojs.registerComponent('CustomBar', CustomBar);
export default videojs
创建custom-video.css文件,video相关css放在这里
@import "video.js/dist/video-js.css";
更改组件引入,以及在options中加入customBar
import videojs from '../utils/video'
import '../assets/css/custom-video.css'
....
options =
customBar:
...
效果
修改CustomBar,界面显示
...
// 截图图片
import cameraImg from '../assets/images/camera.png'
// 录制图片
import monitorImg from '../assets/images/monitor.png'
var CustomBar = videojs.extend(Component,
...
createEl: function ()
const divDom = videojs.dom.createEl('div',
className: 'vjs-custom-bar',
innerHTML: `
<div class="vjs-custom-control-bar vjs-button ac">
<img src="$cameraImg" style="width:13px" />
<span class="ml10">截图</span>
</div>
<div class="mt10 vjs-custom-control-bar ac" >
<img src="$monitorImg" style="width:13px" />
<span class="ml10">录像</span>
</div>
`
)
return divDom
修改custom-video.css
customBar位于右侧中间显示,鼠标活动以及悬浮在customBar显示customBar,不活动时隐藏
录制中时红点闪烁
@import "video.js/dist/video-js.css";
.vjs-custom-bar
position: absolute;
color: #fff;
right: 10px;
transform: translateY(-50%);
top: 50%;
.vjs-custom-bar:hover
opacity: 1 !important;
.vjs-custom-control-bar
padding: 10px;
background: rgba(43, 51, 63, 0.7);
border-radius: 5px;
cursor: pointer;
/* 开始录制 闪烁 */
.record-procees
display: inline-block;
height: 10px;
width: 10px;
background: red;
border-radius: 8px;
animation: blings 1s linear infinite;
.mt10
margin-top: 10px;
.ml10
margin-left: 10px;
.ac
display: flex;
align-items: center;
@keyframes blings
0%
opacity: 0.1;
100%
opacity: 1;
.video-js .vjs-custom-bar
color: white;
/* font-size: 2em; */
padding: .5em;
.vjs-has-started .vjs-custom-bar
/* display: flex; */
visibility: visible;
opacity: 1;
transition: visibility 0.1s, opacity 0.1s;
/* 用户不活动时设计title bar自动隐藏 */
.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-custom-bar
visibility: visible;
/*visibility: hidden;*/
opacity: 0;
transition: visibility 1s, opacity 1s;
.vjs-controls-disabled .vjs-custom-bar,
.vjs-using-native-controls .vjs-custom-bar,
.vjs-error .vjs-custom-bar
display: none !important;
.vjs-audio.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-custom-bar
opacity: 0;
visibility: visible;
/*visibility: hidden;*/
.vjs-has-started.vjs-no-flex .vjs-custom-bar
display: table;
CustomBar加入隐藏逻辑
...
constructor: function (player, options)
// Equivalent of `super(this, arguments)`
Component.apply(this, arguments);
// 隐藏截图
if (options.screenshot === false) this.hiddenEl(0)
// 隐藏录像
if (options.recorder === false) this.hiddenEl(1)
,
createEl: function ()
...
hiddenEl (index)
const myDom = this.el().querySelectorAll('div')[index]
myDom.setAttribute('style', 'display:none')
当需要隐藏录像时,修改options中customBar属性
options =
// 截图、录制Bar
customBar:
screenshot: true,
recorder: false
....
customBar加入截图、录制逻辑
...
constructor: function (player, options)
// Equivalent of `super(this, arguments)`
Component.apply(this, arguments);
// player 实列
this.player = player
// 录像所需要的 canvas
this.canvas = null
// 录像实列
this.recorder = null
// 停止循环帧 需要用到的参数
this.animationFrame = null
// 录像状态 false 未录像 true 录像中
this.isRecorder = false
// 隐藏截图
if (options.screenshot === false) this.hiddenEl(0)
// 隐藏录像
if (options.recorder === false) this.hiddenEl(1)
,
createEl: function ()
const divDom = videojs.dom.createEl('div',
className: 'vjs-custom-bar',
innerHTML: `
<div class="vjs-custom-control-bar vjs-button ac">
<img src="$cameraImg" style="width:13px" />
<span class="ml10">截图</span>
</div>
<div class="mt10 vjs-custom-control-bar ac" >
<img src="$monitorImg" style="width:13px" />
<span class="ml10">录像</span>
</div>
`
)
const [screenshotDom, recordDom] = divDom.querySelectorAll('div')
screenshotDom.onclick = () => this.screenshotHandle()
recordDom.onclick = () => this.recordHandle(recordDom)
return divDom
,
...
截图 screenshotHandle方法
// 截图
screenshotHandle()
const fileType = "png";
// 找到需要截图的video标签
// video 实列
const video = this.player.el().querySelector('video')
// const video = this.video;
console.log(video, 'video');
const canvas = document.createElement("canvas");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
console.log(canvas, 'canvas')
canvas
.getContext("2d")
.drawImage(video, 0, 0, canvas.width, canvas.height); // 图片大小和视频分辨率一致
const strDataURL = canvas.toDataURL("image/" + fileType); // canvas中video中取一帧图片并转成dataURL
let arr = strDataURL.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--)
u8arr[n] = bstr.charCodeAt(n);
const blob = new Blob([u8arr],
type: mime,
);
const url = window.URL.createObjectURL(blob);
this.downloadFile(url, "png");
,
录制 recordHandle方法,
// 录像
recordHandle (recordDom)
this.isRecorder = !this.isRecorder
if (this.isRecorder)
recordDom.innerHTML = `<i class="record-procees"></i><span class="ml10">结束</span>`
if (!this.canvas) this.canvas = document.createElement("canvas");
this.recorder = RecordRTC(this.canvas,
type: "canvas",
);
this.recorder.startRecording();
this.drawMedia();
else
// recordDom.innerHTML = `<i class="el-icon-video-camera-solid"></i><span class="ml10">录像</span>`
recordDom.innerHTML = `<img src="$monitorImg" style="width:13px" /><span class="ml10">录像</span>`
this.recorder.stopRecording(() =>
const url = window.URL.createObjectURL(this.recorder.getBlob());
this.downloadFile(url, "mp4");
cancelAnimationFrame(this.animationFrame);
this.canvas = null;
this.animationFrame = null;
);
,
// 刷新canvas
drawMedia()
const ctx = this.canvas.getContext("2d");
// 找到需要截图的video标签
const video = this.player.el().querySelector('video')
this.canvas.setAttribute("width", video.videoWidth);
this.canvas.setAttribute("height", video.videoHeight);
ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
// requestAnimationFrame 根据电脑显示帧数进行循环
this.animationFrame = requestAnimationFrame(() => this.drawMedia());
,
文件下载downloadFile
// 下载
downloadFile: function (blob, fileType)
const a = document.createElement("a");
a.style.display = "none";
a.href = blob;
// const time = this.parseTime(new Date())
const time = new Date().getTime();
a.download = `$time.$fileType`;
document.body.appendChild(a);
a.click();
setTimeout(function ()
document.body.removeChild(a);
window.URL.revokeObjectURL(blob);
, 1000);
,
解决RecordRTC录制报错
在public下index.html引入该文件
<!-- 下载到本地引入 -->
<script src="screenshot.js"></script>
<!-- 官方路径引入 -->
<!-- <script src="https://www.webrtc-experiment.com/screenshot.js"></script> -->
效果
截图和视频录制
其中使用到的图片
vue自定义按钮放在视频文件上
参考技术A 首先需要安装一个插件(video.js)安装命令:npm install vue-video-player --save
plugins文件夹下新建一个video-player.js文件
在这里插入图片描述
video-player.js里添加以下内容:
import Vue from ‘vue’
const VueVideoPlayer = require(‘vue-video-player/dist/ssr’)
Vue.use(VueVideoPlayer)
修改配置文件nuxt.config.js
css下添加:
src: ‘video.js/dist/video-js.css’
在这里插入图片描述
plugins下添加:
src: ‘@/plugins/video-player’, ssr: f在这里插入图片描述
alse
以上是关于video.js 视频截图录制自定义全屏,hlsflvmp4视频播放的主要内容,如果未能解决你的问题,请参考以下文章