网络传输是整个直播过程中的第三个环节
传输的阶段包括三部分:
主播端推流到服务端、服务端的流分发、观众端的拉流
下图是视频直播的架构图
咱们来看一下主播推流和观众端拉流的大体流程
首先,一个主播进行开播,他肯定要安装推流软件(就是推流SDK)
然后向直播云服务器申请一个推流的地址
你可以理解成他的直播房间号 没有房间你直播个啥
相应的他也得有推流地址才能把他的直播数据推送给服务器
像旭旭宝宝房间号99999(九五至尊)
映射的就是他有自己固定的一组推流地址
为什么不说一个地址呢,因为推流地址可能不止一个
接下来呢
主播就通过手机摄像头,或者专门的录像设备等进行视频采集
生成原始的视频数据格式,RGB或者YUV(一般都是YUV)
再通过麦克风,或者话筒进行声音的采集,生成原始的音频数据格式:PCM
再然后就是分别进行编码了:它会将
视频文件编码:YUV --> H.264 (H.265)
声音文件编码:PCM --> AAC
这些都是咱们上面说过的内容了
接下来呢 就是将两个文件进行合并,使 H.264 和 AAC 结合成 FLV、TS、RTMP Packet
具体是哪种格式就看你使用的 传输协议 了
合并之后的这几种格式的文件呢,就是 流媒体文件 了
这里又引申出两个个概念,流媒体、传输协议
流媒体是指将一连串的媒体数据压缩后,经过网上分段发送数据,在网上即时传输影音以供观赏的一种技术与过程,
此技术使得数据包得以像流水一样发送;
大体了解一下这个流媒体有啥用呢
就是说呀,如果不使用流媒体技术,你如果想看一部电影
那你必须得下载完整个电影文件你才能看,又或者你想看一个美女主播直播,那根本不可能!
为啥?
因为你必须得等到她直播完毕才能去看她完整的录播视频文件
而流媒体技术就打破了这一限制
它可传送现场的影音或预存于服务器上的影片
当观看者在收看这些影音文件时,影音数据在送达观看者的计算机后立即由特定播放软件播放
你送多少 我播多少 就这个意思!
那 传输协议 又是什么呢?
哎!你看
主播在他的设备上通过一系列的操作:
音视频采集、降噪、图像、流量控制、美颜滤镜等等
当然,这也不是主播做的,是直播SDK做的工作!
就生成了直播的数据:流媒体文件了
有了这些数据之后,你得推送过去呀
那怎么推呢?
这个 怎么推 就是 传输协议了
你选协议A,你就用方式A去推送
你选协议B,你就用方式B去推送
而国内常见公开的直播协议有几个:RTMP、HLS、HDL(HTTP-FLV)、RTP
RTMP:
是Adobe的专利协议,现在大部分国外的CDN已不支持
CDN(Content Delivery Network)是指内容分发网络,也称为内容传送网络
它是一种新型网络构建方式,能为传统的IP网发布宽带丰富媒体而特别优化的网络覆盖层
这个大体了解一下就行,就把它理解成一个网络加速器
RTMP 在国内流行度很高,原因有几个方面:
1、 开源软件和开源库的支持稳定完整:
如斗鱼主播常用的OBS软件,开源的librtmp库,服务端有nginx-rtmp插件等
玩直播的童鞋应该知道
OBS是一个开源PC推流工具,支持Windows和Mac平台
它是免费开源的,国内外论坛上也有很多脚本插件
而且操作界面很善良,稍一研究就会使用,容易上手
功能可以满足游戏直播、秀场直播基本功能
librtmp库也是一个支持RTMP推流的这么一个开源库
2、 播放端安装率高
只要浏览器支持FlashPlayer就能非常简易的播放RTMP的直播
相对其他协议而言,RTMP协议(底层基于TCP)初次建立连接的时候握手过程过于复杂
视不同的网络状况会带来给首开带来100ms以上的延迟,
而且当你网络不好造成重传时,延时也会大量增加
3、延时低
基于RTMP的直播一般内容延迟在2~5秒
这个延时是比较低的,相比于HLS的5-20秒,这个算是很好了
说到这个第二点这里提个事:
这个图片大家应该都见过了吧:
Adobe公司已经宣布在2020年彻底停止Flash的更新,
而Chrome浏览器也提示“自2020年12月开始,Flash Player将不再受支持。”
所有浏览器不再支持flash插件之后,那就会带来一个问题:
RTMP的视频流就不能在浏览器中播放了
当然了,人家也有对应的解决策略,有兴趣的可以搜一下,很多这种文章
这里说一个解决方案:
这个时候可以采用将 RTMP 重新封装成 HTTP-FLV 的视频流,采用flv.js来进行播放
因为 RTMP 和 HTTP-FLV 的视频格式都是FLV格式的,它俩只是传输协议而不同
这时候就可以对 RTMP 流重新封装为 HTTP-FLV 的流进行分发
那既然说到这个HTTP-FLV了,咱们接下来就看下它:
HDL(HTTP-FLV)
它是使用HTTP协议流式的传输媒体内容
相对于RTMP,HTTP更简单和广为人知,而且不担心被Adobe的专利绑架
内容延迟同样可以做到2~5秒,低延时
而且打开速度更快,因为HTTP本身没有复杂的状态交互。
所以从延迟角度来看,HTTP-FLV要优于RTMP
HLS:
是由苹果提出基于HTTP的流媒体传输协议
首先一点 咱们知道它延迟很大,但是它不光延时很大,而且极易受网络波动影响造成卡顿
那要它有啥用呢?
哎!HLS有一个非常大的优点:HTML5可以直接打开播放!
这个意味着可以把一个直播链接通过微信等转发分享,不需要安装任何独立的APP
你只要有浏览器就可以打开播放,所以流行度很高!
针对那种社交直播APP,HLS可以说是刚需!
它的直播流URL其实是一个m3u8的文件,里面包含了最近若干个小视频TS文件
它的延时数据:
同城网络可以做到比较好的效果是5~7秒的延迟,一般来说还是5-20秒
RTP:
最后咱们再来看一下RTP协议
RTP在视频监控、视频会议、IP电话上有广泛的应用
因为视频会议、IP电话的一个重要的使用体验:内容实时性强
因为实时音视频流等这些场景是不需要可靠保障的
因此也不需要有重传的机制,就是要实时的看到图像声音
至于你网络抖动时丢了一些内容,画面模糊和花屏,完全不重要
所以说到这,你应该就会明白它基于 UDP协议 来传输数据
当然,这只是常见的几种协议,还有其他的协议
比如说WebRTC协议等,有兴趣的可以搜一下
所以,你选了哪种协议,你的直播数据就会被封装成对应的协议格式
然后把这些数据按照传输协议,推送给直播云服务器
而到了服务器这块,就会生成相应的拉流url,通过CDN节点,进行分发直播流数据
当然服务器还有其他一大堆功能
比如:高清转码、录制直播回放、截图、智能鉴黄审核等
而到了用户这,想要观看直播
就需要在终端(手机、电脑等)上,通过某些渠道,去获取拉流的地址
比如去斗鱼点击旭旭宝宝的直播间,你就获取到了他的拉流url
拉流协议 和 推流协议一样,也是上面那几种
这里需要注意一点
推流协议和拉流协议不一定保持一致!
比如:
主播使用RTMP去推流,但是用户拉流的时候使用的协议不一定也是RTMP
因为服务器可能会对数据进行协议转换!
这里可能会有一些童鞋有个疑问,我当时也有这个疑问
为什么服务器要进行协议转换呢,保持一致不好吗?
其实是有原因的,你想一下这种情况:
如果主播使用协议A去推流,但是某一些用户的终端他不支持协议A
那人家还想看这个主播,打赏这个主播,那你能不让别人去看嘛!
但是有一点是肯定的:
用户的拉流协议只需要和用户去拉取的Server端存储的数据流协议保持一致就可以了
确定了拉流协议,那么就对应的把拉取到的流媒体文件进行解码 去播放!
其实解码、播放这俩流程基本是分不开的,内容也都会互相交错,咱就放一块去讲了
既然说到解码了,就不得不说 FFmpeg 了
像B站的ijkplayer播放器,也是基于它做出来的
它被叫做万能播放器的根基,属于神级的播放软件!
为什么这么说呢?
咱们在日常生活中的
用来播放视频的那么多花样迭出的视频播放器
它们给你播放电影、电视、综艺等,但是它们要运作起来,往往都离不开FFmpeg!
播放器要播放一个视频,首先需要做的,就是要支持这个视频的格式,
然后把视频流、音频流等内容从格式容器中分离出来,进而解码、渲染,
然后用户就可以播放出视频、音频乃至字幕等内容了。
但是这个过程是特别繁琐而且非常有技术含量的
而且这个世界上可不止一种视频格式,视频的编码格式那是海了去了!
那要做一个播放器就意味着需要对这些东西吃得非常的透,这无疑是相当费时费力的
因此,很多视频播放器实际上都使用了统一的解决方案 ——FFmpeg!!!
那这个FFmpeg到底是何方神圣呢?
FFmpeg是一套视频音频的完整解决方案
它提供了视频解码、编码、后期处理等一系列功能
对世界上千奇百怪 成千上万的视频音频编码有着完善的支持。
FFmpeg是在Linux平台下开发的,完全开源
也可以在其他平台编译运行,它的功能非常非常强大,也绝不仅仅限于播放!
FFmpeg可以解码非常多的视频音频编码格式
它同时也提供了视频音频转换、编码、封装的功能
还可以进行视频裁剪、缩放、色域转换等一系列后期处理。
不仅如此,FFmpeg还支持HTTP、FTP、SMB等协议
可以说,无论你想要本地播片,还是转换视频格式
亦或是利用网络看视频,FFmpeg都可以胜任!
而它在2011年分裂出的Libav项目,该项目诞生出了迄今为止最强力的视频解码器LAV!
无论对于软硬件,LAV解码器都有着颇为完善的支持,大量播放软件都可以调用LAV解码
而LAV本身也可以利用各种显卡进行视频硬解。
很多播放器都宣称自己多么多么万能,支持先进的4K乃至8K视频播放,
乍看这播放器搞定了世界上如此繁杂的视频容器、视频音频编码,太了不起了!
然而实际上,真正的原因往往是因为这播放器自带了FFmpeg/LAV解码器
真正神的不是播放器,而是背后的FFmpeg/LAV!
你能用播放器顺利放出小电影,可千万别忘记感谢FFmpeg!
因此,把FFmpeg称作为神级软件,完全实至名归
FFmpeg是如此强大且开源的,那就肯定涉及到了一个问题:
抄!
由于FFmpeg是一个开源软件,因此诸多我们日常接触的软件和各大播放器开发商
都会使用其技术来实现视频播放功能。
这其中,既包括常见的播放软件,也包括浏览器之类的能够播放视频的产品。
但是FFmpeg虽然开源,但人家也有个协议:GPL/LGPL
就是说
你想用我的开源代码,那你必须遵守这个协议,在我的协议范围内进行使用。
那这个协议是怎么规定的呢?如下:
如果某软件使用了FFmpeg的代码,那么这个软件涉及这些代码的部分,也必须开源!
但是业界的风气嘛……一般都是我用了就是用了,我赚钱的东西能开源出来给你看?
因此,不少著名的播放软件因为违反了人家的这个协议,
都上了FFmpeg的耻辱柱名单被曝光了出来:
暴风影音、QQ影音,国外著名的KMPlayer、PotPlayer等等
它们都大量的使用了FFmpeg源代码但却不开源
当然也有很多守规矩的良心软件都开源了!
那么基于FFmpeg做出来的拉流SDK
根据协议的不同,把拉取到的文件FLV、TS、PTMP Packet进行解码成:H.264视频文件 和 AAC音频文件
然后继续进行解码:
把H.264解码成YUV或者是RGB
把AAC解码成PCM
这一阶段的解码分为两种:一种是硬解码 另一种是软解码
这里扯一下它们的由来吧:
咱们上面说过了,我们在计算机上播放的视频文件都是经过压缩的,因为这样有利于节约存储空间
那么在播放过程,就需要进行一个反过程的解压缩
在以前这项工作全部都是由 CPU 来完成的
对于普通分辨率的AVI、RMVB等格式的视频文件,绝大多数的 CPU 都可以完全胜任;
但是随着技术的发展,后来就出现了高清视频,比如1080p或者更大
数据解压缩的工作量比以前翻了数倍,这让很多 CPU 感到难受!
你说这个解压缩难吗?哎!它不难,非常简单!
但架不住量多呀!这样就类似于把CPU给捆绑了,我本来能做更有难度!更有技术含量的活!
你却天天让我给你做一些没难度但是还很多的工作,这效率一下就拉低了!
后来呢,又随着技术的发展
工程师们发现显卡GPU 要比 CPU 更适合这类大数据量的、但是低难度的重复工作!
这样视频解码的工作就从 CPU处理器 那里分离出来,交给了显卡去做,这就叫做 硬解码
而与之对应的,以前那种纯粹依靠 CPU 来解压缩的方式则是 软解码
不过受到技术条件的限制,纯粹的 硬解码 在现阶段是不存在的,也是实现不了的
在硬解码过程中 CPU依然在发挥一部分作用,只不过GPU成为了运算的主力,CPU辅助GPU去工作。
按照国际惯例,
那接下来肯定要说一下它们的优缺点和区别了:
软编码:
实现直接、简单,参数调整方便,升级易。
但CPU负载重,一个人干所有的活,性能较硬编码低。
低码率下质量通常比硬编码要好一点。
硬编码:
性能高,这个很好理解,两个人干活总比一个人快嘛!
低码率下通常质量低于软编码器,但部分产品在GPU硬件平台移植了优秀的软编码算法(如X264)的,质量基本等同于软编码。
罗列一下它俩的区别:
来,继续往下走,解码出来 YUV(RGB)和 PCM 之后呢,
就需要执行 音画同步 的操作了!
因为解码出来之后得到两份文件:一份是图像文件,一份是音频文件
那播放的时候就需要进行一一对应了,否则就会存在声音与画面不同步,严重的影响观看体验!
之前我们在搞互动视频的时候,也涉及到同时在线播放课件和老师两个视频同步的问题
我们那时候的解决思路就是对比两个视频的时间进度,快的等慢的,或者Seek到相同的时间
放大到现实生活中它也是一样的原理
如何判断两件事情是否同一时刻发生的?
你去记录、比较事件发生的日期时间就好了嘛!
原理都一样!都是 Time To Time!
像图像文件有帧率,比如1秒20帧
音频文件有采样率,比如一秒采集8000个采样点
这些都是它们的时间信息
理论上来说每一帧数据都会有一个固定的时长
而在视频采集过程中也都会给每帧数据标记一个时间戳,以方便来确定什么时刻去播放。
这样就能严格的将声音与画面对应起来
如图所示:
音频、图像在采集的时候,会给每一帧都打上 时间戳
这时候就需要注意了:
既然音频、图像都有时间戳,那么需要定一个共同的时间戳去做基准!
假设以音频开始采集时刻的系统绝对时间点作为时间戳 0 点
假设音频 20ms 采样一帧,视频帧率 30fps ,即 33ms 产生一帧
那么,视频帧和音频帧的时间戳大致的递增规律为:
视频帧:10,43,76,109,142,……
音频帧:0,20,40,60,80,120,……
但是实际上数据采集也不会这么 “均匀”,会有略微的浮动
所以肯定还是会存在略微偏差,尤其是那种声音和画面还都是分开采集的情况下!
但在这些误差一定的阈值之内人是感觉不到音画的差异
而这个阀值,国际标准上是这么定义的:这个标准叫 RFC-1359
无法察觉:音频和视频的时间戳差值在:-100ms ~ +25ms 之间
能够察觉:音频滞后了 100ms 以上,或者超前了 25ms 以上
无法接受:音频滞后了 185ms 以上,或者超前了 90ms 以上
好了,现在时间戳有了,咱们再来看Time to Time
那问题也来了,谁的Time to 谁的Time呢?
也就是以谁为基准呢?
哎!这就诞生了音视频同步的三种方式:
以声音为播放基准,调控画面渲染的速度
以画面渲染为基准,调控声音播放速度快慢
参考一个外部时钟,将声音与画面同步至此时间
那我们会采用哪种方式去同步呢?
通常我们都采用第一种方式去同步
也就是以声音为基准去调整画面的播放速度。
也就是说,声音就是按照既定的速率进行持续播放,不去干扰它,而是去随着声音动态的去调节图像的展示
为啥要挑音频的时间?
因为一般画面的播放速度要比声音播放要快
人的耳朵对声音的快慢感知度是比较强的,能够快速的感知声音的快或慢
而对于画面的渲染其实就是一张张的图片刷新,反而人眼的感知度却没有那么强
而且因为音频是流式的,按照规律 匀速的速率去播放,显得更加平滑
而视频的播放因为是一张一张的进行刷新
它的刷新时间的调整相对而言就更加容易一些,用户肉眼的敏感度也更弱一些!
那下面具体来看看怎么实现画面同步到音频的:
先来了解一下时间相关概念
每帧数据都有时间戳,这个时间戳呢,就是 DTS(解码时间戳)和PTS(显示时间戳)
DTS:
即解码时间戳,这个时间戳的意义在于告诉播放器该在什么时候解码这一帧的数据
PTS:
即显示时间戳,这个时间戳用来告诉播放器该在什么时候显示这一帧的数据
它俩是用于指导播放端同步行为的,在编码的时候由编码器生成
怎么每帧的时间戳还有两个?
别着急,说这个之前,再来回顾学习下前面讲的
图片在编码压缩的时候 I帧、B帧、P帧
视频的播放过程可以简单理解为一帧一帧的画面按照时间顺序呈现出来的过程
就像在一个本子的每一页画上画,然后快速翻动的感觉。
但是在实际应用中,并不是每一帧都是完整的画面,因为如果每一帧画面都是完整的图片
那么一个视频的体积就会很大,这样对于网络传输或者视频数据存储来说成本太高
所以通常会对视频流中的一部分画面进行编码压缩处理。
由于压缩处理的方式不同,视频中的画面帧就分为了不同的类别
其中包括:I 帧、P 帧、B 帧
I 帧:
I 帧图像采用帧内编码方式,即只利用了单帧图像内的空间相关性,而没有利用时间相关性
I 帧使用帧内压缩,不使用运动补偿,由于 I 帧不依赖其它帧,所以是随机存取的入点,同时是解码的基准帧
I 帧主要用于接收机的初始化和信道的获取,以及节目的切换和插入
I 帧图像的压缩倍数相对较低
I 帧图像是周期性出现在图像序列中的,出现频率可由编码器选择
P 帧:
P 帧和 B 帧图像采用帧间编码方式,即同时利用了空间和时间上的相关性
P 帧图像只采用前向时间预测,可以提高压缩效率和图像质量
P 帧图像中可以包含帧内编码的部分,即 P 帧中的每一个宏块可以是前向预测,也可以是帧内编码
B 帧:
B 帧图像采用双向时间预测,可以大大提高压缩倍数
值得注意的是,由于 B 帧图像采用了未来帧作为参考,因此编码流中图像帧的传输顺序和显示顺序可能是不同的
也就是说
一个 I 帧可以不依赖其他帧就解码出一幅完整的图像,而 P 帧、B 帧不行
P 帧需要依赖视频流中排在它前面的帧才能解码出图像
B 帧则需要依赖视频流中排在它前面或后面的帧才能解码出图像
这就带来一个问题:
在视频流中,先到来的 B 帧无法立即解码,需要等待它依赖的后面的 I、P 帧先解码完成
这样一来播放时间与解码时间不一致了,顺序打乱了,那这些帧该如何播放呢?
这时就需要 DTS 和 PTS 信息了
比如在一个视频中,帧的显示顺序是:I B B P
现在我们需要在解码 B 帧时知道 P 帧中信息
因此这几帧在视频流中的顺序可能是:I P B B
这时候就体现出每帧都有 DTS 和 PTS 的作用了
DTS 告诉我们该按什么顺序解码这几帧图像,PTS 告诉我们该按什么顺序显示这几帧图像
顺序大概如下:
如果一个视频的帧展示顺序为:I B B P
视频流中的顺序Stream: I P B B
但是视频流中的顺序不一定就是显示的顺序
解码顺序为 DTS: 1 2 3 4
按照 I P B B 的顺序就能完整的解码出来完整的图片
显示顺序,就是上面咱们说的 I B B P
PTS: 1 4 2 3(对应Stream中的顺序)
音频流的PTS和DTS是一致的,都是按照顺序去解码和展示
视频流的PTS和DTS就有可能不一样了:
当视频流中没有 B 帧时,通常 DTS 和 PTS 的顺序是一致的。
但如果有 B 帧时,就像上面的例子一样,那顺序就可能会不一样了
现在我们知道了音频、视频的各个帧的时间戳、展示顺序、展示的时长等信息
这时候就可以用画面去同步声音了:
在音频播放线程不断的去计算当前播放的音频帧的实际时间
在画面渲染线程获取当前画面的播放实际时间
两个时间做比较,如果画面当前时间大于音频播放时间,计算出画面应该延时的时间
将画面渲染线程进入休眠等待音频追上画面播放再去播放。
如果画面当前时间小于音频时间(这种情况比较少出现,一般画面的速度都是快与声音的播放速度)
当时间差大于一点的阈值这时候我们可以不进行该帧的渲染(跳帧丢帧去追上音频的播放)
这个就是音画同步的基本原理!
将同步后的音频数据推送到音频输出设备进行播放
将同步后的视频推送到视频输出设备进行播放
就是用户最终看到的直播视频画面了