mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-01 22:58:47 -04:00
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
This commit is contained in:
parent
8fb58e3a90
commit
22e299ad3e
3 changed files with 97 additions and 11 deletions
|
|
@ -73,10 +73,10 @@
|
||||||
|
|
||||||
|
|
||||||
struct pa_raop_client {
|
struct pa_raop_client {
|
||||||
pa_rtsp_context *rtsp;
|
pa_mainloop_api *mainloop;
|
||||||
pa_socket_client *sc;
|
|
||||||
const char *host;
|
const char *host;
|
||||||
char *sid;
|
char *sid;
|
||||||
|
pa_rtsp_context *rtsp;
|
||||||
|
|
||||||
uint8_t jack_type;
|
uint8_t jack_type;
|
||||||
uint8_t jack_status;
|
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_nv[AES_CHUNKSIZE]; /* next vector for aes-cbc */
|
||||||
uint8_t aes_key[AES_CHUNKSIZE]; /* key for aes-cbc */
|
uint8_t aes_key[AES_CHUNKSIZE]; /* key for aes-cbc */
|
||||||
|
|
||||||
|
pa_socket_client *sc;
|
||||||
pa_iochannel *io;
|
pa_iochannel *io;
|
||||||
pa_iochannel_cb_t callback;
|
pa_iochannel_cb_t callback;
|
||||||
void* userdata;
|
void* userdata;
|
||||||
|
|
||||||
|
uint8_t *buffer;
|
||||||
|
/*pa_memchunk memchunk;*/
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -219,6 +223,25 @@ static int remove_char_from_string(char *str, char rc)
|
||||||
return num;
|
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)
|
static void rtsp_cb(pa_rtsp_context *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata)
|
||||||
{
|
{
|
||||||
pa_raop_client* c = 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;
|
const char *ip;
|
||||||
char *url;
|
char *url;
|
||||||
|
|
||||||
|
pa_log_debug("RAOP: CONNECTED");
|
||||||
ip = pa_rtsp_localip(c->rtsp);
|
ip = pa_rtsp_localip(c->rtsp);
|
||||||
/* First of all set the url properly */
|
/* First of all set the url properly */
|
||||||
url = pa_sprintf_malloc("rtsp://%s/%s", ip, c->sid);
|
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:
|
case STATE_ANNOUNCE:
|
||||||
|
pa_log_debug("RAOP: ANNOUNCED");
|
||||||
pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
|
pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
|
||||||
pa_rtsp_setup(c->rtsp);
|
pa_rtsp_setup(c->rtsp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_SETUP: {
|
case STATE_SETUP: {
|
||||||
char *aj = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Jack-Status"));
|
char *aj = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Jack-Status"));
|
||||||
|
pa_log_debug("RAOP: SETUP");
|
||||||
if (aj) {
|
if (aj) {
|
||||||
char *token, *pc;
|
char *token, *pc;
|
||||||
char delimiters[] = ";";
|
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(token);
|
||||||
}
|
}
|
||||||
pa_xfree(aj);
|
pa_xfree(aj);
|
||||||
pa_rtsp_record(c->rtsp);
|
|
||||||
} else {
|
} else {
|
||||||
pa_log("Audio Jack Status missing");
|
pa_log_warn("Audio Jack Status missing");
|
||||||
}
|
}
|
||||||
|
pa_rtsp_record(c->rtsp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case STATE_RECORD:
|
case STATE_RECORD: {
|
||||||
/* Connect to the actual stream ;) */
|
uint32_t port = pa_rtsp_serverport(c->rtsp);
|
||||||
/* if(raopcl_stream_connect(raopcld)) goto erexit; */
|
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;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case STATE_TEARDOWN:
|
case STATE_TEARDOWN:
|
||||||
case STATE_SET_PARAMETER:
|
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(c);
|
||||||
pa_assert(host);
|
pa_assert(host);
|
||||||
|
|
||||||
|
c->mainloop = mainloop;
|
||||||
c->host = host;
|
c->host = host;
|
||||||
c->rtsp = pa_rtsp_context_new("iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
|
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)
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
***/
|
***/
|
||||||
|
|
||||||
#include <pulse/mainloop-api.h>
|
#include <pulse/mainloop-api.h>
|
||||||
|
#include <pulsecore/iochannel.h>
|
||||||
|
|
||||||
typedef struct pa_raop_client pa_raop_client;
|
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_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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -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 :) */
|
/* Our packet is created... now we can send it :) */
|
||||||
hdrs = pa_strbuf_tostring_free(buf);
|
hdrs = pa_strbuf_tostring_free(buf);
|
||||||
|
pa_log_debug("Submitting request:");
|
||||||
|
pa_log_debug(hdrs);
|
||||||
l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
|
l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
|
||||||
pa_xfree(hdrs);
|
pa_xfree(hdrs);
|
||||||
|
|
||||||
|
|
@ -220,15 +222,22 @@ static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void *userdata) {
|
||||||
char delimiters[] = " ";
|
char delimiters[] = " ";
|
||||||
pa_rtsp_context *c = userdata;
|
pa_rtsp_context *c = userdata;
|
||||||
pa_assert(c);
|
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 */
|
/* 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();
|
response_headers = pa_headerlist_new();
|
||||||
}
|
}
|
||||||
timeout = 5000;
|
timeout = 5000;
|
||||||
/* read in any response headers */
|
/* read in any response headers */
|
||||||
if (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
|
if (pa_read_line(c->io, response, sizeof(response), timeout) > 0) {
|
||||||
const char* token_state = NULL;
|
const char* token_state = NULL;
|
||||||
|
pa_log_debug("Response Line: %s", response);
|
||||||
|
|
||||||
timeout = 1000;
|
timeout = 1000;
|
||||||
pa_xfree(pa_split(response, delimiters, &token_state));
|
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? */
|
/* We want to return the headers? */
|
||||||
if (!response_headers) {
|
if (!response_headers) {
|
||||||
/* We have no storage, so just clear out the response. */
|
/* 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 {
|
} else {
|
||||||
/* TODO: Move header reading into the headerlist. */
|
/* TODO: Move header reading into the headerlist. */
|
||||||
header = NULL;
|
header = NULL;
|
||||||
buf = pa_strbuf_new();
|
buf = pa_strbuf_new();
|
||||||
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);
|
||||||
/* If the first character is a space, it's a continuation header */
|
/* If the first character is a space, it's a continuation header */
|
||||||
if (header && ' ' == response[0]) {
|
if (header && ' ' == response[0]) {
|
||||||
/* Add this line to the buffer (sans the space. */
|
/* 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 */
|
/* Deal with a SETUP response */
|
||||||
if (STATE_CONNECT == c->state) {
|
if (STATE_SETUP == c->state) {
|
||||||
const char* token_state = NULL;
|
const char* token_state = NULL;
|
||||||
const char* pc = NULL;
|
const char* pc = NULL;
|
||||||
c->session = pa_xstrdup(pa_headerlist_gets(response_headers, "Session"));
|
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 */
|
/* Call our callback */
|
||||||
|
do_callback:
|
||||||
if (c->callback)
|
if (c->callback)
|
||||||
c->callback(c, c->state, response_headers, c->userdata);
|
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)
|
if (res)
|
||||||
c->localip = pa_xstrdup(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) {
|
int pa_rtsp_connect(pa_rtsp_context *c, pa_mainloop_api *mainloop, const char* hostname, uint16_t port) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue