实时流(直播流)播放、上墙(大屏播放)解决方案
场景描述
将实时流采集终端的视频数据实时推送到另外一个(多个)播放终端,完成远距离实时视频播放的功能。典型场景:
(1)远程查看监控摄像头。选择指定摄像头,将该摄像头采集到的实时数据推送到指定播放终端,供值班(监控中心)人员查看。包括实时视频上墙,推送到指定大屏幕上;
(2)直播系统。用户在PC上安装推流工具,抓取屏幕(麦克风)实时数据,推送给观看直播的观众。
解决方案
要完成端到端的流推送,我们需要借助中间件来完成,常用到的有Nginx Rtmp模块,用来中转视频数据。实时流采集终端根据给定的地址将数据推送到Nginx流媒体服务器,播放终端根据给定的地址从Nginx流媒体服务器上拉取数据,呈现给用户。
实时流播放结构图
如果多个用户需要播放同一个实时流数据,那么理想情况下,推流端的数据应该可以复用,也就是说,推流端只需要推一路流,供多个播放终端使用。此时推流端和播放端是一对N的关系。
多个用户播放同一个实时流数据
如果播放用户很多,推流端和播放端数量大,只靠一台流媒体服务器转发数据肯定不行。这时候需要多台流媒体服务器协同工作,当有多个流媒体服务器时,就会出现一个问题:当用户请求实时流,该如何为它分配流媒体服务器呢?此时,就会引入另外一个概念:负载均衡。当有多台流媒体服务器时,我们需要通过某种策略去计算,得出最适合的流媒体服务器,比如找出当前负载量最小的服务器,给用户使用。
多个流媒体服务器
如上图,存在多个流媒体服务器时,负载均衡需要根据指定的策略计算出最佳的服务器地址,然后推流端和播放端根据地址分别推流拉流。图中由计算得出,使用流媒体服务器1。
实现技术
用到的技术、工具:
(1)CentOS 6.5 + Tomacat 8.0 + Mysql + Spring,java web后台,接收用户请求、负载计算、流状态同步、发送推流指令等等;
(2)ffmpeg,推流工具,供C++调用;
(3)RabbitMQ,web后台与推流端传递消息;
(4)Nginx 1.12.0 + rtmp module,具体可以查看官方第三方module list, 流媒体转发;
(5)VLC C#开发工具,用来拉流,网上有公开API调用方法,用来做客户端demo,后面的截图都是基于该demo;
(6)jwplayer,实际中web前端实时流播放控件。
详细实现
(1)关于Nginx+rtmp模块实现实时流转发的内容这里就不再写了,网上很多教程,也非常简单,不需要手写任何代码;
(2)Java web后台采用SpringMVC + Mybatis,只需要实现一些http接口即可;
这里详细说一下负载计算这块的逻辑,该模块与web后台分开,可以单独部署。
该模块属于java 后台的一部分,当然也可以分开部署,它与web后台是通过数据库中的请求表同步数据。接下来是负载均衡中生成rtmp的逻辑,rtmp也就是本文开头提到的推流\拉流地址了。
推流端和拉流端可以通过给定的rtmp进行推拉流,Nginx流媒体服务器(具体应该是rtmp模块)在收到推流开始(publish_start)、推流结束(publish_done)、拉流开始(play_start)以及拉流结束(play_done)时,都会按照配置文件中的配置进行http回调,该回调地址配置成java 负载均衡后台。我们需要在该回调中更新流媒体服务器的状态,比如流媒体服务器的当前负载数,用于下次负载计算。
有时候http回调会失败,这就会导致负载均衡模块中保存的流媒体服务器的状态有误差,所以我们需要主动同步流媒体服务器的负载状态:
负载计算的另一个非常重要的标准就是检查流媒体服务器是否在线,如果不在线,那么这台流媒体服务器就不在我们考虑的范围之内。主动检查流媒体服务器状态:
Demo演示
由于各种原因,这里只能挂一些demo的图片:
Demo1
在百度地图中按区域查找摄像头,选择摄像头,查看摄像头实时视频数据。流程为:请求视频->负载计算rtmp->推流开始->拉流开始。百度地图控件请参照之前开源的代码:https://github.com/sherlockchou86/BMap.NET
Demo2
选择摄像头,将摄像头实时流推到大屏上。流程为:请求大屏上墙(携带大屏ID)->计算rtmp->推流开始->拉流开始。下图为大屏模拟管理器:
当大屏就绪后,开始播放,管理器状态更新:
大屏模拟器界面,支持断线恢复。关闭终端后,再次打开,流推送不会中断:
方案总结
(1)推流端和拉流端是1->N的关系,对于每一路推流,可以同时存在多个拉流端,即推流可以复用。当多个用户请求同一资源(如同一摄像头)时,只需要推一路流即可,这时候每个用户的拉流地址rtmp相同。
(2)推流和拉流之间需要流媒体服务器作为桥梁,负责实时流的转发工作。这里使用的时Nginx+rtmp module,网上有详细教程。
(3)流媒体服务器的选择需要经过负载均衡计算得出,负载计算的策略包含:流媒体服务器是否在线、负载数(当前播放链路)是否达到服务器的上限、请求的资源是否已被推流(即可以复用,这种情况下,直接返回之前的rtmp即可,不需要重新分配服务器)。
(4)Nginx+rtmp模块的配置文件中,有一项是配置‘状态回调’的地址,当流媒体服务器的状态发生变化时,会通过该回调告知java 后台。
没有源码提供,有问题的朋友可以留言或者私信。