最近在做视频相关的项目,自然就遇到了如何让视频顺畅播放的问题
现在的视频网站,特别是长视频网站,一般不会直接请求一整个视频文件;这样直接导致响应时间过长,用户等待过久
通常采用流媒体传输协议,就是将视频切成一小段一小段,比如10s为一段,这每一段是一个ts格式的文件,播放器根据播放进度请求对应或提前请求相应的ts视频文件,进而实现流畅播放,即使是点播也能保证顺畅播放
像一些大型长视频网站,b站、腾讯视频等都是采用流媒体传输协议
下面先来了解一下一种流媒体传输协议:HLS
1、HLS
HLS
是一种基于HTTP
的流媒体传输协议,可以实现流媒体的直播和点播,其工作原理就是把整个视频流分成一个个基于HTTP
的文件,每次只下载一部分
HLS
协议是由苹果提出的,它包括三个部分:
HTTP
:传输协议m3u8
:索引文件ts
文件:音视频媒体信息
其中,m3u8
是一种UTF-8
编码的索引纯文本文件,它就像一个播放列表,记录了一段段的视频流ts文件
前端首先解析m3u8
文件,然后通过HTTP
请求分片内容即ts
文件,然后再使用MSE
的appendBuffer
进行buffer
的封装,完成合流的工作,最后交给播放器进行播放
2、实现
我们上传的通常是一个mp4
格式的文件,那么就需要将其进行分片,而FFmpeg
能够实现将mp4
转换为HLS
文件
FFmpeg
是一个用于操作、转换流媒体的命令行工具
首先需要先安装FFmpeg
,控制台执行:ffmpeg
不会报错即为安装成功
2.1 后端
第一步:先将mp4
视频转化成一个ts文件
ffmpeg -y -i './video.mp4' -vcodec copy -acodec copy -bsf:v h264_mp4toannexb "./index.ts"
-bsf:v h264_mp4toannexb
设置比特流过滤器
第二步:将改ts文件切成一段一段的小ts文件,并将各个ts
文件信息存放到一个m3u8
格式的文件
ffmpeg -i "./index.ts" -c copy -map 0 -f segment -segment_list "./index.m3u8"
-segment_time 10 "./index-%04d.ts"
此处将视频分成每10秒为一个ts
文件
对于这种命令行的操作,通常是放到node
的子进程中执行,即:
const { exec } = require('child_process')
exec(command, (error, stdout) => {
if (error) {
reject(error);
return;
}
console.log(stdout);
});
用Promise
封装一下:
function executeCommand(command) {
return new Promise((resolve, reject) => {
exec(command, (error, stdout) => {
if (error) {
reject(error);
return;
}
resolve(stdout);
});
});
}
await executeCommand('第一步')
await executeCommand('第二步')
2.2 前端
前端通过HLS.js
库来处理m3u8
格式文件,其基本使用如下:
import Hls from 'hls.js'
const video = document.getElementById('video') as HTMLVideoElement
const hls = new Hls()
hls.loadSource('./index.m3u8')
hls.attachMedia(video)
refPlyr.current!.plyr.media = video
hls.on(Hls.Events.MANIFEST_PARSED, () => {
(refPlyr.current!.plyr as PlyrInstance).play()
})
具体代码已上传至githuh 😄
3、总结
实现流媒体的播放,主要有两步:
- 后端利用
FFmpeg
工具将mp4
转换成m3u8
格式的文件 - 前端通过
hls.js
逐段请求ts
文件,并合成为视频流,交给播放器进行播放
比起传统的方式,流媒体传输可以实现视频的流畅播放,支持视频的点播、直播等;不仅如此,客户端还可以根据网络宽带 请求不同码率的视频文件(标准、高清、超清...)
参考: