鹏程,携程 Android 开发工程师,Android google jetpack和kotlin语言的拥护者。
直播行业大概在10年前就开始兴起了,秀场直播和游戏直播是pc时代比较成功的应用场景。现阶段,移动互联网的大规模普及,流量价格越来越便宜,移动视频直播异常火爆,随着各行各业的不断融合,直播带货超高的营业额,明星艺人、销售、秀场网红的涌入,直播行业迎来了空前的繁荣发展。从pc直播到渐渐火爆的移动直播,直播技术也在不断地更新迭代,趋于成熟。本文从直播流的选择、交互优化、快速迭代等方面介绍携程直播技术。视频直播流程如下图。简单来说,推流端通过视频采集功能,把采集到的视频画面经过一系列的业务特效处理后,进行视频编码推送。拉流端使用流播放器把视频画面播放出来。- 采集:视频采集的主要采集源:摄像头、屏幕录制、视频文件推流
- 处理:视频采集后得到原始数据,为了增强一些现场效果,需要在编码前进行处理(logo、美颜、变声)
- 编码:编码性能、编码速率和编码压缩比直接影响整个流媒体传送的用户体验和传送成本
- 推流:推流是直播的第一步。推流协议的选择会直接影响到观看的用户体验,常见的流协议(RTMP、HLS)
前四步我们通常情况下称为推流操作,第五步称为服务分发或者cdn分发,第六步称为拉流操作。简化图如下:在兼容性方面,有时内容已经被压缩到足够的大小,但仍然需要进行编码以实现兼容性。这通常被更准确地描述为代码转换。兼容可能涉及某些服务或程序,这些服务或程序需要某些编码规范。RTMP(Real Time MessagingProtocol):实时消息传输协议,是Adobe公司为flash播放器和服务器之间实现音频、视频和数据传输开发的实时消息传输协议。在RTMP协议中,视频必须是H264编码,音频必须是AAC或者MP3编码,且多以Flv格式封包。
RTMP的劣势是:RTMP是基于TCP协议,不会丢包。所以当网络状态差时,服务器会将包缓存起来,导致累积的延迟;待网络状况好了,就一起发给客户端。
HLS(HTTPLive Straming): 是苹果公司实现的基于HTTP的流媒体传输协议。它将整个流分为多个小文件来下载,客户端只要不停的按顺序播放从服务器获取的文件,就实现了直播。HLS的优势是:客户端支持简单,只需要支持HTTP请求即可。并且HTTP协议很方便通过防火墙或者代理服务器。CDN支持良好。由于是苹果公司提出的,所以在苹果的全系列产品都支持。HLS的劣势:相比 RTMP 这类长连接协议,HLS的延时较高, 难以用到互动直播场景。WebRTC(Web Real TimeCommunication):是一个支持浏览器进行实时语音、视频对话的开源协议。基于UDP,即使在网络信号一般的情况下也具备较好的稳定性。
WebRTC的优点:开发者使用简单的HTML标签和JavaScriptAPI就能够实现Web音/视频通信的功能。WebRTC的缺点:WebRTC中很多的参数都是由GIPS公司的工程师们依靠经验所设定的值,这就会出现卡顿、延时、回声、丢包、多人视频不稳定等问题。WebRTC缺乏服务器方案的设计和部署。对Native开发支持不够。HTTP-Flv:是一种将直播流模拟成flv文件,通过http协议进行下载的模式实现流媒体传输的协议。它结合了RTMP的低延时,以及可以复用现有HTTP分发资源的流式协议。优势在于可以在一定程度上避免防火墙的干扰,可以使用HTTPS做加密通道,很好的支持移动端。缺点在于由于它的传输特性,会让流媒体资源缓存在本地客户端,在保密性方面不够好。因为网络流量较大,它也不适合做拉流协议。 | | | | |
| | | | |
| | | | |
| | | 集合一段时间的数据,生产TS切片并更新m3u8索引 | Client-client之间传输,要解决NAT 穿透问题。 |
| | | | |
| | | | |
| | | | |
| |
| | |
1)RTMP是编码器输入的工业标准协议,基本上所有的编码器(摄像头等)都支持RTMP输出。2)RTMP适合长时间播放的优势,可以保证尽可能的减少用户重连的次数。3)直播场景互动性交高,对时延要求高。RTMP通常情况下可以做到3秒延迟,满足大多数场景(hls大概10秒)。4)WebRTC对浏览器支持较好,对native支持不够,需要做大量的开发工作。5)由于开源软件和开源库的支持稳定完整(OBS软件,开源的librtmp库,服务端有nginx-rtmp插件),RTMP在国内流行度很高,技术相对成熟。目前市面上有很多云直播厂商。可以综合考虑推流协议,时延要求、推拉流费用,SDK的size以及扩展直播场景等方面来选择适合自己的sdk。第一步:从sdk中拿出推流Manager,设置预览View,设置推流地址。@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.live_push);
livePusher = new LivePusher();
videoView = findViewById(R.id. live_video_preview);
livePusher. startCameraPreview(videoView);
String pushUrl = "rtmp://...";
livePusher.startPusher(PushUrl);
}
第二步:使用拉流Manager,设置播放预览view,设置拉流地址和流类型(RTMP、flv等)。@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.live_pull);
livePlayer = new LivePlayer();
playerView = findViewById(R.id.live_video_view);
String pullURl = "rtmp://...";
livePlayer.setPlayerView(playerView)
livePlayer.startPlay(pullURl, PLAY_TYPE_LIVE_RTMP)
}
携程直播就是在这个基础之上,进行了复杂的业务开发。视频推流和拉流是需要调用Native直播sdk的方法,所以需要保留在Native中。页面上的互动区域需要更快速的迭代方式,所以选择了RN。ClientProxy:网络请求的通用字段封装和返回的常规错误码处理、序列化处理、通用Log日志控制等。PushManger 和 PullManger 使用了 Proxy 设计模式用于减少sdk和业务代码的耦合。使用单例模式保证多个直播的配置统一。Event:跨平台事件传递类(EventBus),RN、Hybrid的事件都最终调用native的Event方法发送事件来保证多平台事件传递的可能。RN:
Event.sendEvent("EventName",{ params: 0 });
Native:
CtripEventCenter.getInstance().sendMessage(EventName,jsonObject)
IMManger:消息的管理类。控制直播间的弹幕消息,开始结束消息以及礼物等消息的分发。
把推流和拉流页面称为Page。主要作用有两个,首先调用SDK做推拉流。其次,在页面中对RN和Native进行交互,例如从消息中获取流状态、礼物或者其他的消息。然后给RN发Event事件或者调用Native方法来完成相关消息的后续动作。interface CTLiveChatMessageListener {
fun startLive()
}
Page.registerCTLiveChatMessageListener(listener)
Page上挂载了一个透明的RNView作为直播中交互View。主要负责渲染业务交互view,比如头像名称、评论列表、礼物动画、商品卡片、分享等。在 page 的 onCreate 的时候把 view 挂载上去。manager.beginTransaction().add(R.id.live_crn_fragment, rnFragment,
"livePage").commitAllowingStateLoss()
效果图上,摄像头的内容为原生Native,其他的可见为RNVIew。在Native-RN 混合开发过程中,我们遇到了一些棘手的问题:1)在多次唤醒直播间,或者同时打开多个直播间时,会存在画面和声音对不上,或者出现多个声道的问题。为了解决这个问题我们把直播间做成单例,保证整个app的运行过程中只存在一个直播间。2)覆盖在直播预览页面上面的交互RNView设置为透明背景不生效问题。我们在RN的render返回一个透明View,但是在页面上还是出现了白色的底色。这个时候需要检查一下fragment是否挂载在一个白色View上。3)在Page初始化的时候发送Event事件,但是RN没有收到的问题。这个时候RN容器可能还没有创建完成,我们需要保证发送事件的时机在RN容器创建完成之后。视频直播在近几年是一个比较火爆的技术点。直播的场景每年都在迅速地更新中。我们需要使用一种更快速的迭代方式,所以需要在优化时把稳定的东西使用native封装成基础页面,在保证底层稳定的前提下,选择跨平台的技术栈进行快速的页面迭代开发。ReactNative或者Flutter等跨平台开发语言都是不错的选择。 【推荐阅读】