1
2 /*
3 * Copyright (C) Roman Arutyunyan
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include "ngx_rtmp_cmd_module.h"
10 #include "ngx_rtmp_streams.h"
11
12
13 #define NGX_RTMP_FMS_VERSION "FMS/3,0,1,123"
14 #define NGX_RTMP_CAPABILITIES 31
15
16
17 static ngx_int_t ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s,
18 ngx_rtmp_connect_t *v);
19 static ngx_int_t ngx_rtmp_cmd_disconnect(ngx_rtmp_session_t *s);
20 static ngx_int_t ngx_rtmp_cmd_create_stream(ngx_rtmp_session_t *s,
21 ngx_rtmp_create_stream_t *v);
22 static ngx_int_t ngx_rtmp_cmd_close_stream(ngx_rtmp_session_t *s,
23 ngx_rtmp_close_stream_t *v);
24 static ngx_int_t ngx_rtmp_cmd_delete_stream(ngx_rtmp_session_t *s,
25 ngx_rtmp_delete_stream_t *v);
26 static ngx_int_t ngx_rtmp_cmd_publish(ngx_rtmp_session_t *s,
27 ngx_rtmp_publish_t *v);
28 static ngx_int_t ngx_rtmp_cmd_play(ngx_rtmp_session_t *s,
29 ngx_rtmp_play_t *v);
30 static ngx_int_t ngx_rtmp_cmd_seek(ngx_rtmp_session_t *s,
31 ngx_rtmp_seek_t *v);
32 static ngx_int_t ngx_rtmp_cmd_pause(ngx_rtmp_session_t *s,
33 ngx_rtmp_pause_t *v);
34
35
36 static ngx_int_t ngx_rtmp_cmd_stream_begin(ngx_rtmp_session_t *s,
37 ngx_rtmp_stream_begin_t *v);
38 static ngx_int_t ngx_rtmp_cmd_stream_eof(ngx_rtmp_session_t *s,
39 ngx_rtmp_stream_eof_t *v);
40 static ngx_int_t ngx_rtmp_cmd_stream_dry(ngx_rtmp_session_t *s,
41 ngx_rtmp_stream_dry_t *v);
42 static ngx_int_t ngx_rtmp_cmd_recorded(ngx_rtmp_session_t *s,
43 ngx_rtmp_recorded_t *v);
44 static ngx_int_t ngx_rtmp_cmd_set_buflen(ngx_rtmp_session_t *s,
45 ngx_rtmp_set_buflen_t *v);
46
47 static ngx_int_t ngx_rtmp_cmd_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v);
48
49 ngx_rtmp_connect_pt ngx_rtmp_connect;
50 ngx_rtmp_disconnect_pt ngx_rtmp_disconnect;
51 ngx_rtmp_create_stream_pt ngx_rtmp_create_stream;
52 ngx_rtmp_close_stream_pt ngx_rtmp_close_stream;
53 ngx_rtmp_delete_stream_pt ngx_rtmp_delete_stream;
54 ngx_rtmp_publish_pt ngx_rtmp_publish;
55 ngx_rtmp_play_pt ngx_rtmp_play;
56 ngx_rtmp_seek_pt ngx_rtmp_seek;
57 ngx_rtmp_pause_pt ngx_rtmp_pause;
58
59
60 ngx_rtmp_stream_begin_pt ngx_rtmp_stream_begin;
61 ngx_rtmp_stream_eof_pt ngx_rtmp_stream_eof;
62 ngx_rtmp_stream_dry_pt ngx_rtmp_stream_dry;
63 ngx_rtmp_recorded_pt ngx_rtmp_recorded;
64 ngx_rtmp_set_buflen_pt ngx_rtmp_set_buflen;
65
66 ngx_rtmp_playlist_pt ngx_rtmp_playlist;
67
68 static ngx_int_t ngx_rtmp_cmd_postconfiguration(ngx_conf_t *cf);
69
70
71 static ngx_rtmp_module_t ngx_rtmp_cmd_module_ctx = {
72 NULL, /* preconfiguration */
73 ngx_rtmp_cmd_postconfiguration, /* postconfiguration */
74 NULL, /* create main configuration */
75 NULL, /* init main configuration */
76 NULL, /* create server configuration */
77 NULL, /* merge server configuration */
78 NULL, /* create app configuration */
79 NULL /* merge app configuration */
80 };
81
82
83 ngx_module_t ngx_rtmp_cmd_module = {
84 NGX_MODULE_V1,
85 &ngx_rtmp_cmd_module_ctx, /* module context */
86 NULL, /* module directives */
87 NGX_RTMP_MODULE, /* module type */
88 NULL, /* init master */
89 NULL, /* init module */
90 NULL, /* init process */
91 NULL, /* init thread */
92 NULL, /* exit thread */
93 NULL, /* exit process */
94 NULL, /* exit master */
95 NGX_MODULE_V1_PADDING
96 };
97
98
99 void
ngx_rtmp_cmd_fill_args(u_char name[NGX_RTMP_MAX_NAME],u_char args[NGX_RTMP_MAX_ARGS])100 ngx_rtmp_cmd_fill_args(u_char name[NGX_RTMP_MAX_NAME],
101 u_char args[NGX_RTMP_MAX_ARGS])
102 {
103 u_char *p;
104
105 p = (u_char *)ngx_strchr(name, '?');
106 if (p == NULL) {
107 return;
108 }
109
110 *p++ = 0;
111 ngx_cpystrn(args, p, NGX_RTMP_MAX_ARGS);
112 }
113
114
115 static ngx_int_t
ngx_rtmp_cmd_connect_init(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)116 ngx_rtmp_cmd_connect_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
117 ngx_chain_t *in)
118 {
119 size_t len;
120
121 static ngx_rtmp_connect_t v;
122
123 static ngx_rtmp_amf_elt_t in_cmd[] = {
124
125 { NGX_RTMP_AMF_STRING,
126 ngx_string("app"),
127 v.app, sizeof(v.app) },
128
129 { NGX_RTMP_AMF_STRING,
130 ngx_string("flashVer"),
131 v.flashver, sizeof(v.flashver) },
132
133 { NGX_RTMP_AMF_STRING,
134 ngx_string("swfUrl"),
135 v.swf_url, sizeof(v.swf_url) },
136
137 { NGX_RTMP_AMF_STRING,
138 ngx_string("tcUrl"),
139 v.tc_url, sizeof(v.tc_url) },
140
141 { NGX_RTMP_AMF_NUMBER,
142 ngx_string("audioCodecs"),
143 &v.acodecs, sizeof(v.acodecs) },
144
145 { NGX_RTMP_AMF_NUMBER,
146 ngx_string("videoCodecs"),
147 &v.vcodecs, sizeof(v.vcodecs) },
148
149 { NGX_RTMP_AMF_STRING,
150 ngx_string("pageUrl"),
151 v.page_url, sizeof(v.page_url) },
152
153 { NGX_RTMP_AMF_NUMBER,
154 ngx_string("objectEncoding"),
155 &v.object_encoding, 0},
156 };
157
158 static ngx_rtmp_amf_elt_t in_elts[] = {
159
160 { NGX_RTMP_AMF_NUMBER,
161 ngx_null_string,
162 &v.trans, 0 },
163
164 { NGX_RTMP_AMF_OBJECT,
165 ngx_null_string,
166 in_cmd, sizeof(in_cmd) },
167 };
168
169 ngx_memzero(&v, sizeof(v));
170 if (ngx_rtmp_receive_amf(s, in, in_elts,
171 sizeof(in_elts) / sizeof(in_elts[0])))
172 {
173 return NGX_ERROR;
174 }
175
176 len = ngx_strlen(v.app);
177 if (len > 10 && !ngx_memcmp(v.app + len - 10, "/_definst_", 10)) {
178 v.app[len - 10] = 0;
179 } else if (len && v.app[len - 1] == '/') {
180 v.app[len - 1] = 0;
181 }
182
183 ngx_rtmp_cmd_fill_args(v.app, v.args);
184
185 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
186 "connect: app='%s' args='%s' flashver='%s' swf_url='%s' "
187 "tc_url='%s' page_url='%s' acodecs=%uD vcodecs=%uD "
188 "object_encoding=%ui",
189 v.app, v.args, v.flashver, v.swf_url, v.tc_url, v.page_url,
190 (uint32_t)v.acodecs, (uint32_t)v.vcodecs,
191 (ngx_int_t)v.object_encoding);
192
193 return ngx_rtmp_connect(s, &v);
194 }
195
196
197 static ngx_int_t
ngx_rtmp_cmd_connect(ngx_rtmp_session_t * s,ngx_rtmp_connect_t * v)198 ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s, ngx_rtmp_connect_t *v)
199 {
200 ngx_rtmp_core_srv_conf_t *cscf;
201 ngx_rtmp_core_app_conf_t **cacfp;
202 ngx_uint_t n;
203 ngx_rtmp_header_t h;
204 u_char *p;
205
206 static double trans;
207 static double capabilities = NGX_RTMP_CAPABILITIES;
208 static double object_encoding = 0;
209
210 static ngx_rtmp_amf_elt_t out_obj[] = {
211
212 { NGX_RTMP_AMF_STRING,
213 ngx_string("fmsVer"),
214 NGX_RTMP_FMS_VERSION, 0 },
215
216 { NGX_RTMP_AMF_NUMBER,
217 ngx_string("capabilities"),
218 &capabilities, 0 },
219 };
220
221 static ngx_rtmp_amf_elt_t out_inf[] = {
222
223 { NGX_RTMP_AMF_STRING,
224 ngx_string("level"),
225 "status", 0 },
226
227 { NGX_RTMP_AMF_STRING,
228 ngx_string("code"),
229 "NetConnection.Connect.Success", 0 },
230
231 { NGX_RTMP_AMF_STRING,
232 ngx_string("description"),
233 "Connection succeeded.", 0 },
234
235 { NGX_RTMP_AMF_NUMBER,
236 ngx_string("objectEncoding"),
237 &object_encoding, 0 }
238 };
239
240 static ngx_rtmp_amf_elt_t out_elts[] = {
241
242 { NGX_RTMP_AMF_STRING,
243 ngx_null_string,
244 "_result", 0 },
245
246 { NGX_RTMP_AMF_NUMBER,
247 ngx_null_string,
248 &trans, 0 },
249
250 { NGX_RTMP_AMF_OBJECT,
251 ngx_null_string,
252 out_obj, sizeof(out_obj) },
253
254 { NGX_RTMP_AMF_OBJECT,
255 ngx_null_string,
256 out_inf, sizeof(out_inf) },
257 };
258
259 if (s->connected) {
260 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
261 "connect: duplicate connection");
262 return NGX_ERROR;
263 }
264
265 cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
266
267 trans = v->trans;
268
269 /* fill session parameters */
270 s->connected = 1;
271
272 ngx_memzero(&h, sizeof(h));
273 h.csid = NGX_RTMP_CSID_AMF_INI;
274 h.type = NGX_RTMP_MSG_AMF_CMD;
275
276
277 #define NGX_RTMP_SET_STRPAR(name) \
278 s->name.len = ngx_strlen(v->name); \
279 s->name.data = ngx_palloc(s->connection->pool, s->name.len); \
280 ngx_memcpy(s->name.data, v->name, s->name.len)
281
282 NGX_RTMP_SET_STRPAR(app);
283 NGX_RTMP_SET_STRPAR(args);
284 NGX_RTMP_SET_STRPAR(flashver);
285 NGX_RTMP_SET_STRPAR(swf_url);
286 NGX_RTMP_SET_STRPAR(tc_url);
287 NGX_RTMP_SET_STRPAR(page_url);
288
289 #undef NGX_RTMP_SET_STRPAR
290
291 p = ngx_strlchr(s->app.data, s->app.data + s->app.len, '?');
292 if (p) {
293 s->app.len = (p - s->app.data);
294 }
295
296 s->acodecs = (uint32_t) v->acodecs;
297 s->vcodecs = (uint32_t) v->vcodecs;
298
299 /* find application & set app_conf */
300 cacfp = cscf->applications.elts;
301 for(n = 0; n < cscf->applications.nelts; ++n, ++cacfp) {
302 if ((*cacfp)->name.len == s->app.len &&
303 ngx_strncmp((*cacfp)->name.data, s->app.data, s->app.len) == 0)
304 {
305 /* found app! */
306 s->app_conf = (*cacfp)->app_conf;
307 break;
308 }
309 }
310
311 if (s->app_conf == NULL) {
312 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
313 "connect: application not found: '%V'", &s->app);
314 return NGX_ERROR;
315 }
316
317 object_encoding = v->object_encoding;
318
319 return ngx_rtmp_send_ack_size(s, cscf->ack_window) != NGX_OK ||
320 ngx_rtmp_send_bandwidth(s, cscf->ack_window,
321 NGX_RTMP_LIMIT_DYNAMIC) != NGX_OK ||
322 ngx_rtmp_send_chunk_size(s, cscf->chunk_size) != NGX_OK ||
323 ngx_rtmp_send_amf(s, &h, out_elts,
324 sizeof(out_elts) / sizeof(out_elts[0]))
325 != NGX_OK ? NGX_ERROR : NGX_OK;
326 }
327
328
329 static ngx_int_t
ngx_rtmp_cmd_create_stream_init(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)330 ngx_rtmp_cmd_create_stream_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
331 ngx_chain_t *in)
332 {
333 static ngx_rtmp_create_stream_t v;
334
335 static ngx_rtmp_amf_elt_t in_elts[] = {
336
337 { NGX_RTMP_AMF_NUMBER,
338 ngx_null_string,
339 &v.trans, sizeof(v.trans) },
340 };
341
342 if (ngx_rtmp_receive_amf(s, in, in_elts,
343 sizeof(in_elts) / sizeof(in_elts[0])))
344 {
345 return NGX_ERROR;
346 }
347
348 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "createStream");
349
350 return ngx_rtmp_create_stream(s, &v);
351 }
352
353
354 static ngx_int_t
ngx_rtmp_cmd_create_stream(ngx_rtmp_session_t * s,ngx_rtmp_create_stream_t * v)355 ngx_rtmp_cmd_create_stream(ngx_rtmp_session_t *s, ngx_rtmp_create_stream_t *v)
356 {
357 /* support one message stream per connection */
358 static double stream;
359 static double trans;
360 ngx_rtmp_header_t h;
361
362 static ngx_rtmp_amf_elt_t out_elts[] = {
363
364 { NGX_RTMP_AMF_STRING,
365 ngx_null_string,
366 "_result", 0 },
367
368 { NGX_RTMP_AMF_NUMBER,
369 ngx_null_string,
370 &trans, 0 },
371
372 { NGX_RTMP_AMF_NULL,
373 ngx_null_string,
374 NULL, 0 },
375
376 { NGX_RTMP_AMF_NUMBER,
377 ngx_null_string,
378 &stream, sizeof(stream) },
379 };
380
381 trans = v->trans;
382 stream = NGX_RTMP_MSID;
383
384 ngx_memzero(&h, sizeof(h));
385
386 h.csid = NGX_RTMP_CSID_AMF_INI;
387 h.type = NGX_RTMP_MSG_AMF_CMD;
388
389 return ngx_rtmp_send_amf(s, &h, out_elts,
390 sizeof(out_elts) / sizeof(out_elts[0])) == NGX_OK ?
391 NGX_DONE : NGX_ERROR;
392 }
393
394
395 static ngx_int_t
ngx_rtmp_cmd_close_stream_init(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)396 ngx_rtmp_cmd_close_stream_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
397 ngx_chain_t *in)
398 {
399 static ngx_rtmp_close_stream_t v;
400
401 static ngx_rtmp_amf_elt_t in_elts[] = {
402
403 { NGX_RTMP_AMF_NUMBER,
404 ngx_null_string,
405 &v.stream, 0 },
406 };
407
408 if (ngx_rtmp_receive_amf(s, in, in_elts,
409 sizeof(in_elts) / sizeof(in_elts[0])))
410 {
411 return NGX_ERROR;
412 }
413
414 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "closeStream");
415
416 return ngx_rtmp_close_stream(s, &v);
417 }
418
419
420 static ngx_int_t
ngx_rtmp_cmd_close_stream(ngx_rtmp_session_t * s,ngx_rtmp_close_stream_t * v)421 ngx_rtmp_cmd_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v)
422 {
423 return NGX_OK;
424 }
425
426
427 static ngx_int_t
ngx_rtmp_cmd_delete_stream_init(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)428 ngx_rtmp_cmd_delete_stream_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
429 ngx_chain_t *in)
430 {
431 static ngx_rtmp_delete_stream_t v;
432
433 static ngx_rtmp_amf_elt_t in_elts[] = {
434
435 { NGX_RTMP_AMF_NUMBER,
436 ngx_null_string,
437 NULL, 0 },
438
439 { NGX_RTMP_AMF_NULL,
440 ngx_null_string,
441 NULL, 0 },
442
443 { NGX_RTMP_AMF_NUMBER,
444 ngx_null_string,
445 &v.stream, 0 },
446 };
447
448 if (ngx_rtmp_receive_amf(s, in, in_elts,
449 sizeof(in_elts) / sizeof(in_elts[0])))
450 {
451 return NGX_ERROR;
452 }
453
454 return ngx_rtmp_delete_stream(s, &v);
455 }
456
457
458 static ngx_int_t
ngx_rtmp_cmd_delete_stream(ngx_rtmp_session_t * s,ngx_rtmp_delete_stream_t * v)459 ngx_rtmp_cmd_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t *v)
460 {
461 ngx_rtmp_close_stream_t cv;
462
463 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "deleteStream");
464
465 cv.stream = 0;
466
467 return ngx_rtmp_close_stream(s, &cv);
468 }
469
470
471 static ngx_int_t
ngx_rtmp_cmd_publish_init(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)472 ngx_rtmp_cmd_publish_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
473 ngx_chain_t *in)
474 {
475 static ngx_rtmp_publish_t v;
476
477 static ngx_rtmp_amf_elt_t in_elts[] = {
478
479 /* transaction is always 0 */
480 { NGX_RTMP_AMF_NUMBER,
481 ngx_null_string,
482 NULL, 0 },
483
484 { NGX_RTMP_AMF_NULL,
485 ngx_null_string,
486 NULL, 0 },
487
488 { NGX_RTMP_AMF_STRING,
489 ngx_null_string,
490 &v.name, sizeof(v.name) },
491
492 { NGX_RTMP_AMF_OPTIONAL | NGX_RTMP_AMF_STRING,
493 ngx_null_string,
494 &v.type, sizeof(v.type) },
495 };
496
497 ngx_memzero(&v, sizeof(v));
498
499 if (ngx_rtmp_receive_amf(s, in, in_elts,
500 sizeof(in_elts) / sizeof(in_elts[0])))
501 {
502 return NGX_ERROR;
503 }
504
505 ngx_rtmp_cmd_fill_args(v.name, v.args);
506
507 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
508 "publish: name='%s' args='%s' type=%s silent=%d",
509 v.name, v.args, v.type, v.silent);
510
511 return ngx_rtmp_publish(s, &v);
512 }
513
514
515 static ngx_int_t
ngx_rtmp_cmd_publish(ngx_rtmp_session_t * s,ngx_rtmp_publish_t * v)516 ngx_rtmp_cmd_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
517 {
518 return NGX_OK;
519 }
520
521 static ngx_int_t
ngx_rtmp_cmd_play_init(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)522 ngx_rtmp_cmd_play_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
523 ngx_chain_t *in)
524 {
525 static ngx_rtmp_play_t v;
526
527 static ngx_rtmp_amf_elt_t in_elts[] = {
528
529 /* transaction is always 0 */
530 { NGX_RTMP_AMF_NUMBER,
531 ngx_null_string,
532 NULL, 0 },
533
534 { NGX_RTMP_AMF_NULL,
535 ngx_null_string,
536 NULL, 0 },
537
538 { NGX_RTMP_AMF_STRING,
539 ngx_null_string,
540 &v.name, sizeof(v.name) },
541
542 { NGX_RTMP_AMF_OPTIONAL | NGX_RTMP_AMF_NUMBER,
543 ngx_null_string,
544 &v.start, 0 },
545
546 { NGX_RTMP_AMF_OPTIONAL | NGX_RTMP_AMF_NUMBER,
547 ngx_null_string,
548 &v.duration, 0 },
549
550 { NGX_RTMP_AMF_OPTIONAL | NGX_RTMP_AMF_BOOLEAN,
551 ngx_null_string,
552 &v.reset, 0 }
553 };
554
555 ngx_memzero(&v, sizeof(v));
556
557 if (ngx_rtmp_receive_amf(s, in, in_elts,
558 sizeof(in_elts) / sizeof(in_elts[0])))
559 {
560 return NGX_ERROR;
561 }
562
563 ngx_rtmp_cmd_fill_args(v.name, v.args);
564
565 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
566 "play: name='%s' args='%s' start=%i duration=%i "
567 "reset=%i silent=%i",
568 v.name, v.args, (ngx_int_t) v.start,
569 (ngx_int_t) v.duration, (ngx_int_t) v.reset,
570 (ngx_int_t) v.silent);
571
572 return ngx_rtmp_play(s, &v);
573 }
574
575
576 static ngx_int_t
ngx_rtmp_cmd_play(ngx_rtmp_session_t * s,ngx_rtmp_play_t * v)577 ngx_rtmp_cmd_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v)
578 {
579 ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0,
580 "cmd: ngx_rtmp_cmd_play");
581 return NGX_OK;
582 }
583
584
585 static ngx_int_t
ngx_rtmp_cmd_play2_init(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)586 ngx_rtmp_cmd_play2_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
587 ngx_chain_t *in)
588 {
589 static ngx_rtmp_play_t v;
590 static ngx_rtmp_close_stream_t vc;
591
592 static ngx_rtmp_amf_elt_t in_obj[] = {
593
594 { NGX_RTMP_AMF_NUMBER,
595 ngx_string("start"),
596 &v.start, 0 },
597
598 { NGX_RTMP_AMF_STRING,
599 ngx_string("streamName"),
600 &v.name, sizeof(v.name) },
601 };
602
603 static ngx_rtmp_amf_elt_t in_elts[] = {
604
605 /* transaction is always 0 */
606 { NGX_RTMP_AMF_NUMBER,
607 ngx_null_string,
608 NULL, 0 },
609
610 { NGX_RTMP_AMF_NULL,
611 ngx_null_string,
612 NULL, 0 },
613
614 { NGX_RTMP_AMF_OBJECT,
615 ngx_null_string,
616 &in_obj, sizeof(in_obj) }
617 };
618
619 ngx_memzero(&v, sizeof(v));
620
621 if (ngx_rtmp_receive_amf(s, in, in_elts,
622 sizeof(in_elts) / sizeof(in_elts[0])))
623 {
624 return NGX_ERROR;
625 }
626
627 ngx_rtmp_cmd_fill_args(v.name, v.args);
628
629 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
630 "play2: name='%s' args='%s' start=%i",
631 v.name, v.args, (ngx_int_t) v.start);
632
633 /* continue from current timestamp */
634
635 if (v.start < 0) {
636 v.start = s->current_time;
637 }
638
639 ngx_memzero(&vc, sizeof(vc));
640
641 /* close_stream should be synchronous */
642 ngx_rtmp_close_stream(s, &vc);
643
644 return ngx_rtmp_play(s, &v);
645 }
646
647
648 static ngx_int_t
ngx_rtmp_cmd_pause_init(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)649 ngx_rtmp_cmd_pause_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
650 ngx_chain_t *in)
651 {
652 static ngx_rtmp_pause_t v;
653
654 static ngx_rtmp_amf_elt_t in_elts[] = {
655
656 { NGX_RTMP_AMF_NUMBER,
657 ngx_null_string,
658 NULL, 0 },
659
660 { NGX_RTMP_AMF_NULL,
661 ngx_null_string,
662 NULL, 0 },
663
664 { NGX_RTMP_AMF_BOOLEAN,
665 ngx_null_string,
666 &v.pause, 0 },
667
668 { NGX_RTMP_AMF_NUMBER,
669 ngx_null_string,
670 &v.position, 0 },
671 };
672
673 ngx_memzero(&v, sizeof(v));
674
675 if (ngx_rtmp_receive_amf(s, in, in_elts,
676 sizeof(in_elts) / sizeof(in_elts[0])))
677 {
678 return NGX_ERROR;
679 }
680
681 ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
682 "pause: pause=%i position=%i",
683 (ngx_int_t) v.pause, (ngx_int_t) v.position);
684
685 return ngx_rtmp_pause(s, &v);
686 }
687
688
689 static ngx_int_t
ngx_rtmp_cmd_pause(ngx_rtmp_session_t * s,ngx_rtmp_pause_t * v)690 ngx_rtmp_cmd_pause(ngx_rtmp_session_t *s, ngx_rtmp_pause_t *v)
691 {
692 return NGX_OK;
693 }
694
695
696 static ngx_int_t
ngx_rtmp_cmd_disconnect_init(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)697 ngx_rtmp_cmd_disconnect_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
698 ngx_chain_t *in)
699 {
700 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "disconnect");
701
702 return ngx_rtmp_disconnect(s);
703 }
704
705
706 static ngx_int_t
ngx_rtmp_cmd_disconnect(ngx_rtmp_session_t * s)707 ngx_rtmp_cmd_disconnect(ngx_rtmp_session_t *s)
708 {
709 return ngx_rtmp_delete_stream(s, NULL);
710 }
711
712
713 static ngx_int_t
ngx_rtmp_cmd_seek_init(ngx_rtmp_session_t * s,ngx_rtmp_header_t * h,ngx_chain_t * in)714 ngx_rtmp_cmd_seek_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
715 ngx_chain_t *in)
716 {
717 static ngx_rtmp_seek_t v;
718
719 static ngx_rtmp_amf_elt_t in_elts[] = {
720
721 /* transaction is always 0 */
722 { NGX_RTMP_AMF_NUMBER,
723 ngx_null_string,
724 NULL, 0 },
725
726 { NGX_RTMP_AMF_NULL,
727 ngx_null_string,
728 NULL, 0 },
729
730 { NGX_RTMP_AMF_NUMBER,
731 ngx_null_string,
732 &v.offset, sizeof(v.offset) },
733 };
734
735 ngx_memzero(&v, sizeof(v));
736
737 if (ngx_rtmp_receive_amf(s, in, in_elts,
738 sizeof(in_elts) / sizeof(in_elts[0])))
739 {
740 return NGX_ERROR;
741 }
742
743 ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
744 "seek: offset=%i", (ngx_int_t) v.offset);
745
746 return ngx_rtmp_seek(s, &v);
747 }
748
749
750 static ngx_int_t
ngx_rtmp_cmd_seek(ngx_rtmp_session_t * s,ngx_rtmp_seek_t * v)751 ngx_rtmp_cmd_seek(ngx_rtmp_session_t *s, ngx_rtmp_seek_t *v)
752 {
753 return NGX_OK;
754 }
755
756
757 static ngx_int_t
ngx_rtmp_cmd_stream_begin(ngx_rtmp_session_t * s,ngx_rtmp_stream_begin_t * v)758 ngx_rtmp_cmd_stream_begin(ngx_rtmp_session_t *s, ngx_rtmp_stream_begin_t *v)
759 {
760 return NGX_OK;
761 }
762
763
764 static ngx_int_t
ngx_rtmp_cmd_stream_eof(ngx_rtmp_session_t * s,ngx_rtmp_stream_eof_t * v)765 ngx_rtmp_cmd_stream_eof(ngx_rtmp_session_t *s, ngx_rtmp_stream_eof_t *v)
766 {
767 return NGX_OK;
768 }
769
770
771 static ngx_int_t
ngx_rtmp_cmd_stream_dry(ngx_rtmp_session_t * s,ngx_rtmp_stream_dry_t * v)772 ngx_rtmp_cmd_stream_dry(ngx_rtmp_session_t *s, ngx_rtmp_stream_dry_t *v)
773 {
774 return NGX_OK;
775 }
776
777
778 static ngx_int_t
ngx_rtmp_cmd_recorded(ngx_rtmp_session_t * s,ngx_rtmp_recorded_t * v)779 ngx_rtmp_cmd_recorded(ngx_rtmp_session_t *s,
780 ngx_rtmp_recorded_t *v)
781 {
782 return NGX_OK;
783 }
784
785
786 static ngx_int_t
ngx_rtmp_cmd_set_buflen(ngx_rtmp_session_t * s,ngx_rtmp_set_buflen_t * v)787 ngx_rtmp_cmd_set_buflen(ngx_rtmp_session_t *s, ngx_rtmp_set_buflen_t *v)
788 {
789 return NGX_OK;
790 }
791
792
793 static ngx_int_t
ngx_rtmp_cmd_playlist(ngx_rtmp_session_t * s,ngx_rtmp_playlist_t * v)794 ngx_rtmp_cmd_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v)
795 {
796 return NGX_OK;
797 }
798
799
800
801 static ngx_rtmp_amf_handler_t ngx_rtmp_cmd_map[] = {
802 { ngx_string("connect"), ngx_rtmp_cmd_connect_init },
803 { ngx_string("createStream"), ngx_rtmp_cmd_create_stream_init },
804 { ngx_string("closeStream"), ngx_rtmp_cmd_close_stream_init },
805 { ngx_string("deleteStream"), ngx_rtmp_cmd_delete_stream_init },
806 { ngx_string("publish"), ngx_rtmp_cmd_publish_init },
807 { ngx_string("play"), ngx_rtmp_cmd_play_init },
808 { ngx_string("play2"), ngx_rtmp_cmd_play2_init },
809 { ngx_string("seek"), ngx_rtmp_cmd_seek_init },
810 { ngx_string("pause"), ngx_rtmp_cmd_pause_init },
811 { ngx_string("pauseraw"), ngx_rtmp_cmd_pause_init },
812 };
813
814
815 static ngx_int_t
ngx_rtmp_cmd_postconfiguration(ngx_conf_t * cf)816 ngx_rtmp_cmd_postconfiguration(ngx_conf_t *cf)
817 {
818 ngx_rtmp_core_main_conf_t *cmcf;
819 ngx_rtmp_handler_pt *h;
820 ngx_rtmp_amf_handler_t *ch, *bh;
821 size_t n, ncalls;
822
823 cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module);
824
825 /* redirect disconnects to deleteStream
826 * to free client modules from registering
827 * disconnect callback */
828
829 h = ngx_array_push(&cmcf->events[NGX_RTMP_DISCONNECT]);
830 if (h == NULL) {
831 return NGX_ERROR;
832 }
833
834 *h = ngx_rtmp_cmd_disconnect_init;
835
836 /* register AMF callbacks */
837
838 ncalls = sizeof(ngx_rtmp_cmd_map) / sizeof(ngx_rtmp_cmd_map[0]);
839
840 ch = ngx_array_push_n(&cmcf->amf, ncalls);
841 if (ch == NULL) {
842 return NGX_ERROR;
843 }
844
845 bh = ngx_rtmp_cmd_map;
846
847 for(n = 0; n < ncalls; ++n, ++ch, ++bh) {
848 *ch = *bh;
849 }
850
851 ngx_rtmp_connect = ngx_rtmp_cmd_connect;
852 ngx_rtmp_disconnect = ngx_rtmp_cmd_disconnect;
853 ngx_rtmp_create_stream = ngx_rtmp_cmd_create_stream;
854 ngx_rtmp_close_stream = ngx_rtmp_cmd_close_stream;
855 ngx_rtmp_delete_stream = ngx_rtmp_cmd_delete_stream;
856 ngx_rtmp_publish = ngx_rtmp_cmd_publish;
857 ngx_rtmp_play = ngx_rtmp_cmd_play;
858 ngx_rtmp_seek = ngx_rtmp_cmd_seek;
859 ngx_rtmp_pause = ngx_rtmp_cmd_pause;
860
861 ngx_rtmp_stream_begin = ngx_rtmp_cmd_stream_begin;
862 ngx_rtmp_stream_eof = ngx_rtmp_cmd_stream_eof;
863 ngx_rtmp_stream_dry = ngx_rtmp_cmd_stream_dry;
864 ngx_rtmp_recorded = ngx_rtmp_cmd_recorded;
865 ngx_rtmp_set_buflen = ngx_rtmp_cmd_set_buflen;
866
867 ngx_rtmp_playlist = ngx_rtmp_cmd_playlist;
868
869 return NGX_OK;
870 }
871