mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-10-22 02:13:46 +00:00
433 lines
8.4 KiB
C
433 lines
8.4 KiB
C
|
/* GStreamer
|
||
|
* Copyright (C) <2005> 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
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Library General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Library General Public
|
||
|
* License along with this library; if not, write to the
|
||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||
|
* Boston, MA 02111-1307, USA.
|
||
|
*/
|
||
|
|
||
|
#include <arpa/inet.h>
|
||
|
#include <netdb.h>
|
||
|
#include <stdio.h>
|
||
|
#include <unistd.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "rtspconnection.h"
|
||
|
|
||
|
RTSPResult
|
||
|
rtsp_connection_open (RTSPUrl * url, RTSPConnection ** conn)
|
||
|
{
|
||
|
gint fd;
|
||
|
struct sockaddr_in sin;
|
||
|
struct hostent *hostinfo;
|
||
|
char **addrs;
|
||
|
gchar *ip;
|
||
|
struct in_addr addr;
|
||
|
gint ret;
|
||
|
|
||
|
if (url == NULL || conn == NULL)
|
||
|
return RTSP_EINVAL;
|
||
|
|
||
|
if (url->protocol != RTSP_PROTO_TCP)
|
||
|
return RTSP_ENOTIMPL;
|
||
|
|
||
|
/* first check if it already is an IP address */
|
||
|
if (inet_aton (url->host, &addr)) {
|
||
|
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 */
|
||
|
|
||
|
addrs = hostinfo->h_addr_list;
|
||
|
ip = inet_ntoa (*(struct in_addr *) *addrs);
|
||
|
}
|
||
|
|
||
|
fd = socket (AF_INET, SOCK_STREAM, 0);
|
||
|
if (fd == -1)
|
||
|
goto sys_error;
|
||
|
|
||
|
memset (&sin, 0, sizeof (sin));
|
||
|
sin.sin_family = AF_INET; /* network socket */
|
||
|
sin.sin_port = htons (url->port); /* on port */
|
||
|
sin.sin_addr.s_addr = inet_addr (ip); /* on host ip */
|
||
|
|
||
|
ret = connect (fd, (struct sockaddr *) &sin, sizeof (sin));
|
||
|
if (ret != 0)
|
||
|
goto sys_error;
|
||
|
|
||
|
return rtsp_connection_create (fd, conn);
|
||
|
|
||
|
sys_error:
|
||
|
{
|
||
|
return RTSP_ESYS;
|
||
|
}
|
||
|
not_resolved:
|
||
|
{
|
||
|
g_warning ("could not resolve host \"%s\"\n", url->host);
|
||
|
return RTSP_ESYS;
|
||
|
}
|
||
|
not_ip:
|
||
|
{
|
||
|
g_warning ("host \"%s\" is not IP\n", url->host);
|
||
|
return RTSP_ESYS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RTSPResult
|
||
|
rtsp_connection_create (gint fd, RTSPConnection ** conn)
|
||
|
{
|
||
|
RTSPConnection *newconn;
|
||
|
|
||
|
/* FIXME check fd */
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
const gchar *keystr = rtsp_header_as_text (key);
|
||
|
|
||
|
g_string_append_printf (str, "%s: %s\r\n", keystr, value);
|
||
|
}
|
||
|
|
||
|
RTSPResult
|
||
|
rtsp_connection_send (RTSPConnection * conn, RTSPMessage * message)
|
||
|
{
|
||
|
GString *str;
|
||
|
|
||
|
if (conn == NULL || message == NULL)
|
||
|
return RTSP_EINVAL;
|
||
|
|
||
|
str = g_string_new ("");
|
||
|
|
||
|
g_string_append_printf (str, "%s %s RTSP/1.0\r\n"
|
||
|
"CSeq: %d\r\n",
|
||
|
rtsp_method_as_text (message->type_data.request.method),
|
||
|
message->type_data.request.uri, conn->cseq);
|
||
|
|
||
|
if (conn->session_id[0] != '\0') {
|
||
|
rtsp_message_add_header (message, RTSP_HDR_SESSION, conn->session_id);
|
||
|
}
|
||
|
|
||
|
g_hash_table_foreach (message->hdr_fields, (GHFunc) append_header, str);
|
||
|
|
||
|
|
||
|
g_string_append (str, "\r\n");
|
||
|
|
||
|
write (conn->fd, str->str, str->len);
|
||
|
g_string_free (str, TRUE);
|
||
|
|
||
|
return RTSP_OK;
|
||
|
}
|
||
|
|
||
|
static RTSPResult
|
||
|
read_line (gint fd, gchar * buffer, guint size)
|
||
|
{
|
||
|
gint idx;
|
||
|
gchar c;
|
||
|
gint ret;
|
||
|
|
||
|
idx = 0;
|
||
|
while (TRUE) {
|
||
|
ret = read (fd, &c, 1);
|
||
|
if (ret < 1)
|
||
|
goto error;
|
||
|
|
||
|
if (c == '\n') /* end on \n */
|
||
|
break;
|
||
|
if (c == '\r') /* ignore \r */
|
||
|
continue;
|
||
|
|
||
|
if (idx < size - 1)
|
||
|
buffer[idx++] = c;
|
||
|
}
|
||
|
buffer[idx] = '\0';
|
||
|
|
||
|
return RTSP_OK;
|
||
|
|
||
|
error:
|
||
|
{
|
||
|
perror ("read");
|
||
|
return RTSP_ESYS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
read_string (gchar * dest, gint size, gchar ** src)
|
||
|
{
|
||
|
gint idx;
|
||
|
|
||
|
idx = 0;
|
||
|
/* skip spaces */
|
||
|
while (g_ascii_isspace (**src))
|
||
|
(*src)++;
|
||
|
|
||
|
while (!g_ascii_isspace (**src) && **src != '\0') {
|
||
|
if (idx < size - 1)
|
||
|
dest[idx++] = **src;
|
||
|
(*src)++;
|
||
|
}
|
||
|
if (size > 0)
|
||
|
dest[idx] = '\0';
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
read_key (gchar * dest, gint size, gchar ** src)
|
||
|
{
|
||
|
gint idx;
|
||
|
|
||
|
idx = 0;
|
||
|
while (**src != ':' && **src != '\0') {
|
||
|
if (idx < size - 1)
|
||
|
dest[idx++] = **src;
|
||
|
(*src)++;
|
||
|
}
|
||
|
if (size > 0)
|
||
|
dest[idx] = '\0';
|
||
|
}
|
||
|
|
||
|
static RTSPResult
|
||
|
parse_response_status (gchar * buffer, RTSPMessage * msg)
|
||
|
{
|
||
|
gchar versionstr[20];
|
||
|
gchar codestr[4];
|
||
|
gint code;
|
||
|
gchar *bptr;
|
||
|
|
||
|
bptr = buffer;
|
||
|
|
||
|
read_string (versionstr, sizeof (versionstr), &bptr);
|
||
|
if (strcmp (versionstr, "RTSP/1.0") != 0)
|
||
|
goto wrong_version;
|
||
|
|
||
|
read_string (codestr, sizeof (codestr), &bptr);
|
||
|
code = atoi (codestr);
|
||
|
|
||
|
while (g_ascii_isspace (*bptr))
|
||
|
bptr++;
|
||
|
|
||
|
rtsp_message_init_response (code, bptr, NULL, msg);
|
||
|
|
||
|
return RTSP_OK;
|
||
|
|
||
|
wrong_version:
|
||
|
{
|
||
|
return RTSP_EINVAL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static RTSPResult
|
||
|
parse_line (gchar * buffer, RTSPMessage * msg)
|
||
|
{
|
||
|
gchar key[32];
|
||
|
gchar *bptr;
|
||
|
RTSPHeaderField field;
|
||
|
|
||
|
bptr = buffer;
|
||
|
|
||
|
read_key (key, sizeof (key), &bptr);
|
||
|
if (*bptr != ':')
|
||
|
return RTSP_EINVAL;
|
||
|
|
||
|
bptr++;
|
||
|
|
||
|
field = rtsp_find_header_field (key);
|
||
|
if (field == -1) {
|
||
|
g_warning ("unknown header field '%s'\n", key);
|
||
|
} else {
|
||
|
while (g_ascii_isspace (*bptr))
|
||
|
bptr++;
|
||
|
rtsp_message_add_header (msg, field, bptr);
|
||
|
}
|
||
|
|
||
|
return RTSP_OK;
|
||
|
}
|
||
|
|
||
|
static RTSPResult
|
||
|
read_body (gint fd, glong content_length, RTSPMessage * msg)
|
||
|
{
|
||
|
gchar *body, *bodyptr;
|
||
|
gint to_read, r;
|
||
|
|
||
|
if (content_length <= 0) {
|
||
|
rtsp_message_set_body (msg, NULL, 0);
|
||
|
return RTSP_OK;
|
||
|
}
|
||
|
|
||
|
body = g_malloc (content_length);
|
||
|
bodyptr = body;
|
||
|
to_read = content_length;
|
||
|
while (to_read > 0) {
|
||
|
r = read (fd, bodyptr, to_read);
|
||
|
|
||
|
to_read -= r;
|
||
|
bodyptr += r;
|
||
|
}
|
||
|
|
||
|
rtsp_message_set_body (msg, body, content_length);
|
||
|
|
||
|
return RTSP_OK;
|
||
|
}
|
||
|
|
||
|
RTSPResult
|
||
|
rtsp_connection_receive (RTSPConnection * conn, RTSPMessage * msg)
|
||
|
{
|
||
|
gchar buffer[4096];
|
||
|
gint line;
|
||
|
gchar *hdrval;
|
||
|
glong content_length;
|
||
|
RTSPResult res;
|
||
|
gboolean need_body;
|
||
|
|
||
|
if (conn == NULL || msg == NULL)
|
||
|
return RTSP_EINVAL;
|
||
|
|
||
|
line = 0;
|
||
|
|
||
|
need_body = TRUE;
|
||
|
|
||
|
/* parse first line and headers */
|
||
|
while (TRUE) {
|
||
|
gchar c;
|
||
|
gint ret;
|
||
|
|
||
|
ret = read (conn->fd, &c, 1);
|
||
|
if (ret < 0)
|
||
|
goto read_error;
|
||
|
if (ret < 1)
|
||
|
break;
|
||
|
|
||
|
/* check for data packet */
|
||
|
if (c == '$') {
|
||
|
guint16 size;
|
||
|
|
||
|
/* read channel */
|
||
|
ret = read (conn->fd, &c, 1);
|
||
|
if (ret < 0)
|
||
|
goto read_error;
|
||
|
if (ret < 1)
|
||
|
goto error;
|
||
|
|
||
|
rtsp_message_init_data ((gint) c, msg);
|
||
|
|
||
|
ret = read (conn->fd, &size, 2);
|
||
|
if (ret < 0)
|
||
|
goto read_error;
|
||
|
if (ret < 2)
|
||
|
goto error;
|
||
|
|
||
|
size = GUINT16_FROM_BE (size);
|
||
|
|
||
|
read_body (conn->fd, size, msg);
|
||
|
need_body = FALSE;
|
||
|
break;
|
||
|
} else {
|
||
|
gint offset = 0;
|
||
|
|
||
|
if (c != '\r') {
|
||
|
buffer[0] = c;
|
||
|
offset = 1;
|
||
|
}
|
||
|
/* should not happen */
|
||
|
if (c == '\n')
|
||
|
break;
|
||
|
|
||
|
read_line (conn->fd, buffer + offset, sizeof (buffer) - offset);
|
||
|
|
||
|
if (buffer[0] == '\0')
|
||
|
break;
|
||
|
|
||
|
if (line == 0) {
|
||
|
if (g_str_has_prefix (buffer, "RTSP")) {
|
||
|
parse_response_status (buffer, msg);
|
||
|
} else {
|
||
|
g_warning ("parsing request not implemented\n");
|
||
|
goto error;
|
||
|
}
|
||
|
} else {
|
||
|
parse_line (buffer, msg);
|
||
|
}
|
||
|
}
|
||
|
line++;
|
||
|
}
|
||
|
|
||
|
if (need_body) {
|
||
|
/* parse body */
|
||
|
res = rtsp_message_get_header (msg, RTSP_HDR_CONTENT_LENGTH, &hdrval);
|
||
|
if (res == RTSP_OK) {
|
||
|
content_length = atol (hdrval);
|
||
|
read_body (conn->fd, content_length, msg);
|
||
|
}
|
||
|
|
||
|
/* save session id */
|
||
|
{
|
||
|
gchar *session_id;
|
||
|
|
||
|
if (rtsp_message_get_header (msg, RTSP_HDR_SESSION,
|
||
|
&session_id) == RTSP_OK) {
|
||
|
strncpy (conn->session_id, session_id, sizeof (conn->session_id) - 1);
|
||
|
conn->session_id[sizeof (conn->session_id) - 1] = '\0';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return RTSP_OK;
|
||
|
|
||
|
error:
|
||
|
{
|
||
|
return RTSP_EPARSE;
|
||
|
}
|
||
|
read_error:
|
||
|
{
|
||
|
perror ("read");
|
||
|
return RTSP_ESYS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RTSPResult
|
||
|
rtsp_connection_close (RTSPConnection * conn)
|
||
|
{
|
||
|
gint res;
|
||
|
|
||
|
if (conn == NULL)
|
||
|
return RTSP_EINVAL;
|
||
|
|
||
|
res = close (conn->fd);
|
||
|
conn->fd = -1;
|
||
|
if (res != 0)
|
||
|
goto sys_error;
|
||
|
|
||
|
return RTSP_OK;
|
||
|
|
||
|
sys_error:
|
||
|
{
|
||
|
return RTSP_ESYS;
|
||
|
}
|
||
|
}
|