From 8715121755ad1aa9c083dd70781a63ced13359c2 Mon Sep 17 00:00:00 2001 From: Colin Guthrie Date: Sun, 3 Aug 2008 22:46:21 +0100 Subject: Modularise the RAOP stuff that requires OpenSSL and make it optional at compile time --- src/modules/rtp/base64.c | 126 ---------- src/modules/rtp/base64.h | 34 --- src/modules/rtp/raop_client.c | 561 ------------------------------------------ src/modules/rtp/raop_client.h | 46 ---- 4 files changed, 767 deletions(-) delete mode 100644 src/modules/rtp/base64.c delete mode 100644 src/modules/rtp/base64.h delete mode 100644 src/modules/rtp/raop_client.c delete mode 100644 src/modules/rtp/raop_client.h (limited to 'src/modules/rtp') diff --git a/src/modules/rtp/base64.c b/src/modules/rtp/base64.c deleted file mode 100644 index 8918def8..00000000 --- a/src/modules/rtp/base64.c +++ /dev/null @@ -1,126 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2008 Colin Guthrie - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/* - This file was originally inspired by a file developed by - Kungliga Tekniska H�gskolan -*/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include - -#include "base64.h" - -static const char base64_chars[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -static int pos(char c) -{ - if (c >= 'A' && c <= 'Z') return c - 'A' + 0; - if (c >= 'a' && c <= 'z') return c - 'a' + 26; - if (c >= '0' && c <= '9') return c - '0' + 52; - if (c == '+') return 62; - if (c == '/') return 63; -} - -int pa_base64_encode(const void *data, int size, char **str) -{ - char *s, *p; - int i; - int c; - const unsigned char *q; - - p = s = pa_xnew(char, size * 4 / 3 + 4); - q = (const unsigned char *) data; - i = 0; - for (i = 0; i < size;) { - c = q[i++]; - c *= 256; - if (i < size) - c += q[i]; - i++; - c *= 256; - if (i < size) - c += q[i]; - i++; - p[0] = base64_chars[(c & 0x00fc0000) >> 18]; - p[1] = base64_chars[(c & 0x0003f000) >> 12]; - p[2] = base64_chars[(c & 0x00000fc0) >> 6]; - p[3] = base64_chars[(c & 0x0000003f) >> 0]; - if (i > size) - p[3] = '='; - if (i > size + 1) - p[2] = '='; - p += 4; - } - *p = 0; - *str = s; - return strlen(s); -} - -#define DECODE_ERROR 0xffffffff - -static unsigned int token_decode(const char *token) -{ - int i; - unsigned int val = 0; - int marker = 0; - if (strlen(token) < 4) - return DECODE_ERROR; - for (i = 0; i < 4; i++) { - val *= 64; - if (token[i] == '=') - marker++; - else if (marker > 0) - return DECODE_ERROR; - else - val += pos(token[i]); - } - if (marker > 2) - return DECODE_ERROR; - return (marker << 24) | val; -} - -int pa_base64_decode(const char *str, void *data) -{ - const char *p; - unsigned char *q; - - q = data; - for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) { - unsigned int val = token_decode(p); - unsigned int marker = (val >> 24) & 0xff; - if (val == DECODE_ERROR) - return -1; - *q++ = (val >> 16) & 0xff; - if (marker < 2) - *q++ = (val >> 8) & 0xff; - if (marker < 1) - *q++ = val & 0xff; - } - return q - (unsigned char *) data; -} diff --git a/src/modules/rtp/base64.h b/src/modules/rtp/base64.h deleted file mode 100644 index dac0e707..00000000 --- a/src/modules/rtp/base64.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef foobase64hfoo -#define foobase64hfoo - -/*** - This file is part of PulseAudio. - - Copyright 2008 Colin Guthrie - Copyright Kungliga Tekniska Høgskolan - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/* - This file was originally inspired by a file developed by - Kungliga Tekniska Høgskolan -*/ - -int pa_base64_encode(const void *data, int size, char **str); -int pa_base64_decode(const char *str, void *data); - -#endif diff --git a/src/modules/rtp/raop_client.c b/src/modules/rtp/raop_client.c deleted file mode 100644 index 4627545e..00000000 --- a/src/modules/rtp/raop_client.c +++ /dev/null @@ -1,561 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2008 Colin Guthrie - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_FILIO_H -#include -#endif - -/* TODO: Replace OpenSSL with NSS */ -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "raop_client.h" -#include "rtsp_client.h" -#include "base64.h" - -#define AES_CHUNKSIZE 16 - -#define JACK_STATUS_DISCONNECTED 0 -#define JACK_STATUS_CONNECTED 1 - -#define JACK_TYPE_ANALOG 0 -#define JACK_TYPE_DIGITAL 1 - -#define VOLUME_DEF -30 -#define VOLUME_MIN -144 -#define VOLUME_MAX 0 - - -struct pa_raop_client { - pa_core *core; - char *host; - char *sid; - pa_rtsp_client *rtsp; - - uint8_t jack_type; - uint8_t jack_status; - - /* Encryption Related bits */ - AES_KEY aes; - uint8_t aes_iv[AES_CHUNKSIZE]; /* initialization 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 */ - - pa_socket_client *sc; - int fd; - - uint16_t seq; - uint32_t rtptime; - - pa_raop_client_cb_t callback; - void* userdata; - pa_raop_client_closed_cb_t closed_callback; - void* closed_userdata; -}; - -/** - * Function to write bits into a buffer. - * @param buffer Handle to the buffer. It will be incremented if new data requires it. - * @param bit_pos A pointer to a position buffer to keep track the current write location (0 for MSB, 7 for LSB) - * @param size A pointer to the byte size currently written. This allows the calling function to do simple buffer overflow checks - * @param data The data to write - * @param data_bit_len The number of bits from data to write - */ -static inline void bit_writer(uint8_t **buffer, uint8_t *bit_pos, int *size, uint8_t data, uint8_t data_bit_len) { - int bits_left, bit_overflow; - uint8_t bit_data; - - if (!data_bit_len) - return; - - /* If bit pos is zero, we will definatly use at least one bit from the current byte so size increments. */ - if (!*bit_pos) - *size += 1; - - /* Calc the number of bits left in the current byte of buffer */ - bits_left = 7 - *bit_pos + 1; - /* Calc the overflow of bits in relation to how much space we have left... */ - bit_overflow = bits_left - data_bit_len; - if (bit_overflow >= 0) { - /* We can fit the new data in our current byte */ - /* As we write from MSB->LSB we need to left shift by the overflow amount */ - bit_data = data << bit_overflow; - if (*bit_pos) - **buffer |= bit_data; - else - **buffer = bit_data; - /* If our data fits exactly into the current byte, we need to increment our pointer */ - if (0 == bit_overflow) { - /* Do not increment size as it will be incremeneted on next call as bit_pos is zero */ - *buffer += 1; - *bit_pos = 0; - } else { - *bit_pos += data_bit_len; - } - } else { - /* bit_overflow is negative, there for we will need a new byte from our buffer */ - /* Firstly fill up what's left in the current byte */ - bit_data = data >> -bit_overflow; - **buffer |= bit_data; - /* Increment our buffer pointer and size counter*/ - *buffer += 1; - *size += 1; - **buffer = data << (8 + bit_overflow); - *bit_pos = -bit_overflow; - } -} - -static int rsa_encrypt(uint8_t *text, int len, uint8_t *res) { - const char n[] = - "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC" - "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR" - "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB" - "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ" - "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh" - "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew=="; - const char e[] = "AQAB"; - uint8_t modules[256]; - uint8_t exponent[8]; - int size; - RSA *rsa; - - rsa = RSA_new(); - size = pa_base64_decode(n, modules); - rsa->n = BN_bin2bn(modules, size, NULL); - size = pa_base64_decode(e, exponent); - rsa->e = BN_bin2bn(exponent, size, NULL); - - size = RSA_public_encrypt(len, text, res, rsa, RSA_PKCS1_OAEP_PADDING); - RSA_free(rsa); - return size; -} - -static int aes_encrypt(pa_raop_client* c, uint8_t *data, int size) -{ - uint8_t *buf; - int i=0, j; - - pa_assert(c); - - memcpy(c->aes_nv, c->aes_iv, AES_CHUNKSIZE); - while (i+AES_CHUNKSIZE <= size) { - buf = data + i; - for (j=0; jaes_nv[j]; - - AES_encrypt(buf, buf, &c->aes); - memcpy(c->aes_nv, buf, AES_CHUNKSIZE); - i += AES_CHUNKSIZE; - } - return i; -} - -static inline void rtrimchar(char *str, char rc) -{ - char *sp = str + strlen(str) - 1; - while (sp >= str && *sp == rc) { - *sp = '\0'; - sp -= 1; - } -} - -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_assert(c->fd < 0); - pa_assert(c->callback); - - pa_socket_client_unref(c->sc); - c->sc = NULL; - - if (!io) { - pa_log("Connection failed: %s", pa_cstrerror(errno)); - return; - } - - c->fd = pa_iochannel_get_send_fd(io); - - pa_iochannel_set_noclose(io, TRUE); - pa_iochannel_socket_set_sndbuf(io, 1024); - pa_iochannel_free(io); - - pa_make_tcp_socket_low_delay(c->fd); - - pa_log_debug("Connection established"); - c->callback(c->fd, c->userdata); -} - -static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata) -{ - pa_raop_client* c = userdata; - pa_assert(c); - pa_assert(rtsp); - pa_assert(rtsp == c->rtsp); - - switch (state) { - case STATE_CONNECT: { - int i; - uint8_t rsakey[512]; - char *key, *iv, *sac, *sdp; - uint16_t rand_data; - 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); - pa_rtsp_set_url(c->rtsp, url); - pa_xfree(url); - - /* Now encrypt our aes_public key to send to the device */ - i = rsa_encrypt(c->aes_key, AES_CHUNKSIZE, rsakey); - pa_base64_encode(rsakey, i, &key); - rtrimchar(key, '='); - pa_base64_encode(c->aes_iv, AES_CHUNKSIZE, &iv); - rtrimchar(iv, '='); - - pa_random(&rand_data, sizeof(rand_data)); - pa_base64_encode(&rand_data, AES_CHUNKSIZE, &sac); - rtrimchar(sac, '='); - pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac); - sdp = pa_sprintf_malloc( - "v=0\r\n" - "o=iTunes %s 0 IN IP4 %s\r\n" - "s=iTunes\r\n" - "c=IN IP4 %s\r\n" - "t=0 0\r\n" - "m=audio 0 RTP/AVP 96\r\n" - "a=rtpmap:96 AppleLossless\r\n" - "a=fmtp:96 4096 0 16 40 10 14 2 255 0 0 44100\r\n" - "a=rsaaeskey:%s\r\n" - "a=aesiv:%s\r\n", - c->sid, ip, c->host, key, iv); - pa_rtsp_announce(c->rtsp, sdp); - pa_xfree(key); - pa_xfree(iv); - pa_xfree(sac); - pa_xfree(sdp); - break; - } - - 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[] = ";"; - const char* token_state = NULL; - c->jack_type = JACK_TYPE_ANALOG; - c->jack_status = JACK_STATUS_DISCONNECTED; - - while ((token = pa_split(aj, delimiters, &token_state))) { - if ((pc = strstr(token, "="))) { - *pc = 0; - if (!strcmp(token, "type") && !strcmp(pc+1, "digital")) { - c->jack_type = JACK_TYPE_DIGITAL; - } - } else { - if (!strcmp(token,"connected")) - c->jack_status = JACK_STATUS_CONNECTED; - } - pa_xfree(token); - } - pa_xfree(aj); - } else { - pa_log_warn("Audio Jack Status missing"); - } - pa_rtsp_record(c->rtsp, &c->seq, &c->rtptime); - break; - } - - 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->core->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_FLUSH: - pa_log_debug("RAOP: FLUSHED"); - break; - - case STATE_TEARDOWN: - case STATE_SET_PARAMETER: - pa_log_debug("RAOP: SET_PARAMETER"); - break; - case STATE_DISCONNECTED: - pa_assert(c->closed_callback); - pa_assert(c->rtsp); - - pa_log_debug("RTSP control channel closed"); - pa_rtsp_client_free(c->rtsp); - c->rtsp = NULL; - if (c->fd > 0) { - /* We do not close the fd, we leave it to the closed callback to do that */ - c->fd = -1; - } - if (c->sc) { - pa_socket_client_unref(c->sc); - c->sc = NULL; - } - pa_xfree(c->sid); - c->sid = NULL; - c->closed_callback(c->closed_userdata); - break; - } -} - -pa_raop_client* pa_raop_client_new(pa_core *core, const char* host) -{ - pa_raop_client* c = pa_xnew0(pa_raop_client, 1); - - pa_assert(core); - pa_assert(host); - - c->core = core; - c->fd = -1; - c->host = pa_xstrdup(host); - - if (pa_raop_connect(c)) { - pa_raop_client_free(c); - return NULL; - } - return c; -} - - -void pa_raop_client_free(pa_raop_client* c) -{ - pa_assert(c); - - if (c->rtsp) - pa_rtsp_client_free(c->rtsp); - pa_xfree(c->host); - pa_xfree(c); -} - - -int pa_raop_connect(pa_raop_client* c) -{ - char *sci; - struct { - uint32_t a; - uint32_t b; - uint32_t c; - } rand_data; - - pa_assert(c); - - if (c->rtsp) { - pa_log_debug("Connection already in progress"); - return 0; - } - - c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, 5000, "iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)"); - - /* Initialise the AES encryption system */ - pa_random(c->aes_iv, sizeof(c->aes_iv)); - pa_random(c->aes_key, sizeof(c->aes_key)); - memcpy(c->aes_nv, c->aes_iv, sizeof(c->aes_nv)); - AES_set_encrypt_key(c->aes_key, 128, &c->aes); - - /* Generate random instance id */ - pa_random(&rand_data, sizeof(rand_data)); - c->sid = pa_sprintf_malloc("%u", rand_data.a); - sci = pa_sprintf_malloc("%08x%08x",rand_data.b, rand_data.c); - pa_rtsp_add_header(c->rtsp, "Client-Instance", sci); - pa_xfree(sci); - pa_rtsp_set_callback(c->rtsp, rtsp_cb, c); - return pa_rtsp_connect(c->rtsp); -} - - -int pa_raop_flush(pa_raop_client* c) -{ - pa_assert(c); - - pa_rtsp_flush(c->rtsp, c->seq, c->rtptime); - return 0; -} - - -int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume) -{ - int rv; - double db; - char *param; - - pa_assert(c); - - db = pa_sw_volume_to_dB(volume); - if (db < VOLUME_MIN) - db = VOLUME_MIN; - else if (db > VOLUME_MAX) - db = VOLUME_MAX; - - param = pa_sprintf_malloc("volume: %0.6f\r\n", db); - - /* We just hit and hope, cannot wait for the callback */ - rv = pa_rtsp_setparameter(c->rtsp, param); - pa_xfree(param); - return rv; -} - - -int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded) -{ - uint16_t len; - size_t bufmax; - uint8_t *bp, bpos; - uint8_t *ibp, *maxibp; - int size; - uint8_t *b, *p; - uint32_t bsize; - size_t length; - static uint8_t header[] = { - 0x24, 0x00, 0x00, 0x00, - 0xF0, 0xFF, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - }; - int header_size = sizeof(header); - - pa_assert(c); - pa_assert(c->fd > 0); - pa_assert(raw); - pa_assert(raw->memblock); - pa_assert(raw->length > 0); - pa_assert(encoded); - - /* We have to send 4 byte chunks */ - bsize = (int)(raw->length / 4); - length = bsize * 4; - - /* Leave 16 bytes extra to allow for the ALAC header which is about 55 bits */ - bufmax = length + header_size + 16; - pa_memchunk_reset(encoded); - encoded->memblock = pa_memblock_new(c->core->mempool, bufmax); - b = pa_memblock_acquire(encoded->memblock); - memcpy(b, header, header_size); - - /* Now write the actual samples */ - bp = b + header_size; - size = bpos = 0; - bit_writer(&bp,&bpos,&size,1,3); /* channel=1, stereo */ - bit_writer(&bp,&bpos,&size,0,4); /* unknown */ - bit_writer(&bp,&bpos,&size,0,8); /* unknown */ - bit_writer(&bp,&bpos,&size,0,4); /* unknown */ - bit_writer(&bp,&bpos,&size,1,1); /* hassize */ - bit_writer(&bp,&bpos,&size,0,2); /* unused */ - bit_writer(&bp,&bpos,&size,1,1); /* is-not-compressed */ - - /* size of data, integer, big endian */ - bit_writer(&bp,&bpos,&size,(bsize>>24)&0xff,8); - bit_writer(&bp,&bpos,&size,(bsize>>16)&0xff,8); - bit_writer(&bp,&bpos,&size,(bsize>>8)&0xff,8); - bit_writer(&bp,&bpos,&size,(bsize)&0xff,8); - - ibp = p = pa_memblock_acquire(raw->memblock); - maxibp = p + raw->length - 4; - while (ibp <= maxibp) { - /* Byte swap stereo data */ - bit_writer(&bp,&bpos,&size,*(ibp+1),8); - bit_writer(&bp,&bpos,&size,*(ibp+0),8); - bit_writer(&bp,&bpos,&size,*(ibp+3),8); - bit_writer(&bp,&bpos,&size,*(ibp+2),8); - ibp += 4; - raw->index += 4; - raw->length -= 4; - } - pa_memblock_release(raw->memblock); - encoded->length = header_size + size; - - /* store the lenght (endian swapped: make this better) */ - len = size + header_size - 4; - *(b + 2) = len >> 8; - *(b + 3) = len & 0xff; - - /* encrypt our data */ - aes_encrypt(c, (b + header_size), size); - - /* We're done with the chunk */ - pa_memblock_release(encoded->memblock); - - return 0; -} - - -void pa_raop_client_set_callback(pa_raop_client* c, pa_raop_client_cb_t callback, void *userdata) -{ - pa_assert(c); - - c->callback = callback; - c->userdata = userdata; -} - -void pa_raop_client_set_closed_callback(pa_raop_client* c, pa_raop_client_closed_cb_t callback, void *userdata) -{ - pa_assert(c); - - c->closed_callback = callback; - c->closed_userdata = userdata; -} diff --git a/src/modules/rtp/raop_client.h b/src/modules/rtp/raop_client.h deleted file mode 100644 index ec3136a7..00000000 --- a/src/modules/rtp/raop_client.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef fooraopclientfoo -#define fooraopclientfoo - -/*** - This file is part of PulseAudio. - - Copyright 2008 Colin Guthrie - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#include -#include -#include - -typedef struct pa_raop_client pa_raop_client; - -pa_raop_client* pa_raop_client_new(pa_core *core, const char* host); -void pa_raop_client_free(pa_raop_client* c); - -int pa_raop_connect(pa_raop_client* c); -int pa_raop_flush(pa_raop_client* c); - -int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume); -int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded); - -typedef void (*pa_raop_client_cb_t)(int fd, void *userdata); -void pa_raop_client_set_callback(pa_raop_client* c, pa_raop_client_cb_t callback, void *userdata); - -typedef void (*pa_raop_client_closed_cb_t)(void *userdata); -void pa_raop_client_set_closed_callback(pa_raop_client* c, pa_raop_client_closed_cb_t callback, void *userdata); - -#endif -- cgit