文章目录
零、说明
1、系统
Linux VM-122-135-ubuntu 3.13.0-36-generic
#63-Ubuntu SMP Wed Sep 3 21:30:07 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
2、nginx
版本:nginx-1.10.2
源码:http://nginx.org/en/download.html
配置文件:nginx.conf.txt
3、nginx-rtmp-module
版本:1.1.4
源码:https://github.com/arut/nginx-rtmp-module
一、文件结构
首先通过源码之间的引用关系进行分析如下,因为ngx_rtmp、ngx_config、ngx_core等的引用关系过多,所以舍弃掉那些关系,得到如下图:
二、函数调用
为了清楚nginx_rtmp_module的运行过程,对其源码进行了修改,让每个函数输出其所在的文件和函数名称,源码参见:https://git.oschina.net/evan-xia/nginx-rtmp-module/repository/archive/v_1_0_notes
2.1、启动nginx,初始化过程
nginx启动时主要完成一些配置,和加载各个模块。通过对输出的函数进行分析,nginx_rtmp_module对应的初始化过程顺序如下:
1、配置:ngx_rtmp_auto_push_module.c -> ngx_rtmp_auto_push_create_conf,将全局变量ngx_cycle_t;
2、执行:ngx_rtmp.c -> ngx_rtmp_block,是正规rtmp模块初始化的入口。
3、通过2中函数源码,我们发现这一步是依次初始化各个rtmp模块中的各个module。
a) 首先设置rtmp main_conf context的ngx_rtmp_conf_ctx_t;
b) 然后设置server{} block。
i. 通过for循环依次配置各个模块,调用各模块的接口:create_main_conf、create_srv_conf、create_app_conf来配置。注释说 通过输出可看出顺序为:
ngx_rtmp_core_module
ngx_rtmp_codec_module
ngx_rtmp_access_module
ngx_rtmp_record_module
ngx_rtmp_live_module
ngx_rtmp_play_module
ngx_rtmp_netcall_module
ngx_rtmp_relay_module
ngx_rtmp_exec_module
ngx_rtmp_notify_module
ngx_rtmp_limit_module
ii. 然后通过for循环依次通过各个模块的preconfiguration接口应用模块的配置,顺序如下:
ngx_rtmp_core_server
ngx_rtmp_core_listen
ngx_rtmp_record_recorder
ngx_rtmp_record_recorder
ngx_rtmp_core_application
ngx_rtmp_play_url
ngx_rtmp_core_application
ngx_rtmp_play_url
ngx_rtmp_core_application
c)进入rtmp{} block设置 // init rtmp{} main_conf's, merge the server{}s' srv_conf's
i. 通过for循环中,先调用各个模块的init_main_conf初始化各个模块的main_conf;
ii. 然后内层for循环,通过merge_srv_conf、merge_app_conf来合并server{}的srv_conf、app_conf;
d)通过ngx_rtmp_init_events来初始化event;
e)通过for循环来postconfiguration各个模块的配置;
f)ngx_rtmp_init_event_handlers来初始化event_handlers
g)然后通过for循环ngx_rtmp_add_ports来添加监听端口;
h)调用ngx_rtmp_optimize_servers来进一步配置servers
i)最后通过ngx_rtmp_init_process来初始化进程;记录结果显示有两个进程,一个负责处理连接时的业务,另一个在退出时会处理一些业务,详细参见附录1;
整个初始化入口是如下函数:
ngx_rtmp_auto_push_module.c -> ngx_rtmp_auto_push_create_conf
ngx_rtmp.c -> ngx_rtmp_block
ngx_rtmp.c -> ngx_rtmp_optimize_servers
ngx_rtmp.c -> ngx_rtmp_init_process
整个初始化过程函数调用记录init.txt
2.2、ffmpeg推送时函数调用关系
利用ffmpeg推送视频,其流程关系如下图所示,这部分主要是涉及到NGINX左边部分,ffmpeg将mp4文件推送到nginx服务器。同样利用运行时的函数的顺序辅助理解。
2.2.1、建立rtmp连接
ffmpeg推送数据之前也需要建立rtmp连接,在nginx-rtmp模块中的入口为ngx_rtmp_init.c -> ngx_rtmp_init_connection,其中关键数据类型ngx_connection_t作为传入参数,其过程如下:
1、ngx_connection_local_sockaddr建立连接获取链接信息;
2、设置ngx_rtmp_addr_conf_t;
3、利用ngx_rtmp_init_session来初始化session;
4、判断,如果ngx_rtmp_addr_conf_t是proxy_protocol则调用ngx_rtmp_proxy_protocol,否则需要调用ngx_rtmp_handshake来完成rtmp握手来建立连接;
对于ngx_rtmp_init_session函数,以ngx_connection_t和ngx_rtmp_addr_conf_t作为参数,处理中关键是初始化ngx_rtmp_session_t结构,最后利用ngx_rtmp_fire_event激发事件NGX_RTMP_CONNECT。其中ngx_rtmp_session_t内容如下:
typedef struct { uint32_t signature; /* "RTMP" */ /* <-- FIXME wtf */ ngx_event_t close; void **ctx; void **main_conf; void **srv_conf; void **app_conf; ngx_str_t *addr_text; int connected; #if (nginx_version >= 1007005) ngx_queue_t posted_dry_events; #else ngx_event_t *posted_dry_events; #endif /* client buffer time in msec */ uint32_t buflen; uint32_t ack_size; /* connection parameters */ ngx_str_t app; ngx_str_t args; ngx_str_t flashver; ngx_str_t swf_url; ngx_str_t tc_url; uint32_t acodecs; uint32_t vcodecs; ngx_str_t page_url; /* handshake data */ ngx_buf_t *hs_buf; u_char *hs_digest; unsigned hs_old:1; ngx_uint_t hs_stage; /* connection timestamps */ ngx_msec_t epoch; ngx_msec_t peer_epoch; ngx_msec_t base_time; uint32_t current_time; /* ping */ ngx_event_t ping_evt; unsigned ping_active:1; unsigned ping_reset:1; /* auto-pushed? */ unsigned auto_pushed:1; unsigned relay:1; unsigned static_relay:1; /* input stream 0 (reserved by RTMP spec) * is used as free chain link */ ngx_rtmp_stream_t *in_streams; uint32_t in_csid; ngx_uint_t in_chunk_size; ngx_pool_t *in_pool; uint32_t in_bytes; uint32_t in_last_ack; ngx_pool_t *in_old_pool; ngx_int_t in_chunk_size_changing; ngx_connection_t *connection; /* circular buffer of RTMP message pointers */ ngx_msec_t timeout; uint32_t out_bytes; size_t out_pos, out_last; ngx_chain_t *out_chain; u_char *out_bpos; unsigned out_buffer:1; size_t out_queue; size_t out_cork; ngx_chain_t *out[0]; } ngx_rtmp_session_t;
因为推送时先建立连接需要握手,即4中调用的函数为ngx_rtmp_handshake,传入参数为ngx_rtmp_session_t。函数位于ngx_rtmp_handshake.c文件,过程如下:
1、获取ngx_rtmp_session_t中的connection,然后设置ngx_connection_t读写handler,这是两个关键的处理函数:
c->read->handler = ngx_rtmp_handshake_recv; c->write->handler = ngx_rtmp_handshake_send;
2、然后利用ngx_rtmp_alloc_handshake_buffer为ngx_rtmp_session_t分配hs_buf;
3、调用ngx_rtmp_handshake_recv开始进行握手;
4、在握手完成之后,会调用ngx_rtmp_free_handshake_buffers来释放hs_buf,然后调用ngx_rtmp_fire_event来激发了事件NGX_RTMP_HANDSHAKE_DONE,最后调用ngx_rtmp_cycle来重新设置ngx_connection_t读写handler,这两个函数将完成接下来nginx的收发处理:
c->read->handler = ngx_rtmp_recv; c->write->handler = ngx_rtmp_send;
在完成连接后,就是rtmp数据的接收了。而握手流程如下图:
2.2.2、建立网络连接
在建立rtmp连接后,ngx_rtmp_recv函数利用ngx_event_t结构会获取推送过来的原始数据,接受到的数据会存储在ngx_rtmp_stream_t的in链表结构中。ngx_rtmp_recv的主要逻辑就是解析chunk为RTMP包,并调用处理包的函数。主要逻辑如下:
1、接收chunk数据(默认最大为128);
2、分析处理chunk数据,如果message还没哟接收完,继续步骤1,否则继续;
3、将chunk组成一个rtmp message,然后交给ngx_rtmp_receive_message处理,这个函数根据消息的类型来找到对应的handler。
在流程上,建立网络连接后,会建立网络连接,即客户端和服务端的连接信息。这部分流程如下:
1、从客户端(推送端)接收amf的command message信息,利用ngx_rtmp_amf_message_handler函数处理数据;
2、利用ngx_rtmp_cmd_connect_init初始化网络连接;先利用ngx_rtmp_receive_amf获取amf信息,然后ngx_rtmp_cmd_fill_args设置参数;
3、利用ngx_rtmp_notify_connect来通知客户端参数,具体步骤如下:
A、ngx_rtmp_send_ack_size
B、ngx_rtmp_send_bandwidth
C、ngx_rtmp_send_chunk_size
D、ngx_rtmp_send_amf
完成这些后网络连接即建立完成,具体交互图如下:
2.2.3、建立网络流
在建立完网络连接后,就需要建立网络流了,网络流代表了发送多媒体数据的通道。服务器和客户端之间只能建立一个网络连接,且多个网络流可以复用这一个网络连接。
和2.2.2一样,ngx_rtmp_recv接收数据后交给ngx_rtmp_receive_message,然后由message类型跳转到相应的处理函数。建立网络流过程如下:
1、接收message后跳转到处理函数ngx_rtmp_protocol_message_handler。它会根据类型再次区别处理。此时客户端发送NGX_RTMP_MSG_CHUNK_SIZE类型的消息。;
2、接收message后跳转到处理函数ngx_rtmp_amf_message_handler。处理客户端发送的amf控制消息。此时连续接收3个amf数据;
3、调用ngx_rtmp_cmd_create_stream_init开始初始化网络流。利用ngx_rtmp_receive_amf获取接收的amf数据;
4、调用ngx_rtmp_cmd_create_stream创建网络流。利用ngx_rtmp_send_amf向客户端发送amf控制消息。
到此网络流建立完成,大致具体过程如下图红色标注部分:
2.2.4、传输媒体数据
在完成以上步骤后客户端和服务器建立了一个网络流,接下来就可以传输媒体数据了。大致流程如上图红色标注下面部分。一般来讲媒体数据分为两部分,一部分是meta元信息,另一部分是音视频数据。首先传送的是meta元信息。客户端推送媒体,服务器处理如下:
1、从客户端(推送端)接收一个amf命令消息,然后服务器会利用ngx_rtmp_cmd_publish_init来初始化发布环境。该函数会读取刚接收端amf信息,然后设置参数;
2、利用ngx_rtmp_auto_push_publish、ngx_rtmp_notify_publish、ngx_rtmp_exec_publish来建立发布环境,其中利用ngx_rtmp_live_get_stream来获取媒体信息,会向客户端发送一个amf命令消息;
3、如果conf中配置了record命令记录媒体文件,此时会调用ngx_rtmp_record_publish来初始化记录环境;
4、执行ngx_rtmp_access_publish;
I、meta信息:
5、接收客户端amf,并调用ngx_rtmp_codec_meta_data来获取meta元信息;
6、结合服务器的参数信息,利用ngx_rtmp_codec_reconstruct_meta接口来重构meta元信息,并调用函数ngx_rtmp_prepare_message组成消息保存,不会调用free释放;
II、媒体数据:
7、接收客户端推送,根据类型转到ngx_rtmp_codec_av来处理音视频(avc/aac)数据;其中利用函数ngx_rtmp_codec_parse_avc_header来处理头部信息,ngx_rtmp_append_shared_bufs来存储;
8、调用record模块记录数据,接口为ngx_rtmp_codec_av;
9、调用live模块来广播发布数据,接口为ngx_rtmp_live_av,广播到所有观看者;第一次调用时会调用接口ngx_rtmp_live_start创建广播,之后直接调用ngx_rtmp_append_shared_bufs。
2.2.5、断开推送
客户端(推送端)关闭推送,会结束推送流,此时调用过程如下:
1、从客户端接收amf结束命令;
2、调用ngx_rtmp_cmd_delete_stream_init接口,读取amf命令;
3、调用各个子模块的结束流处理接口,包括:
ngx_rtmp_auto_push_delete_stream
ngx_rtmp_relay_delete_stream
ngx_rtmp_cmd_delete_stream
ngx_rtmp_notify_close_stream
ngx_rtmp_exec_close_stream
ngx_rtmp_relay_close_stream
ngx_rtmp_play_close_stream
ngx_rtmp_live_close_stream
4、利用ngx_rtmp_create_stream_eof创建eof消息,并向各个观看者发送状态amf消息;
5、利用ngx_rtmp_record_close_stream接口关闭record模块的记录;
此部分只给出了大题过程、关键函数和接口,具体实现参考源码。整个过程函数调用记录,由于多线程原因,日志顺序仅作为调用顺序的参考。init+ffmpeg推送.txt
2.3、客户端rtmp连接观看时函数调用
和ffmpeg推送视频一样,客户端连接服务器观看时流程也分为:建立rtmp连接–>建立网络连接–>建立网络流–>传输媒体数据。前面建立rtmp连接和建立网络连接过程是相同的,区别在于建立网络流和传输媒体数据过程。
此部分为客户端连接nginx-rtmp时的交互,没有推送视频流。其中建立rtmp连接和建立网络连接过程和2.2.1、2.2.2节相同,参考对应章节内容。
而建立网络流过程与2.2.3区别于两点:
1、第一步中,从客户端接收一个protocol_message,推送时该message转到ngx_rtmp_set_chunk_size处理,而观看时该message未被处理;
2、第二步中,接收客户端一个amf控制消息,推送时是连续3个;
而媒体数据传输步骤中,在服务器没有接收推送时,过程如下:
1、从客户端(观看端)接收两个amf命令消息;
2、然后服务器会利用ngx_rtmp_cmd_publish_init来初始化发布环境。该函数会读取刚接收端amf信息,然后设置参数;
3、和2.2.4类似,初始化其他子模块:
A、ngx_rtmp_notify_module.c -> ngx_rtmp_notify_play
B、ngx_rtmp_exec_module.c -> ngx_rtmp_exec_play
C、ngx_rtmp_relay_module.c -> ngx_rtmp_relay_play
D、ngx_rtmp_live_module.c -> ngx_rtmp_live_play,会向客户端发送两个amf状态消息
E、ngx_rtmp_access_module.c -> ngx_rtmp_access_play
F、ngx_rtmp_cmd_module.c -> ngx_rtmp_cmd_play
4、客户端向服务器发送set_buflen的用户消息,ngx_rtmp_user_message_handler将对其处理;
5、因为没有推送数据,所以现在不会有媒体数据交互。
当断开客户端(观看者)连接时,过程如下:
1、服务器接收客户端断开连接请求,调用ngx_rtmp_finalize_session来ngx_post_event;
2、然后触发ngx_rtmp_close_session_handler,利用ngx_rtmp_fire_event触发各个子模块的断开连接处理,包括:
A、ngx_rtmp_cmd_module.c -> ngx_rtmp_cmd_disconnect_init
B、ngx_rtmp_notify_module.c -> ngx_rtmp_notify_disconnect
C、ngx_rtmp_cmd_module.c -> ngx_rtmp_cmd_disconnect
D、ngx_rtmp_auto_push_module.c -> ngx_rtmp_auto_push_delete_stream
E、ngx_rtmp_relay_module.c -> ngx_rtmp_relay_delete_stream
F、ngx_rtmp_exec_module.c -> ngx_rtmp_exec_close_stream
G、ngx_rtmp_play_module.c -> ngx_rtmp_play_close_stream
H、ngx_rtmp_live_module.c -> ngx_rtmp_live_close_stream
I、ngx_rtmp_record_module.c -> ngx_rtmp_record_close_stream
J、ngx_rtmp_codec_module.c -> ngx_rtmp_codec_disconnect
K、…
对应的函数调用次序:init+play.txt
参考:
1、https://github.com/arut/nginx-rtmp-module/wiki/Directives
转载标明出处:https://blog.evanxia.com/2017/02/1264
楼主,你好,在《【总结】Nginx-rtmp-module模块源码学习》文章中,请问“对应的函数调用次序”你是如何做的?如何让所有的函数调次序都打印到文档中?是手写的?还是,通过某个配置方式做到?
我的邮箱是,liu00009090@sina.com。
你好,请问“对应的函数调用次序”你是如何做的?是手写的?还是,通过某个配置方式做到?
Comments are closed.