【前端】如何流畅地播放视频

7,853 阅读3分钟

最近在做视频相关的项目,自然就遇到了如何让视频顺畅播放的问题

现在的视频网站,特别是长视频网站,一般不会直接请求一整个视频文件;这样直接导致响应时间过长,用户等待过久

通常采用流媒体传输协议,就是将视频切成一小段一小段,比如10s为一段,这每一段是一个ts格式的文件,播放器根据播放进度请求对应或提前请求相应的ts视频文件,进而实现流畅播放,即使是点播也能保证顺畅播放

像一些大型长视频网站,b站、腾讯视频等都是采用流媒体传输协议

下面先来了解一下一种流媒体传输协议:HLS

1、HLS

HLS是一种基于HTTP的流媒体传输协议,可以实现流媒体的直播和点播,其工作原理就是把整个视频流分成一个个基于HTTP的文件,每次只下载一部分

HLS协议是由苹果提出的,它包括三个部分:

  1. HTTP:传输协议
  2. m3u8:索引文件
  3. ts文件:音视频媒体信息

其中,m3u8是一种UTF-8编码的索引纯文本文件,它就像一个播放列表,记录了一段段的视频流ts文件

前端首先解析m3u8文件,然后通过HTTP请求分片内容即ts文件,然后再使用MSEappendBuffer进行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、总结

实现流媒体的播放,主要有两步:

  1. 后端利用FFmpeg工具将mp4转换成m3u8格式的文件
  2. 前端通过hls.js逐段请求ts文件,并合成为视频流,交给播放器进行播放

比起传统的方式,流媒体传输可以实现视频的流畅播放,支持视频的点播、直播等;不仅如此,客户端还可以根据网络宽带 请求不同码率的视频文件(标准、高清、超清...)


参考:

  1. github.com/CharonChui/…
  2. juejin.cn/post/684490…