模块作用
直播模块代码 ngx_rtmp_live_module.c,主要作用是:当客户端推流或者拉流的时候,创建的rtmp session会加入到 live 模块的存储链表中。
模块配置命令
static ngx_command_t ngx_rtmp_live_commands[] = {{ ngx_string("live"),NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,ngx_conf_set_flag_slot,NGX_RTMP_APP_CONF_OFFSET,offsetof(ngx_rtmp_live_app_conf_t, live),NULL },// 表示开启直播{ ngx_string("stream_buckets"),NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,ngx_conf_set_str_slot,NGX_RTMP_APP_CONF_OFFSET,offsetof(ngx_rtmp_live_app_conf_t, nbuckets),NULL },// 录制流名hash桶的大小{ ngx_string("buffer"),NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,ngx_conf_set_msec_slot,NGX_RTMP_APP_CONF_OFFSET,offsetof(ngx_rtmp_live_app_conf_t, buflen),NULL },// 设置默认缓冲区长度。通常客户端set_buflen在播放之前发送RTMP 命令,并重置此设置。默认是1000 ms。{ ngx_string("sync"),NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,ngx_rtmp_live_set_msec_slot,NGX_RTMP_APP_CONF_OFFSET,offsetof(ngx_rtmp_live_app_conf_t, sync),NULL },// 同步音频和视频流。如果订阅者带宽不足以以发布者速率接收数据,则服务器会丢弃一些帧。这就导致了同步问题。当时间戳差异超过sync参数指定的值时,将发送一个绝对帧来修复它。默认值是300ms。{ ngx_string("interleave"),NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,ngx_conf_set_flag_slot,NGX_RTMP_APP_CONF_OFFSET,offsetof(ngx_rtmp_live_app_conf_t, interleave),NULL },// 切换交错模式。在这种模式下,音频和视频数据在相同的RTMP块流上传输。默认为关闭。{ ngx_string("wait_key"),NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,ngx_conf_set_flag_slot,NGX_RTMP_APP_CONF_OFFSET,offsetof(ngx_rtmp_live_app_conf_t, wait_key),NULL },// 使视频流开始于关键帧。在某些情况下才会触发{ ngx_string("wait_video"),NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,ngx_conf_set_flag_slot,NGX_RTMP_APP_CONF_OFFSET,offsetof(ngx_rtmp_live_app_conf_t, wait_video),NULL },{ ngx_string("publish_notify"),NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,ngx_conf_set_flag_slot,NGX_RTMP_APP_CONF_OFFSET,offsetof(ngx_rtmp_live_app_conf_t, publish_notify),NULL },{ ngx_string("play_restart"),NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,ngx_conf_set_flag_slot,NGX_RTMP_APP_CONF_OFFSET,offsetof(ngx_rtmp_live_app_conf_t, play_restart),NULL },{ ngx_string("idle_streams"),NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,ngx_conf_set_flag_slot,NGX_RTMP_APP_CONF_OFFSET,offsetof(ngx_rtmp_live_app_conf_t, idle_streams),NULL },// 如果关闭,nginx-rtmp会阻止订阅用户连接到空闲/不存在的直播流,并在流发布者断开连接时断开所有订阅用户。默认为开启。{ ngx_string("drop_idle_publisher"),NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,ngx_rtmp_live_set_msec_slot,NGX_RTMP_APP_CONF_OFFSET,offsetof(ngx_rtmp_live_app_conf_t, idle_timeout),NULL },// 在指定时间内删除已空闲(无音频/视频数据)的发布者连接。默认为关闭。注意,这仅在连接处于发布模式(在发送发布命令之后)时有效。ngx_null_command
};
代码流程模块
1. 定义live_module 模块,加载模块的时候,会调用相应的函数进行初始化操作
static ngx_rtmp_module_t ngx_rtmp_live_module_ctx = {NULL, /* preconfiguration */ngx_rtmp_live_postconfiguration, /* postconfiguration */NULL, /* create main configuration */NULL, /* init main configuration */NULL, /* create server configuration */NULL, /* merge server configuration */ngx_rtmp_live_create_app_conf, /* create app configuration */ngx_rtmp_live_merge_app_conf /* merge app configuration */
};
2. 配置文件初始化
static void * ngx_rtmp_live_create_app_conf(ngx_conf_t *cf)
{ngx_rtmp_live_app_conf_t *lacf;// 从pool内存池中分配 ngx_rtmp_live_app_conf_t 对象lacf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_live_app_conf_t));if (lacf == NULL) {return NULL;}lacf->live = NGX_CONF_UNSET;lacf->nbuckets = NGX_CONF_UNSET;lacf->buflen = NGX_CONF_UNSET_MSEC;lacf->sync = NGX_CONF_UNSET_MSEC;lacf->idle_timeout = NGX_CONF_UNSET_MSEC;lacf->interleave = NGX_CONF_UNSET;lacf->wait_key = NGX_CONF_UNSET;lacf->wait_video = NGX_CONF_UNSET;lacf->publish_notify = NGX_CONF_UNSET;lacf->play_restart = NGX_CONF_UNSET;lacf->idle_streams = NGX_CONF_UNSET;return lacf;
}
3. 解析配置文件,如果配置文件没有配置指定命令,则取默认值,配置值则直接赋值
static char *
ngx_rtmp_live_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
{ngx_rtmp_live_app_conf_t *prev = parent;ngx_rtmp_live_app_conf_t *conf = child;ngx_conf_merge_value(conf->live, prev->live, 0);ngx_conf_merge_value(conf->nbuckets, prev->nbuckets, 1024);ngx_conf_merge_msec_value(conf->buflen, prev->buflen, 0);ngx_conf_merge_msec_value(conf->sync, prev->sync, 300);ngx_conf_merge_msec_value(conf->idle_timeout, prev->idle_timeout, 0);ngx_conf_merge_value(conf->interleave, prev->interleave, 0);ngx_conf_merge_value(conf->wait_key, prev->wait_key, 1);ngx_conf_merge_value(conf->wait_video, prev->wait_video, 0);ngx_conf_merge_value(conf->publish_notify, prev->publish_notify, 0);ngx_conf_merge_value(conf->play_restart, prev->play_restart, 0);ngx_conf_merge_value(conf->idle_streams, prev->idle_streams, 1);conf->pool = ngx_create_pool(4096, &cf->cycle->new_log);if (conf->pool == NULL) {return NGX_CONF_ERROR;}conf->streams = ngx_pcalloc(cf->pool,sizeof(ngx_rtmp_live_stream_t *) * conf->nbuckets);return NGX_CONF_OK;
}
4. ngx_rtmp_live_av:音视频数据到来的时候会回调ngx_rtmp_live_av,进行处理数据
ngx_rtmp_live_publish onpublish 时间回调此函数
ngx_rtmp_live_play onplay 事件回调此函数
ngx_rtmp_live_close_stream 流关闭回到次函数
5. 当客户端推流或者拉流,会通过ngx_rtmp_live_join,把推流或者拉流的rtmp session 加入到流的hash 桶中,hash桶的大小可以通过配置,如果发生hash 冲突,则通过桶对应的链表进行解决,访问的时候通过name进行比较确定是哪一路stream。下面一图表示流的存储数据结构
6. 超时会断开流
static void
ngx_rtmp_live_idle(ngx_event_t *pev)
{ngx_connection_t *c;ngx_rtmp_session_t *s;c = pev->data;s = c->data;ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,"live: drop idle publisher");ngx_rtmp_finalize_session(s);
}