1 
2 /*
3  * Copyright (C) Roman Arutyunyan
4  */
5 
6 
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include "ngx_rtmp_relay_module.h"
10 #include "ngx_rtmp_cmd_module.h"
11 #include "ngx_rtmp_codec_module.h"
12 
13 
14 static ngx_rtmp_publish_pt          next_publish;
15 static ngx_rtmp_play_pt             next_play;
16 static ngx_rtmp_delete_stream_pt    next_delete_stream;
17 static ngx_rtmp_close_stream_pt     next_close_stream;
18 
19 
20 static ngx_int_t ngx_rtmp_relay_init_process(ngx_cycle_t *cycle);
21 static ngx_int_t ngx_rtmp_relay_postconfiguration(ngx_conf_t *cf);
22 static void * ngx_rtmp_relay_create_app_conf(ngx_conf_t *cf);
23 static char * ngx_rtmp_relay_merge_app_conf(ngx_conf_t *cf,
24        void *parent, void *child);
25 static char * ngx_rtmp_relay_push_pull(ngx_conf_t *cf, ngx_command_t *cmd,
26        void *conf);
27 static ngx_int_t ngx_rtmp_relay_publish(ngx_rtmp_session_t *s,
28        ngx_rtmp_publish_t *v);
29 static ngx_rtmp_relay_ctx_t * ngx_rtmp_relay_create_connection(
30        ngx_rtmp_conf_ctx_t *cctx, ngx_str_t* name,
31        ngx_rtmp_relay_target_t *target);
32 
33 
34 /*                _____
35  * =push=        |     |---publish--->
36  * ---publish--->|     |---publish--->
37  *     (src)     |     |---publish--->
38  *                -----  (next,relay)
39  *                      need reconnect
40  * =pull=         _____
41  * -----play---->|     |
42  * -----play---->|     |----play----->
43  * -----play---->|     | (src,relay)
44  *     (next)     -----
45  */
46 
47 
48 typedef struct {
49     ngx_array_t                 pulls;         /* ngx_rtmp_relay_target_t * */
50     ngx_array_t                 pushes;        /* ngx_rtmp_relay_target_t * */
51     ngx_array_t                 static_pulls;  /* ngx_rtmp_relay_target_t * */
52     ngx_array_t                 static_events; /* ngx_event_t * */
53     ngx_log_t                  *log;
54     ngx_uint_t                  nbuckets;
55     ngx_msec_t                  buflen;
56     ngx_flag_t                  session_relay;
57     ngx_msec_t                  push_reconnect;
58     ngx_msec_t                  pull_reconnect;
59     ngx_rtmp_relay_ctx_t        **ctx;
60 } ngx_rtmp_relay_app_conf_t;
61 
62 
63 typedef struct {
64     ngx_rtmp_conf_ctx_t         cctx;
65     ngx_rtmp_relay_target_t    *target;
66 } ngx_rtmp_relay_static_t;
67 
68 
69 #define NGX_RTMP_RELAY_CONNECT_TRANS            1
70 #define NGX_RTMP_RELAY_CREATE_STREAM_TRANS      2
71 
72 
73 #define NGX_RTMP_RELAY_CSID_AMF_INI             3
74 #define NGX_RTMP_RELAY_CSID_AMF                 5
75 #define NGX_RTMP_RELAY_MSID                     1
76 
77 
78 /* default flashVer */
79 #define NGX_RTMP_RELAY_FLASHVER                 "LNX.11,1,102,55"
80 
81 
82 static ngx_command_t  ngx_rtmp_relay_commands[] = {
83 
84     { ngx_string("push"),
85       NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
86       ngx_rtmp_relay_push_pull,
87       NGX_RTMP_APP_CONF_OFFSET,
88       0,
89       NULL },
90 
91     { ngx_string("pull"),
92       NGX_RTMP_APP_CONF|NGX_CONF_1MORE,
93       ngx_rtmp_relay_push_pull,
94       NGX_RTMP_APP_CONF_OFFSET,
95       0,
96       NULL },
97 
98     { ngx_string("relay_buffer"),
99       NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_CONF_TAKE1,
100       ngx_conf_set_msec_slot,
101       NGX_RTMP_APP_CONF_OFFSET,
102       offsetof(ngx_rtmp_relay_app_conf_t, buflen),
103       NULL },
104 
105     { ngx_string("push_reconnect"),
106       NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
107       ngx_conf_set_msec_slot,
108       NGX_RTMP_APP_CONF_OFFSET,
109       offsetof(ngx_rtmp_relay_app_conf_t, push_reconnect),
110       NULL },
111 
112     { ngx_string("pull_reconnect"),
113       NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
114       ngx_conf_set_msec_slot,
115       NGX_RTMP_APP_CONF_OFFSET,
116       offsetof(ngx_rtmp_relay_app_conf_t, pull_reconnect),
117       NULL },
118 
119     { ngx_string("session_relay"),
120       NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
121       ngx_conf_set_flag_slot,
122       NGX_RTMP_APP_CONF_OFFSET,
123       offsetof(ngx_rtmp_relay_app_conf_t, session_relay),
124       NULL },
125 
126 
127       ngx_null_command
128 };
129 
130 
131 static ngx_rtmp_module_t  ngx_rtmp_relay_module_ctx = {
132     NULL,                                   /* preconfiguration */
133     ngx_rtmp_relay_postconfiguration,       /* postconfiguration */
134     NULL,                                   /* create main configuration */
135     NULL,                                   /* init main configuration */
136     NULL,                                   /* create server configuration */
137     NULL,                                   /* merge server configuration */
138     ngx_rtmp_relay_create_app_conf,         /* create app configuration */
139     ngx_rtmp_relay_merge_app_conf           /* merge app configuration */
140 };
141 
142 
143 ngx_module_t  ngx_rtmp_relay_module = {
144     NGX_MODULE_V1,
145     &ngx_rtmp_relay_module_ctx,             /* module context */
146     ngx_rtmp_relay_commands,                /* module directives */
147     NGX_RTMP_MODULE,                        /* module type */
148     NULL,                                   /* init master */
149     NULL,                                   /* init module */
150     ngx_rtmp_relay_init_process,            /* init process */
151     NULL,                                   /* init thread */
152     NULL,                                   /* exit thread */
153     NULL,                                   /* exit process */
154     NULL,                                   /* exit master */
155     NGX_MODULE_V1_PADDING
156 };
157 
158 
159 static void *
ngx_rtmp_relay_create_app_conf(ngx_conf_t * cf)160 ngx_rtmp_relay_create_app_conf(ngx_conf_t *cf)
161 {
162     ngx_rtmp_relay_app_conf_t     *racf;
163 
164     racf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_relay_app_conf_t));
165     if (racf == NULL) {
166         return NULL;
167     }
168 
169     if (ngx_array_init(&racf->pushes, cf->pool, 1, sizeof(void *)) != NGX_OK) {
170         return NULL;
171     }
172 
173     if (ngx_array_init(&racf->pulls, cf->pool, 1, sizeof(void *)) != NGX_OK) {
174         return NULL;
175     }
176 
177     if (ngx_array_init(&racf->static_pulls, cf->pool, 1, sizeof(void *))
178         != NGX_OK)
179     {
180         return NULL;
181     }
182 
183     if (ngx_array_init(&racf->static_events, cf->pool, 1, sizeof(void *))
184         != NGX_OK)
185     {
186         return NULL;
187     }
188 
189     racf->nbuckets = 1024;
190     racf->log = &cf->cycle->new_log;
191     racf->buflen = NGX_CONF_UNSET_MSEC;
192     racf->session_relay = NGX_CONF_UNSET;
193     racf->push_reconnect = NGX_CONF_UNSET_MSEC;
194     racf->pull_reconnect = NGX_CONF_UNSET_MSEC;
195 
196     return racf;
197 }
198 
199 
200 static char *
ngx_rtmp_relay_merge_app_conf(ngx_conf_t * cf,void * parent,void * child)201 ngx_rtmp_relay_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
202 {
203     ngx_rtmp_relay_app_conf_t  *prev = parent;
204     ngx_rtmp_relay_app_conf_t  *conf = child;
205 
206     conf->ctx = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_relay_ctx_t *)
207             * conf->nbuckets);
208 
209     ngx_conf_merge_value(conf->session_relay, prev->session_relay, 0);
210     ngx_conf_merge_msec_value(conf->buflen, prev->buflen, 5000);
211     ngx_conf_merge_msec_value(conf->push_reconnect, prev->push_reconnect,
212             3000);
213     ngx_conf_merge_msec_value(conf->pull_reconnect, prev->pull_reconnect,
214             3000);
215 
216     return NGX_CONF_OK;
217 }
218 
219 
220 static void
ngx_rtmp_relay_static_pull_reconnect(ngx_event_t * ev)221 ngx_rtmp_relay_static_pull_reconnect(ngx_event_t *ev)
222 {
223     ngx_rtmp_relay_static_t    *rs = ev->data;
224 
225     ngx_rtmp_relay_ctx_t       *ctx;
226     ngx_rtmp_relay_app_conf_t  *racf;
227 
228     racf = ngx_rtmp_get_module_app_conf(&rs->cctx, ngx_rtmp_relay_module);
229 
230     ngx_log_debug0(NGX_LOG_DEBUG_RTMP, racf->log, 0,
231                    "relay: reconnecting static pull");
232 
233     ctx = ngx_rtmp_relay_create_connection(&rs->cctx, &rs->target->name,
234                                            rs->target);
235     if (ctx) {
236         ctx->session->static_relay = 1;
237         ctx->static_evt = ev;
238         return;
239     }
240 
241     ngx_add_timer(ev, racf->pull_reconnect);
242 }
243 
244 
245 static void
ngx_rtmp_relay_push_reconnect(ngx_event_t * ev)246 ngx_rtmp_relay_push_reconnect(ngx_event_t *ev)
247 {
248     ngx_rtmp_session_t             *s = ev->data;
249 
250     ngx_rtmp_relay_app_conf_t      *racf;
251     ngx_rtmp_relay_ctx_t           *ctx, *pctx;
252     ngx_uint_t                      n;
253     ngx_rtmp_relay_target_t        *target, **t;
254 
255     ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
256             "relay: push reconnect");
257 
258     racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_relay_module);
259 
260     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
261     if (ctx == NULL) {
262         return;
263     }
264 
265     t = racf->pushes.elts;
266     for (n = 0; n < racf->pushes.nelts; ++n, ++t) {
267         target = *t;
268 
269         if (target->name.len && (ctx->name.len != target->name.len ||
270             ngx_memcmp(ctx->name.data, target->name.data, ctx->name.len)))
271         {
272             continue;
273         }
274 
275         for (pctx = ctx->play; pctx; pctx = pctx->next) {
276             if (pctx->tag == &ngx_rtmp_relay_module &&
277                 pctx->data == target)
278             {
279                 break;
280             }
281         }
282 
283         if (pctx) {
284             continue;
285         }
286 
287         if (ngx_rtmp_relay_push(s, &ctx->name, target) == NGX_OK) {
288             continue;
289         }
290 
291         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
292                 "relay: push reconnect failed name='%V' app='%V' "
293                 "playpath='%V' url='%V'",
294                 &ctx->name, &target->app, &target->play_path,
295                 &target->url.url);
296 
297         if (!ctx->push_evt.timer_set) {
298             ngx_add_timer(&ctx->push_evt, racf->push_reconnect);
299         }
300     }
301 }
302 
303 
304 static ngx_int_t
ngx_rtmp_relay_get_peer(ngx_peer_connection_t * pc,void * data)305 ngx_rtmp_relay_get_peer(ngx_peer_connection_t *pc, void *data)
306 {
307     return NGX_OK;
308 }
309 
310 
311 static void
ngx_rtmp_relay_free_peer(ngx_peer_connection_t * pc,void * data,ngx_uint_t state)312 ngx_rtmp_relay_free_peer(ngx_peer_connection_t *pc, void *data,
313             ngx_uint_t state)
314 {
315 }
316 
317 
318 typedef ngx_rtmp_relay_ctx_t * (* ngx_rtmp_relay_create_ctx_pt)
319     (ngx_rtmp_session_t *s, ngx_str_t *name, ngx_rtmp_relay_target_t *target);
320 
321 
322 static ngx_int_t
ngx_rtmp_relay_copy_str(ngx_pool_t * pool,ngx_str_t * dst,ngx_str_t * src)323 ngx_rtmp_relay_copy_str(ngx_pool_t *pool, ngx_str_t *dst, ngx_str_t *src)
324 {
325     if (src->len == 0) {
326         return NGX_OK;
327     }
328     dst->len = src->len;
329     dst->data = ngx_palloc(pool, src->len);
330     if (dst->data == NULL) {
331         return NGX_ERROR;
332     }
333     ngx_memcpy(dst->data, src->data, src->len);
334     return NGX_OK;
335 }
336 
337 
338 static ngx_rtmp_relay_ctx_t *
ngx_rtmp_relay_create_connection(ngx_rtmp_conf_ctx_t * cctx,ngx_str_t * name,ngx_rtmp_relay_target_t * target)339 ngx_rtmp_relay_create_connection(ngx_rtmp_conf_ctx_t *cctx, ngx_str_t* name,
340         ngx_rtmp_relay_target_t *target)
341 {
342     ngx_rtmp_relay_app_conf_t      *racf;
343     ngx_rtmp_relay_ctx_t           *rctx;
344     ngx_rtmp_addr_conf_t           *addr_conf;
345     ngx_rtmp_conf_ctx_t            *addr_ctx;
346     ngx_rtmp_session_t             *rs;
347     ngx_peer_connection_t          *pc;
348     ngx_connection_t               *c;
349     ngx_addr_t                     *addr;
350     ngx_pool_t                     *pool;
351     ngx_int_t                       rc;
352     ngx_str_t                       v, *uri;
353     u_char                         *first, *last, *p;
354 
355     racf = ngx_rtmp_get_module_app_conf(cctx, ngx_rtmp_relay_module);
356 
357     ngx_log_debug0(NGX_LOG_DEBUG_RTMP, racf->log, 0,
358                    "relay: create remote context");
359 
360     pool = NULL;
361     pool = ngx_create_pool(4096, racf->log);
362     if (pool == NULL) {
363         return NULL;
364     }
365 
366     rctx = ngx_pcalloc(pool, sizeof(ngx_rtmp_relay_ctx_t));
367     if (rctx == NULL) {
368         goto clear;
369     }
370 
371     if (name && ngx_rtmp_relay_copy_str(pool, &rctx->name, name) != NGX_OK) {
372         goto clear;
373     }
374 
375     if (ngx_rtmp_relay_copy_str(pool, &rctx->url, &target->url.url) != NGX_OK) {
376         goto clear;
377     }
378 
379     rctx->tag = target->tag;
380     rctx->data = target->data;
381 
382 #define NGX_RTMP_RELAY_STR_COPY(to, from)                                     \
383     if (ngx_rtmp_relay_copy_str(pool, &rctx->to, &target->from) != NGX_OK) {  \
384         goto clear;                                                           \
385     }
386 
387     NGX_RTMP_RELAY_STR_COPY(app,        app);
388     NGX_RTMP_RELAY_STR_COPY(tc_url,     tc_url);
389     NGX_RTMP_RELAY_STR_COPY(page_url,   page_url);
390     NGX_RTMP_RELAY_STR_COPY(swf_url,    swf_url);
391     NGX_RTMP_RELAY_STR_COPY(flash_ver,  flash_ver);
392     NGX_RTMP_RELAY_STR_COPY(play_path,  play_path);
393 
394     rctx->live  = target->live;
395     rctx->start = target->start;
396     rctx->stop  = target->stop;
397 
398 #undef NGX_RTMP_RELAY_STR_COPY
399 
400     if (rctx->app.len == 0 || rctx->play_path.len == 0) {
401         /* parse uri */
402         uri = &target->url.uri;
403         first = uri->data;
404         last  = uri->data + uri->len;
405         if (first != last && *first == '/') {
406             ++first;
407         }
408 
409         if (first != last) {
410 
411             /* deduce app */
412             p = ngx_strlchr(first, last, '/');
413             if (p == NULL) {
414                 p = last;
415             }
416 
417             if (rctx->app.len == 0 && first != p) {
418                 v.data = first;
419                 v.len = p - first;
420                 if (ngx_rtmp_relay_copy_str(pool, &rctx->app, &v) != NGX_OK) {
421                     goto clear;
422                 }
423             }
424 
425             /* deduce play_path */
426             if (p != last) {
427                 ++p;
428             }
429 
430             if (rctx->play_path.len == 0 && p != last) {
431                 v.data = p;
432                 v.len = last - p;
433                 if (ngx_rtmp_relay_copy_str(pool, &rctx->play_path, &v)
434                         != NGX_OK)
435                 {
436                     goto clear;
437                 }
438             }
439         }
440     }
441 
442     pc = ngx_pcalloc(pool, sizeof(ngx_peer_connection_t));
443     if (pc == NULL) {
444         goto clear;
445     }
446 
447     if (target->url.naddrs == 0) {
448         ngx_log_error(NGX_LOG_ERR, racf->log, 0,
449                       "relay: no address");
450         goto clear;
451     }
452 
453     /* get address */
454     addr = &target->url.addrs[target->counter % target->url.naddrs];
455     target->counter++;
456 
457     /* copy log to keep shared log unchanged */
458     rctx->log = *racf->log;
459     pc->log = &rctx->log;
460     pc->get = ngx_rtmp_relay_get_peer;
461     pc->free = ngx_rtmp_relay_free_peer;
462     pc->name = &addr->name;
463     pc->socklen = addr->socklen;
464     pc->sockaddr = (struct sockaddr *)ngx_palloc(pool, pc->socklen);
465     if (pc->sockaddr == NULL) {
466         goto clear;
467     }
468     ngx_memcpy(pc->sockaddr, addr->sockaddr, pc->socklen);
469 
470     rc = ngx_event_connect_peer(pc);
471     if (rc != NGX_OK && rc != NGX_AGAIN ) {
472         ngx_log_debug0(NGX_LOG_DEBUG_RTMP, racf->log, 0,
473                 "relay: connection failed");
474         goto clear;
475     }
476     c = pc->connection;
477     c->pool = pool;
478     c->addr_text = rctx->url;
479 
480     addr_conf = ngx_pcalloc(pool, sizeof(ngx_rtmp_addr_conf_t));
481     if (addr_conf == NULL) {
482         goto clear;
483     }
484     addr_ctx = ngx_pcalloc(pool, sizeof(ngx_rtmp_conf_ctx_t));
485     if (addr_ctx == NULL) {
486         goto clear;
487     }
488     addr_conf->ctx = addr_ctx;
489     addr_ctx->main_conf = cctx->main_conf;
490     addr_ctx->srv_conf  = cctx->srv_conf;
491     ngx_str_set(&addr_conf->addr_text, "ngx-relay");
492 
493     rs = ngx_rtmp_init_session(c, addr_conf);
494     if (rs == NULL) {
495         /* no need to destroy pool */
496         return NULL;
497     }
498     rs->app_conf = cctx->app_conf;
499     rs->relay = 1;
500     rs->ready_for_publish = 0;
501     rctx->session = rs;
502     ngx_rtmp_set_ctx(rs, rctx, ngx_rtmp_relay_module);
503     ngx_str_set(&rs->flashver, "ngx-local-relay");
504     ngx_memcpy(&rs->app, &rctx->app, sizeof(rctx->app));
505 
506 #if (NGX_STAT_STUB)
507     (void) ngx_atomic_fetch_add(ngx_stat_active, 1);
508 #endif
509 
510     ngx_rtmp_client_handshake(rs, 1);
511     return rctx;
512 
513 clear:
514     if (pool) {
515         ngx_destroy_pool(pool);
516     }
517     return NULL;
518 }
519 
520 
521 static ngx_rtmp_relay_ctx_t *
ngx_rtmp_relay_create_remote_ctx(ngx_rtmp_session_t * s,ngx_str_t * name,ngx_rtmp_relay_target_t * target)522 ngx_rtmp_relay_create_remote_ctx(ngx_rtmp_session_t *s, ngx_str_t* name,
523         ngx_rtmp_relay_target_t *target)
524 {
525     ngx_rtmp_conf_ctx_t         cctx;
526 
527     cctx.app_conf = s->app_conf;
528     cctx.srv_conf = s->srv_conf;
529     cctx.main_conf = s->main_conf;
530 
531     return ngx_rtmp_relay_create_connection(&cctx, name, target);
532 }
533 
534 
535 static ngx_rtmp_relay_ctx_t *
ngx_rtmp_relay_create_local_ctx(ngx_rtmp_session_t * s,ngx_str_t * name,ngx_rtmp_relay_target_t * target)536 ngx_rtmp_relay_create_local_ctx(ngx_rtmp_session_t *s, ngx_str_t *name,
537         ngx_rtmp_relay_target_t *target)
538 {
539     ngx_rtmp_relay_ctx_t           *ctx;
540 
541     ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
542                    "relay: create local context");
543 
544     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
545     if (ctx == NULL) {
546         ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_relay_ctx_t));
547         if (ctx == NULL) {
548             return NULL;
549         }
550         ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_relay_module);
551     }
552     ctx->session = s;
553 
554     ctx->push_evt.data = s;
555     ctx->push_evt.log = s->connection->log;
556     ctx->push_evt.handler = ngx_rtmp_relay_push_reconnect;
557 
558     if (ctx->publish) {
559         return NULL;
560     }
561 
562     if (ngx_rtmp_relay_copy_str(s->connection->pool, &ctx->name, name)
563             != NGX_OK)
564     {
565         return NULL;
566     }
567 
568     return ctx;
569 }
570 
571 
572 static ngx_int_t
ngx_rtmp_relay_create(ngx_rtmp_session_t * s,ngx_str_t * name,ngx_rtmp_relay_target_t * target,ngx_rtmp_relay_create_ctx_pt create_publish_ctx,ngx_rtmp_relay_create_ctx_pt create_play_ctx)573 ngx_rtmp_relay_create(ngx_rtmp_session_t *s, ngx_str_t *name,
574         ngx_rtmp_relay_target_t *target,
575         ngx_rtmp_relay_create_ctx_pt create_publish_ctx,
576         ngx_rtmp_relay_create_ctx_pt create_play_ctx)
577 {
578     ngx_rtmp_relay_app_conf_t      *racf;
579     ngx_rtmp_relay_ctx_t           *publish_ctx, *play_ctx, **cctx;
580     ngx_uint_t                      hash;
581 
582 
583     racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_relay_module);
584     if (racf == NULL) {
585         return NGX_ERROR;
586     }
587 
588     play_ctx = create_play_ctx(s, name, target);
589     if (play_ctx == NULL) {
590         return NGX_ERROR;
591     }
592 
593     hash = ngx_hash_key(name->data, name->len);
594     cctx = &racf->ctx[hash % racf->nbuckets];
595     for (; *cctx; cctx = &(*cctx)->next) {
596         if ((*cctx)->name.len == name->len
597             && !ngx_memcmp(name->data, (*cctx)->name.data,
598                 name->len))
599         {
600             break;
601         }
602     }
603 
604     if (*cctx) {
605         play_ctx->publish = (*cctx)->publish;
606         play_ctx->next = (*cctx)->play;
607         (*cctx)->play = play_ctx;
608         return NGX_OK;
609     }
610 
611     publish_ctx = create_publish_ctx(s, name, target);
612     if (publish_ctx == NULL) {
613         ngx_rtmp_finalize_session(play_ctx->session);
614         return NGX_ERROR;
615     }
616 
617     publish_ctx->publish = publish_ctx;
618     publish_ctx->play = play_ctx;
619     play_ctx->publish = publish_ctx;
620     *cctx = publish_ctx;
621 
622     return NGX_OK;
623 }
624 
625 
626 ngx_int_t
ngx_rtmp_relay_pull(ngx_rtmp_session_t * s,ngx_str_t * name,ngx_rtmp_relay_target_t * target)627 ngx_rtmp_relay_pull(ngx_rtmp_session_t *s, ngx_str_t *name,
628         ngx_rtmp_relay_target_t *target)
629 {
630     ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
631             "relay: create pull name='%V' app='%V' playpath='%V' url='%V'",
632             name, &target->app, &target->play_path, &target->url.url);
633 
634     return ngx_rtmp_relay_create(s, name, target,
635             ngx_rtmp_relay_create_remote_ctx,
636             ngx_rtmp_relay_create_local_ctx);
637 }
638 
639 
640 ngx_int_t
ngx_rtmp_relay_push(ngx_rtmp_session_t * s,ngx_str_t * name,ngx_rtmp_relay_target_t * target)641 ngx_rtmp_relay_push(ngx_rtmp_session_t *s, ngx_str_t *name,
642         ngx_rtmp_relay_target_t *target)
643 {
644     ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
645             "relay: create push name='%V' app='%V' playpath='%V' url='%V'",
646             name, &target->app, &target->play_path, &target->url.url);
647 
648     return ngx_rtmp_relay_create(s, name, target,
649             ngx_rtmp_relay_create_local_ctx,
650             ngx_rtmp_relay_create_remote_ctx);
651 }
652 
653 
654 static ngx_int_t
ngx_rtmp_relay_publish(ngx_rtmp_session_t * s,ngx_rtmp_publish_t * v)655 ngx_rtmp_relay_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
656 {
657     ngx_rtmp_relay_app_conf_t      *racf;
658     ngx_rtmp_relay_target_t        *target, **t;
659     ngx_str_t                       name;
660     size_t                          n;
661     ngx_rtmp_relay_ctx_t           *ctx;
662 
663     if (s->auto_pushed) {
664         goto next;
665     }
666 
667     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
668     if (ctx && s->relay) {
669         goto next;
670     }
671 
672     racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_relay_module);
673     if (racf == NULL || racf->pushes.nelts == 0) {
674         goto next;
675     }
676 
677     name.len = ngx_strlen(v->name);
678     name.data = v->name;
679 
680     t = racf->pushes.elts;
681     for (n = 0; n < racf->pushes.nelts; ++n, ++t) {
682         target = *t;
683 
684         if (target->name.len && (name.len != target->name.len ||
685             ngx_memcmp(name.data, target->name.data, name.len)))
686         {
687             continue;
688         }
689 
690         if (ngx_rtmp_relay_push(s, &name, target) == NGX_OK) {
691             continue;
692         }
693 
694         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
695                 "relay: push failed name='%V' app='%V' "
696                 "playpath='%V' url='%V'",
697                 &name, &target->app, &target->play_path,
698                 &target->url.url);
699 
700         if (!ctx->push_evt.timer_set) {
701             ngx_add_timer(&ctx->push_evt, racf->push_reconnect);
702         }
703     }
704 
705 next:
706     return next_publish(s, v);
707 }
708 
709 
710 static ngx_int_t
ngx_rtmp_relay_play(ngx_rtmp_session_t * s,ngx_rtmp_play_t * v)711 ngx_rtmp_relay_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
712 {
713     ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
714                   "relay: ngx_rtmp_relay_play");
715 
716     ngx_rtmp_relay_app_conf_t      *racf;
717     ngx_rtmp_relay_target_t        *target, **t;
718     ngx_str_t                       name;
719     size_t                          n;
720     ngx_rtmp_relay_ctx_t           *ctx;
721 
722     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
723     if (ctx && s->relay) {
724         goto next;
725     }
726 
727     racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_relay_module);
728     if (racf == NULL || racf->pulls.nelts == 0) {
729         goto next;
730     }
731 
732     name.len = ngx_strlen(v->name);
733     name.data = v->name;
734 
735     t = racf->pulls.elts;
736     for (n = 0; n < racf->pulls.nelts; ++n, ++t) {
737         target = *t;
738 
739         if (target->name.len && (name.len != target->name.len ||
740             ngx_memcmp(name.data, target->name.data, name.len)))
741         {
742             continue;
743         }
744 
745         if (ngx_rtmp_relay_pull(s, &name, target) == NGX_OK) {
746             continue;
747         }
748 
749         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
750                 "relay: pull failed name='%V' app='%V' "
751                 "playpath='%V' url='%V'",
752                 &name, &target->app, &target->play_path,
753                 &target->url.url);
754     }
755 
756 next:
757     ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
758               "relay: ngx_rtmp_relay_play: next");
759 
760     return next_play(s, v);
761 }
762 
763 
764 static ngx_int_t
ngx_rtmp_relay_play_local(ngx_rtmp_session_t * s)765 ngx_rtmp_relay_play_local(ngx_rtmp_session_t *s)
766 {
767     ngx_rtmp_play_t             v;
768     ngx_rtmp_relay_ctx_t       *ctx;
769 
770     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
771     if (ctx == NULL) {
772         return NGX_ERROR;
773     }
774 
775     ngx_memzero(&v, sizeof(ngx_rtmp_play_t));
776     v.silent = 1;
777     *(ngx_cpymem(v.name, ctx->name.data,
778             ngx_min(sizeof(v.name) - 1, ctx->name.len))) = 0;
779 
780     return ngx_rtmp_play(s, &v);
781 }
782 
783 
784 static ngx_int_t
ngx_rtmp_relay_publish_local(ngx_rtmp_session_t * s)785 ngx_rtmp_relay_publish_local(ngx_rtmp_session_t *s)
786 {
787     ngx_rtmp_publish_t          v;
788     ngx_rtmp_relay_ctx_t       *ctx;
789 
790     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
791     if (ctx == NULL) {
792         return NGX_ERROR;
793     }
794 
795     ngx_memzero(&v, sizeof(ngx_rtmp_publish_t));
796     v.silent = 1;
797     *(ngx_cpymem(v.name, ctx->name.data,
798             ngx_min(sizeof(v.name) - 1, ctx->name.len))) = 0;
799 
800     return ngx_rtmp_publish(s, &v);
801 }
802 
803 
804 static ngx_int_t
ngx_rtmp_relay_send_connect(ngx_rtmp_session_t * s)805 ngx_rtmp_relay_send_connect(ngx_rtmp_session_t *s)
806 {
807     static double               trans = NGX_RTMP_RELAY_CONNECT_TRANS;
808     static double               acodecs = 3575;
809     static double               vcodecs = 252;
810 
811     static ngx_rtmp_amf_elt_t   out_cmd[] = {
812 
813         { NGX_RTMP_AMF_STRING,
814           ngx_string("app"),
815           NULL, 0 }, /* <-- fill */
816 
817         { NGX_RTMP_AMF_STRING,
818           ngx_string("tcUrl"),
819           NULL, 0 }, /* <-- fill */
820 
821         { NGX_RTMP_AMF_STRING,
822           ngx_string("pageUrl"),
823           NULL, 0 }, /* <-- fill */
824 
825         { NGX_RTMP_AMF_STRING,
826           ngx_string("swfUrl"),
827           NULL, 0 }, /* <-- fill */
828 
829         { NGX_RTMP_AMF_STRING,
830           ngx_string("flashVer"),
831           NULL, 0 }, /* <-- fill */
832 
833         { NGX_RTMP_AMF_NUMBER,
834           ngx_string("audioCodecs"),
835           &acodecs, 0 },
836 
837         { NGX_RTMP_AMF_NUMBER,
838           ngx_string("videoCodecs"),
839           &vcodecs, 0 }
840     };
841 
842     static ngx_rtmp_amf_elt_t   out_elts[] = {
843 
844         { NGX_RTMP_AMF_STRING,
845           ngx_null_string,
846           "connect", 0 },
847 
848         { NGX_RTMP_AMF_NUMBER,
849           ngx_null_string,
850           &trans, 0 },
851 
852         { NGX_RTMP_AMF_OBJECT,
853           ngx_null_string,
854           out_cmd, sizeof(out_cmd) }
855     };
856 
857     ngx_rtmp_core_app_conf_t   *cacf;
858     ngx_rtmp_core_srv_conf_t   *cscf;
859     ngx_rtmp_relay_ctx_t       *ctx;
860     ngx_rtmp_header_t           h;
861     size_t                      len, url_len;
862     u_char                     *p, *url_end;
863 
864 
865     cacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_core_module);
866     cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
867     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
868     if (cacf == NULL || ctx == NULL) {
869         return NGX_ERROR;
870     }
871 
872     /* app */
873     if (ctx->app.len) {
874         out_cmd[0].data = ctx->app.data;
875         out_cmd[0].len  = ctx->app.len;
876     } else {
877         out_cmd[0].data = cacf->name.data;
878         out_cmd[0].len  = cacf->name.len;
879     }
880 
881     /* tcUrl */
882     if (ctx->tc_url.len) {
883         out_cmd[1].data = ctx->tc_url.data;
884         out_cmd[1].len  = ctx->tc_url.len;
885     } else {
886         len = sizeof("rtmp://") - 1 + ctx->url.len +
887             sizeof("/") - 1 + ctx->app.len;
888         p = ngx_palloc(s->connection->pool, len);
889         if (p == NULL) {
890             return NGX_ERROR;
891         }
892         out_cmd[1].data = p;
893         p = ngx_cpymem(p, "rtmp://", sizeof("rtmp://") - 1);
894 
895         url_len = ctx->url.len;
896         url_end = ngx_strlchr(ctx->url.data, ctx->url.data + ctx->url.len, '/');
897         if (url_end) {
898             url_len = (size_t) (url_end - ctx->url.data);
899         }
900 
901         p = ngx_cpymem(p, ctx->url.data, url_len);
902         *p++ = '/';
903         p = ngx_cpymem(p, ctx->app.data, ctx->app.len);
904         out_cmd[1].len = p - (u_char *)out_cmd[1].data;
905     }
906 
907     /* pageUrl */
908     out_cmd[2].data = ctx->page_url.data;
909     out_cmd[2].len  = ctx->page_url.len;
910 
911     /* swfUrl */
912     out_cmd[3].data = ctx->swf_url.data;
913     out_cmd[3].len  = ctx->swf_url.len;
914 
915     /* flashVer */
916     if (ctx->flash_ver.len) {
917         out_cmd[4].data = ctx->flash_ver.data;
918         out_cmd[4].len  = ctx->flash_ver.len;
919     } else {
920         out_cmd[4].data = NGX_RTMP_RELAY_FLASHVER;
921         out_cmd[4].len  = sizeof(NGX_RTMP_RELAY_FLASHVER) - 1;
922     }
923 
924     ngx_memzero(&h, sizeof(h));
925     h.csid = NGX_RTMP_RELAY_CSID_AMF_INI;
926     h.type = NGX_RTMP_MSG_AMF_CMD;
927 
928     return ngx_rtmp_send_chunk_size(s, cscf->chunk_size) != NGX_OK
929         || ngx_rtmp_send_ack_size(s, cscf->ack_window) != NGX_OK
930         || ngx_rtmp_send_amf(s, &h, out_elts,
931             sizeof(out_elts) / sizeof(out_elts[0])) != NGX_OK
932         ? NGX_ERROR
933         : NGX_OK;
934 }
935 
936 
937 static ngx_int_t
ngx_rtmp_relay_send_create_stream(ngx_rtmp_session_t * s)938 ngx_rtmp_relay_send_create_stream(ngx_rtmp_session_t *s)
939 {
940     static double               trans = NGX_RTMP_RELAY_CREATE_STREAM_TRANS;
941 
942     static ngx_rtmp_amf_elt_t   out_elts[] = {
943 
944         { NGX_RTMP_AMF_STRING,
945           ngx_null_string,
946           "createStream", 0 },
947 
948         { NGX_RTMP_AMF_NUMBER,
949           ngx_null_string,
950           &trans, 0 },
951 
952         { NGX_RTMP_AMF_NULL,
953           ngx_null_string,
954           NULL, 0 }
955     };
956 
957     ngx_rtmp_header_t           h;
958 
959 
960     ngx_memzero(&h, sizeof(h));
961     h.csid = NGX_RTMP_RELAY_CSID_AMF_INI;
962     h.type = NGX_RTMP_MSG_AMF_CMD;
963 
964     return ngx_rtmp_send_amf(s, &h, out_elts,
965             sizeof(out_elts) / sizeof(out_elts[0]));
966 }
967 
968 
969 static ngx_int_t
ngx_rtmp_relay_send_publish(ngx_rtmp_session_t * s)970 ngx_rtmp_relay_send_publish(ngx_rtmp_session_t *s)
971 {
972     static double               trans;
973 
974     static ngx_rtmp_amf_elt_t   out_elts[] = {
975 
976         { NGX_RTMP_AMF_STRING,
977           ngx_null_string,
978           "publish", 0 },
979 
980         { NGX_RTMP_AMF_NUMBER,
981           ngx_null_string,
982           &trans, 0 },
983 
984         { NGX_RTMP_AMF_NULL,
985           ngx_null_string,
986           NULL, 0 },
987 
988         { NGX_RTMP_AMF_STRING,
989           ngx_null_string,
990           NULL, 0 }, /* <- to fill */
991 
992         { NGX_RTMP_AMF_STRING,
993           ngx_null_string,
994           "live", 0 }
995     };
996 
997     ngx_rtmp_header_t           h;
998     ngx_rtmp_relay_ctx_t       *ctx;
999 
1000 
1001     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
1002     if (ctx == NULL) {
1003         return NGX_ERROR;
1004     }
1005 
1006     if (ctx->play_path.len) {
1007         out_elts[3].data = ctx->play_path.data;
1008         out_elts[3].len  = ctx->play_path.len;
1009     } else {
1010         out_elts[3].data = ctx->name.data;
1011         out_elts[3].len  = ctx->name.len;
1012     }
1013 
1014     ngx_memzero(&h, sizeof(h));
1015     h.csid = NGX_RTMP_RELAY_CSID_AMF;
1016     h.msid = NGX_RTMP_RELAY_MSID;
1017     h.type = NGX_RTMP_MSG_AMF_CMD;
1018 
1019     return ngx_rtmp_send_amf(s, &h, out_elts,
1020             sizeof(out_elts) / sizeof(out_elts[0]));
1021 }
1022 
1023 
1024 static ngx_int_t
ngx_rtmp_relay_send_play(ngx_rtmp_session_t * s)1025 ngx_rtmp_relay_send_play(ngx_rtmp_session_t *s)
1026 {
1027     static double               trans;
1028     static double               start, duration;
1029 
1030     static ngx_rtmp_amf_elt_t   out_elts[] = {
1031 
1032         { NGX_RTMP_AMF_STRING,
1033           ngx_null_string,
1034           "play", 0 },
1035 
1036         { NGX_RTMP_AMF_NUMBER,
1037           ngx_null_string,
1038           &trans, 0 },
1039 
1040         { NGX_RTMP_AMF_NULL,
1041           ngx_null_string,
1042           NULL, 0 },
1043 
1044         { NGX_RTMP_AMF_STRING,
1045           ngx_null_string,
1046           NULL, 0 }, /* <- fill */
1047 
1048         { NGX_RTMP_AMF_NUMBER,
1049           ngx_null_string,
1050           &start, 0 },
1051 
1052         { NGX_RTMP_AMF_NUMBER,
1053           ngx_null_string,
1054           &duration, 0 },
1055     };
1056 
1057     ngx_rtmp_header_t           h;
1058     ngx_rtmp_relay_ctx_t       *ctx;
1059     ngx_rtmp_relay_app_conf_t  *racf;
1060 
1061 
1062     racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_relay_module);
1063     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
1064     if (racf == NULL || ctx == NULL) {
1065         return NGX_ERROR;
1066     }
1067 
1068     if (ctx->play_path.len) {
1069         out_elts[3].data = ctx->play_path.data;
1070         out_elts[3].len  = ctx->play_path.len;
1071     } else {
1072         out_elts[3].data = ctx->name.data;
1073         out_elts[3].len  = ctx->name.len;
1074     }
1075 
1076     if (ctx->live) {
1077         start = -1000;
1078         duration = -1000;
1079     } else {
1080         start    = (ctx->start ? ctx->start : -2000);
1081         duration = (ctx->stop  ? ctx->stop - ctx->start : -1000);
1082     }
1083 
1084     ngx_memzero(&h, sizeof(h));
1085     h.csid = NGX_RTMP_RELAY_CSID_AMF;
1086     h.msid = NGX_RTMP_RELAY_MSID;
1087     h.type = NGX_RTMP_MSG_AMF_CMD;
1088 
1089     return ngx_rtmp_send_amf(s, &h, out_elts,
1090             sizeof(out_elts) / sizeof(out_elts[0])) != NGX_OK
1091            || ngx_rtmp_send_set_buflen(s, NGX_RTMP_RELAY_MSID,
1092                    racf->buflen) != NGX_OK
1093            ? NGX_ERROR
1094            : NGX_OK;
1095 }
1096 
1097 
1098 static ngx_int_t
ngx_rtmp_relay_on_result(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)1099 ngx_rtmp_relay_on_result(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
1100         ngx_chain_t *in)
1101 {
1102     ngx_rtmp_relay_ctx_t       *ctx;
1103     static struct {
1104         double                  trans;
1105         u_char                  level[32];
1106         u_char                  code[128];
1107         u_char                  desc[1024];
1108     } v;
1109 
1110     static ngx_rtmp_amf_elt_t   in_inf[] = {
1111 
1112         { NGX_RTMP_AMF_STRING,
1113           ngx_string("level"),
1114           &v.level, sizeof(v.level) },
1115 
1116         { NGX_RTMP_AMF_STRING,
1117           ngx_string("code"),
1118           &v.code, sizeof(v.code) },
1119 
1120         { NGX_RTMP_AMF_STRING,
1121           ngx_string("description"),
1122           &v.desc, sizeof(v.desc) },
1123     };
1124 
1125     static ngx_rtmp_amf_elt_t   in_elts[] = {
1126 
1127         { NGX_RTMP_AMF_NUMBER,
1128           ngx_null_string,
1129           &v.trans, 0 },
1130 
1131         { NGX_RTMP_AMF_NULL,
1132           ngx_null_string,
1133           NULL, 0 },
1134 
1135         { NGX_RTMP_AMF_OBJECT,
1136           ngx_null_string,
1137           in_inf, sizeof(in_inf) },
1138     };
1139 
1140 
1141     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
1142     if (ctx == NULL || !s->relay) {
1143         return NGX_OK;
1144     }
1145 
1146     ngx_memzero(&v, sizeof(v));
1147     if (ngx_rtmp_receive_amf(s, in, in_elts,
1148                 sizeof(in_elts) / sizeof(in_elts[0])))
1149     {
1150         return NGX_ERROR;
1151     }
1152 
1153     ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
1154             "relay: _result: level='%s' code='%s' description='%s'",
1155             v.level, v.code, v.desc);
1156 
1157     switch ((ngx_int_t)v.trans) {
1158         case NGX_RTMP_RELAY_CONNECT_TRANS:
1159             return ngx_rtmp_relay_send_create_stream(s);
1160 
1161         case NGX_RTMP_RELAY_CREATE_STREAM_TRANS:
1162             if (ctx->publish != ctx && !s->static_relay) {
1163                 if (ngx_rtmp_relay_send_publish(s) != NGX_OK) {
1164                     return NGX_ERROR;
1165                 }
1166                 return ngx_rtmp_relay_play_local(s);
1167 
1168             } else {
1169                 if (ngx_rtmp_relay_send_play(s) != NGX_OK) {
1170                     return NGX_ERROR;
1171                 }
1172                 return ngx_rtmp_relay_publish_local(s);
1173             }
1174 
1175         default:
1176             return NGX_OK;
1177     }
1178 }
1179 
1180 
1181 static ngx_int_t
ngx_rtmp_relay_on_error(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)1182 ngx_rtmp_relay_on_error(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
1183         ngx_chain_t *in)
1184 {
1185     ngx_rtmp_relay_ctx_t       *ctx;
1186     static struct {
1187         double                  trans;
1188         u_char                  level[32];
1189         u_char                  code[128];
1190         u_char                  desc[1024];
1191     } v;
1192 
1193     static ngx_rtmp_amf_elt_t   in_inf[] = {
1194 
1195         { NGX_RTMP_AMF_STRING,
1196           ngx_string("level"),
1197           &v.level, sizeof(v.level) },
1198 
1199         { NGX_RTMP_AMF_STRING,
1200           ngx_string("code"),
1201           &v.code, sizeof(v.code) },
1202 
1203         { NGX_RTMP_AMF_STRING,
1204           ngx_string("description"),
1205           &v.desc, sizeof(v.desc) },
1206     };
1207 
1208     static ngx_rtmp_amf_elt_t   in_elts[] = {
1209 
1210         { NGX_RTMP_AMF_NUMBER,
1211           ngx_null_string,
1212           &v.trans, 0 },
1213 
1214         { NGX_RTMP_AMF_NULL,
1215           ngx_null_string,
1216           NULL, 0 },
1217 
1218         { NGX_RTMP_AMF_OBJECT,
1219           ngx_null_string,
1220           in_inf, sizeof(in_inf) },
1221     };
1222 
1223 
1224     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
1225     if (ctx == NULL || !s->relay) {
1226         return NGX_OK;
1227     }
1228 
1229     ngx_memzero(&v, sizeof(v));
1230     if (ngx_rtmp_receive_amf(s, in, in_elts,
1231                 sizeof(in_elts) / sizeof(in_elts[0])))
1232     {
1233         return NGX_ERROR;
1234     }
1235 
1236     ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
1237             "relay: _error: level='%s' code='%s' description='%s'",
1238             v.level, v.code, v.desc);
1239 
1240     return NGX_OK;
1241 }
1242 
1243 static ngx_int_t
ngx_rtmp_relay_send_set_data_frame(ngx_rtmp_session_t * s)1244 ngx_rtmp_relay_send_set_data_frame(ngx_rtmp_session_t *s)
1245 {
1246     ngx_rtmp_relay_ctx_t           *ctx;
1247     ngx_rtmp_codec_ctx_t           *codec_ctx;
1248     ngx_rtmp_header_t               hdr;
1249 
1250     static struct {
1251         double                      width;
1252         double                      height;
1253         double                      duration;
1254         double                      frame_rate;
1255         double                      video_data_rate;
1256         double                      video_codec_id;
1257         double                      audio_data_rate;
1258         double                      audio_codec_id;
1259         u_char                      profile[32];
1260         u_char                      level[32];
1261     }                               v;
1262 
1263     static ngx_rtmp_amf_elt_t       out_inf[] = {
1264 
1265         { NGX_RTMP_AMF_STRING,
1266           ngx_string("Server"),
1267           "NGINX RTMP (github.com/arut/nginx-rtmp-module)", 0 },
1268 
1269         { NGX_RTMP_AMF_NUMBER,
1270           ngx_string("width"),
1271           &v.width, 0 },
1272 
1273         { NGX_RTMP_AMF_NUMBER,
1274           ngx_string("height"),
1275           &v.height, 0 },
1276 
1277         { NGX_RTMP_AMF_NUMBER,
1278           ngx_string("displayWidth"),
1279           &v.width, 0 },
1280 
1281         { NGX_RTMP_AMF_NUMBER,
1282           ngx_string("displayHeight"),
1283           &v.height, 0 },
1284 
1285         { NGX_RTMP_AMF_NUMBER,
1286           ngx_string("duration"),
1287           &v.duration, 0 },
1288 
1289         { NGX_RTMP_AMF_NUMBER,
1290           ngx_string("framerate"),
1291           &v.frame_rate, 0 },
1292 
1293         { NGX_RTMP_AMF_NUMBER,
1294           ngx_string("fps"),
1295           &v.frame_rate, 0 },
1296 
1297         { NGX_RTMP_AMF_NUMBER,
1298           ngx_string("videodatarate"),
1299           &v.video_data_rate, 0 },
1300 
1301         { NGX_RTMP_AMF_NUMBER,
1302           ngx_string("videocodecid"),
1303           &v.video_codec_id, 0 },
1304 
1305         { NGX_RTMP_AMF_NUMBER,
1306           ngx_string("audiodatarate"),
1307           &v.audio_data_rate, 0 },
1308 
1309         { NGX_RTMP_AMF_NUMBER,
1310           ngx_string("audiocodecid"),
1311           &v.audio_codec_id, 0 },
1312 
1313         { NGX_RTMP_AMF_STRING,
1314           ngx_string("profile"),
1315           &v.profile, sizeof(v.profile) },
1316 
1317         { NGX_RTMP_AMF_STRING,
1318           ngx_string("level"),
1319           &v.level, sizeof(v.level) }
1320     };
1321 
1322     static ngx_rtmp_amf_elt_t       out_elts[] = {
1323 
1324         { NGX_RTMP_AMF_STRING,
1325           ngx_null_string,
1326           "@setDataFrame", 0 },
1327 
1328         { NGX_RTMP_AMF_STRING,
1329           ngx_null_string,
1330           "onMetaData", 0 },
1331 
1332         { NGX_RTMP_AMF_OBJECT,
1333           ngx_null_string,
1334           out_inf, sizeof(out_inf) }
1335     };
1336 
1337     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
1338     if (ctx == NULL || !s->relay) {
1339         ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
1340                 "relay: couldn't get relay context");
1341         return NGX_OK;
1342     }
1343 
1344     /* we need to get the codec context from the incoming publisher in order to
1345      * send the metadata along */
1346     codec_ctx = ngx_rtmp_get_module_ctx(ctx->publish->session,
1347             ngx_rtmp_codec_module);
1348     if (codec_ctx == NULL) {
1349         ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
1350                 "relay: couldn't get codec context");
1351         return NGX_OK;
1352     }
1353 
1354     ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
1355             "relay: data frame from codec context: "
1356             "width=%ui height=%ui duration=%ui frame_rate=%ui "
1357             "video_codec_id=%ui audio_codec_id=%ui",
1358             codec_ctx->width, codec_ctx->height, codec_ctx->duration,
1359             codec_ctx->frame_rate, codec_ctx->video_codec_id,
1360             codec_ctx->audio_codec_id);
1361 
1362     /* we only want to send the metadata if the codec module has already
1363      * parsed it -- is there a better way to check this? */
1364     if (codec_ctx->width > 0 && codec_ctx->height > 0) {
1365       v.width = codec_ctx->width;
1366       v.height = codec_ctx->height;
1367       v.duration = codec_ctx->duration;
1368       v.frame_rate = codec_ctx->frame_rate;
1369       v.video_data_rate = codec_ctx->video_data_rate;
1370       v.video_codec_id = codec_ctx->video_codec_id;
1371       v.audio_data_rate = codec_ctx->audio_data_rate;
1372       v.audio_codec_id = codec_ctx->audio_codec_id;
1373       ngx_memcpy(v.profile, codec_ctx->profile, sizeof(codec_ctx->profile));
1374       ngx_memcpy(v.level, codec_ctx->level, sizeof(codec_ctx->level));
1375 
1376       ngx_memzero(&hdr, sizeof(hdr));
1377       hdr.csid = NGX_RTMP_RELAY_CSID_AMF_INI;
1378       hdr.msid = NGX_RTMP_RELAY_MSID;
1379       hdr.type = NGX_RTMP_MSG_AMF_META;
1380 
1381       ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
1382                 "relay: sending @setDataFrame");
1383 
1384       return ngx_rtmp_send_amf(s, &hdr, out_elts,
1385               sizeof(out_elts) / sizeof(out_elts[0]));
1386     }
1387 
1388     return NGX_OK;
1389 }
1390 
1391 static ngx_int_t
ngx_rtmp_relay_on_status(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)1392 ngx_rtmp_relay_on_status(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
1393         ngx_chain_t *in)
1394 {
1395     ngx_rtmp_relay_ctx_t       *ctx;
1396 
1397     static struct {
1398         double                  trans;
1399         u_char                  level[32];
1400         u_char                  code[128];
1401         u_char                  desc[1024];
1402     } v;
1403 
1404     static ngx_rtmp_amf_elt_t   in_inf[] = {
1405 
1406         { NGX_RTMP_AMF_STRING,
1407           ngx_string("level"),
1408           &v.level, sizeof(v.level) },
1409 
1410         { NGX_RTMP_AMF_STRING,
1411           ngx_string("code"),
1412           &v.code, sizeof(v.code) },
1413 
1414         { NGX_RTMP_AMF_STRING,
1415           ngx_string("description"),
1416           &v.desc, sizeof(v.desc) },
1417     };
1418 
1419     static ngx_rtmp_amf_elt_t   in_elts[] = {
1420 
1421         { NGX_RTMP_AMF_NUMBER,
1422           ngx_null_string,
1423           &v.trans, 0 },
1424 
1425         { NGX_RTMP_AMF_NULL,
1426           ngx_null_string,
1427           NULL, 0 },
1428 
1429         { NGX_RTMP_AMF_OBJECT,
1430           ngx_null_string,
1431           in_inf, sizeof(in_inf) },
1432     };
1433 
1434     static ngx_rtmp_amf_elt_t   in_elts_meta[] = {
1435 
1436         { NGX_RTMP_AMF_OBJECT,
1437           ngx_null_string,
1438           in_inf, sizeof(in_inf) },
1439     };
1440 
1441 
1442     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
1443     if (ctx == NULL || !s->relay) {
1444         return NGX_OK;
1445     }
1446 
1447     ngx_memzero(&v, sizeof(v));
1448     if (h->type == NGX_RTMP_MSG_AMF_META) {
1449         ngx_rtmp_receive_amf(s, in, in_elts_meta,
1450                 sizeof(in_elts_meta) / sizeof(in_elts_meta[0]));
1451     } else {
1452         ngx_rtmp_receive_amf(s, in, in_elts,
1453                 sizeof(in_elts) / sizeof(in_elts[0]));
1454     }
1455 
1456     ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
1457             "relay: onStatus: level='%s' code='%s' description='%s'",
1458             v.level, v.code, v.desc);
1459 
1460     /* when doing a push to Adobe Media Server, we have to use the
1461      * @setDataFrame command to send the metadata
1462      * see: http://help.adobe.com/en_US/adobemediaserver/devguide/WS5b3ccc516d4fbf351e63e3d11a0773d56e-7ff6Dev.2.3.html
1463      */
1464     if (!ngx_strncasecmp(v.code, (u_char *)"NetStream.Publish.Start",
1465             ngx_strlen("NetStream.Publish.Start"))) {
1466 
1467         ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
1468                 "relay: sending metadata from NetStream.Publish.Start from player");
1469 
1470         s->ready_for_publish = 1;
1471 
1472         if (ngx_rtmp_relay_send_set_data_frame(s) != NGX_OK) {
1473             ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
1474                     "relay: unable to send metadata via @setDataFrame");
1475         }
1476     }
1477 
1478     return NGX_OK;
1479 }
1480 
1481 static ngx_int_t
ngx_rtmp_relay_on_meta_data(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)1482 ngx_rtmp_relay_on_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
1483         ngx_chain_t *in)
1484 {
1485     /* when we receive onMetaData, the session (s) is our incoming publisher's
1486      * session, so we need to send the @setDataFrame to our ctx->play->session */
1487     ngx_rtmp_relay_ctx_t       *ctx;
1488     ngx_rtmp_relay_ctx_t       *pctx;
1489 
1490     ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
1491         "relay: got metadata from @setDataFrame invocation from publisher.");
1492 
1493     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
1494     if (ctx == NULL) {
1495         return NGX_OK;
1496     }
1497 
1498     for (pctx = ctx->play; pctx; pctx = pctx->next) {
1499         ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
1500                 "relay: %ssending metadata from @setDataFrame invocation from publisher to %V/%V/%V",
1501                 (pctx->session->relay && pctx->session->ready_for_publish) ? "" : "not ", &pctx->url,  &pctx->app, &pctx->play_path);
1502         if (!pctx->session->relay || !pctx->session->ready_for_publish) continue;
1503         if (ngx_rtmp_relay_send_set_data_frame(pctx->session) != NGX_OK) {
1504             ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
1505                     "relay: unable to send @setDataFrame to %V/%V", &pctx->url, &pctx->play_path);
1506         }
1507     }
1508 
1509     return NGX_OK;
1510 }
1511 
1512 static ngx_int_t
ngx_rtmp_relay_handshake_done(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)1513 ngx_rtmp_relay_handshake_done(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
1514         ngx_chain_t *in)
1515 {
1516     ngx_rtmp_relay_ctx_t   *ctx;
1517 
1518     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
1519     if (ctx == NULL || !s->relay) {
1520         return NGX_OK;
1521     }
1522 
1523     return ngx_rtmp_relay_send_connect(s);
1524 }
1525 
1526 
1527 static void
ngx_rtmp_relay_close(ngx_rtmp_session_t * s)1528 ngx_rtmp_relay_close(ngx_rtmp_session_t *s)
1529 {
1530     ngx_rtmp_relay_app_conf_t          *racf;
1531     ngx_rtmp_relay_ctx_t               *ctx, **cctx;
1532     ngx_uint_t                          hash;
1533 
1534     racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_relay_module);
1535 
1536     ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module);
1537     if (ctx == NULL) {
1538         return;
1539     }
1540 
1541     if (s->static_relay) {
1542         ngx_add_timer(ctx->static_evt, racf->pull_reconnect);
1543     }
1544 
1545     if (ctx->publish == NULL) {
1546         return;
1547     }
1548 
1549     /* play end disconnect? */
1550     if (ctx->publish != ctx) {
1551         for (cctx = &ctx->publish->play; *cctx; cctx = &(*cctx)->next) {
1552             if (*cctx == ctx) {
1553                 *cctx = ctx->next;
1554                 break;
1555             }
1556         }
1557 
1558         ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ctx->session->connection->log, 0,
1559                 "relay: play disconnect app='%V' name='%V'",
1560                 &ctx->app, &ctx->name);
1561 
1562         /* push reconnect */
1563         if (s->relay && ctx->tag == &ngx_rtmp_relay_module &&
1564             !ctx->publish->push_evt.timer_set)
1565         {
1566             ngx_add_timer(&ctx->publish->push_evt, racf->push_reconnect);
1567         }
1568 
1569 #ifdef NGX_DEBUG
1570         {
1571             ngx_uint_t  n = 0;
1572             for (cctx = &ctx->publish->play; *cctx; cctx = &(*cctx)->next, ++n);
1573             ngx_log_debug3(NGX_LOG_DEBUG_RTMP, ctx->session->connection->log, 0,
1574                 "relay: play left after disconnect app='%V' name='%V': %ui",
1575                 &ctx->app, &ctx->name, n);
1576         }
1577 #endif
1578 
1579         if (ctx->publish->play == NULL && ctx->publish->session->relay) {
1580             ngx_log_debug2(NGX_LOG_DEBUG_RTMP,
1581                  ctx->publish->session->connection->log, 0,
1582                 "relay: publish disconnect empty app='%V' name='%V'",
1583                 &ctx->app, &ctx->name);
1584             ngx_rtmp_finalize_session(ctx->publish->session);
1585         }
1586 
1587         ctx->publish = NULL;
1588 
1589         return;
1590     }
1591 
1592     /* publish end disconnect */
1593     ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ctx->session->connection->log, 0,
1594             "relay: publish disconnect app='%V' name='%V'",
1595             &ctx->app, &ctx->name);
1596 
1597     if (ctx->push_evt.timer_set) {
1598         ngx_del_timer(&ctx->push_evt);
1599     }
1600 
1601     for (cctx = &ctx->play; *cctx; cctx = &(*cctx)->next) {
1602         (*cctx)->publish = NULL;
1603         ngx_log_debug2(NGX_LOG_DEBUG_RTMP, (*cctx)->session->connection->log,
1604             0, "relay: play disconnect orphan app='%V' name='%V'",
1605             &(*cctx)->app, &(*cctx)->name);
1606         ngx_rtmp_finalize_session((*cctx)->session);
1607     }
1608     ctx->publish = NULL;
1609 
1610     hash = ngx_hash_key(ctx->name.data, ctx->name.len);
1611     cctx = &racf->ctx[hash % racf->nbuckets];
1612     for (; *cctx && *cctx != ctx; cctx = &(*cctx)->next);
1613     if (*cctx) {
1614         *cctx = ctx->next;
1615     }
1616 }
1617 
1618 
1619 static ngx_int_t
ngx_rtmp_relay_close_stream(ngx_rtmp_session_t * s,ngx_rtmp_close_stream_t * v)1620 ngx_rtmp_relay_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v)
1621 {
1622     ngx_rtmp_relay_app_conf_t  *racf;
1623 
1624     racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_relay_module);
1625     if (racf && !racf->session_relay) {
1626         ngx_rtmp_relay_close(s);
1627     }
1628 
1629     return next_close_stream(s, v);
1630 }
1631 
1632 
1633 static ngx_int_t
ngx_rtmp_relay_delete_stream(ngx_rtmp_session_t * s,ngx_rtmp_delete_stream_t * v)1634 ngx_rtmp_relay_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t *v)
1635 {
1636     ngx_rtmp_relay_close(s);
1637 
1638     return next_delete_stream(s, v);
1639 }
1640 
1641 
1642 static char *
ngx_rtmp_relay_push_pull(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)1643 ngx_rtmp_relay_push_pull(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1644 {
1645     ngx_str_t                          *value, v, n;
1646     ngx_rtmp_relay_app_conf_t          *racf;
1647     ngx_rtmp_relay_target_t            *target, **t;
1648     ngx_url_t                          *u;
1649     ngx_uint_t                          i;
1650     ngx_int_t                           is_pull, is_static;
1651     ngx_event_t                       **ee, *e;
1652     ngx_rtmp_relay_static_t            *rs;
1653     u_char                             *p;
1654 
1655     value = cf->args->elts;
1656 
1657     racf = ngx_rtmp_conf_get_module_app_conf(cf, ngx_rtmp_relay_module);
1658 
1659     is_pull = (value[0].data[3] == 'l');
1660     is_static = 0;
1661 
1662     target = ngx_pcalloc(cf->pool, sizeof(*target));
1663     if (target == NULL) {
1664         return NGX_CONF_ERROR;
1665     }
1666 
1667     target->tag = &ngx_rtmp_relay_module;
1668     target->data = target;
1669 
1670     u = &target->url;
1671     u->default_port = 1935;
1672     u->uri_part = 1;
1673     u->url = value[1];
1674 
1675     if (ngx_strncasecmp(u->url.data, (u_char *) "rtmp://", 7) == 0) {
1676         u->url.data += 7;
1677         u->url.len  -= 7;
1678     }
1679 
1680     if (ngx_parse_url(cf->pool, u) != NGX_OK) {
1681         if (u->err) {
1682             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1683                     "%s in url \"%V\"", u->err, &u->url);
1684         }
1685         return NGX_CONF_ERROR;
1686     }
1687 
1688     value += 2;
1689     for (i = 2; i < cf->args->nelts; ++i, ++value) {
1690         p = ngx_strlchr(value->data, value->data + value->len, '=');
1691 
1692         if (p == NULL) {
1693             n = *value;
1694             ngx_str_set(&v, "1");
1695 
1696         } else {
1697             n.data = value->data;
1698             n.len  = p - value->data;
1699 
1700             v.data = p + 1;
1701             v.len  = value->data + value->len - p - 1;
1702         }
1703 
1704 #define NGX_RTMP_RELAY_STR_PAR(name, var)                                     \
1705         if (n.len == sizeof(name) - 1                                         \
1706             && ngx_strncasecmp(n.data, (u_char *) name, n.len) == 0)          \
1707         {                                                                     \
1708             target->var = v;                                                  \
1709             continue;                                                         \
1710         }
1711 
1712 #define NGX_RTMP_RELAY_NUM_PAR(name, var)                                     \
1713         if (n.len == sizeof(name) - 1                                         \
1714             && ngx_strncasecmp(n.data, (u_char *) name, n.len) == 0)          \
1715         {                                                                     \
1716             target->var = ngx_atoi(v.data, v.len);                            \
1717             continue;                                                         \
1718         }
1719 
1720         NGX_RTMP_RELAY_STR_PAR("app",         app);
1721         NGX_RTMP_RELAY_STR_PAR("name",        name);
1722         NGX_RTMP_RELAY_STR_PAR("tcUrl",       tc_url);
1723         NGX_RTMP_RELAY_STR_PAR("pageUrl",     page_url);
1724         NGX_RTMP_RELAY_STR_PAR("swfUrl",      swf_url);
1725         NGX_RTMP_RELAY_STR_PAR("flashVer",    flash_ver);
1726         NGX_RTMP_RELAY_STR_PAR("playPath",    play_path);
1727         NGX_RTMP_RELAY_NUM_PAR("live",        live);
1728         NGX_RTMP_RELAY_NUM_PAR("start",       start);
1729         NGX_RTMP_RELAY_NUM_PAR("stop",        stop);
1730 
1731 #undef NGX_RTMP_RELAY_STR_PAR
1732 #undef NGX_RTMP_RELAY_NUM_PAR
1733 
1734         if (n.len == sizeof("static") - 1 &&
1735             ngx_strncasecmp(n.data, (u_char *) "static", n.len) == 0 &&
1736             ngx_atoi(v.data, v.len))
1737         {
1738             is_static = 1;
1739             continue;
1740         }
1741 
1742         return "unsuppored parameter";
1743     }
1744 
1745     if (is_static) {
1746 
1747         if (!is_pull) {
1748             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1749                                "static push is not allowed");
1750             return NGX_CONF_ERROR;
1751         }
1752 
1753         if (target->name.len == 0) {
1754             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1755                                "stream name missing in static pull "
1756                                "declaration");
1757             return NGX_CONF_ERROR;
1758         }
1759 
1760         ee = ngx_array_push(&racf->static_events);
1761         if (ee == NULL) {
1762             return NGX_CONF_ERROR;
1763         }
1764 
1765         e = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));
1766         if (e == NULL) {
1767             return NGX_CONF_ERROR;
1768         }
1769 
1770         *ee = e;
1771 
1772         rs = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_relay_static_t));
1773         if (rs == NULL) {
1774             return NGX_CONF_ERROR;
1775         }
1776 
1777         rs->target = target;
1778 
1779         e->data = rs;
1780         e->log = &cf->cycle->new_log;
1781         e->handler = ngx_rtmp_relay_static_pull_reconnect;
1782 
1783         t = ngx_array_push(&racf->static_pulls);
1784 
1785     } else if (is_pull) {
1786         t = ngx_array_push(&racf->pulls);
1787 
1788     } else {
1789         t = ngx_array_push(&racf->pushes);
1790     }
1791 
1792     if (t == NULL) {
1793         return NGX_CONF_ERROR;
1794     }
1795 
1796     *t = target;
1797 
1798     return NGX_CONF_OK;
1799 }
1800 
1801 
1802 static ngx_int_t
ngx_rtmp_relay_init_process(ngx_cycle_t * cycle)1803 ngx_rtmp_relay_init_process(ngx_cycle_t *cycle)
1804 {
1805 #if !(NGX_WIN32)
1806     ngx_rtmp_core_main_conf_t  *cmcf = ngx_rtmp_core_main_conf;
1807     ngx_rtmp_core_srv_conf_t  **pcscf, *cscf;
1808     ngx_rtmp_core_app_conf_t  **pcacf, *cacf;
1809     ngx_rtmp_relay_app_conf_t  *racf;
1810     ngx_uint_t                  n, m, k;
1811     ngx_rtmp_relay_static_t    *rs;
1812     ngx_rtmp_listen_t          *lst;
1813     ngx_event_t               **pevent, *event;
1814 
1815     if (cmcf == NULL || cmcf->listen.nelts == 0) {
1816         return NGX_OK;
1817     }
1818 
1819     /* only first worker does static pulling */
1820 
1821     if (ngx_process_slot) {
1822         return NGX_OK;
1823     }
1824 
1825     lst = cmcf->listen.elts;
1826 
1827     pcscf = cmcf->servers.elts;
1828     for (n = 0; n < cmcf->servers.nelts; ++n, ++pcscf) {
1829 
1830         cscf = *pcscf;
1831         pcacf = cscf->applications.elts;
1832 
1833         for (m = 0; m < cscf->applications.nelts; ++m, ++pcacf) {
1834 
1835             cacf = *pcacf;
1836             racf = cacf->app_conf[ngx_rtmp_relay_module.ctx_index];
1837             pevent = racf->static_events.elts;
1838 
1839             for (k = 0; k < racf->static_events.nelts; ++k, ++pevent) {
1840                 event = *pevent;
1841 
1842                 rs = event->data;
1843                 rs->cctx = *lst->ctx;
1844                 rs->cctx.app_conf = cacf->app_conf;
1845 
1846                 ngx_post_event(event, &ngx_rtmp_init_queue);
1847             }
1848         }
1849     }
1850 #endif
1851     return NGX_OK;
1852 }
1853 
1854 
1855 static ngx_int_t
ngx_rtmp_relay_postconfiguration(ngx_conf_t * cf)1856 ngx_rtmp_relay_postconfiguration(ngx_conf_t *cf)
1857 {
1858     ngx_rtmp_core_main_conf_t          *cmcf;
1859     ngx_rtmp_handler_pt                *h;
1860     ngx_rtmp_amf_handler_t             *ch;
1861 
1862     cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module);
1863 
1864 
1865     h = ngx_array_push(&cmcf->events[NGX_RTMP_HANDSHAKE_DONE]);
1866     *h = ngx_rtmp_relay_handshake_done;
1867 
1868 
1869     next_publish = ngx_rtmp_publish;
1870     ngx_rtmp_publish = ngx_rtmp_relay_publish;
1871 
1872     next_play = ngx_rtmp_play;
1873     ngx_rtmp_play = ngx_rtmp_relay_play;
1874 
1875     next_delete_stream = ngx_rtmp_delete_stream;
1876     ngx_rtmp_delete_stream = ngx_rtmp_relay_delete_stream;
1877 
1878     next_close_stream = ngx_rtmp_close_stream;
1879     ngx_rtmp_close_stream = ngx_rtmp_relay_close_stream;
1880 
1881 
1882     ch = ngx_array_push(&cmcf->amf);
1883     ngx_str_set(&ch->name, "_result");
1884     ch->handler = ngx_rtmp_relay_on_result;
1885 
1886     ch = ngx_array_push(&cmcf->amf);
1887     ngx_str_set(&ch->name, "_error");
1888     ch->handler = ngx_rtmp_relay_on_error;
1889 
1890     ch = ngx_array_push(&cmcf->amf);
1891     ngx_str_set(&ch->name, "onStatus");
1892     ch->handler = ngx_rtmp_relay_on_status;
1893 
1894     ch = ngx_array_push(&cmcf->amf);
1895     ngx_str_set(&ch->name, "@setDataFrame");
1896     ch->handler = ngx_rtmp_relay_on_meta_data;
1897 
1898     return NGX_OK;
1899 }
1900