--- sourceforge.net/trunk/rdesktop/rdpsnd.c 2006/10/01 11:00:03 1274 +++ sourceforge.net/trunk/rdesktop/rdpsnd.c 2006/12/06 13:11:35 1340 @@ -19,6 +19,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include + #include "rdesktop.h" #include "rdpsnd.h" #include "rdpsnd_dsp.h" @@ -28,7 +30,7 @@ #define RDPSND_SET_VOLUME 3 #define RDPSND_UNKNOWN4 4 #define RDPSND_COMPLETION 5 -#define RDPSND_SERVERTICK 6 +#define RDPSND_PING 6 #define RDPSND_NEGOTIATE 7 #define MAX_FORMATS 10 @@ -45,11 +47,16 @@ static WAVEFORMATEX formats[MAX_FORMATS]; static unsigned int format_count; static unsigned int current_format; -unsigned int queue_hi, queue_lo; +unsigned int queue_hi, queue_lo, queue_pending; struct audio_packet packet_queue[MAX_QUEUE]; void (*wave_out_play) (void); +static void rdpsnd_queue_write(STREAM s, uint16 tick, uint8 index); +static void rdpsnd_queue_init(void); +static void rdpsnd_queue_complete_pending(void); +static long rdpsnd_queue_next_completion(void); + static STREAM rdpsnd_init_packet(uint16 type, uint16 size) { @@ -72,7 +79,7 @@ channel_send(s, rdpsnd_channel); } -void +static void rdpsnd_send_completion(uint16 tick, uint8 packet_index) { STREAM s; @@ -88,16 +95,20 @@ static void rdpsnd_process_negotiate(STREAM in) { - unsigned int in_format_count, i; + uint16 in_format_count, i; + uint8 pad; + uint16 version; WAVEFORMATEX *format; STREAM out; BOOL device_available = False; int readcnt; int discardcnt; - in_uint8s(in, 14); /* flags, volume, pitch, UDP port */ + in_uint8s(in, 14); /* initial bytes not valid from server */ in_uint16_le(in, in_format_count); - in_uint8s(in, 4); /* pad, status, pad */ + in_uint8(in, pad); + in_uint16_le(in, version); + in_uint8s(in, 1); /* padding */ if (current_driver->wave_out_open()) { @@ -148,9 +159,9 @@ out_uint16(out, 0); /* UDP port */ out_uint16_le(out, format_count); - out_uint8(out, 0x95); /* pad? */ - out_uint16_le(out, 2); /* status */ - out_uint8(out, 0x77); /* pad? */ + out_uint8(out, 0); /* padding */ + out_uint16_le(out, 2); /* version */ + out_uint8(out, 0); /* padding */ for (i = 0; i < format_count; i++) { @@ -169,18 +180,16 @@ } static void -rdpsnd_process_servertick(STREAM in) +rdpsnd_process_ping(STREAM in) { - uint16 tick1, tick2; + uint16 tick; STREAM out; - /* in_uint8s(in, 4); unknown */ - in_uint16_le(in, tick1); - in_uint16_le(in, tick2); - - out = rdpsnd_init_packet(RDPSND_SERVERTICK | 0x2300, 4); - out_uint16_le(out, tick1); - out_uint16_le(out, tick2); + in_uint16_le(in, tick); + + out = rdpsnd_init_packet(RDPSND_PING | 0x2300, 4); + out_uint16_le(out, tick); + out_uint16_le(out, 0); s_mark_end(out); rdpsnd_send(out); } @@ -230,10 +239,9 @@ /* Insert the 4 missing bytes retrieved from last RDPSND_WRITE */ memcpy(s->data, missing_bytes, 4); - current_driver-> - wave_out_write(rdpsnd_dsp_process - (s, current_driver, &formats[current_format]), tick, - packet_index); + rdpsnd_queue_write(rdpsnd_dsp_process + (s, current_driver, &formats[current_format]), tick, + packet_index); awaiting_data_packet = False; return; } @@ -259,8 +267,8 @@ case RDPSND_NEGOTIATE: rdpsnd_process_negotiate(s); break; - case RDPSND_SERVERTICK: - rdpsnd_process_servertick(s); + case RDPSND_PING: + rdpsnd_process_ping(s); break; case RDPSND_SET_VOLUME: in_uint32(s, volume); @@ -316,24 +324,30 @@ reg = &drivers; #if defined(RDPSND_ALSA) *reg = alsa_register(options); + assert(*reg); reg = &((*reg)->next); #endif #if defined(RDPSND_SUN) *reg = sun_register(options); + assert(*reg); reg = &((*reg)->next); #endif #if defined(RDPSND_OSS) *reg = oss_register(options); + assert(*reg); reg = &((*reg)->next); #endif #if defined(RDPSND_SGI) *reg = sgi_register(options); + assert(*reg); reg = &((*reg)->next); #endif #if defined(RDPSND_LIBAO) *reg = libao_register(options); + assert(*reg); reg = &((*reg)->next); #endif + *reg = NULL; } BOOL @@ -355,6 +369,8 @@ return False; } + rdpsnd_queue_init(); + if (optarg != NULL && strlen(optarg) > 0) { driver = options = optarg; @@ -416,13 +432,47 @@ current_driver->wave_out_play(); } -inline void +void +rdpsnd_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv) +{ + long next_pending; + + if (g_dsp_busy) + { + FD_SET(g_dsp_fd, wfds); + *n = (g_dsp_fd > *n) ? g_dsp_fd : *n; + } + + next_pending = rdpsnd_queue_next_completion(); + if (next_pending >= 0) + { + long cur_timeout; + + cur_timeout = tv->tv_sec * 1000000 + tv->tv_usec; + if (cur_timeout > next_pending) + { + tv->tv_sec = next_pending / 1000000; + tv->tv_usec = next_pending % 1000000; + } + } +} + +void +rdpsnd_check_fds(fd_set * rfds, fd_set * wfds) +{ + rdpsnd_queue_complete_pending(); + + if (g_dsp_busy && FD_ISSET(g_dsp_fd, wfds)) + rdpsnd_play(); +} + +static void rdpsnd_queue_write(STREAM s, uint16 tick, uint8 index) { struct audio_packet *packet = &packet_queue[queue_hi]; unsigned int next_hi = (queue_hi + 1) % MAX_QUEUE; - if (next_hi == queue_lo) + if (next_hi == queue_pending) { error("No space to queue audio packet\n"); return; @@ -434,36 +484,51 @@ packet->tick = tick; packet->index = index; + gettimeofday(&packet->arrive_tv, NULL); + if (!g_dsp_busy) current_driver->wave_out_play(); } -inline struct audio_packet * +struct audio_packet * rdpsnd_queue_current_packet(void) { return &packet_queue[queue_lo]; } -inline BOOL +BOOL rdpsnd_queue_empty(void) { return (queue_lo == queue_hi); } -inline void +static void rdpsnd_queue_init(void) { - queue_lo = queue_hi = 0; + queue_pending = queue_lo = queue_hi = 0; } -inline void -rdpsnd_queue_next(void) +void +rdpsnd_queue_next(unsigned long completed_in_us) { - xfree(packet_queue[queue_lo].s.data); + struct audio_packet *packet; + + assert(!rdpsnd_queue_empty()); + + packet = &packet_queue[queue_lo]; + + gettimeofday(&packet->completion_tv, NULL); + + packet->completion_tv.tv_usec += completed_in_us; + packet->completion_tv.tv_sec += packet->completion_tv.tv_usec / 1000000; + packet->completion_tv.tv_usec %= 1000000; + queue_lo = (queue_lo + 1) % MAX_QUEUE; + + rdpsnd_queue_complete_pending(); } -inline int +int rdpsnd_queue_next_tick(void) { if (((queue_lo + 1) % MAX_QUEUE) != queue_hi) @@ -475,3 +540,56 @@ return (packet_queue[queue_lo].tick + 65535) % 65536; } } + +static void +rdpsnd_queue_complete_pending(void) +{ + struct timeval now; + long elapsed; + struct audio_packet *packet; + + gettimeofday(&now, NULL); + + while (queue_pending != queue_lo) + { + packet = &packet_queue[queue_pending]; + + if (now.tv_sec < packet->completion_tv.tv_sec) + break; + + if ((now.tv_sec == packet->completion_tv.tv_sec) && + (now.tv_usec < packet->completion_tv.tv_usec)) + break; + + elapsed = (packet->completion_tv.tv_sec - packet->arrive_tv.tv_sec) * 1000000 + + (packet->completion_tv.tv_usec - packet->arrive_tv.tv_usec); + elapsed /= 1000; + + xfree(packet->s.data); + rdpsnd_send_completion((packet->tick + elapsed) % 65536, packet->index); + queue_pending = (queue_pending + 1) % MAX_QUEUE; + } +} + +static long +rdpsnd_queue_next_completion(void) +{ + struct audio_packet *packet; + long remaining; + struct timeval now; + + if (queue_pending == queue_lo) + return -1; + + gettimeofday(&now, NULL); + + packet = &packet_queue[queue_pending]; + + remaining = (packet->completion_tv.tv_sec - now.tv_sec) * 1000000 + + (packet->completion_tv.tv_usec - now.tv_usec); + + if (remaining < 0) + return 0; + + return remaining; +}