mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2026-04-01 07:15:57 -04:00
raop: set initial volume before sending RTP packets
This commit is contained in:
parent
96cbee1230
commit
e8c459b4f7
3 changed files with 64 additions and 5 deletions
|
|
@ -130,6 +130,8 @@ struct pa_raop_client {
|
||||||
|
|
||||||
pa_raop_client_state_cb_t state_callback;
|
pa_raop_client_state_cb_t state_callback;
|
||||||
void *state_userdata;
|
void *state_userdata;
|
||||||
|
|
||||||
|
pa_volume_t initial_volume;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Audio TCP packet header [16x8] (cf. rfc4571):
|
/* Audio TCP packet header [16x8] (cf. rfc4571):
|
||||||
|
|
@ -1200,6 +1202,13 @@ connect_finish:
|
||||||
case STATE_SET_PARAMETER: {
|
case STATE_SET_PARAMETER: {
|
||||||
pa_log_debug("RAOP: SET_PARAMETER");
|
pa_log_debug("RAOP: SET_PARAMETER");
|
||||||
|
|
||||||
|
if (c->initial_volume != 0){
|
||||||
|
c->initial_volume = 0;
|
||||||
|
// We just have set initial volume, so raise PA_RAOP_VOLUME_INIT to chain
|
||||||
|
if (c->state_callback)
|
||||||
|
c->state_callback((int) PA_RAOP_VOLUME_INIT, c->state_userdata);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1251,6 +1260,7 @@ connect_finish:
|
||||||
/* Polling sockets will be closed by sink */
|
/* Polling sockets will be closed by sink */
|
||||||
c->udp_cfd = c->udp_tfd = c->udp_tport = -1;
|
c->udp_cfd = c->udp_tfd = c->udp_tport = -1;
|
||||||
c->tcp_sfd = -1;
|
c->tcp_sfd = -1;
|
||||||
|
c->initial_volume = 0;
|
||||||
|
|
||||||
pa_log_error("RTSP control channel closed (disconnected)");
|
pa_log_error("RTSP control channel closed (disconnected)");
|
||||||
|
|
||||||
|
|
@ -1508,6 +1518,7 @@ pa_raop_client* pa_raop_client_new(pa_core *core, const char *host, pa_raop_prot
|
||||||
c->udp_cfd = -1;
|
c->udp_cfd = -1;
|
||||||
c->udp_tfd = -1;
|
c->udp_tfd = -1;
|
||||||
c->udp_tport = -1;
|
c->udp_tport = -1;
|
||||||
|
c->initial_volume = 0;
|
||||||
|
|
||||||
c->secret = NULL;
|
c->secret = NULL;
|
||||||
if (c->encryption != PA_RAOP_ENCRYPTION_NONE)
|
if (c->encryption != PA_RAOP_ENCRYPTION_NONE)
|
||||||
|
|
@ -1691,6 +1702,10 @@ int pa_raop_client_stream(pa_raop_client *c) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pa_raop_client_set_initial_volume(pa_raop_client *c, pa_volume_t initial_volume) {
|
||||||
|
c->initial_volume = initial_volume;
|
||||||
|
}
|
||||||
|
|
||||||
int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume) {
|
int pa_raop_client_set_volume(pa_raop_client *c, pa_volume_t volume) {
|
||||||
char *param;
|
char *param;
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ typedef enum pa_raop_state {
|
||||||
PA_RAOP_AUTHENTICATED,
|
PA_RAOP_AUTHENTICATED,
|
||||||
PA_RAOP_CONNECTED,
|
PA_RAOP_CONNECTED,
|
||||||
PA_RAOP_RECORDING,
|
PA_RAOP_RECORDING,
|
||||||
|
PA_RAOP_VOLUME_INIT,
|
||||||
PA_RAOP_DISCONNECTED
|
PA_RAOP_DISCONNECTED
|
||||||
} pa_raop_state_t;
|
} pa_raop_state_t;
|
||||||
|
|
||||||
|
|
@ -83,6 +84,7 @@ ssize_t pa_raop_client_send_audio_packet(pa_raop_client *c, pa_memchunk *block,
|
||||||
typedef void (*pa_raop_client_state_cb_t)(pa_raop_state_t state, void *userdata);
|
typedef void (*pa_raop_client_state_cb_t)(pa_raop_state_t state, void *userdata);
|
||||||
void pa_raop_client_set_state_callback(pa_raop_client *c, pa_raop_client_state_cb_t callback, void *userdata);
|
void pa_raop_client_set_state_callback(pa_raop_client *c, pa_raop_client_state_cb_t callback, void *userdata);
|
||||||
void pa_raop_client_set_tport(pa_raop_client *c, int udp_tport);
|
void pa_raop_client_set_tport(pa_raop_client *c, int udp_tport);
|
||||||
|
void pa_raop_client_set_initial_volume(pa_raop_client *c, pa_volume_t initial_volume);
|
||||||
void pa_raop_client_send_progress (pa_raop_client *c);
|
void pa_raop_client_send_progress (pa_raop_client *c);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,8 @@ static void userdata_free(struct userdata *u);
|
||||||
|
|
||||||
static void sink_set_volume_cb(pa_sink *s);
|
static void sink_set_volume_cb(pa_sink *s);
|
||||||
|
|
||||||
|
static pa_volume_t pa_raop_sink_get_hw_volume(pa_sink *s);
|
||||||
|
|
||||||
static void raop_state_cb(pa_raop_state_t state, void *userdata) {
|
static void raop_state_cb(pa_raop_state_t state, void *userdata) {
|
||||||
struct userdata *u = userdata;
|
struct userdata *u = userdata;
|
||||||
|
|
||||||
|
|
@ -213,14 +215,18 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
||||||
}
|
}
|
||||||
|
|
||||||
case PA_RAOP_CONNECTED: {
|
case PA_RAOP_CONNECTED: {
|
||||||
|
pa_volume_t initial_volume;
|
||||||
|
|
||||||
pa_assert(!u->rtpoll_item);
|
pa_assert(!u->rtpoll_item);
|
||||||
|
|
||||||
u->oob = pa_raop_client_register_pollfd(u->raop, u->rtpoll, &u->rtpoll_item);
|
u->oob = pa_raop_client_register_pollfd(u->raop, u->rtpoll, &u->rtpoll_item);
|
||||||
|
initial_volume = pa_raop_sink_get_hw_volume(u->sink);
|
||||||
|
pa_raop_client_set_initial_volume(u->raop, initial_volume);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case PA_RAOP_RECORDING: {
|
case PA_RAOP_VOLUME_INIT: {
|
||||||
pa_usec_t now;
|
pa_usec_t now;
|
||||||
|
|
||||||
now = pa_rtclock_now();
|
now = pa_rtclock_now();
|
||||||
|
|
@ -234,13 +240,18 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
||||||
pa_rtpoll_set_timer_disabled(u->rtpoll);
|
pa_rtpoll_set_timer_disabled(u->rtpoll);
|
||||||
pa_raop_client_flush(u->raop);
|
pa_raop_client_flush(u->raop);
|
||||||
} else {
|
} else {
|
||||||
/* Set the initial volume */
|
pa_raop_client_send_progress(u->raop);
|
||||||
sink_set_volume_cb(u->sink);
|
|
||||||
pa_sink_process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, data, offset, chunk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
case PA_RAOP_RECORDING: {
|
||||||
|
/* Set the initial volume */
|
||||||
|
sink_set_volume_cb(u->sink);
|
||||||
|
pa_sink_process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, data, offset, chunk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
case PA_RAOP_INVALID_STATE:
|
case PA_RAOP_INVALID_STATE:
|
||||||
case PA_RAOP_DISCONNECTED: {
|
case PA_RAOP_DISCONNECTED: {
|
||||||
|
|
@ -369,6 +380,25 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pa_volume_t pa_raop_sink_get_hw_volume(pa_sink *s){
|
||||||
|
struct userdata *u = s->userdata;
|
||||||
|
pa_volume_t v, v_orig;
|
||||||
|
|
||||||
|
pa_assert(u);
|
||||||
|
|
||||||
|
/* Calculate the max volume of all channels.
|
||||||
|
* We'll use this as our (single) volume on the APEX device and emulate
|
||||||
|
* any variation in channel volumes in software. */
|
||||||
|
v = pa_cvolume_max(&s->real_volume);
|
||||||
|
|
||||||
|
v_orig = v;
|
||||||
|
v = pa_raop_client_adjust_volume(u->raop, v_orig);
|
||||||
|
|
||||||
|
pa_log_debug("Volume adjusted: orig=%u adjusted=%u", v_orig, v);
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
static void sink_set_volume_cb(pa_sink *s) {
|
static void sink_set_volume_cb(pa_sink *s) {
|
||||||
struct userdata *u = s->userdata;
|
struct userdata *u = s->userdata;
|
||||||
pa_cvolume hw;
|
pa_cvolume hw;
|
||||||
|
|
@ -494,8 +524,20 @@ static void thread_func(void *userdata) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (pollfd->revents & pollfd->events) {
|
if (pollfd->revents & pollfd->events) {
|
||||||
|
struct sockaddr_in srcaddr;
|
||||||
|
socklen_t addrlen;
|
||||||
|
|
||||||
pollfd->revents = 0;
|
pollfd->revents = 0;
|
||||||
read = pa_read(pollfd->fd, packet, sizeof(packet), NULL);
|
// read = pa_read(pollfd->fd, packet, sizeof(packet), NULL);
|
||||||
|
// Newest Airplay devices does not provide response to SETUP request if we do not respond
|
||||||
|
// to timing request packets immediatly after the setup request
|
||||||
|
// To do this we use the source port of incoming packets
|
||||||
|
// TBD: Code rework (move this in raop client?) + Ipv6 Support
|
||||||
|
addrlen = sizeof(struct sockaddr_in);
|
||||||
|
read = recvfrom(pollfd->fd, packet, sizeof(packet), 0, (struct sockaddr *)&srcaddr, &addrlen);
|
||||||
|
|
||||||
|
pa_raop_client_set_tport(u->raop, htons(srcaddr.sin_port));
|
||||||
|
pa_log_debug("Source: %d", htons(srcaddr.sin_port));
|
||||||
pa_raop_client_handle_oob_packet(u->raop, pollfd->fd, packet, read);
|
pa_raop_client_handle_oob_packet(u->raop, pollfd->fd, packet, read);
|
||||||
if (pa_raop_client_is_timing_fd(u->raop, pollfd->fd)) {
|
if (pa_raop_client_is_timing_fd(u->raop, pollfd->fd)) {
|
||||||
last_timing = pa_rtclock_now();
|
last_timing = pa_rtclock_now();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue