基于ngx-rtmp-module模块http-flv直播流的实现

http-flv实时流的实现

1.概述

  • 目前我们选择的视频直播方案是nginx+ngx-rtmp-module;
  • http-flv直播流是把rtmp实时流的数据封装成flv格式,并基于http的chunked协议来传输。flv格式、http的chunked块协议及http-flv实时流优势在该文档不再赘述,建议读者在网上查阅;
  • http-flv流实现详见ngx_http_hlf_module、ngx_rtmp_hlf_module模块,以下简称hlf(http-live-flv)模块;
  • 本文档是在已经实现http-flv实时流功能后追加的补充文档,因此阐述的方式与一般的预研文档有所不同,请知悉;
  • 本文档的读者需要对nginx、ngx-rtmp-module有一定的基础,理解rtmp、flv、http的相关知识。

2.实现方案

2.1相关实现文件

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

 

2.2模块配置描述

2.2.1属http模块配置

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;

2.2.2属rtmp模块配置

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。

***********************************************************************

3.实现方案详细描述

  • 还是采用从外到内的描述方式,比较宏观一些;
  • 要增加http-flv实时流,入口一定是从http模块开始,要了解ngx_http_send_header、ngx_http_output_filter两个函数作用及实现原理;
  • 从http模块接轨到rtmp模块,有hlf模块的ngx_rtmp_hlf_add_stream、ngx_rtmp_hlf_del_stream来实现;
  • hlf模块的rtmp模块实现rtmp数据转flv的tag数据,分发flv数据等;
  • 增加或完善了nginx中有关ngx_http_write_filter函数的功能;
  • 再次声明:本文档不会阐述原有模块及基本相关知识点,仅阐述增加部分的功能。

3.1 ngx_http_hlf_module

实现文件

ngx_http_hlf_module.c

 

相关的文件

ngx_http.h

ngx_http.c

ngx_http_write_filter_module.c

 

3.1.1 ngx_http_hlf_module.c

两个主要的函数

3.1.1.1ngx_http_hlf_init

初始化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

3.1.1.2ngx_http_hlf_do

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模块提供的。

3.1.2相关的文件

提供每发送完一个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);

}

3.2ngx_rtmp_hlf_module

3.2.1配置结构体

//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;

 

3.2.2推流上下文

//推流上下文

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;

};

 

3.2.3http-flv拉流上下文

//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.2.4提供的外部函数

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);

3.2.5接入rtmp模块相关的函数链

详见:

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数据上来时

3.2.5.1 ngx_rtmp_hlf_publish

实现描述:

1.      生成一个本模块推流上下议,ngx_rtmp_hlf_ctx_t,并放到本模块配置上下文的ngx_rtmp_hlf_app_conf_t的ctxs链表中;

2.      若开启了无流时等待,即关闭该流下所有http_flv流请求的定时器。

3.2.5.2 ngx_rtmp_hlf_close_stream

实现描述:

1.      清除该流下所有http_flv流请求;

2.      清除缓冲的最后一个gop;

3.      清除本流上下文。

3.2.5.3 ngx_rtmp_hlf_av

本实现参考了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.最终移除多余强制帧,但每个至少保留一个--(仅对强制帧有效)

**/

3.2.6相关的文件

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相关结构。

 

4.总结

源码详见:

https://download.csdn.net/download/laichanghe/10455405

QQ:19993939

备注:文档内QQ号是错的,在此纠正。


你可能感兴趣的:(直播)