output/raop: consistently use GError

This commit is contained in:
Max Kellermann 2011-08-24 00:14:12 +02:00
parent d6290a2f1a
commit 350aa33022
4 changed files with 157 additions and 81 deletions

View File

@ -63,11 +63,11 @@ raop_mixer_get_volume(struct mixer *mixer, G_GNUC_UNUSED GError **error_r)
}
static bool
raop_mixer_set_volume(struct mixer *mixer, unsigned volume, G_GNUC_UNUSED GError **error_r)
raop_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
{
struct raop_mixer_plugin *rm = (struct raop_mixer_plugin *)mixer;
g_debug("raop_mixer_set_volume\n");
return raop_set_volume(rm->rd, volume);
return raop_set_volume(rm->rd, volume, error_r);
}
const struct mixer_plugin raop_mixer_plugin = {

View File

@ -52,7 +52,7 @@ raop_output_quark(void)
}
static struct raop_data *
new_raop_data(void)
new_raop_data(GError **error_r)
{
struct raop_data *ret = g_new(struct raop_data, 1);
int i;
@ -79,7 +79,8 @@ new_raop_data(void)
raop_session->play_state.last_send.tv_usec = 0;
if (!RAND_bytes(raop_session->encrypt.iv, sizeof(raop_session->encrypt.iv)) || !RAND_bytes(raop_session->encrypt.key, sizeof(raop_session->encrypt.key))) {
g_warning("%s:RAND_bytes error code=%ld\n",__func__,ERR_get_error());
g_set_error(error_r, raop_output_quark(), 0,
"RAND_bytes error code=%ld", ERR_get_error());
return NULL;
}
memcpy(raop_session->encrypt.nv, raop_session->encrypt.iv, sizeof(raop_session->encrypt.nv));
@ -206,7 +207,8 @@ remove_char_from_string(char *str, char c)
* if hostname=NULL, use INADDR_ANY.
* if *port=0, use dynamically assigned port
*/
static int bind_host(int sd, char *hostname, unsigned long ulAddr, unsigned short *port)
static int bind_host(int sd, char *hostname, unsigned long ulAddr,
unsigned short *port, GError **error_r)
{
struct sockaddr_in my_addr;
socklen_t nlen = sizeof(struct sockaddr);
@ -222,7 +224,9 @@ static int bind_host(int sd, char *hostname, unsigned long ulAddr, unsigned shor
my_addr.sin_addr.s_addr=-1;
} else {
if ((my_addr.sin_addr.s_addr = inet_addr(hostname)) == 0xFFFFFFFF) {
g_warning("gethostbyname: '%s' \n", hostname);
g_set_error(error_r, raop_output_quark(), 0,
"failed to resolve host '%s'",
hostname);
return -1;
}
}
@ -245,7 +249,9 @@ static int bind_host(int sd, char *hostname, unsigned long ulAddr, unsigned shor
my_addr.sin_port = htons(*port);
if (bind(sd, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
g_warning("bind error: %s\n", strerror(errno));
g_set_error(error_r, raop_output_quark(), errno,
"failed to bind socket: %s",
g_strerror(errno));
return -1;
}
@ -260,17 +266,21 @@ static int bind_host(int sd, char *hostname, unsigned long ulAddr, unsigned shor
/*
* open tcp port
*/
static int open_tcp_socket(char *hostname, unsigned short *port)
static int
open_tcp_socket(char *hostname, unsigned short *port,
GError **error_r)
{
int sd;
/* socket creation */
sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd < 0) {
g_warning("cannot create tcp socket\n");
g_set_error(error_r, raop_output_quark(), errno,
"failed to create TCP socket: %s",
g_strerror(errno));
return -1;
}
if (bind_host(sd, hostname,0, port)) {
if (bind_host(sd, hostname, 0, port, error_r)) {
close(sd);
return -1;
}
@ -281,7 +291,9 @@ static int open_tcp_socket(char *hostname, unsigned short *port)
/*
* open udp port
*/
static int open_udp_socket(char *hostname, unsigned short *port)
static int
open_udp_socket(char *hostname, unsigned short *port,
GError **error_r)
{
int sd;
int size = 30000;
@ -289,14 +301,18 @@ static int open_udp_socket(char *hostname, unsigned short *port)
/* socket creation */
sd = socket(PF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
g_warning("cannot create udp socket\n");
g_set_error(error_r, raop_output_quark(), errno,
"failed to create UDP socket: %s",
g_strerror(errno));
return -1;
}
if (setsockopt(sd, SOL_SOCKET, SO_SNDBUF, (void *) &size, sizeof(size)) < 0) {
g_warning("Could not set udp send buffer to %d\n", size);
g_set_error(error_r, raop_output_quark(), errno,
"failed to set UDP buffer size: %s",
g_strerror(errno));
return -1;
}
if (bind_host(sd, hostname,0, port)) {
if (bind_host(sd, hostname, 0, port, error_r)) {
close(sd);
return -1;
}
@ -310,14 +326,17 @@ static int open_udp_socket(char *hostname, unsigned short *port)
* nsport is network byte order
*/
static bool
get_tcp_connect(int sd, struct sockaddr_in dest_addr)
get_tcp_connect(int sd, struct sockaddr_in dest_addr, GError **error_r)
{
if (connect(sd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr))){
SLEEP_MSEC(100L);
// try one more time
if (connect(sd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr))) {
g_warning("error:get_tcp_nconnect addr=%s, port=%d\n",
inet_ntoa(dest_addr.sin_addr), ntohs(dest_addr.sin_port));
g_set_error(error_r, raop_output_quark(), errno,
"failed to connect to %s:%d: %s",
inet_ntoa(dest_addr.sin_addr),
ntohs(dest_addr.sin_port),
g_strerror(errno));
return false;
}
}
@ -325,7 +344,9 @@ get_tcp_connect(int sd, struct sockaddr_in dest_addr)
}
static bool
get_sockaddr_by_host(const char *host, short destport, struct sockaddr_in *addr)
get_sockaddr_by_host(const char *host, short destport,
struct sockaddr_in *addr,
GError **error_r)
{
struct hostent *h;
@ -336,7 +357,8 @@ get_sockaddr_by_host(const char *host, short destport, struct sockaddr_in *addr)
} else {
addr->sin_family = AF_INET;
if ((addr->sin_addr.s_addr=inet_addr(host))==0xFFFFFFFF) {
g_warning("gethostbyname: '%s' \n", host);
g_set_error(error_r, raop_output_quark(), 0,
"failed to resolve host '%s'", host);
return false;
}
}
@ -345,13 +367,13 @@ get_sockaddr_by_host(const char *host, short destport, struct sockaddr_in *addr)
}
static bool
get_tcp_connect_by_host(int sd, const char *host, short destport)
get_tcp_connect_by_host(int sd, const char *host, short destport,
GError **error_r)
{
struct sockaddr_in addr;
get_sockaddr_by_host(host, destport, &addr);
return get_tcp_connect(sd, addr);
return get_sockaddr_by_host(host, destport, &addr, error_r) &&
get_tcp_connect(sd, addr, error_r);
}
/*
@ -515,7 +537,8 @@ static bool
exec_request(struct rtspcl_data *rtspcld, const char *cmd,
const char *content_type, const char *content,
int get_response,
const struct key_data *hds, struct key_data **kd)
const struct key_data *hds, struct key_data **kd,
GError **error_r)
{
char line[1024];
char req[1024];
@ -531,7 +554,11 @@ exec_request(struct rtspcl_data *rtspcld, const char *cmd,
int fdmax = 0;
struct timeval tout = {.tv_sec=10, .tv_usec=0};
if (!rtspcld) return false;
if (!rtspcld) {
g_set_error_literal(error_r, raop_output_quark(), 0,
"not connected");
return false;
}
sprintf(req, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, rtspcld->url, ++rtspcld->cseq );
@ -568,6 +595,13 @@ exec_request(struct rtspcl_data *rtspcld, const char *cmd,
strncat(req, content, sizeof(req));
rval = write(rtspcld->fd, req, strlen(req));
if (rval < 0) {
g_set_error(error_r, raop_output_quark(), errno,
"write error: %s",
g_strerror(errno));
return false;
}
g_debug("sent %s", req);
if (!get_response) return true;
@ -587,15 +621,23 @@ exec_request(struct rtspcl_data *rtspcld, const char *cmd,
}
if (read_line(rtspcld->fd, line, sizeof(line), timeout, 0) <= 0) {
g_warning("%s: request failed\n",__func__);
g_set_error_literal(error_r, raop_output_quark(), 0,
"request failed");
return false;
}
g_debug("received %s", line);
token = strtok(line, delimiters);
token = strtok(NULL, delimiters);
if (token == NULL || strcmp(token,"200")) {
g_warning("%s: request failed, error %s\n", __func__, token);
if (token == NULL) {
g_set_error_literal(error_r, raop_output_quark(), 0,
"request failed");
return false;
}
if (strcmp(token, "200") != 0) {
g_set_error(error_r, raop_output_quark(), 0,
"request failed: %s", token);
return false;
}
@ -613,9 +655,11 @@ exec_request(struct rtspcl_data *rtspcld, const char *cmd,
}
dp = strstr(line, ":");
if (!dp) {
g_warning("%s: Request failed, bad header\n", __func__);
free_kd(*kd);
*kd = NULL;
g_set_error_literal(error_r, raop_output_quark(), 0,
"request failed, bad header");
return false;
}
*dp = 0;
@ -636,10 +680,11 @@ exec_request(struct rtspcl_data *rtspcld, const char *cmd,
}
static bool
rtspcl_set_parameter(struct rtspcl_data *rtspcld, const char *parameter)
rtspcl_set_parameter(struct rtspcl_data *rtspcld, const char *parameter,
GError **error_r)
{
return exec_request(rtspcld, "SET_PARAMETER", "text/parameters",
parameter, 1, NULL, &rtspcld->kd);
parameter, 1, NULL, &rtspcld->kd, error_r);
}
static struct rtspcl_data *
@ -692,16 +737,16 @@ rtspcl_add_exthds(struct rtspcl_data *rtspcld, const char *key, char *data)
static bool
rtspcl_connect(struct rtspcl_data *rtspcld, const char *host, short destport,
const char *sid)
const char *sid, GError **error_r)
{
unsigned short myport = 0;
struct sockaddr_in name;
socklen_t namelen = sizeof(name);
if ((rtspcld->fd = open_tcp_socket(NULL, &myport)) == -1)
if ((rtspcld->fd = open_tcp_socket(NULL, &myport, error_r)) == -1)
return false;
if (!get_tcp_connect_by_host(rtspcld->fd, host, destport))
if (!get_tcp_connect_by_host(rtspcld->fd, host, destport, error_r))
return false;
getsockname(rtspcld->fd, (struct sockaddr*)&name, &namelen);
@ -713,13 +758,16 @@ rtspcl_connect(struct rtspcl_data *rtspcld, const char *host, short destport,
}
static bool
rtspcl_announce_sdp(struct rtspcl_data *rtspcld, const char *sdp)
rtspcl_announce_sdp(struct rtspcl_data *rtspcld, const char *sdp,
GError **error_r)
{
return exec_request(rtspcld, "ANNOUNCE", "application/sdp", sdp, 1, NULL, &rtspcld->kd);
return exec_request(rtspcld, "ANNOUNCE", "application/sdp", sdp, 1,
NULL, &rtspcld->kd, error_r);
}
static bool
rtspcl_setup(struct rtspcl_data *rtspcld, struct key_data **kd)
rtspcl_setup(struct rtspcl_data *rtspcld, struct key_data **kd,
GError **error_r)
{
struct key_data *rkd = NULL, hds;
const char delimiters[] = ";";
@ -737,14 +785,18 @@ rtspcl_setup(struct rtspcl_data *rtspcld, struct key_data **kd)
hds.key = transport_key;
hds.data = transport_value;
hds.next = NULL;
if (!exec_request(rtspcld, "SETUP", NULL, NULL, 1, &hds, &rkd)) return false;
if (!exec_request(rtspcld, "SETUP", NULL, NULL, 1,
&hds, &rkd, error_r))
return false;
if (!(rtspcld->session = g_strdup(kd_lookup(rkd, "Session")))) {
g_warning("%s: no session in response\n",__func__);
g_set_error_literal(error_r, raop_output_quark(), 0,
"no session in response");
goto erexit;
}
if (!(rtspcld->transport = kd_lookup(rkd, "Transport"))) {
g_warning("%s: no transport in response\n",__func__);
g_set_error_literal(error_r, raop_output_quark(), 0,
"no transport in response");
goto erexit;
}
buf = g_strdup(rtspcld->transport);
@ -764,11 +816,13 @@ rtspcl_setup(struct rtspcl_data *rtspcld, struct key_data **kd)
token = strtok(NULL, delimiters);
}
if (rtspcld->server_port == 0) {
g_warning("%s: no server_port in response\n",__func__);
g_set_error_literal(error_r, raop_output_quark(), 0,
"no server_port in response");
goto erexit;
}
if (rtspcld->control_port == 0) {
g_warning("%s: no control_port in response\n",__func__);
g_set_error_literal(error_r, raop_output_quark(), 0,
"no control_port in response");
goto erexit;
}
rval = true;
@ -783,10 +837,11 @@ rtspcl_setup(struct rtspcl_data *rtspcld, struct key_data **kd)
}
static bool
rtspcl_record(struct rtspcl_data *rtspcld)
rtspcl_record(struct rtspcl_data *rtspcld, GError **error_r)
{
if (!rtspcld->session) {
g_warning("%s: no session in progress\n", __func__);
g_set_error_literal(error_r, raop_output_quark(), 0,
"no session in progress");
return false;
}
@ -807,7 +862,7 @@ rtspcl_record(struct rtspcl_data *rtspcld)
range.next = &rtp;
return exec_request(rtspcld, "RECORD", NULL, NULL, 1, &range,
&rtspcld->kd);
&rtspcld->kd, error_r);
}
static void
@ -958,7 +1013,7 @@ raopcl_stream_connect(G_GNUC_UNUSED struct raop_data *rd)
static bool
raopcl_connect(struct raop_data *rd)
raopcl_connect(struct raop_data *rd, GError **error_r)
{
unsigned char buf[4 + 8 + 16];
char sid[16];
@ -989,7 +1044,8 @@ raopcl_connect(struct raop_data *rd)
rtspcl_add_exthds(rd->rtspcl, "Client-Instance", sci);
rtspcl_add_exthds(rd->rtspcl, "DACP-ID", sci);
rtspcl_add_exthds(rd->rtspcl, "Active-Remote", act_r);
if (!rtspcl_connect(rd->rtspcl, rd->addr, rd->rtsp_port, sid)) goto erexit;
if (!rtspcl_connect(rd->rtspcl, rd->addr, rd->rtsp_port, sid, error_r))
goto erexit;
i = rsa_encrypt(raop_session->encrypt.key, 16, rsakey);
key = g_base64_encode(rsakey, i);
@ -1010,11 +1066,14 @@ raopcl_connect(struct raop_data *rd)
sid, rtspcl_local_ip(rd->rtspcl), rd->addr, NUMSAMPLES, key, iv);
remove_char_from_string(sac, '=');
// rtspcl_add_exthds(rd->rtspcl, "Apple-Challenge", sac);
if (!rtspcl_announce_sdp(rd->rtspcl, sdp)) goto erexit;
if (!rtspcl_announce_sdp(rd->rtspcl, sdp, error_r))
goto erexit;
// if (!rtspcl_mark_del_exthds(rd->rtspcl, "Apple-Challenge")) goto erexit;
if (!rtspcl_setup(rd->rtspcl, &setup_kd)) goto erexit;
if (!rtspcl_setup(rd->rtspcl, &setup_kd, error_r))
goto erexit;
if (!(aj = kd_lookup(setup_kd,"Audio-Jack-Status"))) {
g_warning("%s: Audio-Jack-Status is missing\n",__func__);
g_set_error_literal(error_r, raop_output_quark(), 0,
"Audio-Jack-Status is missing");
goto erexit;
}
@ -1033,10 +1092,16 @@ raopcl_connect(struct raop_data *rd)
token = strtok(NULL, delimiters);
}
if (!get_sockaddr_by_host(rd->addr, rd->rtspcl->control_port, &rd->ctrl_addr)) goto erexit;
if (!get_sockaddr_by_host(rd->addr, rd->rtspcl->server_port, &rd->data_addr)) goto erexit;
if (!get_sockaddr_by_host(rd->addr, rd->rtspcl->control_port,
&rd->ctrl_addr, error_r))
goto erexit;
if (!rtspcl_record(rd->rtspcl)) goto erexit;
if (!get_sockaddr_by_host(rd->addr, rd->rtspcl->server_port,
&rd->data_addr, error_r))
goto erexit;
if (!rtspcl_record(rd->rtspcl, error_r))
goto erexit;
raopcl_stream_connect(rd);
@ -1076,7 +1141,7 @@ difference (struct timeval *t1, struct timeval *t2)
* requests.
*/
static bool
send_audio_data(int fd)
send_audio_data(int fd, GError **error_r)
{
int i = 0;
struct timeval current_time, tout, rtp_time;
@ -1105,11 +1170,14 @@ send_audio_data(int fd)
raop_session->wblk_remsize, 0, (struct sockaddr *) &rd->data_addr,
sizeof(rd->data_addr));
if (i < 0) {
g_warning("%s: write error: %s\n", __func__, strerror(errno));
g_set_error(error_r, raop_output_quark(), errno,
"write error: %s",
g_strerror(errno));
return false;
}
if (i == 0) {
g_warning("%s: write, disconnected on the other end\n", __func__);
g_set_error_literal(error_r, raop_output_quark(), 0,
"disconnected on the other end");
return false;
}
rd = rd->next;
@ -1124,11 +1192,14 @@ send_audio_data(int fd)
static void *
raop_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
G_GNUC_UNUSED const struct config_param *param,
G_GNUC_UNUSED GError **error)
GError **error_r)
{
struct raop_data *rd;
rd = new_raop_data();
rd = new_raop_data(error_r);
if (rd == NULL)
return NULL;
rd->addr = config_get_block_string(param, "host", NULL);
rd->rtsp_port = config_get_block_unsigned(param, "port", 5000);
rd->volume = config_get_block_unsigned(param, "volume", 75);
@ -1136,11 +1207,11 @@ raop_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
}
static bool
raop_set_volume_local(struct raop_data *rd, int volume)
raop_set_volume_local(struct raop_data *rd, int volume, GError **error_r)
{
char vol_str[128];
sprintf(vol_str, "volume: %d.000000\r\n", volume);
return rtspcl_set_parameter(rd->rtspcl, vol_str);
return rtspcl_set_parameter(rd->rtspcl, vol_str, error_r);
}
@ -1162,7 +1233,7 @@ raop_get_volume(struct raop_data *rd)
}
bool
raop_set_volume(struct raop_data *rd, unsigned volume)
raop_set_volume(struct raop_data *rd, unsigned volume, GError **error_r)
{
int raop_volume;
bool rval;
@ -1175,7 +1246,7 @@ raop_set_volume(struct raop_data *rd, unsigned volume)
(RAOP_VOLUME_MAX - RAOP_VOLUME_MIN) * volume / 100;
}
g_mutex_lock(rd->control_mutex);
rval = raop_set_volume_local(rd, raop_volume);
rval = raop_set_volume_local(rd, raop_volume, error_r);
if (rval) rd->volume = volume;
g_mutex_unlock(rd->control_mutex);
@ -1205,7 +1276,8 @@ raop_output_cancel(void *data)
sprintf(buf, "seq=%d; rtptime=%d", raop_session->play_state.seq_num + flush_diff, raop_session->play_state.rtptime + NUMSAMPLES * flush_diff);
kd.data = buf;
kd.next = NULL;
exec_request(rd->rtspcl, "FLUSH", NULL, NULL, 1, &kd, &(rd->rtspcl->kd));
exec_request(rd->rtspcl, "FLUSH", NULL, NULL, 1,
&kd, &(rd->rtspcl->kd), NULL);
g_mutex_unlock(rd->control_mutex);
}
@ -1256,7 +1328,8 @@ raop_output_close(void *data)
g_mutex_unlock(raop_session->list_mutex);
g_mutex_lock(rd->control_mutex);
exec_request(rd->rtspcl, "TEARDOWN", NULL, NULL, 0, NULL, &(rd->rtspcl->kd));
exec_request(rd->rtspcl, "TEARDOWN", NULL, NULL, 0,
NULL, &rd->rtspcl->kd, NULL);
g_mutex_unlock(rd->control_mutex);
rd->started = 0;
@ -1276,11 +1349,21 @@ raop_output_open(void *data, struct audio_format *audio_format, GError **error_r
raop_session->raop_list = rd;
rd->is_master = true;
if ((raop_session->data_fd = open_udp_socket(NULL, &myport)) == -1)
raop_session->data_fd = open_udp_socket(NULL, &myport,
error_r);
if (raop_session->data_fd < 0)
return false;
if ((raop_session->ntp.fd = open_udp_socket(NULL, &raop_session->ntp.port)) == -1) return false;
if ((raop_session->ctrl.fd = open_udp_socket(NULL, &raop_session->ctrl.port)) == -1) {
raop_session->ntp.fd =
open_udp_socket(NULL, &raop_session->ntp.port,
error_r);
if (raop_session->ntp.fd < 0)
return false;
raop_session->ctrl.fd =
open_udp_socket(NULL, &raop_session->ctrl.port,
error_r);
if (raop_session->ctrl.fd < 0) {
close(raop_session->ntp.fd);
raop_session->ctrl.fd = -1;
g_mutex_unlock(raop_session->list_mutex);
@ -1291,16 +1374,11 @@ raop_output_open(void *data, struct audio_format *audio_format, GError **error_r
audio_format->format = SAMPLE_FORMAT_S16;
g_debug("raop_openDevice %s %d\n", rd->addr, rd->rtsp_port);
if (!raopcl_connect(rd)) {
g_set_error(error_r, raop_output_quark(), -1,
"Unable to connect to device");
if (!raopcl_connect(rd, error_r))
return false;
}
if (!raop_set_volume(rd, rd->volume)) {
g_set_error(error_r, raop_output_quark(), -1,
"Unable to set volume after connecting to device");
if (!raop_set_volume(rd, rd->volume, error_r))
return false;
}
g_mutex_lock(raop_session->list_mutex);
if (!rd->is_master) {
@ -1391,11 +1469,8 @@ raop_output_play(void *data, const void *chunk, size_t size,
raop_session->wblk_remsize = count + RAOP_HEADER_SIZE;
raop_session->wblk_wsize = 0;
if (!send_audio_data(raop_session->data_fd)) {
g_set_error(error_r, raop_output_quark(), -1,
"Unable to write to device");
if (!send_audio_data(raop_session->data_fd, error_r))
goto erexit;
}
raop_session->bufferSize = 0;
}

View File

@ -155,7 +155,7 @@ struct raop_session_data {
/*********************************************************************/
bool
raop_set_volume(struct raop_data *rd, unsigned volume);
raop_set_volume(struct raop_data *rd, unsigned volume, GError **error_r);
int
raop_get_volume(struct raop_data *rd);

View File

@ -60,7 +60,8 @@ pulse_output_set_volume(G_GNUC_UNUSED struct pulse_output *po,
bool
raop_set_volume(G_GNUC_UNUSED struct raop_data *rd,
G_GNUC_UNUSED unsigned volume)
G_GNUC_UNUSED unsigned volume,
G_GNUC_UNUSED GError **error_r)
{
return false;
}