summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Guthrie <pulse@colin.guthr.ie>2008-05-06 00:17:17 +0000
committerColin Guthrie <pulse@colin.guthr.ie>2008-10-08 20:32:06 +0100
commitd423605bd9f5fb18b44fde5424b075c977de25ad (patch)
tree3d414b4aeed4c799e2d3905c2654a1fc0840f00e
parenta0d3582fb1bddbb8fb6a7da98bbfeb05b517088e (diff)
Move closer to an asynchronous structure (still some parsing code to be converted).
Move type definition into .c file to keep it private Add more utility functions to add/remove headers and return the serverport now the structure is private. This commit will break the test application but I will fix that in due course git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2365 fefdeb5f-60dc-0310-8127-8f9354f1896f
-rw-r--r--src/modules/rtp/rtsp.c345
-rw-r--r--src/modules/rtp/rtsp.h31
2 files changed, 220 insertions, 156 deletions
diff --git a/src/modules/rtp/rtsp.c b/src/modules/rtp/rtsp.c
index d8440724..55d91018 100644
--- a/src/modules/rtp/rtsp.c
+++ b/src/modules/rtp/rtsp.c
@@ -49,6 +49,22 @@
#include "rtsp.h"
+struct pa_rtsp_context {
+ pa_socket_client *sc;
+ pa_iochannel *io;
+ pa_rtsp_cb_t callback;
+ void* userdata;
+ const char* useragent;
+ pa_headerlist* headers;
+ char* localip;
+ char* url;
+ uint32_t port;
+ uint32_t cseq;
+ char* session;
+ char* transport;
+ pa_rtsp_state state;
+};
+
/*
* read one line from the file descriptor
* timeout: msec unit, -1 for infinite
@@ -112,18 +128,10 @@ static int pa_read_line(pa_iochannel* io, char *line, int maxlen, int timeout)
static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
const char* content_type, const char* content,
int expect_response,
- pa_headerlist* headers, pa_headerlist** response_headers) {
+ pa_headerlist* headers) {
pa_strbuf* buf;
char* hdrs;
ssize_t l;
- char response[1024];
- int timeout;
- char* token;
- const char* token_state;
- char delimiters[2];
- char* header;
- char* delimpos;
-
pa_assert(c);
pa_assert(c->url);
@@ -167,91 +175,6 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd,
l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
pa_xfree(hdrs);
- /* Do we expect a response? */
- if (!expect_response)
- return 0;
-
- timeout = 5000;
- if (pa_read_line(c->io, response, sizeof(response), timeout) <= 0) {
- /*ERRMSG("%s: request failed\n",__func__);*/
- return -1;
- }
-
- delimiters[0] = ' ';
- delimiters[1] = '\0';
- token_state = NULL;
- pa_xfree(pa_split(response, delimiters, &token_state));
- token = pa_split(response, delimiters, &token_state);
- if (!token || strcmp(token, "200")) {
- pa_xfree(token);
- /*ERRMSG("%s: request failed, error %s\n",__func__,token);*/
- return -1;
- }
- pa_xfree(token);
-
- /* We want to return the headers? */
- if (!response_headers)
- {
- /* We have no storage, so just clear out the response. */
- while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
- /* Reduce timeout for future requests */
- timeout = 1000;
- }
- return 0;
- }
-
- /* TODO: Move header reading into the headerlist. */
- header = NULL;
- buf = pa_strbuf_new();
- while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
- /* Reduce timeout for future requests */
- timeout = 1000;
-
- /* If the first character is a space, it's a continuation header */
- if (header && ' ' == response[0]) {
- /* Add this line to the buffer (sans the space. */
- pa_strbuf_puts(buf, &(response[1]));
- continue;
- }
-
- if (header) {
- /* This is not a continuation header so let's dump the full
- header/value into our proplist */
- pa_headerlist_puts(*response_headers, header, pa_strbuf_tostring_free(buf));
- pa_xfree(header);
- buf = pa_strbuf_new();
- }
-
- delimpos = strstr(response, ":");
- if (!delimpos) {
- /*ERRMSG("%s: Request failed, bad header\n",__func__);*/
- return -1;
- }
-
- if (strlen(delimpos) > 1) {
- /* Cut our line off so we can copy the header name out */
- *delimpos++ = '\0';
-
- /* Trim the front of any spaces */
- while (' ' == *delimpos)
- ++delimpos;
-
- pa_strbuf_puts(buf, delimpos);
- } else {
- /* Cut our line off so we can copy the header name out */
- *delimpos = '\0';
- }
-
- /* Save the header name */
- header = pa_xstrdup(response);
- }
- /* We will have a header left from our looping itteration, so add it in :) */
- if (header) {
- /* This is not a continuation header so let's dump it into our proplist */
- pa_headerlist_puts(*response_headers, header, pa_strbuf_tostring(buf));
- }
- pa_strbuf_free(buf);
-
return 0;
}
@@ -286,6 +209,146 @@ void pa_rtsp_context_free(pa_rtsp_context* c) {
}
+static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) {
+ pa_strbuf* buf;
+ pa_headerlist* response_headers = NULL;
+ char response[1024];
+ int timeout;
+ char* token;
+ char* header;
+ char* delimpos;
+ char delimiters[] = " ";
+ pa_rtsp_context *c = userdata;
+ pa_assert(c);
+
+ /* TODO: convert this to a pa_ioline based reader */
+ if (STATE_CONNECT == c->state) {
+ response_headers = pa_headerlist_new();
+ }
+ timeout = 5000;
+ /* read in any response headers */
+ if (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
+ const char* token_state = NULL;
+
+ timeout = 1000;
+ pa_xfree(pa_split(response, delimiters, &token_state));
+ token = pa_split(response, delimiters, &token_state);
+ if (!token || strcmp(token, "200")) {
+ pa_xfree(token);
+ pa_log("Invalid Response");
+ /* TODO: Bail out completely */
+ return;
+ }
+ pa_xfree(token);
+
+ /* We want to return the headers? */
+ if (!response_headers) {
+ /* We have no storage, so just clear out the response. */
+ while (pa_read_line(c->io, response, sizeof(response), timeout) > 0);
+ } else {
+ /* TODO: Move header reading into the headerlist. */
+ header = NULL;
+ buf = pa_strbuf_new();
+ while (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
+ /* If the first character is a space, it's a continuation header */
+ if (header && ' ' == response[0]) {
+ /* Add this line to the buffer (sans the space. */
+ pa_strbuf_puts(buf, &(response[1]));
+ continue;
+ }
+
+ if (header) {
+ /* This is not a continuation header so let's dump the full
+ header/value into our proplist */
+ pa_headerlist_puts(response_headers, header, pa_strbuf_tostring_free(buf));
+ pa_xfree(header);
+ buf = pa_strbuf_new();
+ }
+
+ delimpos = strstr(response, ":");
+ if (!delimpos) {
+ pa_log("Invalid response header");
+ return;
+ }
+
+ if (strlen(delimpos) > 1) {
+ /* Cut our line off so we can copy the header name out */
+ *delimpos++ = '\0';
+
+ /* Trim the front of any spaces */
+ while (' ' == *delimpos)
+ ++delimpos;
+
+ pa_strbuf_puts(buf, delimpos);
+ } else {
+ /* Cut our line off so we can copy the header name out */
+ *delimpos = '\0';
+ }
+
+ /* Save the header name */
+ header = pa_xstrdup(response);
+ }
+ /* We will have a header left from our looping itteration, so add it in :) */
+ if (header) {
+ /* This is not a continuation header so let's dump it into our proplist */
+ pa_headerlist_puts(response_headers, header, pa_strbuf_tostring(buf));
+ }
+ pa_strbuf_free(buf);
+ }
+ }
+
+ /* Deal with a CONNECT response */
+ if (STATE_CONNECT == c->state) {
+ const char* token_state = NULL;
+ const char* pc = NULL;
+ c->session = pa_xstrdup(pa_headerlist_gets(response_headers, "Session"));
+ c->transport = pa_xstrdup(pa_headerlist_gets(response_headers, "Transport"));
+
+ if (!c->session || !c->transport) {
+ pa_headerlist_free(response_headers);
+ return;
+ }
+
+ /* Now parse out the server port component of the response. */
+ c->port = 0;
+ delimiters[0] = ';';
+ while ((token = pa_split(c->transport, delimiters, &token_state))) {
+ if ((pc = strstr(token, "="))) {
+ if (0 == strncmp(token, "server_port", 11)) {
+ pa_atou(pc+1, &c->port);
+ pa_xfree(token);
+ break;
+ }
+ }
+ pa_xfree(token);
+ }
+ if (0 == c->port) {
+ /* Error no server_port in response */
+ pa_headerlist_free(response_headers);
+ return;
+ }
+ }
+
+ /* Call our callback */
+ if (c->callback)
+ c->callback(c, c->state, response_headers, c->userdata);
+
+
+ if (response_headers)
+ pa_headerlist_free(response_headers);
+
+ /*
+ if (do_read(u) < 0 || do_write(u) < 0) {
+
+ if (u->io) {
+ pa_iochannel_free(u->io);
+ u->io = NULL;
+ }
+
+ pa_module_unload_request(u->module);
+ }
+ */
+}
static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
pa_rtsp_context *c = userdata;
@@ -309,6 +372,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
}
pa_assert(!c->io);
c->io = io;
+ pa_iochannel_set_callback(c->io, io_callback, c);
/* Get the local IP address for use externally */
if (0 == getsockname(pa_iochannel_get_recv_fd(io), &sa.sa, &sa_len)) {
@@ -337,9 +401,16 @@ int pa_rtsp_connect(pa_rtsp_context *c, pa_mainloop_api *mainloop, const char* h
}
pa_socket_client_set_callback(c->sc, on_connection, c);
+ c->state = STATE_CONNECT;
return 0;
}
+void pa_rtsp_set_callback(pa_rtsp_context *c, pa_rtsp_cb_t callback, void *userdata) {
+ pa_assert(c);
+
+ c->callback = callback;
+ c->userdata = userdata;
+}
void pa_rtsp_disconnect(pa_rtsp_context *c) {
pa_assert(c);
@@ -356,6 +427,11 @@ const char* pa_rtsp_localip(pa_rtsp_context* c) {
return c->localip;
}
+uint32_t pa_rtsp_serverport(pa_rtsp_context* c) {
+ pa_assert(c);
+
+ return c->port;
+}
void pa_rtsp_set_url(pa_rtsp_context* c, const char* url) {
pa_assert(c);
@@ -363,67 +439,46 @@ void pa_rtsp_set_url(pa_rtsp_context* c, const char* url) {
c->url = pa_xstrdup(url);
}
+void pa_rtsp_add_header(pa_rtsp_context *c, const char* key, const char* value)
+{
+ pa_assert(c);
+ pa_assert(key);
+ pa_assert(value);
+
+ pa_headerlist_puts(c->headers, key, value);
+}
+
+void pa_rtsp_remove_header(pa_rtsp_context *c, const char* key)
+{
+ pa_assert(c);
+ pa_assert(key);
+
+ pa_headerlist_remove(c->headers, key);
+}
+
int pa_rtsp_announce(pa_rtsp_context *c, const char* sdp) {
pa_assert(c);
if (!sdp)
return -1;
- return pa_rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL, NULL);
+ c->state = STATE_ANNOUNCE;
+ return pa_rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);
}
-int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers) {
+int pa_rtsp_setup(pa_rtsp_context* c) {
pa_headerlist* headers;
- pa_headerlist* rheaders;
- char delimiters[2];
- char* token;
- const char* token_state;
- const char* pc;
+ int rv;
pa_assert(c);
headers = pa_headerlist_new();
- rheaders = pa_headerlist_new();
pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
- if (pa_rtsp_exec(c, "SETUP", NULL, NULL, 1, headers, &rheaders)) {
- pa_headerlist_free(headers);
- pa_headerlist_free(rheaders);
- return -1;
- }
+ c->state = STATE_SETUP;
+ rv = pa_rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);
pa_headerlist_free(headers);
-
- c->session = pa_xstrdup(pa_headerlist_gets(rheaders, "Session"));
- c->transport = pa_xstrdup(pa_headerlist_gets(rheaders, "Transport"));
-
- if (!c->session || !c->transport) {
- pa_headerlist_free(rheaders);
- return -1;
- }
-
- /* Now parse out the server port component of the response. */
- c->port = 0;
- delimiters[0] = ';';
- delimiters[1] = '\0';
- token_state = NULL;
- while ((token = pa_split(c->transport, delimiters, &token_state))) {
- if ((pc = strstr(token, "="))) {
- if (0 == strncmp(token, "server_port", 11)) {
- pa_atou(pc+1, &c->port);
- pa_xfree(token);
- break;
- }
- }
- pa_xfree(token);
- }
- if (0 == c->port) {
- /* Error no server_port in response */
- pa_headerlist_free(rheaders);
- return -1;
- }
-
- *response_headers = rheaders;
- return 0;
+ return rv;
}
@@ -441,7 +496,8 @@ int pa_rtsp_record(pa_rtsp_context* c) {
pa_headerlist_puts(headers, "Range", "npt=0-");
pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0");
- rv = pa_rtsp_exec(c, "RECORD", NULL, NULL, 1, headers, NULL);
+ c->state = STATE_RECORD;
+ rv = pa_rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);
pa_headerlist_free(headers);
return rv;
}
@@ -450,7 +506,8 @@ int pa_rtsp_record(pa_rtsp_context* c) {
int pa_rtsp_teardown(pa_rtsp_context *c) {
pa_assert(c);
- return pa_rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL, NULL);
+ c->state = STATE_TEARDOWN;
+ return pa_rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
}
@@ -459,7 +516,8 @@ int pa_rtsp_setparameter(pa_rtsp_context *c, const char* param) {
if (!param)
return -1;
- return pa_rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL, NULL);
+ c->state = STATE_SET_PARAMETER;
+ return pa_rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);
}
@@ -472,7 +530,8 @@ int pa_rtsp_flush(pa_rtsp_context *c) {
headers = pa_headerlist_new();
pa_headerlist_puts(headers, "RTP-Info", "seq=0;rtptime=0");
- rv = pa_rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers, NULL);
+ c->state = STATE_FLUSH;
+ rv = pa_rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);
pa_headerlist_free(headers);
return rv;
}
diff --git a/src/modules/rtp/rtsp.h b/src/modules/rtp/rtsp.h
index 181d0854..6458f851 100644
--- a/src/modules/rtp/rtsp.h
+++ b/src/modules/rtp/rtsp.h
@@ -36,30 +36,35 @@
#include "headerlist.h"
-typedef struct pa_rtsp_context {
- pa_socket_client *sc;
- pa_iochannel *io;
- const char* useragent;
- pa_headerlist* headers;
- char* localip;
- char* url;
- uint32_t port;
- uint32_t cseq;
- char* session;
- char* transport;
-} pa_rtsp_context;
+typedef struct pa_rtsp_context pa_rtsp_context;
+typedef enum {
+ STATE_CONNECT,
+ STATE_ANNOUNCE,
+ STATE_SETUP,
+ STATE_RECORD,
+ STATE_TEARDOWN,
+ STATE_SET_PARAMETER,
+ STATE_FLUSH
+} pa_rtsp_state;
+typedef void (*pa_rtsp_cb_t)(pa_rtsp_context *c, pa_rtsp_state state, pa_headerlist* hl, void *userdata);
pa_rtsp_context* pa_rtsp_context_new(const char* useragent);
void pa_rtsp_context_free(pa_rtsp_context* c);
int pa_rtsp_connect(pa_rtsp_context* c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port);
+void pa_rtsp_set_callback(pa_rtsp_context *c, pa_rtsp_cb_t callback, void *userdata);
+
void pa_rtsp_disconnect(pa_rtsp_context* c);
const char* pa_rtsp_localip(pa_rtsp_context* c);
+uint32_t pa_rtsp_serverport(pa_rtsp_context* c);
void pa_rtsp_set_url(pa_rtsp_context* c, const char* url);
+void pa_rtsp_add_header(pa_rtsp_context *c, const char* key, const char* value);
+void pa_rtsp_remove_header(pa_rtsp_context *c, const char* key);
+
int pa_rtsp_announce(pa_rtsp_context* c, const char* sdp);
-int pa_rtsp_setup(pa_rtsp_context* c, pa_headerlist** response_headers);
+int pa_rtsp_setup(pa_rtsp_context* c);
int pa_rtsp_record(pa_rtsp_context* c);
int pa_rtsp_teardown(pa_rtsp_context* c);