第16讲 | 流媒体协议:如何在直播里看到美女帅哥?

2023-05-16

最近直播比较火,那一个直播系统里有哪些组成部分,都用了哪些协议呢?

无论直播还是点播,都是对于视频数据的传输。这里面涉及到许多的技术名词。

三个名词系列

  • 名词系列一: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。

视频是什么?其实视频是快速播放的一连串连续的图片。

每一张图片,称为一帧。只要图片播放的速度足够快,比如每秒30帧以上,我们就会看到一个动起来的视频。每秒钟的帧数称为帧率。

每一张图片都是由像素组成的,假设1024*768(这是很低的分辨率),每个像素由RGB组成,每个8位,共24位。

那每秒钟的视频有多大?

30帧×1024×768×24=566,231,040Bits=70,778,880Bytes

如果一分钟呢?4,246,732,800Bytes,大约4G。

可以看到这个数据量很大,非常耗费带宽和硬盘空间。

于是,人们想到了编码,就是看如何用尽量少的空间保存视频,同时不影响视频的观看质量。编码就是一个压缩的过程。

视频和图片的压缩过程有什么特点?

视频和图片有这样一些特点。

  1. 空间冗余:图像的相邻像素之间有较强的相关性,一张图片的相邻像素是渐变的,没要必要完整地保存,可以隔几个保存一个,中间的用算法计算出来。
  2. 时间冗余:视频序列的相邻图像之间内容相似。连续出现的图片不是突变的,可以根据已有的图片进行预测和推断。
  3. 视觉冗余:人的视觉对某些细节不敏感,可以允许丢失一些数据。
  4. 编码冗余:不同像素值出现的概率不同,概率高的用的字节少,概率低的用的字节多。

编码的算法非常多,但是编码的过程都很类似。

视频编码的两大流派

视频编码的两大流派,即两大标准。

  • 流派一:ITU(International Telecommunications Union)的VCEG(Video Coding Experts Group),这个称为国际电联下的VCEG。既然是电信,他们最初做编码主要侧重传输。名词系列二,就是这个组织制定的标准。
  • 流派二:ISO(International Standards Organization)的MPEG(Moving Picture Experts Group),这个是ISO旗下的MPEG,原来是做视频存储的。后来侧重视频传输了。名词系列三,就是这个组织指定的标准。

后来,ITU-T(国际电信联盟电信标准化部门)于MPEG联合制定了H.264/MPEG-4 AVC,这才是我们这一讲重点关注的。

经过编码,一帧帧的图像,就变成了一串串看不懂的二进制,这个二进制可以放在一个文件里,按照一定的格式保存起来,这就是名词系列一。

这些视频保存成文件的格式,例如,前几个字节代表什么意思,后几个字节代表什么意思,然后是数据,数据中保存编码好的结果。

如何在直播里看到帅哥美女?

编码之后的二进制可以通过网络协议进行封装,放在互联网上传输,这个时候就可以进行网络直播了。

网络协议将编码好的视频流,从主播推送到服务器,在服务器上有个运行同样协议的服务端来接收这些网络包,从而得到里面的视频流,这个过程称为接流。

服务端接到视频流之后,可以对视频进行一定的处理,例如转码,从一个编码格式转成另一种格式。因为观众的客户端千差万别,要保证他们都能看到直播。

流处理完毕后之后,就可以等待观众的客户端来请求这些视频流,观众的客户端请求过程称为拉流。

如果观众非常多,看同一个视频,都从一个服务器上拉流,压力太大,需要一个视频的分发网络,将视频预先加载到就近的边缘节点,这样观众看的视频,是从边缘节点拉取的,就能降低服务器的压力。

当观众的客户端将视频流拉下来之后,就需要进行解码,即编码的逆过程。将一串串看不懂的二进制转变成一帧帧的图片,通过客户端播放出来,就能看到美女帅哥了。

整个直播过程,用下面的图描述。

我看下每个过程。

编码:如何将图片变成二进制流?

视频是一张张图片的序列,如果每张图片都完整传输,就太大了,因而将视频序列分成三种帧。

  • I帧,称为关键帧。里面是完整的图片,只需要本帧数据,就可以完成解码。
  • P帧,前向预测编码帧。P帧表示的这一帧跟之前的关键帧(或P帧)的差别,解码时需要用之前缓存的画面,叠加上和本帧定义的差别,生成最终画面。
  • B帧,双向预测内插编码帧。B帧记录本帧和前后帧的差别。要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的数据与本帧数据的叠加,取得最终画面。

可以看出,I帧最完整,B帧压缩率最高,而压缩后帧的序列,应该是以IBBP的间隔出现的。这就是通过时序进行编码。

在一帧中,分成多个片,每个片分成多个宏块,每个宏块分成多个子块,这样将一张大图分解成一个个小块,方便空间上的编码。

最终要压缩成一个二进制流。这个流是有结构的,是一个个的网络提取单元(NALU,Network Abstration Layer Unit)。变成这种格式是为了传输,因为网络上的传输,默认的是一个个的包,因而这里就分成了一个个单元。

每一个NALU首先是一个起始标识符,用于标识NALU之间的间隔;然后是NALU的头,里面配置了NALU的类型;最终Payload里面是NALU承载的数据。

在NALU头里面,主要的内容是类型NALU Type。

  • 0x07表示SPS,是序列参数集,包括一个图像序列的所有信息,如图像尺寸、视频格式等。
  • 0x08表示PPS,是图像参数集,包括一个图像的所有分片的所有相关信息,包括图像类型、序列号等。

在传输视频流之前,必须传输这两类参数,不然无法解码。为了保证容错性,每个I帧前面都会传一遍这两个参数集合。

如果NALU Header里面的表示类型是SPS或PPS,则Payload中就是真正的参数集的内容。

如果类型是帧,则Payload中才是真正的视频数据。

这样,整个格式就有了。一个视频,可以拆分成一系列的帧,每一帧拆分成一系列的片,每一片都放在一个NALU里面,NALU之间通过特殊的起始标识符分隔,在每一个I帧的第一片前面,要插入单独保存的SPS和PPS的NALU,最终形成一个长长的NALU序列。

推流:如何把数据流打包传输到对端?

这个格式是不能直接传输到对端的,还需要将这个二进制的流打包成网络包进行发送,这里我们使用RTMP协议。这就进入第二个过程,推流。

RTMP是基于TCP的,所以需要双方建立一个TCP连接,在TCP连接的基础上,还需要建立一个RTMP连接,即在程序里,调用RTMP类库的Connect函数,显示创建了一个连接。

RTMP为什么要创建单独的连接呢?

因为它们需要商量一些事情,保证以后的传输能正常运行。主要是两个事情,一个是版本号,如果客户端和服务端的版本号不一致,就不能正常工作。另一个是时间戳,视频播放中时间是很重要的,因为一开始要知道对方的时间戳。

沟通这些事情需要发送六条消息:客户端发送C0、C1、C2,服务端发送S0、S1、S2.

首先,客户端发送C0表示自己的版本号,不必等待对方的回复,然后发送C1表示自己的时间戳。

服务器收到C0 的时候,返回S0,表明自己的版本号,如果版本号不匹配,可以断开连接。

服务器发送完S0后,也不用等什么,就直接发送自己的时间戳S1。客户端收到S1后,发送一个直到了对方时间戳的ACK S2。同理,服务器收到C1的时候,发送一个直到了对方时间戳的ACK S2。

于是,握手完成。

握手之后,双方需要相互传递一些控制信息,例如Chunk块的大小、窗口大小等。

真正传输数据的时候,还是需要创建一个流Stream,然后通过这个Stream来推流publish。

推流的过程,就是将NALU放在Message里面发送,这个也称为RTMP Packet包。Message 的格式就像这样。

发送的时候去掉NALU的起始标识符。接下来,将SPS和PPS参数集封装成一个RTMP包发送,然后发送一个个片的NALU。

RTMP在收发数据的时候也不是以Message为单位的,而是把Message拆分成Chunk发送,而且必须在一个Chunk发送完之后,开始下一个Chunk。每个Chunk都带有Message ID,表示属于哪个Message,接收端按照这个ID将Chunk组装成Message。

前面建立的时候,设置的Chunk块大小就是指这个Chunk。将大消息变成小块再发送,可以在低带宽的情况下,减少网路拥塞。

这有一个分块的例子。

假设一个视频的消息长度为307,但是Chunk大小约定为128,于是拆分成三个Chunk。

第一个Chunk的Type=0,表示Chunk头是完整的;头里面Timestamp为1000,总长度Length为307,类型为9,是视频,Stream

 ID为12346,正文部分承担128个字节的Data。

第二个Chunk也要发送128个字节,Chunk头由于和抵押给Chunk一样,因此采用Chunk Type=3,表示头一样,不再重复发送了。

第三个Chunk要发送的Data长度为307-128-128=51个字节,还是采用Type=3。

这样数据就不断到达流媒体服务器,整个过程像这样。

这个时候,大量看直播的观众就可以通过RTMP协议从流媒体服务器上拉取,但是这么多用户从一个地方拉取,服务器压力会很大,而且用户分布在世界各地,如果统一去一个地方下载,会很耗时,需要有分发网络。

分发网络分为中心和编译两层。边缘服务器部署在各地,利用户很近。中心曾是流媒体服务集群,负责内容的转发。智能负载均衡系统,根据用户的地理位置信息,就近选择边缘服务器,为用户提供推/拉流服务。中心曾也负责转码服务。例如,把RTMP协议的码流转换为HLS码流。

拉流:观众的客户端如何看到视频?

先读到的是H.264的解码参数,例如SPS和PPS,然后对收到的NALU组成的一个个帧,进行解码,交给播放器播放,视频画面就出来了。

小结

  • 视频名词比较多,编码的两大流派达成一致,通过时间、空间的各种算法来压缩数据。
  • 压缩好的数据,为了传输组成一系列NALU,按照帧和片依次排列;
  • 排列好的NALU,在网络传输的时候,要按照RTMP包的格式进行包装,RTMP的包会拆分成Chunk进行传输。
  • 推送到流媒体集群的视频流经过转码和分发,可以被客户端通过RTMP协议拉取,然后组合为NALU,解码成视频格式进行播放。

两个思考题:

1. 你觉得基于RTMP的视频流传输的机制存在什么问题?如何进行优化?

2. 在线看视频之前,大家都是把电影下载下来看,电影这么大,你知道如何快速下载码?

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

第16讲 | 流媒体协议:如何在直播里看到美女帅哥? 的相关文章

  • 【Python学习】计算水仙花数(for循环)

    Python学习 计算水仙花数 xff08 for循环 xff09 说明 xff1a 水仙花数是一个三位数 xff0c 三位数各位的立方之和等于三位数本身 计算出的水仙花数有4个 xff1a 153 370 371和407 一 代码 spa
  • 【Python学习】遍历字典

    Python学习 遍历字典 字典 字典 xff08 dict xff09 是可迭代的 通过键 xff08 key xff09 来访问元素的可变的容器类型的数据 字典由两部分视图构成 xff1a 键视图和值视图 键视图不能包含重复的元素 xf
  • 【Python学习】格式化控制符

    Python学习 格式化控制符 在占位符中还可以有格式化控制符 xff0c 对字符串的格式进行更加精准的控制 格式化控制符位于占位符索引或占位符名字的后面 xff0c 之间用冒号分隔 xff0c 语法 xff1a 参数序号 xff1a 格式
  • Oracle:SQL语句--对表的操作——修改列名(即修改字段名)

    修改列名 即修改字段名 alter table 表名 rename column 现列名 to 新列名
  • 社交技能讲座笔记

    作者 xff1a 朱金灿 来源 xff1a clever101的专栏 为什么大多数人学不会人工智能编程 xff1f gt gt gt 感谢张鹏老师做了一堂实用的社交技能讲座 我特地做了一些笔记 xff08 其中包含我的一些理解 xff09
  • Oracle:SQL语句--对表的操作—— 删除字段(即删除列)

    删除一个字段 即删除一列 xff08 未验证在有数据 xff0c 并且互有主外键时 xff0c 是否可用 xff09 语法 xff1a alter table 表名 drop column 字段名 即列名 例 xff1a alter tab
  • Oracle:SQL语句--对表的操作——删除表

    删除表 xff08 未验证在有数据 xff0c 并且互有主外键时 xff0c 是否可用 xff09 表中 列 为 其他表 外键 且有数据 应先解除约束 xff0c 或删除相关表 语法 xff1a drop table 表名 例 xff1a
  • Java作业:输入一个数字判断他是奇数还是偶数

    span class hljs comment 2 输入一个数字判断他是奇数还是偶数 span span class hljs keyword public span span class hljs keyword static span
  • Linux基础知识学习:Linux下修改文件名或修改文件夹名称(有待解决问题)

    Linux下修改文件名或修改文件夹名称 1 修改文件夹名称 1 1我先创建一个test文件夹用来测试 span class hljs keyword mkdir span test 1 2用 mv 命令 将文件移动 xff0c 目标地址如果
  • C语言学习:平方-->乘方(m的n方)

    平方 xff1a 直接用两个数 或变量 相乘就可以表示平方 xff0c 比如x x 不过如果 xff0c 需要求m的n次方 xff0c 就需要用到pow x y 乘方 包括开方 这个库函数了 xff0c 使用pow x y 这个库函数 xf
  • MySql学习:自定义函数之带参函数

    delimiter 如果数据库 test 里的存在函数 formatDate xff0c 就删除这个函数 DROP FUNCTION IF EXISTS test formatDate 创建一个函数 CREATE FUNCTION test
  • docker离线安装

    1 下载离线包 docker官网下载地址 本示例下载的是 xff1a docker 19 03 14 tgz 2 解压到对应目录 解压文件 span class token function tar span xzvf docker 19
  • 2013年:一个技术领导的启程

    作者 xff1a 朱金灿 来源 xff1a http blog csdn net clever101 又到一年总结时 总的来说 xff0c 这一年忙碌而充实 xff0c 现在有点胸中有千言却又不知从何说起 可能每一个希望有所作为的开发人员都
  • STM32——硬件IIC从机通信

    前言 xff1a 根据网上的资料 xff0c 大部分网友表示STM32自带的硬件IIC存在bug xff0c 读写时很容易卡死 自己在调试的时候也出现卡死的情况 xff0c 最后一点一点调试 xff0c 也还是调通了 本文将记录自己调试ST
  • HI3516的编译参数-mcpu=cortex-a7、-mfloat-abi=softfp和-mfpu=neon-vfpv4

    前言 Hi3516A具有浮点运算单元和neon 文件系统中的库是采用软浮点和neon编译而成 xff0c 因此所有Hi3516A板端代码编译时需要在Makefile里面添加选项 mcpu 61 cortex a7 mfloat abi 61
  • 算法移植优化基础

    PS xff1a 为了面试准备的 xff0c 总结的有点粗糙 ARM xff1a Advanced RISC Machines xff0c ARM架构是面向低预算市场设计的第一款RISC微处理器 xff0c 基本是32位单片机的行业标准 x
  • DBoW2在windows上的vs工程搭建方法

    xfeff xfeff 注释 xff1a 蓝体字是 opencv249 专用的修改 xff1b 黑体字是 opencv3 需要的修改 环境 xff1a vs2012 32bit 叙述比较简略 不明之处可以留言 1 配置 opencv 2 4
  • MSCKF_vio学习笔记

    最近因为项目需求 xff0c 对MSCKF vio的论文和代码进行了一番研读 xff0c 现将学习过程记下 MSCKF vio是一种基于多状态约束卡尔曼滤波器的双目视觉里程计 其中多状态约束是指将多帧图像的相机位姿加入卡尔曼状态向量中 xf
  • MSCKF2.0(Mingyang Li-IJRR2013) 论文要点总结

    论文 xff1a Li M Mourikis A I High precision consistent EKF based visual inertial odometry J The International Journal of R
  • SLAM,SLAM+IMU的状态估计问题描述-个人理解

    1 状态估计问题 令状态变量为x 61 x1 x2 x3 观测变量为z 61 z1 z2 z3 状态估计问题等同于求解条件概率分布 xff1a P x z xff0c 在当前观测状态z下的状态x分布 xff0c 也就是最可能的状态是什么 由

随机推荐