nginx-mod-rtmp/ngx_rtmp_app.c
2012-03-23 16:03:32 +04:00

220 lines
7.2 KiB
C

/*
* Copyright (c) 2012 Roman Arutyunyan
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_rtmp.h"
#define NGX_RTMP_FMS_VERSION "FMS/3,0,1,123"
#define NGX_RTMP_CAPABILITIES 31
ngx_int_t
ngx_rtmp_connect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_core_srv_conf_t *cscf;
ngx_uint_t n;
ngx_rtmp_core_app_conf_t **cacfp;
size_t len;
static double trans;
static double capabilities = NGX_RTMP_CAPABILITIES;
static struct {
u_char app[1024];
u_char flashver[1024];
u_char swf_url[1024];
u_char tc_url[1024];
double acodecs;
double vcodecs;
u_char page_url[1024];
} v;
static ngx_rtmp_amf0_elt_t in_cmd[] = {
{ NGX_RTMP_AMF0_STRING, "app", v.app, sizeof(v.app) },
{ NGX_RTMP_AMF0_STRING, "flashver", v.flashver, sizeof(v.flashver) },
{ NGX_RTMP_AMF0_STRING, "swfUrl", v.swf_url, sizeof(v.swf_url) },
{ NGX_RTMP_AMF0_STRING, "tcUrl", v.tc_url, sizeof(v.tc_url) },
{ NGX_RTMP_AMF0_NUMBER, "audioCodecs", &v.acodecs, sizeof(v.acodecs) },
{ NGX_RTMP_AMF0_NUMBER, "videoCodecs", &v.vcodecs, sizeof(v.vcodecs) },
{ NGX_RTMP_AMF0_STRING, "pageUrl", v.page_url, sizeof(v.page_url) },
};
static ngx_rtmp_amf0_elt_t in_elts[] = {
{ NGX_RTMP_AMF0_NUMBER, 0, &trans, 0 },
{ NGX_RTMP_AMF0_OBJECT, NULL, in_cmd, sizeof(in_cmd) },
};
static ngx_rtmp_amf0_elt_t out_obj[] = {
{ NGX_RTMP_AMF0_STRING, "fmsVer", NGX_RTMP_FMS_VERSION, 0 },
{ NGX_RTMP_AMF0_NUMBER, "capabilities", &capabilities, 0 },
};
static ngx_rtmp_amf0_elt_t out_inf[] = {
{ NGX_RTMP_AMF0_STRING, "level", "status", 0 },
{ NGX_RTMP_AMF0_STRING, "code", "NetConnection.Connect.Success", 0 },
{ NGX_RTMP_AMF0_STRING, "description", "Connection succeeded.", 0 },
};
static ngx_rtmp_amf0_elt_t out_elts[] = {
{ NGX_RTMP_AMF0_STRING, NULL, "_result", 0 },
{ NGX_RTMP_AMF0_NUMBER, NULL, &trans, 0 },
{ NGX_RTMP_AMF0_OBJECT, NULL, out_obj, sizeof(out_obj) },
{ NGX_RTMP_AMF0_OBJECT, NULL, out_inf, sizeof(out_inf) },
};
if (s->connected) {
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "duplicate connection");
return NGX_ERROR;
}
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
/* parse input */
ngx_memzero(&v, sizeof(v));
if (ngx_rtmp_receive_amf0(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
ngx_log_debug7(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"connect: app='%s' flashver='%s' swf_url='%s' "
"tc_url='%s' page_url='%s' acodecs=%uD vcodecs=%uD",
v.app, v.flashver, v.swf_url, v.tc_url, v.page_url,
(uint32_t)v.acodecs, (uint32_t)v.vcodecs);
/* fill session parameters */
s->connected = 1;
#define NGX_RTMP_SET_STRPAR(name) \
s->name.len = ngx_strlen(v.name); \
s->name.data = ngx_palloc(s->connection->pool, s->name.len); \
ngx_memcpy(s->name.data, v.name, s->name.len)
NGX_RTMP_SET_STRPAR(app);
NGX_RTMP_SET_STRPAR(flashver);
NGX_RTMP_SET_STRPAR(swf_url);
NGX_RTMP_SET_STRPAR(tc_url);
NGX_RTMP_SET_STRPAR(page_url);
#undef NGX_RTMP_SET_STRPAR
s->acodecs = v.acodecs;
s->vcodecs = v.vcodecs;
/* find application & set app_conf */
len = ngx_strlen(v.app);
cacfp = cscf->applications.elts;
for(n = 0; n < cscf->applications.nelts; ++n, ++cacfp) {
if ((*cacfp)->name.len == len
&& !ngx_strncmp((*cacfp)->name.data, v.app, len))
{
/* found app! */
s->app_conf = (*cacfp)->app_conf;
break;
}
}
if (s->app_conf == NULL) {
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"application not found: '%s'", v.app);
return NGX_ERROR;
}
/* send all replies */
return ngx_rtmp_send_ack_size(s, cscf->ack_window)
|| ngx_rtmp_send_bandwidth(s, cscf->ack_window, NGX_RTMP_LIMIT_DYNAMIC)
|| ngx_rtmp_send_user_stream_begin(s, 0)
|| ngx_rtmp_send_chunk_size(s, cscf->chunk_size)
|| ngx_rtmp_send_amf0(s, h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]))
? NGX_ERROR
: NGX_OK;
/* we need NGX_OK not NGX_DONE to make access module
* work after connect has successfully finished */
}
ngx_int_t
ngx_rtmp_create_stream(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
/* support one message stream per connection */
static double stream = 1;
static double trans;
static ngx_rtmp_amf0_elt_t in_elts[] = {
{ NGX_RTMP_AMF0_NUMBER, 0, &trans, sizeof(trans) },
};
static ngx_rtmp_amf0_elt_t out_elts[] = {
{ NGX_RTMP_AMF0_STRING, NULL, "_result", 0 },
{ NGX_RTMP_AMF0_NUMBER, NULL, &trans, 0 },
{ NGX_RTMP_AMF0_NULL , NULL, NULL, 0 },
{ NGX_RTMP_AMF0_NUMBER, NULL, &stream, sizeof(stream) },
};
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"createStream");
/* parse input */
if (ngx_rtmp_receive_amf0(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
/* send result with standard stream */
return ngx_rtmp_send_amf0(s, h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0])) == NGX_OK
? NGX_DONE
: NGX_ERROR;
}
ngx_int_t
ngx_rtmp_amf0_default(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_header_t sh;
static double trans;
static ngx_rtmp_amf0_elt_t in_elts[] = {
{ NGX_RTMP_AMF0_NUMBER, 0, &trans, sizeof(trans) },
};
static ngx_rtmp_amf0_elt_t out_elts[] = {
{ NGX_RTMP_AMF0_STRING, NULL, "_result", 0 },
{ NGX_RTMP_AMF0_NUMBER, NULL, &trans, 0 },
{ NGX_RTMP_AMF0_NULL , NULL, NULL, 0 },
{ NGX_RTMP_AMF0_NULL , NULL, NULL, 0 },
};
/* parse input */
if (ngx_rtmp_receive_amf0(s, in, in_elts,
sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
memset(&sh, 0, sizeof(sh));
sh.csid = h->csid;
sh.type = NGX_RTMP_MSG_AMF0_CMD;
sh.msid = 0;
/* send simple _result */
return ngx_rtmp_send_amf0(s, &sh, out_elts,
sizeof(out_elts) / sizeof(out_elts[0])) == NGX_OK
? NGX_DONE
: NGX_ERROR;
}