From 22e299ad3e16d1a2636653a7be9d625ecdc23802 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Tue, 6 May 2008 18:39:09 +0000 Subject: [PATCH] Add a pa_iochannel callback for when the RAOP connection connects. Properly handle the sequence of events that establish a connection. git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/coling@2369 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/rtp/raop_client.c | 83 +++++++++++++++++++++++++++++++++++++++---- src/modules/rtp/raop_client.h | 3 ++ src/modules/rtp/rtsp.c | 22 +++++++++--- 3 files changed, 97 insertions(+), 11 deletions(-) diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c index fbcbe4b..8f6f259 100644 --- a/src/modules/rtp/raop_client.c +++ b/src/modules/rtp/raop_client.c @@ -73,10 +73,10 @@ struct pa_raop_client { - pa_rtsp_context *rtsp; - pa_socket_client *sc; + pa_mainloop_api *mainloop; const char *host; char *sid; + pa_rtsp_context *rtsp; uint8_t jack_type; uint8_t jack_status; @@ -87,9 +87,13 @@ struct pa_raop_client { uint8_t aes_nv[AES_CHUNKSIZE]; /* next vector for aes-cbc */ uint8_t aes_key[AES_CHUNKSIZE]; /* key for aes-cbc */ + pa_socket_client *sc; pa_iochannel *io; pa_iochannel_cb_t callback; void* userdata; + + uint8_t *buffer; + /*pa_memchunk memchunk;*/ }; /** @@ -219,6 +223,25 @@ static int remove_char_from_string(char *str, char rc) return num; } +static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) { + pa_raop_client *c = userdata; + + pa_assert(sc); + pa_assert(c); + pa_assert(c->sc == sc); + + pa_socket_client_unref(c->sc); + c->sc = NULL; + + if (!io) { + pa_log("Connection failed: %s", pa_cstrerror(errno)); + return; + } + pa_assert(!c->io); + c->io = io; + pa_iochannel_set_callback(c->io, c->callback, c->userdata); +} + static void rtsp_cb(pa_rtsp_context *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata) { pa_raop_client* c = userdata; @@ -235,6 +258,7 @@ static void rtsp_cb(pa_rtsp_context *rtsp, pa_rtsp_state state, pa_headerlist* h const char *ip; char *url; + pa_log_debug("RAOP: CONNECTED"); ip = pa_rtsp_localip(c->rtsp); /* First of all set the url properly */ url = pa_sprintf_malloc("rtsp://%s/%s", ip, c->sid); @@ -273,12 +297,14 @@ static void rtsp_cb(pa_rtsp_context *rtsp, pa_rtsp_state state, pa_headerlist* h } case STATE_ANNOUNCE: + pa_log_debug("RAOP: ANNOUNCED"); pa_rtsp_remove_header(c->rtsp, "Apple-Challenge"); pa_rtsp_setup(c->rtsp); break; case STATE_SETUP: { char *aj = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Jack-Status")); + pa_log_debug("RAOP: SETUP"); if (aj) { char *token, *pc; char delimiters[] = ";"; @@ -299,17 +325,24 @@ static void rtsp_cb(pa_rtsp_context *rtsp, pa_rtsp_state state, pa_headerlist* h pa_xfree(token); } pa_xfree(aj); - pa_rtsp_record(c->rtsp); } else { - pa_log("Audio Jack Status missing"); + pa_log_warn("Audio Jack Status missing"); } + pa_rtsp_record(c->rtsp); break; } - case STATE_RECORD: - /* Connect to the actual stream ;) */ - /* if(raopcl_stream_connect(raopcld)) goto erexit; */ + case STATE_RECORD: { + uint32_t port = pa_rtsp_serverport(c->rtsp); + pa_log_debug("RAOP: RECORDED"); + + if (!(c->sc = pa_socket_client_new_string(c->mainloop, c->host, port))) { + pa_log("failed to connect to server '%s:%d'", c->host, port); + return; + } + pa_socket_client_set_callback(c->sc, on_connection, c); break; + } case STATE_TEARDOWN: case STATE_SET_PARAMETER: @@ -330,6 +363,7 @@ int pa_raop_client_connect(pa_raop_client* c, pa_mainloop_api *mainloop, const c pa_assert(c); pa_assert(host); + c->mainloop = mainloop; c->host = host; c->rtsp = pa_rtsp_context_new("iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)"); @@ -356,5 +390,40 @@ void pa_raop_client_disconnect(pa_raop_client* c) void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, unsigned int count) { + ssize_t l; + uint16_t len; + static uint8_t header[] = { + 0x24, 0x00, 0x00, 0x00, + 0xF0, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + const int header_size = sizeof(header); + + pa_assert(c); + pa_assert(buffer); + pa_assert(count > 0); + + c->buffer = pa_xrealloc(c->buffer, (count + header_size + 16)); + memcpy(c->buffer, header, header_size); + len = count + header_size - 4; + + /* store the lenght (endian swapped: make this better) */ + *(c->buffer + 2) = len >> 8; + *(c->buffer + 3) = len & 0xff; + + memcpy((c->buffer+header_size), buffer, count); + aes_encrypt(c, (c->buffer + header_size), count); + len = header_size + count; + + /* TODO: move this into a memchunk/memblock and write only in callback */ + l = pa_iochannel_write(c->io, c->buffer, len); +} + +void pa_raop_client_set_callback(pa_raop_client* c, pa_iochannel_cb_t callback, void *userdata) +{ + pa_assert(c); + c->callback = callback; + c->userdata = userdata; } diff --git a/src/modules/rtp/raop_client.h b/src/modules/rtp/raop_client.h index 499b124..99c75fd 100644 --- a/src/modules/rtp/raop_client.h +++ b/src/modules/rtp/raop_client.h @@ -25,6 +25,7 @@ ***/ #include +#include typedef struct pa_raop_client pa_raop_client; @@ -37,4 +38,6 @@ void pa_raop_client_disconnect(pa_raop_client* c); void pa_raop_client_send_sample(pa_raop_client* c, const uint8_t* buffer, unsigned int count); +void pa_raop_client_set_callback(pa_raop_client* c, pa_iochannel_cb_t callback, void *userdata); + #endif diff --git a/src/modules/rtp/rtsp.c b/src/modules/rtp/rtsp.c index 55d9101..4f2411a 100644 --- a/src/modules/rtp/rtsp.c +++ b/src/modules/rtp/rtsp.c @@ -172,6 +172,8 @@ static int pa_rtsp_exec(pa_rtsp_context* c, const char* cmd, /* Our packet is created... now we can send it :) */ hdrs = pa_strbuf_tostring_free(buf); + pa_log_debug("Submitting request:"); + pa_log_debug(hdrs); l = pa_iochannel_write(c->io, hdrs, strlen(hdrs)); pa_xfree(hdrs); @@ -220,15 +222,22 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) { char delimiters[] = " "; pa_rtsp_context *c = userdata; pa_assert(c); + pa_assert(c->io == io); + + if (!pa_iochannel_is_readable(c->io)) { + if (STATE_SETUP == c->state || STATE_ANNOUNCE == c->state) return; + goto do_callback; + } /* TODO: convert this to a pa_ioline based reader */ - if (STATE_CONNECT == c->state) { + if (STATE_SETUP == c->state || STATE_ANNOUNCE == 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; + pa_log_debug("Response Line: %s", response); timeout = 1000; pa_xfree(pa_split(response, delimiters, &token_state)); @@ -244,12 +253,15 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) { /* 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); + while (pa_read_line(c->io, response, sizeof(response), timeout) > 0){ + pa_log_debug("Response Line: %s", response); + } } 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) { + pa_log_debug("Response Line: %s", response); /* 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. */ @@ -297,8 +309,8 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) { } } - /* Deal with a CONNECT response */ - if (STATE_CONNECT == c->state) { + /* Deal with a SETUP response */ + if (STATE_SETUP == c->state) { const char* token_state = NULL; const char* pc = NULL; c->session = pa_xstrdup(pa_headerlist_gets(response_headers, "Session")); @@ -330,6 +342,7 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) { } /* Call our callback */ +do_callback: if (c->callback) c->callback(c, c->state, response_headers, c->userdata); @@ -387,6 +400,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata if (res) c->localip = pa_xstrdup(res); } + pa_log_debug("Established RTSP connection from local ip %s", c->localip); } int pa_rtsp_connect(pa_rtsp_context *c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port) { -- 2.7.4