You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rtpengine/daemon/sdp.c

215 lines
4.2 KiB

#include <glib.h>
#include "sdp.h"
#include "call.h"
#include "log.h"
struct string {
const char *s;
int len;
};
struct sdp_origin {
struct string username;
struct string session_id;
struct string version;
struct string network_type;
struct string address_type;
struct string address;
int parsed:1;
};
struct sdp_connection {
struct string network_type;
struct string address_type;
struct string address;
int parsed:1;
};
struct sdp_session {
struct sdp_origin origin;
struct sdp_connection connection;
GQueue media_streams;
GQueue attributes;
};
struct sdp_media {
struct string media_type;
struct string port;
struct string transport;
/* ... format list */
struct sdp_connection connection;
GQueue attributes;
};
static inline int extract_token(const char **sp, const char *end, struct string *out) {
const char *space;
out->s = *sp;
space = memchr(*sp, ' ', end - *sp);
if (space == *sp || end == *sp)
return -1;
if (!space) {
out->len = end - *sp;
*sp = end;
}
else {
out->len = space - *sp;
*sp = space + 1;
}
return 0;
}
#define EXTRACT_TOKEN(field) if (extract_token(&start, end, &output->field)) return -1
static int parse_origin(const char *start, const char *end, struct sdp_origin *output) {
if (output->parsed)
return -1;
EXTRACT_TOKEN(username);
EXTRACT_TOKEN(session_id);
EXTRACT_TOKEN(version);
EXTRACT_TOKEN(network_type);
EXTRACT_TOKEN(address_type);
EXTRACT_TOKEN(address);
output->parsed = 1;
return 0;
}
static int parse_connection(const char *start, const char *end, struct sdp_connection *output) {
if (output->parsed)
return -1;
EXTRACT_TOKEN(network_type);
EXTRACT_TOKEN(address_type);
EXTRACT_TOKEN(address);
output->parsed = 1;
return 0;
}
static int parse_media(const char *start, const char *end, struct sdp_media *output) {
EXTRACT_TOKEN(media_type);
EXTRACT_TOKEN(port);
EXTRACT_TOKEN(transport);
return 0;
}
GQueue *sdp_parse(const char *body, int len, GQueue *streams) {
const char *b, *end, *value, *line_end, *next_line;
struct sdp_session *session = NULL;
GQueue *sessions;
struct sdp_media *media = NULL;
const char *errstr;
struct string *attribute;
sessions = g_queue_new();
b = body;
end = body + len;
while (b && b < end - 1) {
errstr = "Missing '=' sign";
if (b[1] != '=')
goto error;
value = &b[2];
line_end = memchr(value, '\n', end - value);
if (!line_end) {
/* assume missing LF at end of body */
line_end = end;
next_line = NULL;
}
else {
next_line = line_end + 1;
if (next_line >= end)
next_line = NULL;
if (line_end[-1] == '\r')
line_end--;
}
switch (b[0]) {
case 'v':
errstr = "Error in v= line";
if (line_end != value + 1)
goto error;
if (value[0] != '0')
goto error;
session = g_slice_alloc0(sizeof(*session));
g_queue_init(&session->media_streams);
g_queue_init(&session->attributes);
g_queue_push_tail(sessions, session);
media = NULL;
break;
case 'o':
errstr = "o= line found within media section";
if (media)
goto error;
errstr = "Error parsing o= line";
if (parse_origin(value, line_end, &session->origin))
goto error;
break;
case 'm':
media = g_slice_alloc0(sizeof(*media));
g_queue_init(&media->attributes);
errstr = "Error parsing m= line";
if (parse_media(value, line_end, media))
goto error;
g_queue_push_tail(&session->media_streams, media);
break;
case 'c':
errstr = "Error parsing c= line";
if (parse_connection(value, line_end,
media ? &media->connection : &session->connection))
goto error;
break;
case 'a':
attribute = g_slice_alloc(sizeof(*attribute));
attribute->s = value;
attribute->len = line_end - value;
g_queue_push_tail(media ? &media->attributes : &session->attributes,
attribute);
break;
case 's':
case 'i':
case 'u':
case 'e':
case 'p':
case 'b':
case 't':
case 'r':
case 'z':
case 'k':
break;
default:
errstr = "Unknown SDP line type found";
goto error;
}
b = next_line;
}
return sessions;
error:
mylog(LOG_WARNING, "Error parsing SDP: %s", errstr);
/* XXX free sessions */
return NULL;
}