http-flv实时流的实现
Hlf查块实现有如下文件
ngx_http_hlf_module.c #hlf入口模块,属http模块
ngx_rtmp_hlf_module.h #hlf实现模块,属rtmp模块
ngx_rtmp_hlf_module.c
ngx_rtmp_hlf_shared.c #相关内存块的管理
其它相关部分
ngx_rtmp.h #增加hlf相关成员
ngx_rtmp.c
ngx_http.h #增加写完一帧(flv -tag)的回调函数
ngx_http.c
ngx_http_write_filter_module.c
hlf
Syntax: hlf on|off
Context: location
desc:(http模块)指定location是否启用hlf功能,默认off。
hlf_srv
Syntax: hlf_svr numbers
Context: location
desc:(http模块) rtmp server序号,默认0 。
备注:
代码中,见红色部分,flv_relay有识,其实有错识,纠正为hlf,但可不必理会,不影响运行结果。
//模块配置
typedef struct
{
ngx_flag_t hlf_relay; //联动hlf中继,默认不启用--,纠正为hlf
ngx_uint_t hlf_srv; //rtmp server序号,默认0
} ngx_http_hlf_loc_conf_t;
hlf_enable
Syntax: hlf_enable on|off
Context: rtmp, server, application
desc:hlf启用选项,默认off。
hlf_buckets
Syntax: hlf_buckets numbers
Context: rtmp, server, application
desc:针对每个app建立hash链表,即以推流名称生成hask_key并存放入hash表,表示hash表桶大小,默认1024。
hlf_queue_max
Syntax: hlf_queue_max numbers
Context: rtmp, server, application
desc:针对每个http请求流建立发送缓冲,表示缓冲帧最大帧数,默认256,最小32。
hlf_idle_timeout
Syntax: hlf_idle_timeout time
Context: rtmp, server, application
desc:当http请求指定app的某个流不存在时,等待的时间,默认0,即直接断开。
hlf_relay
Syntax: hlf_relay on|off
Context: rtmp, server, application
desc: 联动中继,默认off。
***********************************************************************
实现文件
ngx_http_hlf_module.c
相关的文件
ngx_http.h
ngx_http.c
ngx_http_write_filter_module.c
两个主要的函数
初始化ngx_http_hlf_module,注册ngx_http_top_write_filter_peerfinished函数,该函数是ngx_http.h文件中新增加的,最终有ngx_http_write_filter来驱动,注册后实际会调用ngx_http_hlf_filter_peerframe_finished,用于实现每发送完一个flv-tag后,继续驱动下一个flv-tag的发送。
ngx_http_top_write_filter_peerfinished =ngx_http_hlf_filter_peerframe_finished;
ngx_rtmp_hlf_peerframe_finished # ngx_rtmp_hlf_module
ngx_rtmp_hlf_output_filter # ngx_rtmp_hlf_module
ngx_http_hlf_do
ngx_http_hlf_handler
ngx_http_hlf_handler函数就是处理hlf配置项的;
http-flv实时流的url格式如下:
http://192.168.1.80/room_hd/6000.flv
每一个http-flv请求上来,会生成一个该模块的上下文ngx_rtmp_hlf_stream_t,该上文的结构参考了rtmp拉流上下文结构,其实整个实现都参考rtmp流实现的方案,如果你已经知道其中原理,建议跳过本文档,直接看实现代码;
ngx_http_hlf_handler实现描述
1. 检验url地址有效性,获取app及name。有ngx_http_hlf_check_uri、ngx_http_hlf_get_app_name函数实现;
2. 根据appt和name把该请求追加到ngx_rtmp_hlf _module模块中,并返回该请求上下文(ngx_rtmp_hlf_stream_t)。有ngx_rtmp_hlf_add_stream函数实现;
3. 设置模块的上下文ngx_rtmp_hlf_stream_t;
4. 注册http断开清除函数,即断开后能调用ngx_rtmp_hlf_module的ngx_rtmp_hlf_del_stream函数;
5. 生成http回复头并发送
6. 生成第一个flv-tag(flv文件头),并发送,ngx_http_hlf_gengerate_header。
备注:
以上红色标注的部分,五个函数。其中ngx_http_top_write_filter_peerfinish是nginx-http.h增加的函数,其它四个是ngx_rtmp_hlf_module模块提供的。
提供每发送完一个flv-tag后,能通知出来,以便ngx_rtmp_hlf_module能继续一下flv-tag的发送。
ngx_http_top_write_filter_peerfinished
if (chain == NULL&& ngx_http_top_write_filter_peerfinished)
{
ngx_http_top_write_filter_peerfinished(r);
}
//hlf配置
typedef struct {
ngx_flag_t enable; //启用标识(其它选项,后续增加)
ngx_int_t nbuckets; //hash桶个数,默认1024
ngx_int_t queue_max; //缓冲最大帧数,默认256,最小32
ngx_msec_t idle_timeout; //无流时保持时长 ,默认直接断开(0)
ngx_flag_t hlf_relay; //联动中继,默认off
ngx_rtmp_hlf_ctx_t **ctxs;
ngx_pool_t *pool;
ngx_rtmp_hlf_ctx_t *free_ctxs;
} ngx_rtmp_hlf_app_conf_t;
//推流上下文
struct ngx_rtmp_hlf_ctx_s {
ngx_rtmp_hlf_ctx_t *next;
ngx_rtmp_hlf_app_conf_t *conf;
ngx_rtmp_session_t *s;
ngx_rtmp_publish_t *v;
ngx_uint_t hlf_srv;
u_char name[NGX_RTMP_MAX_NAME];
ngx_rtmp_header_t header; //最近一个关键帧--头
ngx_chain_t *near_iframe; //最近一个关键帧--数据
ngx_rtmp_hlf_stream_t *streams;
};
//http拉流上下文
struct ngx_rtmp_hlf_stream_s {
ngx_rtmp_hlf_stream_t *next;
ngx_http_request_t *r;
ngx_rtmp_hlf_ctx_t *ctx;
ngx_flag_t del;
ngx_str_t app;
ngx_str_t name;
ngx_msec_t begin_time;
unsigned start_sent:1;
unsigned aac_header_sent:1;
unsigned avc_header_sent:1;
unsigned video_key_send:1;
//** 用于统计(之后改用ngx_rtmp_bandwidth_t)
size_t out_bytes; //已发送字节数
size_t lost_frames; //丢弃帧数
ngx_event_t *idle_timer; //ngx_rtmp_hlf_app_conf_t.idle_timeout>0时,启用
ngx_chain_t *out_chain,*out_chain_c;
size_t out_chain_len;
ngx_chain_t *free_chains; //ngx_chain_t+ ngx_buf_t
ngx_chain_t **ncs; //用于ngx_rtmp_hlf_remove_nframes()函数
//**用于环形缓冲
size_t out_max,out_size,out_pos,out_last;
ngx_chain_t *out[0];
};
3.1节有描述,ngx_rtmp_hlf_module模块提供了三个函数给ngx_http_hlf_module模块调用。
分别http_flv请求上来时,请求断开时及发送完一个flv-tag时:
ngx_int_t
ngx_rtmp_hlf_add_stream(ngx_http_request_t*r, ngx_str_t *app, ngx_str_t *name, ngx_uint_t srv, ngx_rtmp_hlf_stream_t**st);
void
ngx_rtmp_hlf_del_stream(ngx_rtmp_hlf_stream_t*st);
ngx_int_t
ngx_rtmp_hlf_peerframe_finished(ngx_rtmp_hlf_stream_t*st);
详见:
static ngx_int_t
ngx_rtmp_hlf_postconfiguration(ngx_conf_t*cf)
{
ngx_rtmp_core_main_conf_t *cmcf;
ngx_rtmp_handler_pt *h;
cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module);
h= ngx_array_push(&cmcf->events[NGX_RTMP_MSG_AUDIO]);
*h = ngx_rtmp_hlf_av;
h= ngx_array_push(&cmcf->events[NGX_RTMP_MSG_VIDEO]);
*h = ngx_rtmp_hlf_av;
next_publish = ngx_rtmp_publish;
ngx_rtmp_publish = ngx_rtmp_hlf_publish;
next_close_stream = ngx_rtmp_close_stream;
ngx_rtmp_close_stream = ngx_rtmp_hlf_close_stream;
next_stream_begin = ngx_rtmp_stream_begin;
ngx_rtmp_stream_begin = ngx_rtmp_hlf_stream_begin;
next_stream_eof = ngx_rtmp_stream_eof;
ngx_rtmp_stream_eof = ngx_rtmp_hlf_stream_eof;
return NGX_OK;
}
主要有
ngx_rtmp_hlf_publish #rtmp推流上来时
ngx_rtmp_hlf_close_stream #rtmp推流断开时
ngx_rtmp_hlf_av #av数据上来时
实现描述:
1. 生成一个本模块推流上下议,ngx_rtmp_hlf_ctx_t,并放到本模块配置上下文的ngx_rtmp_hlf_app_conf_t的ctxs链表中;
2. 若开启了无流时等待,即关闭该流下所有http_flv流请求的定时器。
实现描述:
1. 清除该流下所有http_flv流请求;
2. 清除缓冲的最后一个gop;
3. 清除本流上下文。
本实现参考了rtmp实时流的分发机制,但也加入自己的一些认识,比如分发时的丢包机制。
实现描述:
1. 给该流下的所有http_flv请求推送flv-tag数据;
2. 同样的,ngx_rtmp_core_srv_conf_t配置上下文件中,增加了hlf相关内存块链接,例如:
ngx_chain_t *free_hlf; //用于http live flv
ngx_chain_t *free_iframe; //用于http liveflv,save near i frame
3. 第一个http_flv上下建立自己的发送缓冲,ngx_rtmp_hlf_stream_s,例如:
size_t out_max,out_size,out_pos,out_last;
ngx_chain_t *out[0];
4. flv_tag缓冲是通过引用计数的原理实现的;
5. 结合h264编码原理,引入更科学的丢包策略;
丢包策略,详见ngx_rtmp_hlf_remove_out:
移除策略:优先级从低到高分别是p帧/音频帧,i帧,强制帧
1.优先移除P帧,音频帧--(仅对i帧,强制帧有效)
2.再移除I帧--(仅对i帧,强制帧有效)
3.最终移除多余强制帧,但每个至少保留一个--(仅对强制帧有效)
**/
ngx_rtmp.h
ngx_rtmp.cpp
ngx_rtmp_hlf_shared.c
主要实现分发flv-tag数据时一些内存的管理,其原理同rtmp流数据分发时内存管理策略是一样的。
主要有:
typedef struct ngx_rtmp_core_srv_conf_s {
ngx_array_t applications; /* ngx_rtmp_core_app_conf_t */
ngx_msec_t timeout;
ngx_msec_t ping;
ngx_msec_t ping_timeout;
ngx_flag_t so_keepalive;
ngx_int_t max_streams;
ngx_uint_t ack_window;
ngx_int_t chunk_size;
ngx_pool_t *pool;
ngx_chain_t *free;
ngx_chain_t *free_hs;
ngx_chain_t *free_hlf; //用于http live flv
ngx_chain_t *free_iframe; //用于http live flv,save near i frame
size_t max_message;
ngx_flag_t play_time_fix;
ngx_flag_t publish_time_fix;
ngx_flag_t busy;
size_t out_queue;
size_t out_cork;
ngx_msec_t buflen;
ngx_rtmp_conf_ctx_t *ctx;
} ngx_rtmp_core_srv_conf_t;
//******** begin hlf shared bufs***********************
#define NGX_RTMP_FLV_TAG_HEADER_SIZE 11 //tag header size
#define NGX_RTMP_FLV_PREVIOUS_TAG_SIZE 4 //pre tagsize
ngx_chain_t*
ngx_rtmp_hlf_alloc_shared_buf(ngx_rtmp_core_srv_conf_t*cscf);
void
ngx_rtmp_hlf_free_shared_chain(ngx_rtmp_core_srv_conf_t*cscf, ngx_chain_t *in);
ngx_chain_t*
ngx_rtmp_hlf_append_shared_bufs(ngx_rtmp_core_srv_conf_t*cscf, ngx_chain_t *head, ngx_chain_t *in);
//hlf,near i frame
ngx_chain_t*
ngx_rtmp_hlf_neariframe_alloc_chain(ngx_rtmp_core_srv_conf_t*cscf);
void
ngx_rtmp_hlf_neariframe_free_chain(ngx_rtmp_core_srv_conf_t*cscf, ngx_chain_t *in);
ngx_chain_t*
ngx_rtmp_hlf_neariframe_copy_chain(ngx_rtmp_core_srv_conf_t*cscf, ngx_chain_t *in);
//******** end hlf shared bufs*************************
ngx_rtmp_hlf_shared.c
内存管理的具体实现。
备注:
理解该部分需要对rtmp分发数据的原理有一定的了解,同时知道ngx_chain_t,ngx_buf_t相关结构。
源码详见:
https://download.csdn.net/download/laichanghe/10455405
QQ:19993939
备注:文档内QQ号是错的,在此纠正。