From 7cc6d95338b4e6089009f9af530ec22f21127684 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 23 Jan 2013 12:40:09 -0500 Subject: [PATCH] adding preliminary SDP parser --- daemon/Makefile | 2 +- daemon/sdp.c | 214 +++++++++++++++++++++++++++++++++++++++++ daemon/sdp.h | 8 ++ tests/sdp-parse-test.c | 17 ++++ 4 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 daemon/sdp.c create mode 100644 daemon/sdp.h create mode 100644 tests/sdp-parse-test.c diff --git a/daemon/Makefile b/daemon/Makefile index 0559a0066..7d1c13088 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -26,7 +26,7 @@ CPPFLAGS+= `dpkg-buildflags --get CPPFLAGS` LDFLAGS+= `dpkg-buildflags --get LDFLAGS` SRCS= main.c kernel.c poller.c aux.c control.c streambuf.c call.c control_udp.c redis.c \ - bencode.c cookie_cache.c udp_listener.c control_ng.c + bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c OBJS= $(SRCS:.c=.o) diff --git a/daemon/sdp.c b/daemon/sdp.c new file mode 100644 index 000000000..3fc05400b --- /dev/null +++ b/daemon/sdp.c @@ -0,0 +1,214 @@ +#include + +#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; +} diff --git a/daemon/sdp.h b/daemon/sdp.h new file mode 100644 index 000000000..84a158110 --- /dev/null +++ b/daemon/sdp.h @@ -0,0 +1,8 @@ +#ifndef _SDP_H_ +#define _SDP_H_ + +#include + +GQueue *sdp_parse(const char *body, int len, GQueue *streams); + +#endif diff --git a/tests/sdp-parse-test.c b/tests/sdp-parse-test.c new file mode 100644 index 000000000..1c1fbae8c --- /dev/null +++ b/tests/sdp-parse-test.c @@ -0,0 +1,17 @@ +/* gcc -Wall -g `pkg-config glib-2.0 --cflags --libs` sdp-parse-test.c ../daemon/sdp.c -o sdp-parse-test */ +#include +#include +#include + +#include "../daemon/sdp.h" + +int main() { + char *sdp = "v=0\r\no=root 25669 25669 IN IP4 192.168.51.133\r\ns=session\r\nc=IN IP4 192.168.51.133\r\nt=0 0\r\nm=audio 30018 RTP/AVP 8 0 101\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:101 telephone-event/8000\r\na=fmtp:101 0-16\r\na=silenceSupp:off - - - -\r\na=ptime:20\r\na=sendrecv\r\na=nortpproxy:yes\r\n"; + int len = strlen(sdp); + GQueue *ret; + + ret = sdp_parse(sdp, len, NULL); + printf("%i stream(s)\n", g_queue_get_length(ret)); + + return 0; +}