fl427's Studio.

流媒体协议简要介绍

字数统计: 2.3k阅读时长: 9 min
2023/01/22

前言

我们打开B站可以看到视频的src属性表现为Blob Url的形式:

1
<bwp-video src="blob:https://www.bilibili.com/2ce0d1df-fa56-477a-8a03-0e9560d1e95c"></bwp-video>

这个以blog:开头的url是什么?通过MDN的介绍看到,这类url是用如下方式生成的:

1
createObjectURL(object)

object的类型可以是FileBlob或者MediaSource。浏览器内部为每个通过 URL.createObjectURL 生成的 URL 存储了一个URL → Blob 映射。因此,此类 URL 较短,但可以访问 BlobBlob本身驻留在内存中,浏览器无法释放它,映射在文档卸载时自动清除,Blob对象随后被释放。我们可以调用 URL.revokeObjectURL(url) 方法,从内部映射中删除引用,从而删除Blob并释放内存。

iOS系统不支持MediaSource

Blog全称Binary Large Object,表示二进制类型的大对象,通常是影像、声音或多媒体文件,在JSBlob类型的对象表示不可变的类似文件对象的原始数据。

1
2
const a = new Blob(["<html><h2>BLOB</h2></html>"], { type: "text/plain" });
-> Blob {size: 26, type: 'text/plain'}

其中 size 属性用于表示数据的大小-以字节为单位,type 是 MIME 类型的字符串,如果类型未知则该值为空字符串。

File 接口基于 Blob,继承了Blob的功能并将其扩展使其支持用户系统上的文件。

视频编码

视频容器格式是我们平时看到的视频文件的后缀名如.mp4.mov.mkv等这些格式,里面装了什么取决于编码的不同,常见的如h.264(AVC)h.265(HEVC)vp9av1等。

1
.mp4,.mov,.mkv -> h.264(AVC), h.265(HEVC), vp9, av1

视频格式及编码:https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs

流媒体协议

在线观看视频时没有办法等浏览器把视频都下载完成后再进行播放,需要边下边播来提升观看体验,这就是流媒体协议出现的原因。流媒体协议决定了我们的视频该如何播放,而视频本身依然是基于上一节的视频编码格式。

流媒体协议通常包括客户端和服务器端协议。客户端协议允许播放器请求和接收音频和视频数据流,而服务器端协议则负责传输数据流。

常用的流媒体协议主要有:

  • 基于 RTSP/RTP 的实时流媒体协议
  • HTTP 渐进下载

RTMP、RTSP、HTTP协议,这些是互联网 TCP/IP 五层体系结构中应用层的协议,都可以用来做视频直播或点播。通常来说,直播一般用 RTMP、RTSP点播用 HTTP

RTMP: 即 Real Time Messaging Protocol(实时消息传输协议),基于TCP,是一种设计用来进行实时数据通信的网络协议。主要用来在 流媒体/交互服务器 之间进行音视频和数据通信

RTSP: 即 Real Time Streaming Protocol (实时流传输协议),它使用TCP或UDP完成数据传输。使用RTSP时,客户机和服务器都可以发出请求,即RTSP可以是双向的。RTSP 不方便在 浏览器上使用

我们看到 RTSP 不方便在 浏览器上使用,于是就有了这些解决方案:

HLS : 基于HTTP的自适应码率流媒体传输协议 (HTTP Live Streaming),它是Apple的动态码率自适应技术,使用HTTP传输。它最初是苹果公司针对移动设备而开发的流。后来桌面也有很多应用了,HTML5 是直接支持了。

WebRTC:即 网页即时通信 ( Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的API。它实现了基于网页的视频会议(标准是WHATWG 协议),目的是通过浏览器提供简单的javascript就可以达到实时通讯 (RTC))能力。

常见的流媒体协议有以下几种:

截屏2023-02-11 20.57.49

首先回顾下历史,当年乔布斯决定把Flash干掉后,到现在Flash在浏览器里面基本已经没有存在的必要了,但是随Flash一起诞生的flv容器格式在浏览器里似乎还活的有滋有味,当然也要归功于B站开源的flv.js,即使浏览器里没有了Flash, 我们通过flv.js还是能够正常播放flv容器格式的视频内容,flv在国内网站直播中用的还是很多的,也主要是觉得http-flv的低延迟还是很有优势的。

HLS(Http Live Streming),苹果搞的协议,苹果说既然我们遵循乔帮主的思想不再支持Flash,那总得再造个轮子搞流媒体播放吧,我们呢也不想造太底层的轮子,还是基于http来吧,毕竟http服务已经很成熟了,而且兼容性不要太好,然后就诞生了HLS流媒体协议,依托于iphone的使用量,HLS在流媒体当中的占有率还是相当高的。

跟HLS相对应的MPEG-DASH(还是那个MPEG组织),MPEG想苹果你怎么老是喜欢自己造轮子,又不开源,哎,算了,为了不被你垄断,我们还是很有必要搞一个跟HLS类似的开源流媒体协议出来,然后MPEG-DASH就出来了,从上图看起来占有率似乎还不是很高,但是毕竟开源,各个视频大厂门都已经在用了包括国内的B站,国外的油管、网飞等,未来可期。

上面的表中说明了HLS, MPEG-DASH支持自适应比特流,简单来说就是对于这2种流媒体协议来说,一个源视频文件会根据需要支持的不同分辨率,切割成不同的一个个小的视频文件,一般在几百K到小几M之间,这样的话当网络速度变化的时候就可以快速切到满足当前网速的分辨率上对应的切片文件了,可以做到保证播放流畅度的时候无缝切换,大大提升用户体验

直播链路

生成符合HLS、MPEG-DASH的工具及前端播放器

FFmpeg

FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。随着浏览器能力的不断发展,我们可以通过 WebAssembly 在浏览器运行各种语言的程序,这其中就包括了使用 c++ 写的 FFmpeg。

虽然 FFmpeg.wasm 在浏览器中运行的性能并不是很乐观,但是,它展示了一种在浏览器中运行 ffmpeg 的可能性,也许在不远的将来,我们可以在浏览器中解码任何封装格式的视频,又或许以后有大牛会做出一款Web OBS或者 Web PR。

FFmpeg.wasm

官网:https://ffmpegwasm.netlify.app/

FFmpeg.wasm是一款基于WebAssembly技术的FFmpeg库,可以在Web端实现视频和音频处理功能。WebAssembly是一种新的低级字节码,可以在现代浏览器中运行原生代码,FFmpeg.wasm利用WebAssembly将FFmpeg的核心功能转换为可在Web浏览器中运行的格式。

FFmpeg.wasm支持多种音视频格式的解码和编码,包括H.264、HEVC、VP8、VP9、AAC、MP3等。它提供了一系列API,可以通过JavaScript调用FFmpeg.wasm中的方法来实现各种音视频处理任务,例如音视频解码、编码、转码、裁剪、合并、截取等。

与传统的基于C语言编写的FFmpeg库相比,FFmpeg.wasm具有更高的跨平台性和可移植性,可以在多种Web浏览器和设备上运行。同时,由于FFmpeg.wasm可以直接在浏览器中运行,因此可以将音视频处理任务移动到客户端,减轻服务器的负担,提高应用的性能和响应速度。

代码DEMO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// react
import React, {useState} from "react";
// other
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';

const Stream: React.FC = () => {

const [videoSrc, setVideoSrc] = useState('');
const [message, setMessage] = useState('Click Start to transcode');
const ffmpeg = createFFmpeg({
log: true,
});

const doTranscode = async (): Promise<void> => {
setMessage('Loading ffmpeg-core.js');
console.log('test2');
await ffmpeg.load();
console.log('test');
setMessage('Start transcoding');
ffmpeg.FS('writeFile', 'test.avi', await fetchFile('http://localhost:3000/static/test-video2.mp4'));
await ffmpeg.run('-i', 'test.avi', 'test.mp4');
setMessage('Complete transcoding');
const data = ffmpeg.FS('readFile', './test.mp4');
setVideoSrc(URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' })));
};

return (
<div>
<video src={videoSrc} controls></video><br/>
<button onClick={doTranscode}>Start</button>
<p>{message}</p>
</div>
)
};

export default Stream;

SharedArrayBuffer

https://juejin.cn/post/7179580933123604517

1
2
3
4
5
6
7
8
9
10
11
12
devServer: {
headers: [
{
key: 'Cross-Origin-Embedder-Policy',
value: 'require-corp',
},
{
key: 'Cross-Origin-Opener-Policy',
value: 'same-origin',
},
],
}

原理

编译WebAssembly版本的FFmpeg:https://juejin.cn/post/7087967583885852685

参考

https://juejin.cn/post/7015100196631609351

https://zhuanlan.zhihu.com/p/500199997

https://juejin.cn/post/6976958022597738504

https://juejin.cn/post/7179580933123604517

CATALOG
  1. 1. 前言
  2. 2. 视频编码
  3. 3. 流媒体协议
  4. 4. 直播链路
  5. 5. 生成符合HLS、MPEG-DASH的工具及前端播放器
    1. 5.1. FFmpeg
    2. 5.2. FFmpeg.wasm
      1. 5.2.1. 代码DEMO
      2. 5.2.2. SharedArrayBuffer
      3. 5.2.3. 原理
  6. 6. 参考