diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 4ca76e052..2e509ba53 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -173,6 +173,7 @@ struct impl { uint16_t server_port; int server_fd; + struct spa_source *server_source; uint32_t block_size; uint32_t delay; @@ -537,7 +538,7 @@ error: return res; } -static int connect_udp_socket(struct impl *impl, int fd, uint16_t port) +static int connect_socket(struct impl *impl, int type, int fd, uint16_t port) { const char *host; struct sockaddr_in sa4; @@ -566,12 +567,13 @@ static int connect_udp_socket(struct impl *impl, int fd, uint16_t port) } if (fd < 0 && - (fd = socket(af, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) { + (fd = socket(af, type | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) { pw_log_error("socket failed: %m"); return -errno; } - if (connect(fd, sa, salen) < 0) { + res = connect(fd, sa, salen); + if (res < 0 && errno != EINPROGRESS) { res = -errno; pw_log_error("connect failed: %m"); goto error; @@ -726,6 +728,37 @@ static int rtsp_do_record(struct impl *impl) return res; } +static void +on_server_source_io(void *data, int fd, uint32_t mask) +{ + struct impl *impl = data; + + if (mask & (SPA_IO_ERR | SPA_IO_HUP)) + goto error; + if (mask & SPA_IO_OUT) { + int res; + socklen_t len; + + pw_loop_update_io(impl->loop, impl->server_source, + impl->server_source->mask & ~SPA_IO_OUT); + + len = sizeof(res); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &res, &len) < 0) { + pw_log_error("getsockopt: %m"); + goto error; + } + if (res != 0) + goto error; + + impl->ready = true; + if (pw_stream_get_state(impl->stream, NULL) == PW_STREAM_STATE_STREAMING) + rtsp_do_record(impl); + } + return; +error: + pw_loop_update_io(impl->loop, impl->server_source, 0); +} + static void rtsp_setup_reply(void *data, int status, const struct spa_dict *headers) { struct impl *impl = data; @@ -763,10 +796,19 @@ static void rtsp_setup_reply(void *data, int status, const struct spa_dict *head pw_log_error("missing server port in Transport"); return; } + + pw_getrandom(&impl->seq, sizeof(impl->seq), 0); + pw_getrandom(&impl->rtptime, sizeof(impl->rtptime), 0); + pw_log_info("server port:%u", impl->server_port); switch (impl->protocol) { case PROTO_TCP: + if ((impl->server_fd = connect_socket(impl, SOCK_STREAM, -1, impl->server_port)) <= 0) + return; + + impl->server_source = pw_loop_add_io(impl->loop, impl->server_fd, + SPA_IO_OUT, false, on_server_source_io, impl); break; case PROTO_UDP: @@ -776,11 +818,11 @@ static void rtsp_setup_reply(void *data, int status, const struct spa_dict *head } pw_log_info("control:%u timing:%u", control_port, timing_port); - if ((impl->server_fd = connect_udp_socket(impl, -1, impl->server_port)) <= 0) + if ((impl->server_fd = connect_socket(impl, SOCK_DGRAM, -1, impl->server_port)) <= 0) return; - if ((impl->control_fd = connect_udp_socket(impl, impl->control_fd, control_port)) <= 0) + if ((impl->control_fd = connect_socket(impl, SOCK_DGRAM, impl->control_fd, control_port)) <= 0) return; - if ((impl->timing_fd = connect_udp_socket(impl, impl->timing_fd, timing_port)) <= 0) + if ((impl->timing_fd = connect_socket(impl, SOCK_DGRAM, impl->timing_fd, timing_port)) <= 0) return; ntp = ntp_now(CLOCK_MONOTONIC); @@ -790,18 +832,14 @@ static void rtsp_setup_reply(void *data, int status, const struct spa_dict *head SPA_IO_IN, false, on_timing_source_io, impl); impl->control_source = pw_loop_add_io(impl->loop, impl->control_fd, SPA_IO_IN, false, on_control_source_io, impl); + + impl->ready = true; + if (pw_stream_get_state(impl->stream, NULL) == PW_STREAM_STATE_STREAMING) + rtsp_do_record(impl); break; default: return; } - - pw_getrandom(&impl->seq, sizeof(impl->seq), 0); - pw_getrandom(&impl->rtptime, sizeof(impl->rtptime), 0); - - impl->ready = true; - - if (pw_stream_get_state(impl->stream, NULL) == PW_STREAM_STATE_STREAMING) - rtsp_do_record(impl); } static int rtsp_do_setup(struct impl *impl) @@ -1168,6 +1206,10 @@ static void connection_cleanup(struct impl *impl) close(impl->timing_fd); impl->timing_fd = -1; } + if (impl->server_source != NULL) { + pw_loop_destroy_source(impl->loop, impl->server_source); + impl->server_source = NULL; + } if (impl->timing_source != NULL) { pw_loop_destroy_source(impl->loop, impl->timing_source); impl->timing_source = NULL; diff --git a/src/modules/module-raop/rtsp-client.c b/src/modules/module-raop/rtsp-client.c index 48196e1c4..c1740ea5f 100644 --- a/src/modules/module-raop/rtsp-client.c +++ b/src/modules/module-raop/rtsp-client.c @@ -388,51 +388,52 @@ error: int pw_rtsp_client_connect(struct pw_rtsp_client *client, const char *hostname, uint16_t port, const char *session_id) { - struct sockaddr_in my_addr, dest_addr; - struct hostent *h; - int fd; + struct addrinfo hints; + struct addrinfo *result, *rp; + int res, fd; + char port_str[12]; if (client->source != NULL) pw_rtsp_client_disconnect(client); pw_log_info("%p: connect %s:%u", client, hostname, port); - fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); - if (fd < 0) - return -errno; - spa_zero(my_addr); - my_addr.sin_addr.s_addr = htonl(INADDR_ANY); - my_addr.sin_family = AF_INET; - my_addr.sin_port = 0; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = 0; + hints.ai_protocol = 0; - if (bind(fd, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) { - pw_log_error("%p: bind failed: %m", client); - close(fd); - return -errno; + spa_scnprintf(port_str, sizeof(port_str), "%u", port); + + if ((res = getaddrinfo(hostname, port_str, &hints, &result)) != 0) { + pw_log_error("getaddrinfo: %s", gai_strerror(res)); + return -EINVAL; } + for (rp = result; rp != NULL; rp = rp->ai_next) { + fd = socket(rp->ai_family, + rp->ai_socktype | SOCK_CLOEXEC | SOCK_NONBLOCK, + rp->ai_protocol); + if (fd == -1) + continue; - h = gethostbyname(hostname); - if (h != NULL) { - dest_addr.sin_family = h->h_addrtype; - memcpy((char*) &dest_addr.sin_addr.s_addr, h->h_addr_list[0], h->h_length); - } else { - dest_addr.sin_family = AF_INET; - if ((dest_addr.sin_addr.s_addr = inet_addr(hostname)) == 0xFFFFFFFF) - return -1; - } - dest_addr.sin_port = htons(port); + res = connect(fd, rp->ai_addr, rp->ai_addrlen); + if (res == 0 || (res < 0 && errno == EINPROGRESS)) + break; - if (connect(fd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr)) < 0) { - if (errno != EINPROGRESS) { - pw_log_error("%p: connect failed: %m", client); - close(fd); - return -errno; - } + close(fd); + } + freeaddrinfo(result); + + if (rp == NULL) { + pw_log_error("Could not connect to %s:%u", hostname, port); + return -EINVAL; } client->source = pw_loop_add_io(client->loop, fd, SPA_IO_IN | SPA_IO_OUT | SPA_IO_HUP | SPA_IO_ERR, true, on_source_io, client); + if (client->source == NULL) { pw_log_error("%p: source create failed: %m", client); close(fd);