RTSP: add support for Quicktime tunneled RTSP
Add support for tunneling RTSP over HTTP. Fix documentation some more. See also #573173. API: RTSP:gst_rtsp_connection_is_tunneled() API: RTSP:gst_rtsp_connection_set_tunneled()
This commit is contained in:
parent
40db590e71
commit
fbc4f2d4fe
@ -1210,14 +1210,30 @@ gst_rtsp_connection_accept
|
|||||||
gst_rtsp_connection_connect
|
gst_rtsp_connection_connect
|
||||||
gst_rtsp_connection_close
|
gst_rtsp_connection_close
|
||||||
gst_rtsp_connection_free
|
gst_rtsp_connection_free
|
||||||
|
|
||||||
gst_rtsp_connection_read
|
gst_rtsp_connection_read
|
||||||
gst_rtsp_connection_write
|
gst_rtsp_connection_write
|
||||||
|
gst_rtsp_connection_poll
|
||||||
|
|
||||||
gst_rtsp_connection_send
|
gst_rtsp_connection_send
|
||||||
gst_rtsp_connection_receive
|
gst_rtsp_connection_receive
|
||||||
|
|
||||||
gst_rtsp_connection_next_timeout
|
gst_rtsp_connection_next_timeout
|
||||||
gst_rtsp_connection_reset_timeout
|
gst_rtsp_connection_reset_timeout
|
||||||
|
|
||||||
gst_rtsp_connection_flush
|
gst_rtsp_connection_flush
|
||||||
|
|
||||||
gst_rtsp_connection_set_auth
|
gst_rtsp_connection_set_auth
|
||||||
|
gst_rtsp_connection_set_auth_param
|
||||||
|
gst_rtsp_connection_clear_auth_params
|
||||||
|
|
||||||
|
gst_rtsp_connection_set_qos_dscp
|
||||||
|
|
||||||
|
gst_rtsp_connection_get_ip
|
||||||
|
gst_rtsp_connection_get_url
|
||||||
|
|
||||||
|
gst_rtsp_connection_set_tunneled
|
||||||
|
gst_rtsp_connection_is_tunneled
|
||||||
|
|
||||||
GstRTSPWatch
|
GstRTSPWatch
|
||||||
GstRTSPWatchFuncs
|
GstRTSPWatchFuncs
|
||||||
|
@ -95,6 +95,12 @@
|
|||||||
#include "gstrtspbase64.h"
|
#include "gstrtspbase64.h"
|
||||||
#include "md5.h"
|
#include "md5.h"
|
||||||
|
|
||||||
|
static GstRTSPResult read_line (gint fd, guint8 * buffer, guint * idx,
|
||||||
|
guint size);
|
||||||
|
static GstRTSPResult parse_key_value (guint8 * buffer, gchar * key,
|
||||||
|
guint keysize, gchar ** value);
|
||||||
|
static void parse_string (gchar * dest, gint size, gchar ** src);
|
||||||
|
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
#define READ_SOCKET(fd, buf, len) recv (fd, (char *)buf, len, 0)
|
#define READ_SOCKET(fd, buf, len) recv (fd, (char *)buf, len, 0)
|
||||||
#define WRITE_SOCKET(fd, buf, len) send (fd, (const char *)buf, len, 0)
|
#define WRITE_SOCKET(fd, buf, len) send (fd, (const char *)buf, len, 0)
|
||||||
@ -115,20 +121,20 @@
|
|||||||
#define ERRNO_IS_EINPROGRESS (errno == EINPROGRESS)
|
#define ERRNO_IS_EINPROGRESS (errno == EINPROGRESS)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ADD_POLLFD(fdset, pfd, fd) \
|
#define ADD_POLLFD(fdset, pfd, fd) \
|
||||||
G_STMT_START { \
|
G_STMT_START { \
|
||||||
pfd.fd = fd; \
|
(pfd)->fd = fd; \
|
||||||
gst_poll_add_fd (fdset, &pfd); \
|
gst_poll_add_fd (fdset, pfd); \
|
||||||
} G_STMT_END
|
} G_STMT_END
|
||||||
|
|
||||||
#define REMOVE_POLLFD(fdset, pfd) \
|
#define REMOVE_POLLFD(fdset, pfd) \
|
||||||
G_STMT_START { \
|
G_STMT_START { \
|
||||||
if (pfd.fd != -1) { \
|
if ((pfd)->fd != -1) { \
|
||||||
GST_DEBUG ("remove fd %d", pfd.fd); \
|
GST_DEBUG ("remove fd %d", (pfd)->fd); \
|
||||||
gst_poll_remove_fd (fdset, &pfd); \
|
gst_poll_remove_fd (fdset, pfd); \
|
||||||
CLOSE_SOCKET (pfd.fd); \
|
CLOSE_SOCKET ((pfd)->fd); \
|
||||||
pfd.fd = -1; \
|
(pfd)->fd = -1; \
|
||||||
} \
|
} \
|
||||||
} G_STMT_END
|
} G_STMT_END
|
||||||
|
|
||||||
struct _GstRTSPConnection
|
struct _GstRTSPConnection
|
||||||
@ -144,6 +150,8 @@ struct _GstRTSPConnection
|
|||||||
GstPollFD *readfd;
|
GstPollFD *readfd;
|
||||||
GstPollFD *writefd;
|
GstPollFD *writefd;
|
||||||
|
|
||||||
|
gboolean tunneled;
|
||||||
|
|
||||||
GstPoll *fdset;
|
GstPoll *fdset;
|
||||||
gchar *ip;
|
gchar *ip;
|
||||||
|
|
||||||
@ -247,6 +255,7 @@ gst_rtsp_connection_create (GstRTSPUrl * url, GstRTSPConnection ** conn)
|
|||||||
newconn->fd1.fd = -1;
|
newconn->fd1.fd = -1;
|
||||||
newconn->timer = g_timer_new ();
|
newconn->timer = g_timer_new ();
|
||||||
newconn->timeout = 60;
|
newconn->timeout = 60;
|
||||||
|
newconn->tunneled = FALSE;
|
||||||
|
|
||||||
newconn->auth_method = GST_RTSP_AUTH_NONE;
|
newconn->auth_method = GST_RTSP_AUTH_NONE;
|
||||||
newconn->username = NULL;
|
newconn->username = NULL;
|
||||||
@ -331,7 +340,7 @@ gst_rtsp_connection_accept (gint sock, GstRTSPConnection ** conn)
|
|||||||
|
|
||||||
/* now create the connection object */
|
/* now create the connection object */
|
||||||
gst_rtsp_connection_create (url, &newconn);
|
gst_rtsp_connection_create (url, &newconn);
|
||||||
ADD_POLLFD (newconn->fdset, newconn->fd0, fd);
|
ADD_POLLFD (newconn->fdset, &newconn->fd0, fd);
|
||||||
|
|
||||||
newconn->readfd = &newconn->fd0;
|
newconn->readfd = &newconn->fd0;
|
||||||
newconn->writefd = &newconn->fd0;
|
newconn->writefd = &newconn->fd0;
|
||||||
@ -347,6 +356,331 @@ accept_failed:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const gchar *
|
||||||
|
do_resolve (const gchar * host)
|
||||||
|
{
|
||||||
|
struct hostent *hostinfo;
|
||||||
|
struct in_addr addr;
|
||||||
|
const gchar *ip;
|
||||||
|
#ifdef G_OS_WIN32
|
||||||
|
struct in_addr *addrp;
|
||||||
|
#else
|
||||||
|
char **addrs;
|
||||||
|
gchar ipbuf[INET_ADDRSTRLEN];
|
||||||
|
#endif /* G_OS_WIN32 */
|
||||||
|
|
||||||
|
ip = NULL;
|
||||||
|
|
||||||
|
/* first check if it already is an IP address */
|
||||||
|
if (inet_aton (host, &addr)) {
|
||||||
|
ip = host;
|
||||||
|
} else {
|
||||||
|
hostinfo = gethostbyname (host);
|
||||||
|
if (!hostinfo)
|
||||||
|
goto not_resolved; /* h_errno set */
|
||||||
|
|
||||||
|
if (hostinfo->h_addrtype != AF_INET)
|
||||||
|
goto not_ip; /* host not an IP host */
|
||||||
|
#ifdef G_OS_WIN32
|
||||||
|
addrp = (struct in_addr *) hostinfo->h_addr_list[0];
|
||||||
|
/* this is not threadsafe */
|
||||||
|
ip = inet_ntoa (*addrp);
|
||||||
|
#else
|
||||||
|
addrs = hostinfo->h_addr_list;
|
||||||
|
ip = inet_ntop (AF_INET, (struct in_addr *) addrs[0], ipbuf,
|
||||||
|
sizeof (ipbuf));
|
||||||
|
#endif /* G_OS_WIN32 */
|
||||||
|
}
|
||||||
|
return ip;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
not_resolved:
|
||||||
|
{
|
||||||
|
GST_ERROR ("could not resolve %s", host);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
not_ip:
|
||||||
|
{
|
||||||
|
GST_ERROR ("not an IP address");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstRTSPResult
|
||||||
|
do_connect (const gchar * ip, guint16 port, GstPollFD * fdout,
|
||||||
|
GstPoll * fdset, GTimeVal * timeout)
|
||||||
|
{
|
||||||
|
gint fd;
|
||||||
|
struct sockaddr_in sa_in;
|
||||||
|
gint ret;
|
||||||
|
#ifdef G_OS_WIN32
|
||||||
|
unsigned long flags = 1;
|
||||||
|
#endif /* G_OS_WIN32 */
|
||||||
|
GstClockTime to;
|
||||||
|
gint retval;
|
||||||
|
|
||||||
|
g_message ("connect %s:%u", ip, port);
|
||||||
|
|
||||||
|
memset (&sa_in, 0, sizeof (sa_in));
|
||||||
|
sa_in.sin_family = AF_INET; /* network socket */
|
||||||
|
sa_in.sin_port = htons (port); /* on port */
|
||||||
|
sa_in.sin_addr.s_addr = inet_addr (ip); /* on host ip */
|
||||||
|
|
||||||
|
fd = socket (AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (fd == -1)
|
||||||
|
goto no_socket;
|
||||||
|
|
||||||
|
/* set to non-blocking mode so that we can cancel the connect */
|
||||||
|
#ifndef G_OS_WIN32
|
||||||
|
fcntl (fd, F_SETFL, O_NONBLOCK);
|
||||||
|
#else
|
||||||
|
ioctlsocket (fd, FIONBIO, &flags);
|
||||||
|
#endif /* G_OS_WIN32 */
|
||||||
|
|
||||||
|
/* add the socket to our fdset */
|
||||||
|
ADD_POLLFD (fdset, fdout, fd);
|
||||||
|
|
||||||
|
/* we are going to connect ASYNC now */
|
||||||
|
ret = connect (fd, (struct sockaddr *) &sa_in, sizeof (sa_in));
|
||||||
|
if (ret == 0)
|
||||||
|
goto done;
|
||||||
|
if (!ERRNO_IS_EINPROGRESS)
|
||||||
|
goto sys_error;
|
||||||
|
|
||||||
|
/* wait for connect to complete up to the specified timeout or until we got
|
||||||
|
* interrupted. */
|
||||||
|
gst_poll_fd_ctl_write (fdset, fdout, TRUE);
|
||||||
|
|
||||||
|
to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
|
do {
|
||||||
|
retval = gst_poll_wait (fdset, to);
|
||||||
|
} while (retval == -1 && (errno == EINTR || errno == EAGAIN));
|
||||||
|
|
||||||
|
if (retval == 0)
|
||||||
|
goto timeout;
|
||||||
|
else if (retval == -1)
|
||||||
|
goto sys_error;
|
||||||
|
|
||||||
|
/* we can still have an error connecting on windows */
|
||||||
|
if (gst_poll_fd_has_error (fdset, fdout)) {
|
||||||
|
socklen_t len = sizeof (errno);
|
||||||
|
#ifndef G_OS_WIN32
|
||||||
|
getsockopt (fd, SOL_SOCKET, SO_ERROR, &errno, &len);
|
||||||
|
#else
|
||||||
|
getsockopt (fd, SOL_SOCKET, SO_ERROR, (char *) &errno, &len);
|
||||||
|
#endif
|
||||||
|
goto sys_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_poll_fd_ignored (fdset, fdout);
|
||||||
|
done:
|
||||||
|
|
||||||
|
return GST_RTSP_OK;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
no_socket:
|
||||||
|
{
|
||||||
|
GST_ERROR ("no socket %d (%s)", errno, g_strerror (errno));
|
||||||
|
return GST_RTSP_ESYS;
|
||||||
|
}
|
||||||
|
sys_error:
|
||||||
|
{
|
||||||
|
GST_ERROR ("system error %d (%s)", errno, g_strerror (errno));
|
||||||
|
REMOVE_POLLFD (fdset, fdout);
|
||||||
|
return GST_RTSP_ESYS;
|
||||||
|
}
|
||||||
|
timeout:
|
||||||
|
{
|
||||||
|
GST_ERROR ("timeout");
|
||||||
|
REMOVE_POLLFD (fdset, fdout);
|
||||||
|
return GST_RTSP_ETIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstRTSPResult
|
||||||
|
setup_tunneling (GstRTSPConnection * conn, GTimeVal * timeout)
|
||||||
|
{
|
||||||
|
gchar sessionid[24];
|
||||||
|
gint i;
|
||||||
|
GstRTSPResult res;
|
||||||
|
gchar *str;
|
||||||
|
guint idx, line;
|
||||||
|
gint retval;
|
||||||
|
GstClockTime to;
|
||||||
|
const gchar *ip;
|
||||||
|
guint16 port;
|
||||||
|
gchar codestr[4];
|
||||||
|
gint code;
|
||||||
|
|
||||||
|
/* create a random sessionid */
|
||||||
|
for (i = 0; i < 24; i++)
|
||||||
|
sessionid[i] = g_random_int_range ('a', 'z');
|
||||||
|
sessionid[23] = '\0';
|
||||||
|
|
||||||
|
/* */
|
||||||
|
str = g_strdup_printf ("GET %s HTTP/1.0\r\n"
|
||||||
|
"x-sessioncookie: %s\r\n"
|
||||||
|
"Accept: application/x-rtsp-tunnelled\r\n"
|
||||||
|
"Pragma: no-cache\r\n"
|
||||||
|
"Cache-Control: no-cache\r\n" "\r\n", conn->url->host, sessionid);
|
||||||
|
|
||||||
|
/* we start by writing to this fd */
|
||||||
|
conn->writefd = &conn->fd0;
|
||||||
|
|
||||||
|
res = gst_rtsp_connection_write (conn, (guint8 *) str, strlen (str), timeout);
|
||||||
|
g_free (str);
|
||||||
|
if (res != GST_RTSP_OK)
|
||||||
|
goto write_failed;
|
||||||
|
|
||||||
|
gst_poll_fd_ctl_write (conn->fdset, &conn->fd0, FALSE);
|
||||||
|
gst_poll_fd_ctl_read (conn->fdset, &conn->fd0, TRUE);
|
||||||
|
|
||||||
|
to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
|
line = 0;
|
||||||
|
while (TRUE) {
|
||||||
|
guint8 buffer[4096];
|
||||||
|
|
||||||
|
idx = 0;
|
||||||
|
while (TRUE) {
|
||||||
|
res = read_line (conn->fd0.fd, buffer, &idx, sizeof (buffer));
|
||||||
|
if (res == GST_RTSP_EEOF)
|
||||||
|
goto eof;
|
||||||
|
if (res == GST_RTSP_OK)
|
||||||
|
break;
|
||||||
|
if (res != GST_RTSP_EINTR)
|
||||||
|
goto read_error;
|
||||||
|
|
||||||
|
do {
|
||||||
|
retval = gst_poll_wait (conn->fdset, to);
|
||||||
|
} while (retval == -1 && (errno == EINTR || errno == EAGAIN));
|
||||||
|
|
||||||
|
/* check for timeout */
|
||||||
|
if (retval == 0)
|
||||||
|
goto timeout;
|
||||||
|
|
||||||
|
if (retval == -1) {
|
||||||
|
if (errno == EBUSY)
|
||||||
|
goto stopped;
|
||||||
|
else
|
||||||
|
goto select_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for last line */
|
||||||
|
if (buffer[0] == '\r')
|
||||||
|
buffer[0] = '\0';
|
||||||
|
if (buffer[0] == '\0')
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (line == 0) {
|
||||||
|
/* first line, parse response */
|
||||||
|
gchar versionstr[20];
|
||||||
|
gchar *bptr;
|
||||||
|
|
||||||
|
bptr = (gchar *) buffer;
|
||||||
|
|
||||||
|
parse_string (versionstr, sizeof (versionstr), &bptr);
|
||||||
|
parse_string (codestr, sizeof (codestr), &bptr);
|
||||||
|
code = atoi (codestr);
|
||||||
|
|
||||||
|
if (code != 200)
|
||||||
|
goto wrong_result;
|
||||||
|
} else {
|
||||||
|
gchar key[32];
|
||||||
|
gchar *value;
|
||||||
|
|
||||||
|
/* other lines, parse key/value */
|
||||||
|
res = parse_key_value (buffer, key, sizeof (key), &value);
|
||||||
|
if (res == GST_RTSP_OK) {
|
||||||
|
/* we got a new ip address */
|
||||||
|
if (g_ascii_strcasecmp (key, "x-server-ip-address") == 0) {
|
||||||
|
g_free (conn->ip);
|
||||||
|
conn->ip = g_strdup (value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(ip = do_resolve (conn->ip)))
|
||||||
|
goto not_resolved;
|
||||||
|
|
||||||
|
/* get the port from the url */
|
||||||
|
gst_rtsp_url_get_port (conn->url, &port);
|
||||||
|
|
||||||
|
/* connect to the host/port */
|
||||||
|
res = do_connect (ip, port, &conn->fd1, conn->fdset, timeout);
|
||||||
|
if (res != GST_RTSP_OK)
|
||||||
|
goto connect_failed;
|
||||||
|
|
||||||
|
/* this is now our writing socket */
|
||||||
|
conn->writefd = &conn->fd1;
|
||||||
|
|
||||||
|
/* */
|
||||||
|
str = g_strdup_printf ("POST %s HTTP/1.0\r\n"
|
||||||
|
"x-sessioncookie: %s\r\n"
|
||||||
|
"Content-Type: application/x-rtsp-tunnelled\r\n"
|
||||||
|
"Pragma: no-cache\r\n"
|
||||||
|
"Cache-Control: no-cache\r\n"
|
||||||
|
"Content-Length: 32767\r\n"
|
||||||
|
"Expires: Sun, 9 Jan 1972 00:00:00 GMT\r\n"
|
||||||
|
"\r\n", conn->url->host, sessionid);
|
||||||
|
|
||||||
|
/* we start by writing to this fd */
|
||||||
|
conn->writefd = &conn->fd1;
|
||||||
|
|
||||||
|
res = gst_rtsp_connection_write (conn, (guint8 *) str, strlen (str), timeout);
|
||||||
|
g_free (str);
|
||||||
|
if (res != GST_RTSP_OK)
|
||||||
|
goto write_failed;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
write_failed:
|
||||||
|
{
|
||||||
|
GST_ERROR ("write failed", res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
eof:
|
||||||
|
{
|
||||||
|
return GST_RTSP_EEOF;
|
||||||
|
}
|
||||||
|
read_error:
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
timeout:
|
||||||
|
{
|
||||||
|
return GST_RTSP_ETIMEOUT;
|
||||||
|
}
|
||||||
|
select_error:
|
||||||
|
{
|
||||||
|
return GST_RTSP_ESYS;
|
||||||
|
}
|
||||||
|
stopped:
|
||||||
|
{
|
||||||
|
return GST_RTSP_EINTR;
|
||||||
|
}
|
||||||
|
wrong_result:
|
||||||
|
{
|
||||||
|
GST_ERROR ("got failure response %d %s", code, codestr);
|
||||||
|
return GST_RTSP_ERROR;
|
||||||
|
}
|
||||||
|
not_resolved:
|
||||||
|
{
|
||||||
|
GST_ERROR ("could not resolve %s", conn->ip);
|
||||||
|
return GST_RTSP_ENET;
|
||||||
|
}
|
||||||
|
connect_failed:
|
||||||
|
{
|
||||||
|
GST_ERROR ("failed to connect");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gst_rtsp_connection_connect:
|
* gst_rtsp_connection_connect:
|
||||||
* @conn: a #GstRTSPConnection
|
* @conn: a #GstRTSPConnection
|
||||||
@ -364,23 +698,12 @@ accept_failed:
|
|||||||
GstRTSPResult
|
GstRTSPResult
|
||||||
gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
|
gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
|
||||||
{
|
{
|
||||||
gint fd;
|
GstRTSPResult res;
|
||||||
struct sockaddr_in sa_in;
|
|
||||||
struct hostent *hostinfo;
|
|
||||||
const gchar *ip;
|
const gchar *ip;
|
||||||
struct in_addr addr;
|
|
||||||
gint ret;
|
|
||||||
guint16 port;
|
guint16 port;
|
||||||
GstRTSPUrl *url;
|
GstRTSPUrl *url;
|
||||||
GstClockTime to;
|
|
||||||
gint retval;
|
|
||||||
|
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
unsigned long flags = 1;
|
unsigned long flags = 1;
|
||||||
struct in_addr *addrp;
|
|
||||||
#else
|
|
||||||
char **addrs;
|
|
||||||
gchar ipbuf[INET_ADDRSTRLEN];
|
|
||||||
#endif /* G_OS_WIN32 */
|
#endif /* G_OS_WIN32 */
|
||||||
|
|
||||||
g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
|
g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
|
||||||
@ -389,116 +712,47 @@ gst_rtsp_connection_connect (GstRTSPConnection * conn, GTimeVal * timeout)
|
|||||||
|
|
||||||
url = conn->url;
|
url = conn->url;
|
||||||
|
|
||||||
/* first check if it already is an IP address */
|
if (!(ip = do_resolve (url->host)))
|
||||||
if (inet_aton (url->host, &addr)) {
|
goto not_resolved;
|
||||||
ip = url->host;
|
|
||||||
} else {
|
|
||||||
hostinfo = gethostbyname (url->host);
|
|
||||||
if (!hostinfo)
|
|
||||||
goto not_resolved; /* h_errno set */
|
|
||||||
|
|
||||||
if (hostinfo->h_addrtype != AF_INET)
|
|
||||||
goto not_ip; /* host not an IP host */
|
|
||||||
#ifdef G_OS_WIN32
|
|
||||||
addrp = (struct in_addr *) hostinfo->h_addr_list[0];
|
|
||||||
/* this is not threadsafe */
|
|
||||||
ip = inet_ntoa (*addrp);
|
|
||||||
#else
|
|
||||||
addrs = hostinfo->h_addr_list;
|
|
||||||
ip = inet_ntop (AF_INET, (struct in_addr *) addrs[0], ipbuf,
|
|
||||||
sizeof (ipbuf));
|
|
||||||
#endif /* G_OS_WIN32 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get the port from the url */
|
/* get the port from the url */
|
||||||
gst_rtsp_url_get_port (url, &port);
|
gst_rtsp_url_get_port (url, &port);
|
||||||
|
|
||||||
memset (&sa_in, 0, sizeof (sa_in));
|
/* connect to the host/port */
|
||||||
sa_in.sin_family = AF_INET; /* network socket */
|
res = do_connect (ip, port, &conn->fd0, conn->fdset, timeout);
|
||||||
sa_in.sin_port = htons (port); /* on port */
|
if (res != GST_RTSP_OK)
|
||||||
sa_in.sin_addr.s_addr = inet_addr (ip); /* on host ip */
|
goto connect_failed;
|
||||||
|
|
||||||
fd = socket (AF_INET, SOCK_STREAM, 0);
|
g_free (conn->ip);
|
||||||
if (fd == -1)
|
|
||||||
goto sys_error;
|
|
||||||
|
|
||||||
/* set to non-blocking mode so that we can cancel the connect */
|
|
||||||
#ifndef G_OS_WIN32
|
|
||||||
fcntl (fd, F_SETFL, O_NONBLOCK);
|
|
||||||
#else
|
|
||||||
ioctlsocket (fd, FIONBIO, &flags);
|
|
||||||
#endif /* G_OS_WIN32 */
|
|
||||||
|
|
||||||
/* add the socket to our fdset */
|
|
||||||
ADD_POLLFD (conn->fdset, conn->fd0, fd);
|
|
||||||
|
|
||||||
conn->readfd = &conn->fd0;
|
|
||||||
conn->writefd = &conn->fd0;
|
|
||||||
|
|
||||||
/* we are going to connect ASYNC now */
|
|
||||||
ret = connect (fd, (struct sockaddr *) &sa_in, sizeof (sa_in));
|
|
||||||
if (ret == 0)
|
|
||||||
goto done;
|
|
||||||
if (!ERRNO_IS_EINPROGRESS)
|
|
||||||
goto sys_error;
|
|
||||||
|
|
||||||
/* wait for connect to complete up to the specified timeout or until we got
|
|
||||||
* interrupted. */
|
|
||||||
gst_poll_fd_ctl_write (conn->fdset, conn->writefd, TRUE);
|
|
||||||
|
|
||||||
to = timeout ? GST_TIMEVAL_TO_TIME (*timeout) : GST_CLOCK_TIME_NONE;
|
|
||||||
|
|
||||||
do {
|
|
||||||
retval = gst_poll_wait (conn->fdset, to);
|
|
||||||
} while (retval == -1 && (errno == EINTR || errno == EAGAIN));
|
|
||||||
|
|
||||||
if (retval == 0)
|
|
||||||
goto timeout;
|
|
||||||
else if (retval == -1)
|
|
||||||
goto sys_error;
|
|
||||||
|
|
||||||
/* we can still have an error connecting on windows */
|
|
||||||
if (gst_poll_fd_has_error (conn->fdset, conn->writefd)) {
|
|
||||||
socklen_t len = sizeof (errno);
|
|
||||||
#ifndef G_OS_WIN32
|
|
||||||
getsockopt (fd, SOL_SOCKET, SO_ERROR, &errno, &len);
|
|
||||||
#else
|
|
||||||
getsockopt (fd, SOL_SOCKET, SO_ERROR, (char *) &errno, &len);
|
|
||||||
#endif
|
|
||||||
goto sys_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_poll_fd_ignored (conn->fdset, conn->writefd);
|
|
||||||
gst_poll_fd_ignored (conn->fdset, conn->readfd);
|
|
||||||
|
|
||||||
done:
|
|
||||||
conn->ip = g_strdup (ip);
|
conn->ip = g_strdup (ip);
|
||||||
|
|
||||||
|
/* this is our read URL */
|
||||||
|
conn->readfd = &conn->fd0;
|
||||||
|
|
||||||
|
if (conn->tunneled) {
|
||||||
|
res = setup_tunneling (conn, timeout);
|
||||||
|
if (res != GST_RTSP_OK)
|
||||||
|
goto tunneling_failed;
|
||||||
|
} else {
|
||||||
|
conn->writefd = &conn->fd0;
|
||||||
|
}
|
||||||
|
|
||||||
return GST_RTSP_OK;
|
return GST_RTSP_OK;
|
||||||
|
|
||||||
sys_error:
|
|
||||||
{
|
|
||||||
GST_ERROR ("system error %d (%s)", errno, g_strerror (errno));
|
|
||||||
REMOVE_POLLFD (conn->fdset, conn->fd0);
|
|
||||||
REMOVE_POLLFD (conn->fdset, conn->fd1);
|
|
||||||
return GST_RTSP_ESYS;
|
|
||||||
}
|
|
||||||
not_resolved:
|
not_resolved:
|
||||||
{
|
{
|
||||||
GST_ERROR ("could not resolve %s", url->host);
|
GST_ERROR ("could not resolve %s", url->host);
|
||||||
return GST_RTSP_ENET;
|
return GST_RTSP_ENET;
|
||||||
}
|
}
|
||||||
not_ip:
|
connect_failed:
|
||||||
{
|
{
|
||||||
GST_ERROR ("not an IP address");
|
GST_ERROR ("failed to connect");
|
||||||
return GST_RTSP_ENOTIP;
|
return res;
|
||||||
}
|
}
|
||||||
timeout:
|
tunneling_failed:
|
||||||
{
|
{
|
||||||
GST_ERROR ("timeout");
|
GST_ERROR ("failed to setup tunneling");
|
||||||
REMOVE_POLLFD (conn->fdset, conn->fd0);
|
return res;
|
||||||
REMOVE_POLLFD (conn->fdset, conn->fd1);
|
|
||||||
return GST_RTSP_ETIMEOUT;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -922,20 +1176,31 @@ GstRTSPResult
|
|||||||
gst_rtsp_connection_send (GstRTSPConnection * conn, GstRTSPMessage * message,
|
gst_rtsp_connection_send (GstRTSPConnection * conn, GstRTSPMessage * message,
|
||||||
GTimeVal * timeout)
|
GTimeVal * timeout)
|
||||||
{
|
{
|
||||||
GString *str = NULL;
|
GString *string = NULL;
|
||||||
GstRTSPResult res;
|
GstRTSPResult res;
|
||||||
|
gchar *str;
|
||||||
|
gsize len;
|
||||||
|
|
||||||
g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
|
g_return_val_if_fail (conn != NULL, GST_RTSP_EINVAL);
|
||||||
g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
|
g_return_val_if_fail (message != NULL, GST_RTSP_EINVAL);
|
||||||
|
|
||||||
if (!(str = message_to_string (conn, message)))
|
if (!(string = message_to_string (conn, message)))
|
||||||
goto no_message;
|
goto no_message;
|
||||||
|
|
||||||
/* write request */
|
if (conn->tunneled) {
|
||||||
res =
|
str = g_base64_encode ((const guchar *) string->str, string->len);
|
||||||
gst_rtsp_connection_write (conn, (guint8 *) str->str, str->len, timeout);
|
g_string_free (string, TRUE);
|
||||||
|
len = strlen (str);
|
||||||
|
} else {
|
||||||
|
str = string->str;
|
||||||
|
len = string->len;
|
||||||
|
g_string_free (string, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
g_string_free (str, TRUE);
|
/* write request */
|
||||||
|
res = gst_rtsp_connection_write (conn, (guint8 *) str, len, timeout);
|
||||||
|
|
||||||
|
g_free (str);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
@ -1056,38 +1321,59 @@ parse_request_line (guint8 * buffer, GstRTSPMessage * msg)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* parsing lines means reading a Key: Value pair */
|
|
||||||
static GstRTSPResult
|
static GstRTSPResult
|
||||||
parse_line (guint8 * buffer, GstRTSPMessage * msg)
|
parse_key_value (guint8 * buffer, gchar * key, guint keysize, gchar ** value)
|
||||||
{
|
{
|
||||||
gchar key[32];
|
|
||||||
gchar *bptr;
|
gchar *bptr;
|
||||||
GstRTSPHeaderField field;
|
|
||||||
|
|
||||||
bptr = (gchar *) buffer;
|
bptr = (gchar *) buffer;
|
||||||
|
|
||||||
/* read key */
|
/* read key */
|
||||||
parse_key (key, sizeof (key), &bptr);
|
parse_key (key, keysize, &bptr);
|
||||||
if (*bptr != ':')
|
if (*bptr != ':')
|
||||||
goto no_column;
|
goto no_column;
|
||||||
|
|
||||||
bptr++;
|
bptr++;
|
||||||
|
while (g_ascii_isspace (*bptr))
|
||||||
|
bptr++;
|
||||||
|
|
||||||
field = gst_rtsp_find_header_field (key);
|
*value = bptr;
|
||||||
if (field != GST_RTSP_HDR_INVALID) {
|
|
||||||
while (g_ascii_isspace (*bptr))
|
|
||||||
bptr++;
|
|
||||||
gst_rtsp_message_add_header (msg, field, bptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GST_RTSP_OK;
|
return GST_RTSP_OK;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
no_column:
|
no_column:
|
||||||
{
|
{
|
||||||
return GST_RTSP_EPARSE;
|
return GST_RTSP_EPARSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* parsing lines means reading a Key: Value pair */
|
||||||
|
static GstRTSPResult
|
||||||
|
parse_line (guint8 * buffer, GstRTSPMessage * msg)
|
||||||
|
{
|
||||||
|
GstRTSPResult res;
|
||||||
|
gchar key[32];
|
||||||
|
gchar *value;
|
||||||
|
GstRTSPHeaderField field;
|
||||||
|
|
||||||
|
res = parse_key_value (buffer, key, sizeof (key), &value);
|
||||||
|
if (res != GST_RTSP_OK)
|
||||||
|
goto parse_error;
|
||||||
|
|
||||||
|
field = gst_rtsp_find_header_field (key);
|
||||||
|
if (field != GST_RTSP_HDR_INVALID)
|
||||||
|
gst_rtsp_message_add_header (msg, field, value);
|
||||||
|
|
||||||
|
return GST_RTSP_OK;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
parse_error:
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* returns:
|
/* returns:
|
||||||
* GST_RTSP_OK when a complete message was read.
|
* GST_RTSP_OK when a complete message was read.
|
||||||
* GST_RTSP_EEOF: when the socket is closed
|
* GST_RTSP_EEOF: when the socket is closed
|
||||||
@ -1456,8 +1742,8 @@ gst_rtsp_connection_close (GstRTSPConnection * conn)
|
|||||||
g_free (conn->ip);
|
g_free (conn->ip);
|
||||||
conn->ip = NULL;
|
conn->ip = NULL;
|
||||||
|
|
||||||
REMOVE_POLLFD (conn->fdset, conn->fd0);
|
REMOVE_POLLFD (conn->fdset, &conn->fd0);
|
||||||
REMOVE_POLLFD (conn->fdset, conn->fd1);
|
REMOVE_POLLFD (conn->fdset, &conn->fd1);
|
||||||
conn->writefd = NULL;
|
conn->writefd = NULL;
|
||||||
conn->readfd = NULL;
|
conn->readfd = NULL;
|
||||||
|
|
||||||
@ -1902,6 +2188,45 @@ gst_rtsp_connection_get_ip (const GstRTSPConnection * conn)
|
|||||||
return conn->ip;
|
return conn->ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_rtsp_connection_set_tunneled:
|
||||||
|
* @conn: a #GstRTSPConnection
|
||||||
|
* @tunneled: the new state
|
||||||
|
*
|
||||||
|
* Set the HTTP tunneling state of the connection. This must be configured before
|
||||||
|
* the @conn is connected.
|
||||||
|
*
|
||||||
|
* Since: 0.10.23
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_rtsp_connection_set_tunneled (GstRTSPConnection * conn, gboolean tunneled)
|
||||||
|
{
|
||||||
|
g_return_if_fail (conn != NULL);
|
||||||
|
g_return_if_fail (conn->readfd == NULL);
|
||||||
|
g_return_if_fail (conn->writefd == NULL);
|
||||||
|
|
||||||
|
conn->tunneled = tunneled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_rtsp_connection_is_tunneled:
|
||||||
|
* @conn: a #GstRTSPConnection
|
||||||
|
*
|
||||||
|
* Get the tunneling state of the connection.
|
||||||
|
*
|
||||||
|
* Returns: if @conn is using HTTP tunneling.
|
||||||
|
*
|
||||||
|
* Since: 0.10.23
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
gst_rtsp_connection_is_tunneled (GstRTSPConnection * conn)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (conn != NULL, FALSE);
|
||||||
|
|
||||||
|
return conn->tunneled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR)
|
#define READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR)
|
||||||
#define WRITE_COND (G_IO_OUT | G_IO_ERR)
|
#define WRITE_COND (G_IO_OUT | G_IO_ERR)
|
||||||
|
|
||||||
@ -2080,6 +2405,7 @@ static GSourceFuncs gst_rtsp_source_funcs = {
|
|||||||
* @conn: a #GstRTSPConnection
|
* @conn: a #GstRTSPConnection
|
||||||
* @funcs: watch functions
|
* @funcs: watch functions
|
||||||
* @user_data: user data to pass to @funcs
|
* @user_data: user data to pass to @funcs
|
||||||
|
* @notify: notify when @user_data is not referenced anymore
|
||||||
*
|
*
|
||||||
* Create a watch object for @conn. The functions provided in @funcs will be
|
* Create a watch object for @conn. The functions provided in @funcs will be
|
||||||
* called with @user_data when activity happened on the watch.
|
* called with @user_data when activity happened on the watch.
|
||||||
@ -2151,7 +2477,7 @@ gst_rtsp_watch_attach (GstRTSPWatch * watch, GMainContext * context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gst_rtsp_watch_free:
|
* gst_rtsp_watch_unref:
|
||||||
* @watch: a #GstRTSPWatch
|
* @watch: a #GstRTSPWatch
|
||||||
*
|
*
|
||||||
* Decreases the reference count of @watch by one. If the resulting reference
|
* Decreases the reference count of @watch by one. If the resulting reference
|
||||||
|
@ -65,6 +65,7 @@ GstRTSPResult gst_rtsp_connection_connect (GstRTSPConnection *conn, G
|
|||||||
GstRTSPResult gst_rtsp_connection_close (GstRTSPConnection *conn);
|
GstRTSPResult gst_rtsp_connection_close (GstRTSPConnection *conn);
|
||||||
GstRTSPResult gst_rtsp_connection_free (GstRTSPConnection *conn);
|
GstRTSPResult gst_rtsp_connection_free (GstRTSPConnection *conn);
|
||||||
|
|
||||||
|
|
||||||
/* sending/receiving raw bytes */
|
/* sending/receiving raw bytes */
|
||||||
GstRTSPResult gst_rtsp_connection_read (GstRTSPConnection * conn, guint8 * data,
|
GstRTSPResult gst_rtsp_connection_read (GstRTSPConnection * conn, guint8 * data,
|
||||||
guint size, GTimeVal * timeout);
|
guint size, GTimeVal * timeout);
|
||||||
@ -105,6 +106,9 @@ GstRTSPResult gst_rtsp_connection_set_qos_dscp (GstRTSPConnection *conn,
|
|||||||
GstRTSPUrl * gst_rtsp_connection_get_url (const GstRTSPConnection *conn);
|
GstRTSPUrl * gst_rtsp_connection_get_url (const GstRTSPConnection *conn);
|
||||||
const gchar * gst_rtsp_connection_get_ip (const GstRTSPConnection *conn);
|
const gchar * gst_rtsp_connection_get_ip (const GstRTSPConnection *conn);
|
||||||
|
|
||||||
|
void gst_rtsp_connection_set_tunneled (GstRTSPConnection *conn, gboolean tunneled);
|
||||||
|
gboolean gst_rtsp_connection_is_tunneled (GstRTSPConnection *conn);
|
||||||
|
|
||||||
/* async IO */
|
/* async IO */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,6 +11,7 @@ EXPORTS
|
|||||||
gst_rtsp_connection_free
|
gst_rtsp_connection_free
|
||||||
gst_rtsp_connection_get_ip
|
gst_rtsp_connection_get_ip
|
||||||
gst_rtsp_connection_get_url
|
gst_rtsp_connection_get_url
|
||||||
|
gst_rtsp_connection_is_tunneled
|
||||||
gst_rtsp_connection_next_timeout
|
gst_rtsp_connection_next_timeout
|
||||||
gst_rtsp_connection_poll
|
gst_rtsp_connection_poll
|
||||||
gst_rtsp_connection_read
|
gst_rtsp_connection_read
|
||||||
@ -20,6 +21,7 @@ EXPORTS
|
|||||||
gst_rtsp_connection_set_auth
|
gst_rtsp_connection_set_auth
|
||||||
gst_rtsp_connection_set_auth_param
|
gst_rtsp_connection_set_auth_param
|
||||||
gst_rtsp_connection_set_qos_dscp
|
gst_rtsp_connection_set_qos_dscp
|
||||||
|
gst_rtsp_connection_set_tunneled
|
||||||
gst_rtsp_connection_write
|
gst_rtsp_connection_write
|
||||||
gst_rtsp_event_get_type
|
gst_rtsp_event_get_type
|
||||||
gst_rtsp_extension_after_send
|
gst_rtsp_extension_after_send
|
||||||
|
Loading…
x
Reference in New Issue
Block a user