目录
- 流媒体
- 名词系列
- 视频的本质
- 视频压缩
- 编码过程
- 如何在直播中看到帅哥美女?
- RTMP 协议
- P2P
- P2P 文件下载
- 种子文件 (.torrent)
- 去中心化网络(DHT)
- 哈希值与 DHT 网络
- DHT 网络是如何查找
流媒体
直播系统组成与协议
近几年直播比较火,很多人都喜欢看直播。那么一个直播系统里面都有哪些组成部分,都使用了什么协议呢?
无论是直播还是点播,其实都是对于视频数据的传输。一提到视频,大家都爱看,但一提到视频技术,大家都头疼,因为名词实在是太多了。
名词系列
- 名词系列一:AVI、MPEG、RMVB、MP4、MOV、FLV、WebM、WMV、ASF、MKV。例如 RMVB 和 MP4,看着是不是很熟悉?
- 名词系列二:H.261、H.262、H.263、H.264、H.265。这个是不是就没怎么听过了?别着急,要重点关注 H.264。
- 名词系列三:MPEG-1、MPEG-2、MPEG-4、MPEG-7。MPEG 好像听说过,但后面的数字是怎么回事?熟悉又陌生?
视频的本质
视频是什么?其实就是快速播放一连串连续的图片。每一张图片称为一帧。只要每秒钟帧的数据足够多,即播放得足够快,人的眼睛看不出这是一张张独立的图片,这就是我们常说的帧率(FPS)。
每一张图片,由像素组成,假设为 1024×768,每个像素由 RGB 组成,每个 8 位,共 24 位。
计算视频大小:
每秒钟的视频大小:
30帧 × 1024 × 768 × 24 = 566,231,040 Bits = 70,778,880 Bytes
一分钟视频大小:
4,246,732,800 Bytes = 4 GB
视频压缩
如何将如此大的数据量压缩呢?编码就是一个压缩的过程。
视频和图片的压缩特点
- 空间冗余:相邻像素之间有较强的相关性,可以通过算法计算出中间的像素。
- 时间冗余:视频序列中相邻图像内容相似,利用已有的图片进行预测和推断。
- 视觉冗余:人眼对某些细节不敏感,因此可以丢失一些数据。
- 编码冗余:像素值出现的概率不同,概率高的像素使用较少的字节,概率低的像素使用更多字节,类似霍夫曼编码。
视频编码的两大流派
- ITU-VCEG:侧重传输,主要制定了 H.261, H.264 等标准。
- ISO-MPEG:侧重存储,主要制定了 MPEG-1, MPEG-2 等标准。
后来的 H.264/MPEG-4 AVC 标准就是这两个组织合作制定的。
编码过程
视频流通过编码变成二进制,这些二进制数据会按照一定的格式保存为文件。这个文件格式就是名词系列一中的各类格式(如 MP4、RMVB 等)。
如何在直播中看到帅哥美女?
视频流可以通过网络协议封装并传输。在直播过程中,视频从主播端推送到服务器,服务器再转发到客户端。这是一个典型的推流与拉流过程。
编码过程细节
视频编码后,视频被分成三种帧类型:
- I帧(关键帧):完整的图片,可以单独解码。
- P帧(前向预测编码帧):与前一个关键帧或 P 帧的差异,解码时需要参考之前的图像。
- B帧(双向预测内插编码帧):记录与前后帧的差异,解码时需要前后帧的参考数据。
推流与拉流
- 推流:将编码后的视频数据通过 RTMP 协议传输到流媒体服务器。
- 拉流:观众通过 RTMP 协议从流媒体服务器拉取视频流并解码,最终显示在客户端。
RTMP 协议
RTMP 是基于 TCP 的协议,确保数据有序传输。推流和拉流过程中,RTMP 会将视频流分成多个 Chunk 进行传输,以适应低带宽环境。
观众的客户端通过 RTMP 推流的过程:
观众的客户端通过 RTMP 拉流的过程:
小结
- 视频编码通过时空冗余压缩数据;
- 编码后的视频流被分为 NALU 单元进行网络传输;
- 使用 RTMP 协议进行视频流的推送和拉取;
- 视频流在客户端进行解码,播放出最终的视频。
P2P
在下载电影时,我们通常会选择不同的下载方式。最常见的方式是通过 HTTP 协议进行下载,但是许多人可能有过这样的体验:当文件较大时,使用浏览器直接下载的速度极其缓慢。
另一种常见的下载方式是通过 FTP(文件传输协议)。FTP 采用两个 TCP 连接来传输文件,具体包括控制连接和数据连接。
-
控制连接:服务器通常通过端口 21 以被动的方式监听,客户端则主动发起连接。这条连接负责传递命令和服务器的响应。例如,常见的命令包括:
list
:列出文件目录;retr
:下载文件;store
:上传文件。
-
数据连接:每次文件传输时,客户端和服务器会建立一个独立的数据连接来传输文件内容。
FTP 工作模式
FTP 有两种工作模式,分别是主动模式(PORT)和被动模式(PASV)。这两种模式的区别在于数据连接的建立方式,下面分别介绍:
-
主动模式(PORT):在主动模式下,客户端会随机选择一个大于 1024 的端口 N,然后通过端口 21 向服务器发起连接。客户端会向服务器发出一个
PORT N+1
的命令,告诉服务器自己打开了一个监听端口 N+1。接着,服务器从自己的端口 20 主动连接到客户端的端口 N+1 来建立数据连接。 -
被动模式(PASV):在被动模式下,客户端首先通过端口 21 连接到服务器并发送
PASV
命令。随后,服务器会选择一个大于 1024 的端口 P,并返回227 entering passive mode
消息,告知客户端数据传输的端口号。客户端收到这个消息后,会使用端口 N+1 连接服务器的端口 P,并通过这两个端口进行数据传输。
P2P 文件下载
虽然 HTTP 和 FTP 是常见的文件下载方式,但它们都存在一个主要的缺点:单一服务器的带宽压力大。因为这两种方式采用的是传统的客户端-服务器架构,所有数据都依赖于中心服务器。
为了克服这一问题,P2P(Peer-to-Peer)文件传输方式应运而生。P2P 不再依赖单一的服务器来存储和传输资源,而是通过多台设备(即 Peer)之间的点对点连接来分散资源。
在 P2P 模式下,当你想下载一个文件时,系统会连接到已经拥有该文件的其他 Peer,你可以直接从这些设备下载数据,而无需依赖中心服务器。一旦下载完成,你的设备也会成为 P2P 网络的一部分,其他设备可以通过你的设备获取该文件。这样,随着网络中的 Peer 越多,下载速度也越快。
常见的 P2P 下载工具,如 BitTorrent,就利用这种分布式下载方式。在使用 BitTorrent 等软件时,你不仅会看到下载流量,还会看到上传流量。因为你在下载的同时,也在为其他用户提供文件。
种子文件 (.torrent)
在 P2P 下载中,如何知道哪些 Peer 已经拥有文件是一个关键问题。这个问题通过“种子”文件(.torrent)来解决。我们比较熟悉的 .torrent 文件由两部分组成:announce(tracker URL) 和 文件信息。
种子文件结构
- 文件信息(Info 区):包含文件的基本信息,如文件数量、每个文件的大小、目录结构等。
- Name 字段:指定顶层目录的名称。
- 每个段的大小:BT 协议将文件拆分成多个小段,这样可以并行下载各个段。
- 段哈希值:每个文件段的 SHA-1 哈希值,所有段的哈希值合并在一起,保证文件完整性。
下载过程
- 解析种子文件:BT 客户端首先解析 .torrent 文件,获取 tracker 地址,然后连接到 tracker 服务器。
- 连接和获取 Peer 信息:tracker 服务器回应请求,提供其他下载者的 IP 地址,包括发布者。客户端通过这些信息连接到其他下载者。
- 交换文件段:客户端和其他 Peer 之间交换文件段。每个客户端会告知对方自己已下载的段,未下载的部分会从其他 Peer 处获取。
- 文件完整性验证:每次下载一个文件段后,客户端会计算该段的哈希值并与 .torrent 文件中的值对比。如果匹配,说明该段下载正确,否则需要重新下载。
依赖 Tracker 的问题
这种下载方式依赖于 tracker 服务器,tracker 是一个中心化的服务器,负责登记哪些用户请求哪些文件并协调 Peer 之间的连接。尽管下载过程是去中心化的,但加入 P2P 网络时仍需要借助 tracker 来进行连接。
缺点:如果 tracker 服务器出现故障或被屏蔽,P2P 下载就会受到影响,导致无法正常工作。
去中心化网络(DHT)
能否实现完全的去中心化?答案是可以的,这就是 DHT(Distributed Hash Table) 的应用。
什么是 DHT?
DHT 是一种分布式哈希表,每个加入 DHT 网络的节点都负责存储网络中的资源信息和其他成员的联系信息。简单来说,所有节点共同构成一个庞大的分布式数据库,分担信息存储和查询的工作。
Kademlia 协议
DHT 网络的一个著名协议是 Kademlia 协议,类似于区块链的概念,但更为抽象。下面我们来详细讲解它的工作原理。
DHT 网络中的角色
每个启动的 BitTorrent 节点扮演两个角色:
- Peer:监听一个 TCP 端口,用于上传和下载文件。这个角色标识该节点拥有某个文件。
- DHT Node:监听一个 UDP 端口,加入 DHT 网络。在 DHT 网络中,每个节点都有一个唯一的 ID,它是一个长串的哈希值。
文件索引与责任
在 DHT 网络中,每个节点并不存储所有文件,而是负责存储文件索引信息。具体来说:
- 每个 DHT 节点需要知道某些文件在哪些节点上保存,但它自己不一定存储这些文件。
- 这些节点通过 DHT 网络相互联系,共同维护文件的索引信息,确保文件的分布是去中心化的。
哈希值与 DHT 网络
每个 DHT node 并不拥有全局的文件信息,它只需要知道一部分文件的信息。要确定一个节点需要知道哪些文件,哈希算法便应运而生。
哈希值与文件
每个文件通过哈希算法计算出一个哈希值,而每个 DHT node 的 ID 长度与哈希值相同。
DHT 网络的规则是:如果某个文件的哈希值与某个 DHT node 的 ID 完全相同,那么该节点负责知道这个文件的下载位置。尽管该节点可能没有存储文件本身。
节点 ID 与文件哈希相似性
实际上,完全匹配的 DHT node 很难找到。为了应对这一问题,DHT 网络规定了:除了与哈希值完全相同的节点,还允许 与哈希值接近的 N 个节点 知道该文件的信息。
那么,如何判断“接近”呢?简单来说,接近是指哈希值在某些位上的差异较小。例如,修改文件哈希值的最后几位,仍然算作“接近”。
文件与节点的匹配过程
举个例子:
- 文件 1 的哈希值与 node C 的 ID 完全匹配。因此,node C 知道文件 1 的下载位置,尽管它本身并没有存储文件 1。
- 文件 2 的哈希值与 node E 的 ID 完全匹配,但 node D 的 ID 与 E 的哈希值很接近,因此 node D 也能得知文件 2 的位置。
新节点加入 DHT 网络
当一个新的节点 node new 加入 DHT 网络并想下载文件 1 时,首先它会找到种子文件(.torrent)中的 DHT 节点列表,并通过其中任意一个节点加入网络。
node new 会计算文件 1 的哈希值,并查找与哈希值匹配或接近的节点,如 node C。如果 node new 不能直接联系到 node C,它会向它能够联系到的其他节点询问,直到找到连接 node C 或其他相似节点的路径。
一旦 node new 开始下载文件,它就会向网络中其他节点报告自己也拥有该文件,成为新的文件“源”。
去中心化与分布式共享
此时,DHT 网络已经实现了文件的分布式共享。每个节点既能存储文件信息,又能提供文件下载路径,整个网络去除了中心化的依赖,保证了文件传输的高效性和可靠性。
DHT 网络的工作原理
-
节点 ID 与文件哈希:节点 ID 和文件哈希值都使用 160 位(20 字节)长度的哈希空间。
-
相似度计算:DHT 网络中节点 ID 的相似度通过 异或(XOR)运算 来判断。例如,对于 5 位 ID:
01010
和01000
的距离是00010
,即 2。01010
和00010
的距离是01000
,即 8。01010
和00011
的距离是01001
,即 9。
通过这种计算方式,判断 ID 之间的“距离”有助于确定哪些节点对文件的存储和下载路径最为重要。
类比社交网络
在 DHT 网络中,节点的距离更像是社交网络中的“社交距离”——即不同节点之间的联系程度,而非地理距离。就像在 LinkedIn 上,工作经历丰富的人可能与你的“社交距离”较近,尽管你们没有住在同一地方。
DHT 网络是如何查找
DHT(分布式哈希表)网络的查找和更新机制是通过 Kademlia 协议实现的,允许节点高效地查找其他节点和文件。以下是该协议的工作原理和核心机制:
1. 节点查找
假设节点 A 的 ID 为 00110
,需要查找节点 B,ID 为 10000
。这两个节点的异或距离为 10110
,即 A
和 B
之间的距离是 31。根据 Kademlia 协议,节点 A 会尝试在自己的 k-bucket 中查找 B,并根据以下步骤进行折半查找:
1.1 查找目标节点所在的 k-bucket
- A 计算异或值
10110
,并根据异或的结果确定 B 可能在的k-bucket
。在此例中,B 与 A 的 ID 从第 5 位开始不同,因此 B 可能在k-bucket 5
中。 - 如果 A 的
k-bucket 5
中存在 B,查找成功;如果不存在,继续下一步。
1.2 查找接近的节点
- 如果 A 没有在自己的
k-bucket 5
中找到 B,它会从该桶中随机选择一个节点 C,并请求 C 查询自己的通讯录,看看是否能找到 B。 - 由于 C 和 B 的 ID 在第 5 位相同,因此 C 能找到与 B 更接近的节点,进一步缩小查找范围。
- 这种过程逐步缩小距离,每次通过折半查找来加速查找过程。
1.3 查找过程的递归
- 如果 C 也没有找到 B,它会继续向自己的通讯录请求,直到找到距离 B 更近的节点,如 D,然后 D 继续查找,直到最终找到 B。
- 最坏的情况是,每次找到的节点都离目标节点较远,需要多个步骤才能最终找到 B。
1.4 查找效率
- Kademlia 协议采用了 折半查找 的机制,最多只需要
log2(N)
次查询(其中 N 为网络中节点总数)就能找到目标节点。这保证了查询过程的高效性。
2. 节点通信
在 DHT 网络中,节点之间通过 4 个核心指令进行通信:
- PING:测试一个节点是否在线,类似打电话确认对方是否还活跃。
- STORE:请求一个节点保存一份数据。加入网络的节点需要保存一定的数据。
- FIND_NODE:根据目标节点的 ID 查找该节点。即通过节点的 ID 查找节点的位置。
- FIND_VALUE:根据文件的哈希值(即 KEY)查找存储该文件的节点。实际上,这个操作与 FIND_NODE 类似,只不过目标是文件而非节点。
3. 节点的通讯录更新
Kademlia 协议通过更新每个节点的通讯录来保证网络的稳定性和高效性。每个节点的 k-bucket(即通讯录)按照接触时间倒序排列,最近联系的节点排在最前面。
3.1 更新机制
- 每次节点与其他节点接触时,都会检查这个节点是否已经在自己的 k-bucket 中。如果节点已经存在,它会被移到 k-bucket 列表的末尾,表示最近联系过的节点。
- 如果通讯录满了(通常是一个固定的大小,如 20 个节点),新的节点会替换最旧的节点。如果最旧的节点在线(通过 PING 测试),它会被移到通讯录的末尾;如果下线,则删除并加入新的节点。
3.2 维持网络稳定性
- 通过这种方式,Kademlia 协议保证了即使某些节点加入或离开网络,整体的网络结构和效率不会受到影响。节点始终保持高效且动态的通讯录,不断更新与其他节点的联系。
总结一下:
-
集中式下载 vs 非中心化下载:下载一个文件通常使用 HTTP 或 FTP,这两种方式都是依赖中心化服务器的,而 P2P(点对点)则采用了去中心化的方式,改变了传统的下载模式。
-
P2P 的两种方式:
- 基于 Tracker 的 P2P:这种方式中,元数据(即文件的位置信息)集中存储在 Tracker 上,而文件数据则分散存储在多个节点中。下载过程需要通过 Tracker 来获得文件的相关信息。
- 基于 DHT 的 P2P:这是一种完全去中心化的方式,文件的元数据和文件数据都被分散存储在整个网络中。每个节点都可能既是数据存储者,也可能是数据查找者,通过分布式哈希算法(DHT)来完成文件的查找和下载。
通过这两种方式,P2P 网络能够有效实现文件的共享和分发,并且避免了传统集中式下载的单点故障问题。