模块作用
直播模块代码 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);
}