GitChat 作者:七夜
现在直播已经成为移动互联网时代一个新的重要流量入口,从 YY、斗鱼到花椒直播,直播已经成为人们分享交流的新方式,应用场景众多,主要分为:
等 4 大类。在本文中,我将先从 rtmp 协议开始,一步步带领大家搭建一个简易高性能的直播平台。
RTMP 协议 Real Time Message Protocol(实时信息传输协议)的首字母缩写,是由 Adobe 公司开发的一种用于解决多媒体数据传输流多路复用和分包的网络协议。它工作在 TCP 协议之上,因此是一种提供可靠交付的协议,在传输时不会出现丢包情况,从而保证了用户体验(QoE)。虽然 TCP 协议为了提供可靠交付付出了一些额外的开销做为代价,占用了一些带宽和处理器资源,但是随着网络带宽的提高和硬件的发展,这些开销会显得越来越微不足道。因此 RTMP 协议在为了有很好的发展前景。
官方定义:
The Real-Time Messaging Protocol (RTMP) was designed for
high-performance transmission of audio, video, and data between Adobe
Flash Platform technologies, including Adobe Flash Player and Adobe
AIR. RTMP is now available as an open specification to create products
and technology that enable delivery of video, audio, and data in the
open AMF, SWF, FLV, and F4V formats compatible with Adobe Flash
Player.
虽然协议变种有很多,但实际在我们的直播应用中最常见的是原生的 RTMP 协议,因此本篇文章以该协议的 1.0 版本为基础,对其它演进协议感兴趣的同学可以关注本文的后续知识。
RTMP 的交互流程可以分为握手过程、控制命令传输与数据传输。
流程图中所提到的各种状态如下:
状态 | 描述 |
---|---|
未初始化 | 客户端在 C0 中发送协议版本,如服务端支持,则回发送 S0 和 S1,如果不能,则连接结束 |
版本发送 | 客户端等待 S1 包,服务端等待 C1 包,当接收到想要的包,客户端发送 C2,服务端发送 S2,此时阶段变成了 ACK 的发送 |
ACK 发送 | 客户端和服务端分别等待 S2 和 C2 |
握手完成 | 客户端和服务交换消息 |
理论上来讲只要满足以上条件,如何安排 6 个 Message 的顺序都是可以的。但在实际实现中为了尽量减少通信的次数,客户端发送 C0+C1,服务端发送 S0+S1+S2,再客户端在发送 C2 结束握手。
URI 格式
rtmpt://127.0.0.1/{app}/{stream_name}
- {app} 为音频 / 视频和其他内容定义的一个容器。
- {stream_name} 为具体的一个流名称。
消息(Message)格式
消息是 RTMP 协议中基本的数据单元,不能种类的消息包含有不能的消息类型(Message Type)。RTMP 协议一共规范了十多种消息类型。其中类型为 8,9 的消息分别用于传输音频和视频数据。消息头包含以下信息:
消息块格式
在网络上传输数据时,消息需要被拆分成较小的数据块才适合在相应的网络环境上传输。RTMP 协议中规范了对消息拆分成消息块,每个消息块首部(ChunkHeader)有三部分组成:用于标识本块的 ChunkBasicHeader,用于标识本块负载所属消息的 ChunkMessageHeader,以及当时间戳溢出时才出现的 ExtendedTimestamp。
消息分块
RTMP 传输媒体数据的过程中,发送端首先把媒体数据封装成消息,然后把消息分割成消息块,最后将分割后的消息块通过 TCP 协议发送出去。接收端在通过 TCP 协议收到数据后,首先把消息块重新组合成消息,然后通过对消息进行解封装处理就可以恢复出媒体数据。
目前直播服务器有开源和商业两种版本,商业版本主要又 FMS(Flash Media Server)与 Wowza。本文章仅针对开源版本做介绍,相应的开源项目主要分为 Red5 与 Nginx-Rtmp 两类:
简介
GitHub:https://github.com/Red5/red5-server (1k+ stars)
Red5 是一个采用 Java 开发开源的 Flash 流媒体服务器。它支持:把音频(MP3)和视频(FLV)转换成播放流; 录制客户端播放流(只支持 FLV);共享对象;现场直播流发布;远程调用。Red5 使用 RTMP, RTMPT, RTMPS, 和 RTMPE 作为流媒体传输协议,在其自带的一些示例中演示了在线录制,flash 流媒体播放,在线聊天,视频会议等一些基本功能。
官方给出的主要特性:
Red5 is an Open Source Flash Server written in Java that supports:
- Streaming Video (FLV, F4V, MP4, 3GP)
- Streaming Audio (MP3, F4A, M4A, AAC)
- Recording Client Streams (FLV and AVC+AAC in FLV container)
- Shared Objects
- Live Stream Publishing
- Remoting
- Protocols: RTMP, RTMPT, RTMPS, and RTMPE
Additional features supported via plugin:
- webSocket (ws and wss)
- RTSP (From Axis-type cameras)
- HLS
安装与简单应用实例(Mac 下安装)
前置条件(jdk 已安装)。
mkdir -p /Users/ypzdw/gitchat/rtmp/red5
export RED5_HOME=/Users/ypzdw/gitchat/rtmp/red5
https://github.com/Red5/red5-server/releases/download/v1.0.7-RELEASE/red5-server-1.0.7-RELEASE.tar.gz
目录简介:由于 Red5 是在 Tomcat 中运行的,因此 Red5 项目与普通 JAVAEE 项目结构类似
conf:red5 配置目录
lib: 存放的是一些依赖 jar 包
weapps: 用来存放应用程序,与 tomcat 下的 webapps 目录作用类似。
cd /Users/ypzdw/gitchat/rtmp/red5
./red5.sh &
经过前面的介绍,这里将用 red5 介绍一个简单的实例。打开 http://127.0.0.1:5080 出现 red5 管理控制台。
选择 "Publisher" demo,该项目提供了主播端与听课端。
主播端:"1" 中 Name 表示流名,publish 可以发布一个直播。
直播收听端:"1" 中 Name 为收听的流名;"2" 中 Location 为直播端地址;"3" 中 Log 可以观察到整个直播的交流日志。
cd /Users/ypzdw/gitchat/rtmp/red5
./red5-shutdown.sh
Github:https://github.com/arut/nginx-rtmp-module (5k+ stars)
简介
俄罗斯人民开发的一款 NGINX 的流媒体插件,除了直播发布音视频流之外具备流媒体服务器的常见功能:
官方承诺的功能:
- RTMP/HLS/MPEG-DASH live streaming
- RTMP Video on demand FLV/MP4, playing from local filesystem or HTTP
- Stream relay support for distributed streaming: push & pull models
- Recording streams in multiple FLVs
- H264/AAC support
- Online transcoding with FFmpeg
- HTTP callbacks (publish/play/record/update etc)
- Running external programs on certain events (exec)
- HTTP control module for recording audio/video and dropping clients
- Advanced buffering techniques to keep memory allocations at a minimum level for faster streaming and low memory footprint
- Proved to work with Wirecast, FMS, Wowza, JWPlayer, FlowPlayer, StrobeMediaPlayback, ffmpeg, avconv, rtmpdump, flvstreamer and many more
- Statistics in XML/XSL in machine- & human- readable form
- Linux/FreeBSD/MacOS/Windows
常用指令与语法
语法:rtmp {…}server
上下文:nginx 根上下文
描述:保存所有 RTMP 配置的块
语法:server {…}listen
上下文:rtmp
描述:声明一个 RTMP 实例。
rtmp {
server {
}
}
语法:listen (addr[:port]|port|unix:path)application
上下文:server
描述:给 NGINX 添加一个监听端口以接收 RTMP 连接。
server {
listen 1935;
}
语法:application name {…}timeout
上下文:server
描述:创建一个 RTMP 应用,application 名不支撑正则表达式。
server {
listen 1935;
application myapp {
}
}
语法:timeout valueping
上下文:rtmp, server
描述:Socket 超时。这个值主要用于写数据时。
;
- timeout 60s
语法:ping value
上下文:rtmp, server
描述:RTMP ping 间隔。零值的话将 ping 关掉。RTMP ping 是一个用于检查活动连接的协议功能。发送一个特殊的包到远程连接,然后在指令指定的时间内期待一个回复。如果在这个时间里没有收到 ping 回复,连接断开。ping 默认值为一分钟。ping_timeout 默认值为 30 秒。 ping 3m;
- ping_timeout
ping_timeout 30s;
语法:allow [play|publish] address|subnet|alldeny
上下文:rtmp, server, application
允许来自指定地址或者所有地址发布 / 播放。allow 和 deny 指令的先后顺序可选。
allow publish 127.0.0.1;
deny publish all;
allow play 192.168.0.0/24;
deny play all;
语法:deny [play|publish] address|subnet|all
上下文:rtmp, server, application
描述:参考 allow 的描述。
语法:exec_push command arg*exec_pull
上下文:rtmp, server, application
描述:定义每个流发布时要执行的带有参数的外部命令。发布结束时进程终止。第一个参数是二进制可执行文件的完整路径。执行外部命令时可以使用参数替换:
$name - 流的名字。
$app - 应用名。
$addr - 客户端地址。
$flashver - 客户端 flash 版本。
$swfurl - 客户端 swf url。
$tcurl - 客户端 tc url。
$pageurl - 客户端页面 url。也可以在 exec 指令中定义 Shell 格式的转向符用于写输出和接收输入。
与 exec_push 类似,主要工作在 play 端。
语法:live on|offmeta
上下文:rtmp, server, application
描述:切换直播模式,即一对多广播。
;
- live on
语法:meta on|offinterleave
上下文:rtmp, server, application
描述:切换发送元数据到客户端。默认为 on。
;
- meta off
语法:interleave on|offwait_key
上下文:rtmp, server, application
描述:切换交叉模式。在这个模式下,音频和视频数据会在同一个 RTMP chunk 流中传输。默认为 off。
;
- interleave on
语法:wait_key on|offwait_video
上下文:rtmp, server, application
描述:使视频流从一个关键帧开始。默认为 off。
;
- wait_key on
语法:wait_video on|offpublish_notify
上下文:rtmp, server, application
描述:在第一个视频帧发送之前禁用音频。默认为 off。可以和 wait_key 进行组合以使客户端可以收到具有所有其他数据的视频关键帧。然而这通常增加连接延迟。您可以通过在编码器中调整关键帧间隔来减少延迟。
;
- wait_video on
语法:publish_notify on|offdrop_idle_publisher
上下文:rtmp, server, application
描述:发送 NetStream.Publish.Start 和 NetStream.Publish.Stop 给用户。默认为 off。
;
- publish_notify on
语法:drop_idle_publisher timeoutsync
上下文:rtmp, server, application
描述:终止指定时间内闲置 (没有音频 / 视频数据) 的发布连接。默认为 off。注意这个仅仅对于发布模式的连接起作用(发送 publish 命令之后)。
;
- drop_idle_publisher 10s
语法:sync timeoutplay_restart
上下文:rtmp, server, application
描述:同步音频和视频流。如果用户带宽不足以接收发布率,服务器会丢弃一些帧。这将导致同步问题。当时间戳差超过 sync 指定的值,将会发送一个绝对帧来解决这个问题。默认为 300 ms。
;
- sync 10ms
语法:play_restart on|off
上下文:rtmp, server, application
描述:使 nginx-rtmp 能够在发布启动或停止时发送 NetStream.Play.Start 和 NetStream.Play.Stop 到每个用户。如果关闭的话,那么每个用户就只能在回放的开始和结束时收到这些通知了。默认为 on。
;
- play_restart off
语法:record [off|all|audio|video|keyframes|manual]*record_path
上下文:rtmp, server, application, recorder
描述:切换录制模式。流可以被记录到 flv 文件。本指令指定应该被记录的:
off - 什么也不录制
all - 音频和视频 (所有)
audio - 音频
video - 视频
keyframes - 只录制关键视频帧
manual - 用不自动启动录制,使用控制接口来启动 / 停止
在单个记录指令中可以有任何兼容的组合键。
;
- record all
语法:record_path pathrecord_suffix
上下文:rtmp, server, application, recorder
描述:指定录制的 flv 文件存放目录。
;
- record_path /tmp/rec
语法:record_suffix valuerecord_append
上下文:rtmp, server, application, recorder
描述:设置录制文件后缀名。默认为 '.flv'。
record_suffix _recorded.flv;
录制后缀可以匹配 strftime 格式。以下指令
record_suffix -%d-%b-%y-%T.flv
将会产生形如 mystream-24-Apr-13-18:23:38.flv 的文件。所有支持 strftime 格式的选项可以在 strftime man page 里进行查找
语法:record_append on|off
上下文:rtmp, server, application, recorder
描述:切换文件附加模式。当这一指令为开启是,录制时将把新数据附加到老文件,如果老文件丢失的话将重新创建一个。文件中的老数据和新数据没有时间差。默认为 off。
;
- record_append on
语法:pull url [key=value]*
上下文:application
描述:创建 pull 中继。流将从远程服务器上拉下来,成为本地可用的。仅当至少有一个播放器正在播放本地流时发生。
Url 语法:[rtmp://]host[:port][/app[/playpath]]。如果 application 找不着那么将会使用本地 application 名。如果找不着 playpath 那么就是用当前流的名字。
支持以下参数:
app:明确 application 名。
name:捆绑到 relay 的本地流名字。如果为空或者没有定义,那么将会使用 application 中的所有本地流。
tcUrl:如果为空的话自动构建。
pageUrl:模拟页面 url。
swfUrl:模拟 swf url。
flashVer:模拟 flash 版本,默认为'LNX.11,1,102,55'。
playPath:远程播放地址。
live:切换直播特殊行为,值:0,1。
start:开始时间。
stop:结束时间。
static:创建静态 pull,这样的 pull 在 nginx 启动时创建。
如果某参数的值包含空格,那么你应该在整个 key=value 对周围使用引号,比如:'pageUrl=FAKE PAGE URL'。
;
- pullrtmp://cdn2.example.com/another/a?b=1&c=d pageUrl=http://www.example.com/video.html swfUrl=http://www.example.com/player.swf live=1
push
语法:push url [key=value]*
上下文:application
描述:push 的语法和 pull 一样。不同于 pull 指令的是 push 推送发布流到远程服务器。
push_reconnect
语法:push_reconnect time
上下文:rtmp, server, application
描述:在断开连接后,在 push 重新连接前等待的时间。默认为 3 秒。
;
- push_reconnect 1s
session_relay
语法:session_relay on|off
上下文:rtmp, server, application
描述:切换会话 relay 模式。在这种模式下连接关闭时 relay 销毁。当设置为 off 时,流关闭,relay 销毁,这样子以后另一个 relay 可以被创建。默认为 off。
;
- session_relay on
语法:on_connect urlon_play
上下文:rtmp, server
描述:设置 HTTP 连接回调。当客户分发连接命令一个连接命令时,一个 HTTP 请求异步发送,命令处理将被暂停, 直到它返回结果代码。当 HTTP 2XX 码 (成功状态码) 返回时,RTMP 会话继续。返回码 3XX (重定向状态码)会使 RTMP 重定向到另一个从 HTTP 返回头里获取到的 application。否则 (其他状态码) 连接丢弃。
注意这一指令在 application 域是不允许的,因为 application 在连接阶段还是未知的。
HTTP 请求接收到一些参数。在 application/x-www-form-urlencoded MIME 类型下使用 POST 方法。以下参数将被传给调用者:
call=connect。
addr - 客户端 IP 地址。
app - application 名。
flashVer - 客户端 flash 版本。
swfUrl - 客户端 swf url。
tcUrl - tcUrl。
pageUrl - 客户端页面 url。
除了上述参数以外,所有显式传递给连接命令的参数也由回调发送。你应该将连接参数和 play/publish 参数区分开。播放器常常有独特的方式设置连接字符串不同于 play/publish 流名字。
语法:on_play urlon_publish
上下文:rtmp, server, application
描述:设置 HTTP 播放回调。每次一个客户分发播放命令时,一个 HTTP 请求异步发送,命令处理会挂起 - 直到它返回结果码。之后再解析 HTTP 结果码。
HTTP 2XX 返回码的话继续 RTMP 会话。
HTTP 3XX 返回码的话 重定向 RTMP 到另一个流,这个流的名字在 HTTP 返回头的 Location 获取。
HTTP 请求接收到一些个参数。在 application/x-www-form-urlencoded MIME 类型下使用 POST 方法。以下参数会被传送给调用者:
call=play。
addr - 客户端 IP 地址。
app - application 名。
flashVer - 客户端 flash 版本。
swfUrl - 客户端 swf url。
tcUrl - tcUrl。
pageUrl - 客户端页面 url。
name - 流名。
语法:on_publish urlon_done
上下文:rtmp, server, application
描述:同上面提到的 on_play 一样,唯一的不同点在于这个指令在发布命令设置回调。不同于远程 pull,push 在这里是可以的。
语法:on_done urlon_play_done
上下文:rtmp, server, application
描述:设置播放 / 发布禁止回调。上述所有适用于此。但这个回调并不检查 HTTP 状态码。
语法:on_publish_done urlon_publish_done
上下文:rtmp, server, application
描述:等同于 on_done 的表现,但只适用于播放结束事件。
语法:on_publish_done urlon_record_done
上下文:rtmp, server, application
描述:等同于 on_done 的表现,但只适用于发布结束事件。
语法:on_record_done urlon_update
上下文:rtmp, server, application, recorder
描述:设置 record_done 回调。除了普通 HTTP 回调参数它接受录制文件路径。
on_record_done http://example.com/recorded;
语法:on_update urlnotify_update_timeout
上下文:rtmp, server, application
描述:设置 update 回调。这个回调会在 notify_update_timeout 期间调用。如果一个请求返回结果不是 2XX,连接禁止。这可以用来同步过期的会话。追加 time 参数即播放 / 发布调用后的秒数会被发送给处理程序。
on_update http://example.com/update;
语法:notify_update_timeout timeoutnotify_update_strict
上下文:rtmp, server, application
描述:在 on_update 回调之间的超时设置。默认为 30 秒。
notify_update_timeout 10s;
on_update http://example.com/update;
语法:notify_update_strict on|offnotify_relay_redirect
上下文:rtmp, server, application
描述:切换 on_update 回调严格模式。默认为 off。当设置为 on 时,所有连接错误,超时以及 HTTP 解析错误和空返回会被视为更新失败并导致连接终止。当设置为 off 时只有 HTTP 返回码不同于 2XX 时导致失败。
notify_update_strict on;
on_update http://example.com/update;
语法:notify_relay_redirect on|offnotify_method
上下文:rtmp, server, application
描述:使本地流可以重定向为 on_play 和 on_publish 远程重定向。新的流名字是 RTMP URL 用于远程重定向。默认为 off。
notify_relay_redirect on;
语法:notify_method get|postrtmp_stat
上下文:rtmp, server, application, recorder
描述:设置 HTTP 方法通知。默认是带有 application/x-www-form-urlencoded 的 POST 内容类型。在一些情况下 GET 更好,例如如果你打算在 nginx 的 http{} 部分处理调用。在这种情况下你可以使用 arg_* 变量去访问参数。
notify_method get;
Statistics
statistics 模块不同于本文列举的其他模块,它是 NGINX HTTP 模块。因此 statistics 指令应该位于 http{} 块内部。
语法:rtmp_stat allrtmp_stat_stylesheet
上下文:http, server, location
描述:为当前 HTTP location 设置 RTMP statistics 处理程序。RTMP statistics 是一个静态的 XML 文档。可以使用 rtmp_stat_stylesheet 指令在浏览器中作为 XHTML 页面查看这个文档。
http {
server {
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
root /path/to/stat/xsl/file;
}
}
}
语法:rtmp_stat_stylesheet path
上下文:http, server, location
描述:添加 XML 样式表引用到 statistics XML 使其可以在浏览器中可视。
我所在的公司的直播业务中,前期也是采用 red5,但是随着用户数的不断增长,red5 完全不能支撑整个业务。问题集中爆发在几个方面:
Server | CPU | 内存 | 连接数 | 带宽 | 延迟 |
---|---|---|---|---|---|
nginx-rtmp | 8.3% | 13MB | 500 | 100Mbps | 0.8 秒 |
nginx-rtmp | 27.3% | 19MB | 1000 | 200Mbps | 0.8 秒 |
nginx-rtmp | 50.2% | 37MB | 2500 | 500Mbps | 0.8 秒 |
nginx-rtmp | 70.2% | 61MB | 4000 | 650Mbps | 0.8 秒 |
从测试结果可以得知,nginx-rtmp 模块运行稳定,单 CPU4000 人时负载只有 70%,已经接近网卡流量的极限,比 Red5 在性能上高一个数量级。
阿里云 ECS: CPU:2 核心,内存:8G,硬盘:40G
CentOS 7.2 x86_64 Linux
查看连接数:
- ulimit -HSn 10240
本文为了各位同学能够更快的掌握如何搭建的过程,因此没有采用以上的架构,相信通过下面的实践,各位也能够搭建相应的架构。
架构图中黄色标识了我们要使用的直播 server。
。 进入源码目录:
- mkdir -p /root/rtmp/src
cd /root/rtmp/src
下载 nginx 源码并解压,注意 nginx-rtmp 对 nginx 版本的选择限制较多,在选择时为了少踩不需要的坑,建议根据官方提示选择对应的 nginx 版本,如图本文选择 nginx-1.11.5:
wget http://nginx.org/download/nginx-1.11.5.tar.gz
tar -zxvf nginx-1.11.5.tar.gz
下载 nginx-rtmp 模块源码:
wget https://github.com/arut/nginx-rtmp-module/archive/v1.1.10.tar.gz
tar -zxvf v1.1.10.tar.gz
编译 nginx,如果需要调试消息则打开–with-debug:
./configure –add-module=/root/rtmp/src/nginx-rtmp-module-1.1.10 –with-debug
make
make install
默认编译到路径:
- /usr/local / nginx
验证编译是否成功:
创建录制文件存储目录:
- mkdir - p / usr / local / nginx / files
rtmp 模块配置 (nginx.conf):
本文所采用的配置如下
- user root;//使用root用户运行nginx
- worker_processes 1;//指明了nginx要开启的进程数,一般等于cpu的总核数,如果没有出现io性能问题,最好不要修改
- error_log logs/error.log debug;//错误日志存放路径;日志级别为debug,调试用。
- events {
- worker_connections 1024;//每个工作进程的最大连接数量;
- }
- http {
- include mime.types;//设定mime类型,类型由mime.type文件定义
- default_type application/octet-stream;
- client_max_body_size 3m;//设定通过nginx上传文件的大小
- log_format main '$remote_addr - $remote_user [$time_local] "$request" '
- '$status $body_bytes_sent "$http_referer" '
- '"$http_user_agent" "$http_x_forwarded_for"';//日志格式设置
- access_log logs/access.log main;//访问日志存储路径
- sendfile on;//指定 nginx 是否调用sendfile 函数(zero copy 方式)来输出文件
- keepalive_timeout 65;//keepalive超时时间
- server {//配置虚拟机
- listen 8080;//监听端口
- server_name rtmp.datahq.cn 59.110.237.245;//名称
- location /stat{//配置统计页面路径
- rtmp_stat all;
- rtmp_stat_stylesheet stat.xsl;
- }
- location /stat.xsl{//统计模板路径
- root /usr/local/nginx/conf;
- }
- error_page 500 502 503 504 /50x.html;
- location = /50x.html {
- root html;
- }
- }
- }
- rtmp{
- server{
- listen 0.0.0.0:1935;//监听端口
- ping 30s;//活动连接检查周期
- application live{//应用名
- live on;//打开直播模式
- meta copy; //是否发送直播端元数据信息
- session_relay on;//打开会话转发模式
- drop_idle_publisher 10s;//10s没有推流,自动断开直播端
- sync 10ms;//同步流时间阀值
- record_append on;//打开直播流录制追加模式
- record_path /usr/local/nginx/files;//录制文件地址
- record all;//打开录制功能
- }
- }
- }
拷贝统计模板到 ngin 配置目录:
- cp / root / rtmp / src / nginx - rtmp - module - 1.1.10 / stat.xsl / usr / local / nginx / conf /
启动直播服务器:
- cd / usr / local / nginx / sbin && . / nginx
打开统计页面控制台:
- http://rtmp.datahq.cn:8080/stat
统计表各属性说明为:
clients: 连接数
live streams: 流名
codec: 编码
bits: 分辨率
size: 视频画面大小
fps: 每秒传输帧数
freq: 音频率
chan: 音频声道
State: 流状态
Time: 流活动时间
其它 4 个为输入与输出流的每秒传输速率。
今天给大家分享了直播平台搭建的一些知识,涉及到了 rtmp 协议,直播选型的一些注意点,大规模直播架构,nginx-rtmp 直播服务器实战搭建等。如果各位有直播的需要,可以顺着本文的一些知识点进行实战。
第一次写 Chat,不知道效果如何,如果大家有兴趣,我们在后续推出 nginx 模块开发与 nginx-rtmp 的分享,感谢大家的参与。
实录:《七月:从零搭建高性能直播平台实战解析》
来源: http://blog.csdn.net/gitchat/article/details/77345689