系列文章:
Nginx rtmp 推流(publish) 解析_fdsafwagdagadg6576的专栏-CSDN博客
Nginx rtmp 拉流播放(play)_fdsafwagdagadg6576的专栏-CSDN博客
Nginx rtmp Relay(转发pull)_fdsafwagdagadg6576的专栏-CSDN博客
Nginx rtmp 转推_fdsafwagdagadg6576的专栏-CSDN博客
Nginx rtmp 点播流程_fdsafwagdagadg6576的专栏-CSDN博客
config:
启用push功能,将本机(172.16.6.36)/mypush,app中的数据流推送至172.16.6.39服务器下的/myapp app中
application mypush {
live on;
push rtmp://172.16.6.39:1935/myapp;
}
配置实现pull功能,172.16.6.39服务器能够从172.16.6.36服务器上拉取数据流,并保存在本机/mypull app中.
application mypush {
live on;
push rtmp://172.16.6.39:1935/myapp;
}
启动
ngx rtmp启动过程。rtmp_block,rtmp_recv 时序.
ngx_process_events_and_timers是放入子进程处理。
callback:
ngx_rtmp_cmd_play_init {
.....
return ngx_rtmp_play(s, &v);
}
static ngx_int_t
ngx_rtmp_relay_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v) {
....
return next_play(s,v);
}
ngx_rtmp_play和next_play关系?
监听play指令的所有模块,组成了一个链表.
- relay模块注册:
next_play = ngx_rtmp_play;
ngx_rtmp_play = ngx_rtmp_relay_play;
头插法:ngx_rtmp_play指向链表头,next_play指向上一个头节点,就是本模块的下一个模块.
- 调用的是哪个模块的play函数呢?
根据配置,直播调用relay,live模块的play函数。点播调用play模块的play函数.
对于直播来说,relay,live,cmd的调用顺序是如何的呢?
需要按照模块的编译顺序。参见:....
对于直播ngx_rtmp_play先调用ngx_rtmp_relay_play(即使在cmd模块调用,也不是调用ngx_rtmp_cmd_play), 然后ngx_rtmp_relay_play函数调用next_play,是ngx_rtmp_live_play.
整体结构图
阶段
- 1 handshake
- 2 connect
- 3 createStream
- 4 指令publish,play,seek 等
- 5 发送,接收audio,video
信令和audio,video流独立分开. 码流在ngx_rtmp_live_av执行,与信令函数没有关联.
通信层:
1) handshake
handshake是独立的handshake_send/recv.特别注意此处没有走转推ngx_rtmp_fire_event(s, NGX_RTMP_HANDSHAKE_DONE...).因为此处还没有与被转推的server建立handshake,所以无法connect成功。真正的转推是在接收publish消息
2) ngx_rtmp_receive_message:
在1)之后使用ngx_rtmp_recv/send接收.
ngx_rtmp_receive_message是message接口,解开rtmp包.然后callback分发。
(这是一个统一enter在ngx_rtmp_handler中,给rtmp,hls等使用,所以先拆开rtmp header,hls是裸流写ts文件,http flv是自己的rw callback).
3) ngx_rtmp_cycle: 进入rtmp业务循环
ngx_rtmp_receive_message详解:
ngx_int_t
ngx_rtmp_receive_message(ngx_rtmp_session_t *s,
ngx_rtmp_header_t *h, ngx_chain_t *in)
{
for(n = 0; n < evhs->nelts; ++n, ++evh) {
/*/*amf-->ngx_rtmp_amf_message_handler
*standard protocol-->ngx_rtmp_protocol_message_handler
*user protocol-->ngx_rtmp_user_message_handler*/
/*eg:callee:h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_VIDEO]) 这种ngx_array_push cmcf->events */
ngx_int_t ret = (*evh)(s, h, in);
}
}
ngx_rtmp_cmd_map
ngx_int_t
ngx_rtmp_amf_message_handler(ngx_rtmp_session_t *s,
ngx_rtmp_header_t *h, ngx_chain_t *in)
{
ngx_int_t //ngx_rtmp_amf_message_handler--ngx_rtmp_cmd_map--hash<->callback-->postconfiguration中的业务逻辑函数
ngx_rtmp_amf_message_handler(ngx_rtmp_session_t *s,
ngx_rtmp_header_t *h, ngx_chain_t *in)
{
ngx_int_t ret = (*ph)(s, h, in);
....
}
}
分发: map table 和 push event
ngx_rtmp_cmd_map
notes:信令和媒体分开:信令是relay, live 模块的publish 部分。媒体是live模块的live_av部分。
两部分没有调用关系.
rtmp交互逻辑
relay模块和对端服务器交互主要rtmp协议三次握手,connect,createStream,play/publish命令消息服务器.
Nginx rtmp 获取流有两种方式,一种是别的server push给自己流,一种是pull拉流
publish
在完成以上步骤后客户端和服务器建立了一个网络流,接下来就可以传输媒体数据了。
大致流程如上图红色标注下面部分。
一般来讲媒体数据分为两部分,一部分是meta元信息,另一部分是音视频数据。
首先传送的是meta元信息。客户端推送媒体,服务器处理如下
Relay 模块
整体结构: 采用hash表+链表的结构;横向:同一个流名的链表: 用play串起来;纵向:不同流名用next串起来
pull rtmp的信令交互图:
1: 播放器端发起play
2: ngx_rtmp_relay_play()
3: ngx_rtmp_relay_pull() --主要建立pull请求, 将远程拉流的上下文和本地上下文放到链表中
ngx_rtmp_relay_create_local_ctx()创建本地上下文
ngx_rtmp_relay_create_remote_ctx()创建远端上下文
4: ngx_rtmp_relay_create_remote_ctx详解: 创建远端session, 开始rtmp信令交互
1: ngx_rtmp_relay_create_connection
2: ngx_rtmp_init_session
注意:此时session中的flashver为“ngx-local-relay“
5: rtmp信令交互流程
核心函数: ngx_rtmp_relay_on_result主要用来接收对端服务端发送的amf消息包,而后按正常rtmp协议请求进行下一步交互,这个主要是当前服务端做为客户端发起远程rtmp请求流程
例如: ngx_rtmp_client_handshake(发起握手)
握手完成后调用回调函数: ngx_rtmp_relay_handshake_done
ngx_rtmp_relay_send_connect:发起连接请求, 接着参考rtmp信令交互图
其他:
stat:
static void
ngx_rtmp_process_stat_event_handler(ngx_event_t *ev)
{
......
//5重循环遍历stat 每个client;each server { each app {each bucket {each stream {each client }}} }
for (i = 0; i < cmcf->servers.nelts; ++i, ++cscf) { //for each server
cacf = (*cscf)->applications.elts;
for (n = 0; n < (*cscf)->applications.nelts; ++n, ++cacf) { // for each app
ngx_str_t *pappname = &(*cacf)->name;
lacf = (*cacf)->app_conf[ngx_rtmp_live_module.ctx_index];
/*这个链表法:|
|__>->... 列是hash值,行是同一个hash值的不同的具体值; eg: hash %7 */
/*nbuckets is:根据流名称进行hash时,bucket的数量;stream is 每个bucket中,对应ngx_rtmp_live_stream_t*数组*/
for ( k = 0; k < (size_t)lacf->nbuckets; ++k ) {
for (stream = lacf->streams[k]; stream; stream = stream->next) { //for streams
for (ctx = stream->ctx; ctx; ctx = ctx->next, ++nclients) { //for clients
....
}}}}} }
Rtmp推流地址:rtmp://ip/live0/stream-name
对应的http-flv播放地址:http://ip/live0/stream-name 和 http://ip/flv0/stream-name
http-ts的配置方式和http-flv的配置原理完全相同。
对应的http-ts播放地址:http://ip/live0/stream-name
rtmp {
server {
listen 1935;
application live0 {
live on;
}
}}
http {
server {
listen 80;
location flv0 {
flv_live 1935 app=live0;
}
....}}
play2(播放)
和上面的play命令不同的是,play2命令可以在不改变播放内容时间轴的情况下切换到不同的比特率,服务器端会维护多种比特率的文件来供客户端使用play2命令来切换。
参考:
push,pull config:Nginx-rtmp模块实现流媒体play、push、pull功能_莫失莫忘的博客-CSDN博客