summaryrefslogtreecommitdiffstats
path: root/gst/rtsp/rtspconnection.c
diff options
context:
space:
mode:
Diffstat (limited to 'gst/rtsp/rtspconnection.c')
-rw-r--r--gst/rtsp/rtspconnection.c284
1 files changed, 208 insertions, 76 deletions
diff --git a/gst/rtsp/rtspconnection.c b/gst/rtsp/rtspconnection.c
index 5d1ab22c..41a374fe 100644
--- a/gst/rtsp/rtspconnection.c
+++ b/gst/rtsp/rtspconnection.c
@@ -1,5 +1,5 @@
/* GStreamer
- * Copyright (C) <2005> Wim Taymans <wim@fluendo.com>
+ * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -16,12 +16,37 @@
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+/*
+ * Unless otherwise indicated, Source Code is licensed under MIT license.
+ * See further explanation attached in License Statement (distributed in the file
+ * LICENSE).
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
/* we include this here to get the G_OS_* defines */
#include <glib.h>
@@ -35,9 +60,27 @@
#include <arpa/inet.h>
#endif
-
#include "rtspconnection.h"
+/* the select call is also performed on the control sockets, that way
+ * we can send special commands to unlock or restart the select call */
+#define CONTROL_RESTART 'R' /* restart the select call */
+#define CONTROL_STOP 'S' /* stop the select call */
+#define CONTROL_SOCKETS(conn) conn->control_sock
+#define WRITE_SOCKET(conn) conn->control_sock[1]
+#define READ_SOCKET(conn) conn->control_sock[0]
+
+#define SEND_COMMAND(conn, command) \
+G_STMT_START { \
+ unsigned char c; c = command; \
+ write (WRITE_SOCKET(conn), &c, 1); \
+} G_STMT_END
+
+#define READ_COMMAND(conn, command, res) \
+G_STMT_START { \
+ res = read(READ_SOCKET(conn), &command, 1); \
+} G_STMT_END
+
#ifdef G_OS_WIN32
#define CLOSE_SOCKET(sock) closesocket(sock);
#else
@@ -61,7 +104,52 @@ inet_aton (const char *c, struct in_addr *paddr)
#endif
RTSPResult
-rtsp_connection_open (RTSPUrl * url, RTSPConnection ** conn)
+rtsp_connection_create (RTSPUrl * url, RTSPConnection ** conn)
+{
+ gint ret;
+ RTSPConnection *newconn;
+
+ g_return_val_if_fail (url != NULL, RTSP_EINVAL);
+ g_return_val_if_fail (conn != NULL, RTSP_EINVAL);
+
+ if (url->protocol != RTSP_PROTO_TCP)
+ return RTSP_ENOTIMPL;
+
+ newconn = g_new (RTSPConnection, 1);
+
+#ifdef G_OS_WIN32
+ /* This should work on UNIX too. PF_UNIX sockets replaced with pipe */
+ /* pipe( CONTROL_SOCKETS(newconn) ) */
+ if ((ret = pipe (CONTROL_SOCKETS (newconn))) < 0)
+ goto no_socket_pair;
+#else
+ if ((ret =
+ socketpair (PF_UNIX, SOCK_STREAM, 0, CONTROL_SOCKETS (newconn))) < 0)
+ goto no_socket_pair;
+
+ fcntl (READ_SOCKET (newconn), F_SETFL, O_NONBLOCK);
+ fcntl (WRITE_SOCKET (newconn), F_SETFL, O_NONBLOCK);
+#endif
+
+ newconn->url = url;
+ newconn->cseq = 0;
+ newconn->session_id[0] = 0;
+ newconn->state = RTSP_STATE_INIT;
+
+ *conn = newconn;
+
+ return RTSP_OK;
+
+ /* ERRORS */
+no_socket_pair:
+ {
+ g_free (newconn);
+ return RTSP_ESYS;
+ }
+}
+
+RTSPResult
+rtsp_connection_connect (RTSPConnection * conn)
{
gint fd;
struct sockaddr_in sin;
@@ -70,12 +158,12 @@ rtsp_connection_open (RTSPUrl * url, RTSPConnection ** conn)
gchar *ip;
struct in_addr addr;
gint ret;
+ guint16 port;
+ RTSPUrl *url;
- g_return_val_if_fail (url != NULL, RTSP_EINVAL);
g_return_val_if_fail (conn != NULL, RTSP_EINVAL);
- if (url->protocol != RTSP_PROTO_TCP)
- return RTSP_ENOTIMPL;
+ url = conn->url;
/* first check if it already is an IP address */
if (inet_aton (url->host, &addr)) {
@@ -92,20 +180,25 @@ rtsp_connection_open (RTSPUrl * url, RTSPConnection ** conn)
ip = inet_ntoa (*(struct in_addr *) *addrs);
}
- fd = socket (AF_INET, SOCK_STREAM, 0);
- if (fd == -1)
- goto sys_error;
+ /* get the port from the url */
+ rtsp_url_get_port (url, &port);
memset (&sin, 0, sizeof (sin));
sin.sin_family = AF_INET; /* network socket */
- sin.sin_port = htons (url->port); /* on port */
+ sin.sin_port = htons (port); /* on port */
sin.sin_addr.s_addr = inet_addr (ip); /* on host ip */
+ fd = socket (AF_INET, SOCK_STREAM, 0);
+ if (fd == -1)
+ goto sys_error;
+
ret = connect (fd, (struct sockaddr *) &sin, sizeof (sin));
if (ret != 0)
goto sys_error;
- return rtsp_connection_create (fd, conn);
+ conn->fd = fd;
+
+ return RTSP_OK;
sys_error:
{
@@ -123,27 +216,6 @@ not_ip:
}
}
-RTSPResult
-rtsp_connection_create (gint fd, RTSPConnection ** conn)
-{
- RTSPConnection *newconn;
-
- g_return_val_if_fail (conn != NULL, RTSP_EINVAL);
-
- /* FIXME check fd, must be connected SOCK_STREAM */
-
- newconn = g_new (RTSPConnection, 1);
-
- newconn->fd = fd;
- newconn->cseq = 0;
- newconn->session_id[0] = 0;
- newconn->state = RTSP_STATE_INIT;
-
- *conn = newconn;
-
- return RTSP_OK;
-}
-
static void
append_header (gint key, gchar * value, GString * str)
{
@@ -408,11 +480,99 @@ parse_line (gchar * buffer, RTSPMessage * msg)
return RTSP_OK;
}
+RTSPResult
+rtsp_connection_read (RTSPConnection * conn, gpointer data, guint size)
+{
+ fd_set readfds;
+ guint toread;
+ gint retval;
+ gint avail;
+
+ g_return_val_if_fail (conn != NULL, RTSP_EINVAL);
+ g_return_val_if_fail (data != NULL, RTSP_EINVAL);
+
+ if (size == 0)
+ return RTSP_OK;
+
+ toread = size;
+
+ /* if the call fails, just go in the select.. it should not fail. Else if
+ * there is enough data to read, skip the select call al together.*/
+ if (ioctl (conn->fd, FIONREAD, &avail) < 0)
+ avail = 0;
+ else if (avail >= toread)
+ goto do_read;
+
+ FD_ZERO (&readfds);
+ FD_SET (conn->fd, &readfds);
+ FD_SET (READ_SOCKET (conn), &readfds);
+
+ while (toread > 0) {
+ gint bytes;
+
+ do {
+ retval = select (FD_SETSIZE, &readfds, NULL, NULL, NULL);
+ } while ((retval == -1 && errno == EINTR));
+
+ if (retval == -1)
+ goto select_error;
+
+ if (FD_ISSET (READ_SOCKET (conn), &readfds)) {
+ /* read all stop commands */
+ while (TRUE) {
+ gchar command;
+ int res;
+
+ READ_COMMAND (conn, command, res);
+ if (res < 0) {
+ /* no more commands */
+ break;
+ }
+ }
+ goto stopped;
+ }
+
+ do_read:
+ /* if we get here there is activity on the real fd since the select
+ * completed and the control socket was not readable. */
+ bytes = read (conn->fd, data, toread);
+
+ if (bytes == 0) {
+ goto eof;
+ } else if (bytes < 0) {
+ if (errno != EAGAIN && errno != EINTR)
+ goto read_error;
+ } else {
+ toread -= bytes;
+ data += bytes;
+ }
+ }
+ return RTSP_OK;
+
+ /* ERRORS */
+select_error:
+ {
+ return RTSP_ESYS;
+ }
+stopped:
+ {
+ return RTSP_EINTR;
+ }
+eof:
+ {
+ return RTSP_EEOF;
+ }
+read_error:
+ {
+ return RTSP_ESYS;
+ }
+}
+
static RTSPResult
-read_body (gint fd, glong content_length, RTSPMessage * msg)
+read_body (RTSPConnection * conn, glong content_length, RTSPMessage * msg)
{
- gchar *body, *bodyptr;
- gint to_read, r;
+ gchar *body;
+ RTSPResult res;
if (content_length <= 0) {
body = NULL;
@@ -422,29 +582,19 @@ read_body (gint fd, glong content_length, RTSPMessage * msg)
body = g_malloc (content_length + 1);
body[content_length] = '\0';
- bodyptr = body;
- to_read = content_length;
- while (to_read > 0) {
- r = read (fd, bodyptr, to_read);
- if (r < 0) {
- if (errno != EAGAIN && errno != EINTR)
- goto read_error;
- } else {
- to_read -= r;
- bodyptr += r;
- }
- }
- content_length += 1;
+
+ RTSP_CHECK (rtsp_connection_read (conn, body, content_length), read_error);
done:
- rtsp_message_set_body (msg, (guint8 *) body, content_length);
+ rtsp_message_take_body (msg, (guint8 *) body, content_length + 1);
return RTSP_OK;
+ /* ERRORS */
read_error:
{
g_free (body);
- return RTSP_ESYS;
+ return res;
}
}
@@ -469,14 +619,9 @@ rtsp_connection_receive (RTSPConnection * conn, RTSPMessage * msg)
/* parse first line and headers */
while (res == RTSP_OK) {
gchar c;
- gint ret;
/* read first character, this identifies data messages */
- ret = read (conn->fd, &c, 1);
- if (ret < 0)
- goto read_error;
- if (ret < 1)
- break;
+ RTSP_CHECK (rtsp_connection_read (conn, &c, 1), read_error);
/* check for data packet, first character is $ */
if (c == '$') {
@@ -485,26 +630,18 @@ rtsp_connection_receive (RTSPConnection * conn, RTSPMessage * msg)
/* data packets are $<1 byte channel><2 bytes length,BE><data bytes> */
/* read channel, which is the next char */
- ret = read (conn->fd, &c, 1);
- if (ret < 0)
- goto read_error;
- if (ret < 1)
- goto error;
+ RTSP_CHECK (rtsp_connection_read (conn, &c, 1), read_error);
/* now we create a data message */
rtsp_message_init_data (msg, (gint) c);
/* next two bytes are the length of the data */
- ret = read (conn->fd, &size, 2);
- if (ret < 0)
- goto read_error;
- if (ret < 2)
- goto error;
+ RTSP_CHECK (rtsp_connection_read (conn, &size, 2), read_error);
size = GUINT16_FROM_BE (size);
/* and read the body */
- res = read_body (conn->fd, size, msg);
+ res = read_body (conn, size, msg);
need_body = FALSE;
break;
} else {
@@ -520,9 +657,8 @@ rtsp_connection_receive (RTSPConnection * conn, RTSPMessage * msg)
break;
/* read lines */
- res = read_line (conn->fd, buffer + offset, sizeof (buffer) - offset);
- if (res != RTSP_OK)
- goto read_error;
+ RTSP_CHECK (read_line (conn->fd, buffer + offset,
+ sizeof (buffer) - offset), read_error);
if (buffer[0] == '\0')
break;
@@ -549,7 +685,7 @@ rtsp_connection_receive (RTSPConnection * conn, RTSPMessage * msg)
&hdrval) == RTSP_OK) {
/* there is, read the body */
content_length = atol (hdrval);
- res = read_body (conn->fd, content_length, msg);
+ RTSP_CHECK (read_body (conn, content_length, msg), read_error);
}
/* save session id in the connection for further use */
@@ -577,13 +713,9 @@ rtsp_connection_receive (RTSPConnection * conn, RTSPMessage * msg)
}
return res;
-error:
- {
- return RTSP_EPARSE;
- }
read_error:
{
- return RTSP_ESYS;
+ return res;
}
}