169 lines
6.5 KiB
C++
169 lines
6.5 KiB
C++
#ifndef _rtmp_url_h_
|
|
#define _rtmp_url_h_
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "urlcodec.h"
|
|
#include "uri-parse.h"
|
|
|
|
struct rtmp_url_t
|
|
{
|
|
unsigned short port; // default 1935
|
|
char* scheme;
|
|
char* host;
|
|
char* app;
|
|
char* stream;
|
|
char* tcurl;
|
|
char* vhost; // rtmp://host/vhost/app/stream or rtmp://host/app/stream?vhost=xxx
|
|
char __ptr[1024];
|
|
};
|
|
|
|
static int rtmp_url_parse(const char* url, struct rtmp_url_t* u)
|
|
{
|
|
int r, n;
|
|
size_t i, j;
|
|
char** v[3];
|
|
const char* p[4];
|
|
const char* pend[4];
|
|
struct uri_t* uri;
|
|
struct uri_query_t* q;
|
|
static const int PORT = 1935;
|
|
|
|
uri = uri_parse(url, (int)strlen(url));
|
|
if (!uri) return -1;
|
|
|
|
n = 0;
|
|
memset(u, 0, sizeof(*u));
|
|
u->port = (unsigned short)(0 == uri->port ? PORT : uri->port);
|
|
|
|
u->host = u->__ptr + n;
|
|
r = snprintf(u->host, sizeof(u->__ptr) - n, "%s", uri->host);
|
|
if(r <= 0 || (size_t)r >= sizeof(u->__ptr) - n)
|
|
goto FAILED_PARSE_RTMP_URL;
|
|
n += r + 1;
|
|
u->vhost = u->host; // default value
|
|
|
|
u->scheme = u->__ptr + n;
|
|
r = snprintf(u->scheme, sizeof(u->__ptr) - n, "%s", uri->scheme);
|
|
if(r <= 0 || (size_t)r >= sizeof(u->__ptr) - n)
|
|
goto FAILED_PARSE_RTMP_URL;
|
|
n += r + 1;
|
|
|
|
// parse vhost
|
|
if (uri->query && *uri->query)
|
|
{
|
|
q = NULL;
|
|
for(r = uri_query(uri->query, uri->query + strlen(uri->query), &q) - 1; r >= 0; r--)
|
|
{
|
|
if (5 == q[r].n_name && 0 == strncmp("vhost", q[r].name, 5))
|
|
{
|
|
u->vhost = u->__ptr + n;
|
|
r = snprintf(u->vhost, sizeof(u->__ptr) - n, "%.*s", q[r].n_value, q[r].value);
|
|
if (r <= 0 || (size_t)r >= sizeof(u->__ptr) - n)
|
|
{
|
|
uri_query_free(&q);
|
|
goto FAILED_PARSE_RTMP_URL;
|
|
}
|
|
n += r + 1;
|
|
break;
|
|
}
|
|
}
|
|
uri_query_free(&q);
|
|
}
|
|
uri_free(uri);
|
|
|
|
p[0] = strstr(url, "://");
|
|
p[0] = p[0] ? p[0] + 3 : url;
|
|
p[0] = strchr(p[0], '/');
|
|
if (!p[0]) return -1;
|
|
|
|
for (i = 0; i+1 < sizeof(p)/sizeof(p[0]) && p[i]; i++)
|
|
{
|
|
// filter '/' (one ore more)
|
|
while ('/' == *p[i]) p[i]++;
|
|
pend[i] = strchr(p[i], '/');
|
|
p[i+1] = pend[i];
|
|
}
|
|
|
|
if (i < 2 || i > 3)
|
|
return -1; // invalid rtmp url
|
|
|
|
j = 0;
|
|
if (i > 2)
|
|
v[j++] = &u->vhost;
|
|
v[j++] = &u->app;
|
|
v[j++] = &u->stream;
|
|
for(j = 0; j < i; j++)
|
|
{
|
|
*v[j] = u->__ptr + n;
|
|
r = url_decode(p[j], pend[j] ? (int)(pend[j] - p[j]) : -1, *v[j], sizeof(u->__ptr) - n);
|
|
if (r <= 0 || (size_t)r >= sizeof(u->__ptr) - n)
|
|
return -1;
|
|
n += r + 1;
|
|
}
|
|
|
|
u->tcurl = u->__ptr + n;
|
|
if(PORT != u->port)
|
|
r = snprintf(u->tcurl, sizeof(u->__ptr) - n, "rtmp://%s:%d/%s", u->vhost, u->port, u->app);
|
|
else
|
|
r = snprintf(u->tcurl, sizeof(u->__ptr) - n, "rtmp://%s/%s", u->vhost, u->app);
|
|
if(r <= 0 || (size_t)r >= sizeof(u->__ptr) - n)
|
|
return -1;
|
|
n += r + 1;
|
|
|
|
return 0;
|
|
|
|
FAILED_PARSE_RTMP_URL:
|
|
uri_free(uri);
|
|
return -1;
|
|
}
|
|
|
|
#if !defined(NDEBUG)
|
|
static inline void rtmp_url_parse_test(void)
|
|
{
|
|
struct rtmp_url_t rtmp;
|
|
assert(0 == rtmp_url_parse("rtmp://www.abc.com/app/stream", &rtmp));
|
|
assert(0 == strcmp(rtmp.tcurl, "rtmp://www.abc.com/app") && 0 == strcmp(rtmp.app, "app") && 0 == strcmp(rtmp.stream, "stream"));
|
|
assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "www.abc.com") && 1935 == rtmp.port && 0 == strcmp(rtmp.vhost, "www.abc.com"));
|
|
|
|
// stream name with parameter(s)
|
|
assert(0 == rtmp_url_parse("rtmp://w.a.c/a/s?param1=value1¶m2=value2", &rtmp));
|
|
assert(0 == strcmp(rtmp.tcurl, "rtmp://w.a.c/a") && 0 == strcmp(rtmp.app, "a") && 0 == strcmp(rtmp.stream, "s?param1=value1¶m2=value2"));
|
|
assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "w.a.c") && 1935 == rtmp.port && 0 == strcmp(rtmp.vhost, "w.a.c"));
|
|
|
|
// tcurl with param
|
|
assert(0 == rtmp_url_parse("rtmp://192.168.1.100:1936/www.abc.com/app?param1=value1¶m2=value2/stream?param1=value1¶m2=value2", &rtmp));
|
|
assert(0 == strcmp(rtmp.tcurl, "rtmp://www.abc.com:1936/app?param1=value1¶m2=value2") && 0 == strcmp(rtmp.app, "app?param1=value1¶m2=value2") && 0 == strcmp(rtmp.stream, "stream?param1=value1¶m2=value2"));
|
|
assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "192.168.1.100") && 1936 == rtmp.port && 0 == strcmp(rtmp.vhost, "www.abc.com"));
|
|
|
|
// obs tcurl with '/'
|
|
assert(0 == rtmp_url_parse("rtmp://www.abc.com:1936//app//stream", &rtmp));
|
|
assert(0 == strcmp(rtmp.tcurl, "rtmp://www.abc.com:1936/app") && 0 == strcmp(rtmp.app, "app") && 0 == strcmp(rtmp.stream, "stream"));
|
|
assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "www.abc.com") && 1936 == rtmp.port && 0 == strcmp(rtmp.vhost, "www.abc.com"));
|
|
|
|
// host + vhost
|
|
assert(0 == rtmp_url_parse("rtmp://192.168.1.100:1936/www.abc.com/app//stream", &rtmp));
|
|
assert(0 == strcmp(rtmp.tcurl, "rtmp://www.abc.com:1936/app") && 0 == strcmp(rtmp.app, "app") && 0 == strcmp(rtmp.stream, "stream"));
|
|
assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "192.168.1.100") && 1936 == rtmp.port && 0 == strcmp(rtmp.vhost, "www.abc.com"));
|
|
|
|
// vhost parameter
|
|
assert(0 == rtmp_url_parse("rtmp://192.168.1.100/app//stream?param1=value1&vhost=www.abc.com¶m2=value2", &rtmp));
|
|
assert(0 == strcmp(rtmp.tcurl, "rtmp://www.abc.com/app") && 0 == strcmp(rtmp.app, "app") && 0 == strcmp(rtmp.stream, "stream?param1=value1&vhost=www.abc.com¶m2=value2"));
|
|
assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "192.168.1.100") && 1935 == rtmp.port && 0 == strcmp(rtmp.vhost, "www.abc.com"));
|
|
|
|
// bad case: vhost + parameter
|
|
assert(0 == rtmp_url_parse("rtmp://192.168.1.100/www.abc.com/app//stream?param1=value1&vhost=www.google.com¶m2=value2", &rtmp));
|
|
assert(0 == strcmp(rtmp.tcurl, "rtmp://www.abc.com/app") && 0 == strcmp(rtmp.app, "app") && 0 == strcmp(rtmp.stream, "stream?param1=value1&vhost=www.google.com¶m2=value2"));
|
|
assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "192.168.1.100") && 1935 == rtmp.port && 0 == strcmp(rtmp.vhost, "www.abc.com"));
|
|
|
|
// bad case: no stream
|
|
assert(-1 == rtmp_url_parse("rtmp://192.168.1.100///stream?param1=value1&vhost=www.abc.com¶m2=value2", &rtmp));
|
|
|
|
// bad case: no path
|
|
assert(-1 == rtmp_url_parse("rtmp://192.168.1.100/", &rtmp));
|
|
}
|
|
#endif
|
|
|
|
#endif /* !_rtmp_url_h_ */
|