视频(Video)实现技术
重要通知
类目简介
基本概况
- HTML DOM Video 对象: https://www.runoob.com/jsref/dom-obj-video.html
- HTML 音频/视频: https://www.runoob.com/tags/ref-av-dom.html
- Media Source Extensions:https://w3c.github.io/media-source/
延迟加载视频库
- vanilla-lazyload https://github.com/verlok/vanilla-lazyload
- lozad.js https://github.com/ApoorvSaxena/lozad.js
- yall.js https://github.com/malchata/yall.js
- react-lazyload https://github.com/twobin/react-lazyload
视频格式MIME类型
- MP4 = 带有 H.264 视频编码和 AAC 音频编码的 MPEG 4 文件
- WebM = 带有 VP8 视频编码和 Vorbis 音频编码的 WebM 文件
- Ogg = 带有 Theora 视频编码和 Vorbis 音频编码的 Ogg 文件
----------------------------------------------
Format MIME-type
----------------------------------------------
MP4 video/mp4
WebM video/webm
Ogg video/ogg
----------------------------------------------
代码示例
const loadVideoData = function (url) {
return new Promise((resolve, reject) => {
const video = document.createElement("video");
video.src = url;
video.onloadeddata = function (e) {
resolve(video);
};
video.onerror = function () {
reject(e);
};
});
};
const url = "https://test.ysunlight.com/Reference/video/war.mp4";
loadVideoData(url).then((video) => {
video.autoplay = true;
video.controls = true;
video.muted = true;
window.setTimeout(() => {
video.play();
});
video.onplay = function () {
window.setInterval(() => {
ctx.drawImage(video, 0, 0, width, height);
}, 10);
};
document.getElementById("closeMuted").onclick = function () {
video.muted = false;
};
});
MIME-type
----------------------------------------------------------------------------------
----------------------------------------------------------------------------------
['video/wmv', 'video/mp4', 'video/avi', 'video/flv', 'video/mpg', 'video/mkv']
['video/mp4', ' video/webm', 'video/ogg', 'video/flv']
Video Type Extension MIME Type
MPEG-4 .mp4 video/mp4
Ogg Video .ogv video/ogg
Flash Video .flv video/x-flv
A/V Interleave .avi video/x-msvideo
Microsoft Windows Media .wmv video/x-ms-wmv
RealMedia Variable Bitrate .rmvb application/vnd.rn-realmedia-vbr
Waveform Audio File Format (WAV) .wav audio/x-wav
video/mpeg mp2
video/mpeg mpa
video/mpeg mpe
video/mpeg mpeg
video/mpeg mpg
video/mpeg mpv2
video/quicktime mov
video/quicktime qt
video/x-la-asf lsf
video/x-la-asf lsx
video/x-ms-asf asf
video/x-ms-asf asr
video/x-ms-asf asx
video/x-msvideo avi
video/x-sgi-movie movie
----------------------------------------------------------------------------------
视频格式
[.avi]:全称为Audio Video Interleaved,即音频视频交错格式。它于1992年被Microsoft公司推出。 [.mp4]: [.wmv]:全称为Windows Media Video,也是微软推出的一种采用独立编码方式并且可以直接在网上实时观看视频节目的文件压缩格式。 [.flv]:由Adobe Flash延伸出来的的一种流行网络视频封装格式。 [.mpg] [.mpeg]:MPEG格式, 全称为Moving Picture Experts Group,即运动图像专家组格式,该专家组建于1988年,专门负责为CD建立视频和音频标准,而成员都是为视频、音频及系统领域的技术专家。 [.mkv]:是一种新的多媒体封装格式,这个封装格式可把多种不同编码的视频及16条或以上不同格式的音频和语言不同的字幕封装到一个Matroska Media档内。它也是其中一种开放源代码的多媒体封装格式。Matroska同时还可以提供非常好的交互功能,而且比MPEG的方便、强大。
[.mpg]: [.vob]: [.mov]:美国Apple公司开发的一种视频格式,默认的播放器是苹果的QuickTime。
[.mpe]: [.dat]: [.vob]: [.asf]: [.3gp]: [.rm] [.rmvb]:Real Networks公司所制定的音频视频压缩规范称为Real Media。
视频(Video)
<video>
元素提供了 播放、暂停和音量控件来控制视频,用于在 HTML 或者 XHTML 文档中嵌入媒体播放器,用于支持文档内的视频播放。
注意事项
现代浏览器已经屏蔽了视频自动播放时携带音量的问题,即若设置自动播放需静音模式。
视频格式的MIME类型
- MP4:type="video/mp4",带有 H.264 视频编码和 AAC 音频编码的 MPEG 4 文件。
- Ogg:type="video/ogg",带有 VP8 视频编码和 Vorbis 音频编码的 WebM 文件。
- WebM:type="video/webm",带有 Theora 视频编码和 Vorbis 音频编码的 Ogg 文件。
<video controls>
<source src="video.ogg" type="video/ogg">
<source src="video.mp4" type="video/mp4">
<source src="video.webm" type="video/webm">
</video>
<script type="text/javascript">
const file = document.createElement('input');
file.setAttribute('type', 'file');
file.setAttribute('accept', 'video/mp4,video/webm,video/ogg');
file.click();
</script>
相关类库
- FlvPlayer: https://github.com/zhw2590582/FlvPlayer
- FLV: https://github.com/bilibili/flv.js
- Node-Media-Server:https://github.com/illuspas/Node-Media-Server
- cyberplayer
- JWPlayer
- hls.js
相关样式
video {
-webkit-playsinline: true;
}
video::-webkit-media-controls-fullscreen-button {
display: none;
}
事件列表
const video = document.getElementsById('video')[0];
// 当浏览器开始查找音频/视频时触发
video.addEventListener('loadstart', (e) => {
console.log(e.type);
}, false);
// 当音频/视频的时长已更改时触发
video.addEventListener('durationchange', (e) => {
console.log(e.type);
}, false);
// 当浏览器已加载音频/视频的元数据时触发
video.addEventListener('loadedmetadata', (e) => {
console.log(e.type);
}, false);
// 当浏览器已加载音频/视频的当前帧时触发
video.addEventListener('loadeddata', (e) => {
console.log(e.type);
}, false);
// 当浏览器正在下载音频/视频时触发
video.addEventListener('progress', (e) => {
console.log(e.type);
}, false);
// 当浏览器可以开始播放音频/视频时触发
video.addEventListener('canplay', (e) => {
console.log(e.type);
}, false);
// 当浏览器可在不因缓冲而停顿的情况下进行播放时触发
video.addEventListener('canplaythrough', (e) => {
console.log(e.type);
}, false);
场景代码
import { fetchFile } from '/public/file/file.js';
import { ReaderVideoData } from '/public/module/video.js';
const videoArray = [];
async function initVideo() {
const path = await fetchFile('https://test.ysunlight.com/Reference/video/Format/video.mp4');
const videoElement = await ReaderVideoData(path, 'video/mp4');
console.log(videoElement.duration);
// 获取视频指定范围时间轴每帧缩略图
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.setAttribute('width', videoElement.videoWidth);
canvas.setAttribute('height', videoElement.videoHeight);
// 当用户已移动/跳跃到音频/视频中的新位置时触发
let delayRender = null;
videoElement.onseeked = () => {
delayRender && delayRender();
}
for (let i = 0; i < videoElement.duration; i++) {
if (i == 0) continue;
videoElement.currentTime = i;
// 保障onseeked事件已经触发
await new Promise(r => delayRender = r);
context.drawImage(videoElement, 0, 0, videoElement.videoWidth, videoElement.videoHeight);
videoArray.push(canvas.toDataURL());
}
console.log(videoArray);
}
initVideo();
// 防止死缓
window.setTimeout(function() {
var total = Math.floor(dynamicPlayer.duration);
var audioControl = function() {
var time = dynamicPlayer.currentTime;
// 防止死缓
console.log(time);
if (time >= total) {
current.attr("data-state", "false");
button.attr("src", path + controlArray[0]);
current.removeClass('audio-play');
dynamicPlayer.pause();
window.clearInterval(interval);
}
};
interval = window.setInterval(audioControl, 1000);
}, 150);
//视频播放器
videoPlayer(e) {
e = e || window.event;
const node = e.currentTarget;
const url = node.getAttribute('data-src');
if (!Brower.ios) {
const videoOtherID = document.getElementById("videoOtherID");
videoOtherID.setAttribute("class", "controls");
videoOtherID.src = url;
videoOtherID.autoplay = true;
videoOtherID.controls = 'controls';
videoOtherID.play();
return ;
}
const videoIOSID = document.getElementById("videoIOSID");
videoIOSID.setAttribute("class", "controls");
videoIOSID.src = url;
videoIOSID.loop = true;
videoIOSID.preload = 'auto';
videoIOSID.autoplay = true;
videoIOSID.controls = 'controls';
videoIOSID.setAttribute('x5-video-player-type', "h5");
videoIOSID.setAttribute('x5-video-player-fullscreen', "true");
videoIOSID.play();
//监听退出全屏
}
CSS
video::-webkit-media-controls{
display:none !important;
}
video::-internal-media-controls-overflow-button{
display: none !important;
}
video::-webkit-media-controls-fullscreen-button {display: none;}
video::-webkit-media-controls-play-button {display:none;}
video::-webkit-media-controls-timeline {display:none;}
video::-webkit-media-controls-current-time-display{display:none;}
video::-webkit-media-controls-time-remaining-display {display:none;}
video::-webkit-media-controls-toggle-closed-captions-button {display:none;}
/** 隐藏声音icon */
video::-webkit-media-controls-mute-button {display:none;}
video::-webkit-media-controls-volume-slider {display:none;}
/* 隐藏Chrome/Safari的三点菜单 */
video::-webkit-media-controls-overflow-button {display: none;}
/* 隐藏Edge的三点菜单 */
video::-webkit-media-controls-overflow-menu-button {display: none;}
<!-- controlslist="nodownload noplaybackrate"用于取消更多控件弹窗中的下载功能和播放速度功能,disablePictureInPicture用于隐藏画中画功能。当这三个功能都被隐藏后,右下角的三点图标就会自动消失。 -->
<video controls controlslist="nodownload noplaybackrate" disablePictureInPicture>
<source src="your-video-source.mp4" type="video/mp4">
</video>
<!--解决方案:禁止点击播放就会自动全屏 H5 video标签在ISO系统 Safari浏览器中 点击播放时,会自动全屏-->
<video
playsinline
webkit-playsinline="true"
controls
>
<source src="video.mp4" type="video/mp4">
</video>
常见问题
问题原因:video标签在iPhone14.7与14.8.1版本上层级很高,导致遮挡其他定位元素层级低于video标签。
解决方案:通过CSS中transform: translateZ(0);实现提高层级。
HTML5VideoEditor
- 搜狐云剪辑,360快剪辑,爱奇艺在线非编
- 腾讯云剪:https://yunjian.qq.com/
- 阿里裁剪:https://vod.console.aliyun.com/
- 用于智能无损视频编辑、屏幕捕获和视频调试的轻量级软件:https://www.solveigmm.com/
- HTML5 Video Editor:https://www.solveigmm.com/en/products/html5-cloud-video-editing/
- html5-videoEditor:https://github.com/braveheartCMS/html5-videoEditor
VideoPlayer
initVideoData(duration, videoElement) {
this.video.defaultTimes = duration;
this.video.times = duration;
// 获取视频指定范围时间轴每帧缩略图
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.setAttribute('width', videoElement.videoWidth);
canvas.setAttribute('height', videoElement.videoHeight);
let delayRender = null;
videoElement.onseeked = () => {
delayRender && delayRender();
}
videoElement.addEventListener('loadeddata', async () => {
for (let i = 0; i < this.video.limit; i += 1) {
const times = Math.ceil(this.video.axisDefaultValue / 10) * i;
videoElement.currentTime = this;
// 解决设置视频新位置延迟性渲染问题
await new Promise(r => delayRender = r);
context.drawImage(videoElement, 0, 0, videoElement.videoWidth, videoElement.videoHeight);
this.video.axis.push(canvas.toDataURL());
}
}, false);
}
// 获取视频长度
getVideoTimes(url) {
const scope = this;
this.video.url = yrl;
const videoElement = document.createElement('video');
videoElement.src = url;
videoElement.addEventListener('loadedmetadata', () => {
const duration = videoElement.duration;
if (duration === Infinity) {
videoElement.currentTime = 10000000 * Math.random();
videoElement.ondurationchange = function() {
this.ontimeupdate = function() {
return;
}
videoElement.currentTime = 10000000 * Math.random();
videoElement.currentTime = 0;
scope.initVideoData(videoElement.duration, videoElement);
}
return;
}
scope.initVideoData(duration, videoElement);
}, false);
}
mencoder
流媒体播放解决方案
并行视频实时转播与分发、视频录像分布存储、视频服务器和视频录像服务器的分布存储与资源共享。
Node-Media-Server
GitHub:https://github.com/illuspas/Node-Media-Server 一个 Node.js 实现的RTMP/HTTP/WebSocket/HLS/DASH流媒体服务器
代码案例
<body data-url="https://test.ysunlight.com">
<video controls src="http://127.0.0.1:8088"></video>
</body>
<script src="./test.js"></script>
<script>
let video = document.getElementsByTagName('video')[0];
//http://127.0.0.1:7901/video
/**
ajax({
path: 'http://127.0.0.1:7901/video',
method: 'get',
dataType: 'blob',
data: {
count: 0
},
success(res) {
let url = URL.createObjectURL(res);
console.log(url);
//video.src = url;
//video.src = "../material/download.mp4";
},
fail(err) {
//
}
});
*/
</script>
(function(global, factory) {
typeof exports == 'object' && typeof module == 'object' ? module.exports = factory() : typeof define == 'function' && define.amd ? define([], factory) : typeof exports == 'object' ? exports.ajax = factory : global.ajax = factory;
})(window, function(options) {'use strict';
options = options || {};
options.base = options.base || "";
options.path = options.path || "";
options.method = (options.method || 'GET').toUpperCase();
options.async = options.async || true;
options.timeout = options.timeout || 15000;
options.size = options.size || 0;
options.header = options.header || {};
//设置为"json"时,responseText与responseXML就不能读取,否则报错
options.dataType = options.dataType || '';
options.data = options.data || {};
options.base = options.base.match(/\/$/g) == null ? (options.base + '/') : options.base;
//url
var url = "";
if (options.path.match(/^(https?:\/\/)/g) != null) {
url = options.path;
} else {
options.path = options.path.match(/^\//g) == null ? options.path : options.path.replace(/^\//g, '');
url = options.base + options.path;
}
/**
* XMLHttpRequest
*/
var xhr = null;
if (window.ActiveXObject) {
//IE6、IE7
xhr = new ActiveXObject("Microsoft.xhr");
}
else if (window.XMLHttpRequest) {
//IE7+, Firefox, Chrome, Opera, Safari
xhr = new XMLHttpRequest();
}
else {
throw new Error("it don’t support XMLHttpRequest.");
}
/**
* 响应
*
*/
//time out
var TIMEOUT = null;
if (options.dataType) {
xhr.responseType = options.dataType.toLowerCase();
}
/**
* 下载阶段
*/
xhr.onloadstart = function(e) {
console.log(e);
}
xhr.onprogress = function(e) {
console.log(e);
}
xhr.onload = function(e) {
console.log(e);
}
xhr.onloadend = function(e) {
console.log(e);
}
xhr.ontimeout = function(e) {
console.log(e);
}
xhr.onabort = function(e) {
console.log(e);
}
xhr.onerror = function(e) {
console.log(e);
}
xhr.upload.onloadstart = function(e) {
e['upload'] = e['type'];
console.log(e);
}
/**
* 上传进度设计
*
*/
xhr.upload.onprogress = function(e) {
let size = options.size;
let scale = size/100;
let pros = e.loaded;
let value = 0 + "%";
let current = (pros/scale).toFixed(2);
value = current + "%";
if (pros >= size) {
value = "100%";
}
options.progress && options.progress(value);
}
xhr.upload.onload = function(e) {
e['upload'] = e['type'];
console.log(e);
}
xhr.upload.onloadend = function(e) {
e['upload'] = e['type'];
console.log(e);
}
xhr.upload.ontimeout = function(e) {
e['upload'] = e['type'];
console.log(e);
}
xhr.upload.onabort = function(e) {
e['upload'] = e['type'];
console.log(e);
}
xhr.upload.onerror = function(e) {
e['upload'] = e['type'];
console.log(e);
}
/**
* 上传阶段
*
*/
//request state
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
//complete
window.clearTimeout(TIMEOUT);
if (options.complete) {
options.complete();
}
if (xhr.status == 200) {
//success
if (options.success) {
options.success(xhr.response);
}
return ;
}
//fail
if (options.fail) {
options.fail({state: xhr.status, msg: xhr.responseText});
}
}
}
//转换数据为PATH参数
var transArguments = function(data) {
data = data || {};
var _path = "";
for (var i in data) {
_path += i + '=' + encodeURIComponent(data[i]) + '&';
}
_path = _path.replace(/&$/g, '');
return _path;
}
//GET
if (options.method == 'GET') {
var _url = url + '?' + transArguments(options.data);
xhr.open(options.method, _url, true);
for (var key in options.header) {
xhr.setRequestHeader(key, options.header[key]);
}
xhr.send(null);
}
//POST
if (options.method == 'POST') {
xhr.open(options.method, url, true);
//上传文件清空Content-type, 否则会异常
if (options.type !== 'upload') {
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
}
for (var key in options.header) {
xhr.setRequestHeader(key, options.header[key]);
}
//上传文件
if (options.type === 'upload') {
xhr.send(options.data);
} else {
xhr.send(transArguments(options.data));
}
}
//超时
TIMEOUT = window.setTimeout(function () {
xhr.abort();
//完成
options.complete && options.complete({});
//失败
options.fail && options.fail({state: 408, msg: 'timeout'});
}, options.timeout);
});
代码示例
<video controls src="http://127.0.0.1:8088/video"></video>
const express = require('express');
const app = express();
const fs = require("fs");
const zlib = require('zlib');
app.get('/', function (req, res) {
res.send('Hello World');
});
//流视频
app.get('/video', function (req, res) {
console.log(req.headers.range);
let readerStream = fs.createReadStream('D:/devpt/project/server/item/video/material/download.mp4');
readerStream.pipe(res);
readerStream.on('end', function() {
res.end();
});
});
const port = 8088;
const server = app.listen(port, function () {
var host = server.address().address;
var port = server.address().port;
console.log("应用实例,访问地址为 http://%s:%s", host, port)
});
互动视频编辑技术
官网:https://www.ctrlvideo.com/ 开放平台:https://open.ctrlvideo.com/ API接口:https://open.ctrlvideo.com/doc/detail/100004
智令互动(深圳)科技有限公司
互动视频云服务平台:视频剪辑系统、互动视频封装系统、互动视频内容发布系统、互动视频代理系统、互动视频播控系统
姜磊本人是腾讯4级专家,腾讯M族专家团团长; CEO侯亮拥有十年以上互联网内容、营销从业经验,曾任职网易、迅雷,服务过宝洁、箭牌、嘉士伯、日产等国内外500强企业; CTO张瑞圣拥有16年IT领域工作经验,为音视频领域专家,精通流媒体架构、机器视觉、视频AI等; COO肖国良,拥有十五年以上互联网行业营销、运营经验,曾任前程无忧(51job)销售高管、巴乐兔租房平台(Baletu)联合创始人,拥有多次创业经验。
第三方相关库
xgplayer
西瓜视频播放器
GitHub:https://github.com/bytedance/xgplayer 官网:https://v2.h5player.bytedance.com/
flv.js
具有 H.264 + AAC / MP3 编解码器播放功能的 FLV 容器 多部分分段视频播放 HTTP FLV 低延迟直播流播放 FLV over WebSocket 实时流播放 兼容 Chrome、FireFox、Safari 10、IE11 和 Edge 极低的开销,并由您的浏览器加速硬件!
GitHub:https://github.com/bilibili/flv.js
hls.js
videojs
videojs:https://docs.videojs.com/ Vue版本:https://docs.videojs.com/tutorial-vue.html