diff options
Diffstat (limited to 'server/snd_worker.c')
-rw-r--r-- | server/snd_worker.c | 1628 |
1 files changed, 0 insertions, 1628 deletions
diff --git a/server/snd_worker.c b/server/snd_worker.c deleted file mode 100644 index 70148b76..00000000 --- a/server/snd_worker.c +++ /dev/null @@ -1,1628 +0,0 @@ -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ -/* - Copyright (C) 2009 Red Hat, Inc. - - This library 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.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see <http://www.gnu.org/licenses/>. -*/ -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <fcntl.h> -#include <errno.h> -#include <limits.h> -#include <sys/socket.h> -#include <netinet/ip.h> -#include <netinet/tcp.h> - -#include "common/marshaller.h" -#include "common/generated_server_marshallers.h" - -#include "spice.h" -#include "red_common.h" -#include "main_channel.h" -#include "reds.h" -#include "red_dispatcher.h" -#include "snd_worker.h" -#include "common/snd_codec.h" -#include "demarshallers.h" - -#ifndef IOV_MAX -#define IOV_MAX 1024 -#endif - -#define SND_RECEIVE_BUF_SIZE (16 * 1024 * 2) -#define RECORD_SAMPLES_SIZE (SND_RECEIVE_BUF_SIZE >> 2) - -enum PlaybackCommand { - SND_PLAYBACK_MIGRATE, - SND_PLAYBACK_MODE, - SND_PLAYBACK_CTRL, - SND_PLAYBACK_PCM, - SND_PLAYBACK_VOLUME, - SND_PLAYBACK_LATENCY, -}; - -enum RecordCommand { - SND_RECORD_MIGRATE, - SND_RECORD_CTRL, - SND_RECORD_VOLUME, -}; - -#define SND_PLAYBACK_MIGRATE_MASK (1 << SND_PLAYBACK_MIGRATE) -#define SND_PLAYBACK_MODE_MASK (1 << SND_PLAYBACK_MODE) -#define SND_PLAYBACK_CTRL_MASK (1 << SND_PLAYBACK_CTRL) -#define SND_PLAYBACK_PCM_MASK (1 << SND_PLAYBACK_PCM) -#define SND_PLAYBACK_VOLUME_MASK (1 << SND_PLAYBACK_VOLUME) -#define SND_PLAYBACK_LATENCY_MASK ( 1 << SND_PLAYBACK_LATENCY) - -#define SND_RECORD_MIGRATE_MASK (1 << SND_RECORD_MIGRATE) -#define SND_RECORD_CTRL_MASK (1 << SND_RECORD_CTRL) -#define SND_RECORD_VOLUME_MASK (1 << SND_RECORD_VOLUME) - -typedef struct SndChannel SndChannel; -typedef void (*snd_channel_send_messages_proc)(void *in_channel); -typedef int (*snd_channel_handle_message_proc)(SndChannel *channel, size_t size, uint32_t type, void *message); -typedef void (*snd_channel_on_message_done_proc)(SndChannel *channel); -typedef void (*snd_channel_cleanup_channel_proc)(SndChannel *channel); - -typedef struct SndWorker SndWorker; - -struct SndChannel { - RedsStream *stream; - SndWorker *worker; - spice_parse_channel_func_t parser; - int refs; - - RedChannelClient *channel_client; - - int active; - int client_active; - int blocked; - - uint32_t command; - uint32_t ack_generation; - uint32_t client_ack_generation; - uint32_t out_messages; - uint32_t ack_messages; - - struct { - uint64_t serial; - SpiceMarshaller *marshaller; - uint32_t size; - uint32_t pos; - } send_data; - - struct { - uint8_t buf[SND_RECEIVE_BUF_SIZE]; - uint8_t *message_start; - uint8_t *now; - uint8_t *end; - } receive_data; - - snd_channel_send_messages_proc send_messages; - snd_channel_handle_message_proc handle_message; - snd_channel_on_message_done_proc on_message_done; - snd_channel_cleanup_channel_proc cleanup; -}; - -typedef struct PlaybackChannel PlaybackChannel; - -typedef struct AudioFrame AudioFrame; -struct AudioFrame { - uint32_t time; - uint32_t samples[SND_CODEC_MAX_FRAME_SIZE]; - PlaybackChannel *channel; - AudioFrame *next; -}; - -struct PlaybackChannel { - SndChannel base; - AudioFrame frames[3]; - AudioFrame *free_frames; - AudioFrame *in_progress; - AudioFrame *pending_frame; - uint32_t mode; - uint32_t latency; - SndCodec codec; - uint8_t encode_buf[SND_CODEC_MAX_COMPRESSED_BYTES]; -}; - -struct SndWorker { - RedChannel *base_channel; - SndChannel *connection; - SndWorker *next; - int active; -}; - -typedef struct SpiceVolumeState { - uint8_t volume_nchannels; - uint16_t *volume; - int mute; -} SpiceVolumeState; - -struct SpicePlaybackState { - struct SndWorker worker; - SpicePlaybackInstance *sin; - SpiceVolumeState volume; - uint32_t frequency; -}; - -struct SpiceRecordState { - struct SndWorker worker; - SpiceRecordInstance *sin; - SpiceVolumeState volume; - uint32_t frequency; -}; - -typedef struct RecordChannel { - SndChannel base; - uint32_t samples[RECORD_SAMPLES_SIZE]; - uint32_t write_pos; - uint32_t read_pos; - uint32_t mode; - uint32_t mode_time; - uint32_t start_time; - SndCodec codec; - uint8_t decode_buf[SND_CODEC_MAX_FRAME_BYTES]; -} RecordChannel; - -static SndWorker *workers; -static uint32_t playback_compression = TRUE; - -static void snd_receive(void* data); - -static SndChannel *snd_channel_get(SndChannel *channel) -{ - channel->refs++; - return channel; -} - -static SndChannel *snd_channel_put(SndChannel *channel) -{ - if (!--channel->refs) { - spice_printerr("SndChannel=%p freed", channel); - free(channel); - return NULL; - } - return channel; -} - -static void snd_disconnect_channel(SndChannel *channel) -{ - SndWorker *worker; - - if (!channel || !channel->stream) { - spice_debug("not connected"); - return; - } - spice_debug("SndChannel=%p rcc=%p type=%d", - channel, channel->channel_client, channel->channel_client->channel->type); - worker = channel->worker; - channel->cleanup(channel); - red_channel_client_disconnect(worker->connection->channel_client); - worker->connection->channel_client = NULL; - core->watch_remove(channel->stream->watch); - channel->stream->watch = NULL; - reds_stream_free(channel->stream); - channel->stream = NULL; - spice_marshaller_destroy(channel->send_data.marshaller); - snd_channel_put(channel); - worker->connection = NULL; -} - -static void snd_playback_free_frame(PlaybackChannel *playback_channel, AudioFrame *frame) -{ - frame->channel = playback_channel; - frame->next = playback_channel->free_frames; - playback_channel->free_frames = frame; -} - -static void snd_playback_on_message_done(SndChannel *channel) -{ - PlaybackChannel *playback_channel = (PlaybackChannel *)channel; - if (playback_channel->in_progress) { - snd_playback_free_frame(playback_channel, playback_channel->in_progress); - playback_channel->in_progress = NULL; - if (playback_channel->pending_frame) { - channel->command |= SND_PLAYBACK_PCM_MASK; - } - } -} - -static void snd_record_on_message_done(SndChannel *channel) -{ -} - -static int snd_send_data(SndChannel *channel) -{ - uint32_t n; - - if (!channel) { - return FALSE; - } - - if (!(n = channel->send_data.size - channel->send_data.pos)) { - return TRUE; - } - - for (;;) { - struct iovec vec[IOV_MAX]; - int vec_size; - - if (!n) { - channel->on_message_done(channel); - - if (channel->blocked) { - channel->blocked = FALSE; - core->watch_update_mask(channel->stream->watch, SPICE_WATCH_EVENT_READ); - } - break; - } - - vec_size = spice_marshaller_fill_iovec(channel->send_data.marshaller, - vec, IOV_MAX, channel->send_data.pos); - n = reds_stream_writev(channel->stream, vec, vec_size); - if (n == -1) { - switch (errno) { - case EAGAIN: - channel->blocked = TRUE; - core->watch_update_mask(channel->stream->watch, SPICE_WATCH_EVENT_READ | - SPICE_WATCH_EVENT_WRITE); - return FALSE; - case EINTR: - break; - case EPIPE: - snd_disconnect_channel(channel); - return FALSE; - default: - spice_printerr("%s", strerror(errno)); - snd_disconnect_channel(channel); - return FALSE; - } - } else { - channel->send_data.pos += n; - } - n = channel->send_data.size - channel->send_data.pos; - } - return TRUE; -} - -static int snd_record_handle_write(RecordChannel *record_channel, size_t size, void *message) -{ - SpiceMsgcRecordPacket *packet; - uint32_t write_pos; - uint32_t* data; - uint32_t len; - uint32_t now; - - if (!record_channel) { - return FALSE; - } - - packet = (SpiceMsgcRecordPacket *)message; - - if (record_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) { - data = (uint32_t *)packet->data; - size = packet->data_size >> 2; - size = MIN(size, RECORD_SAMPLES_SIZE); - } else { - int decode_size; - decode_size = sizeof(record_channel->decode_buf); - if (snd_codec_decode(record_channel->codec, packet->data, packet->data_size, - record_channel->decode_buf, &decode_size) != SND_CODEC_OK) - return FALSE; - data = (uint32_t *) record_channel->decode_buf; - size = decode_size >> 2; - } - - write_pos = record_channel->write_pos % RECORD_SAMPLES_SIZE; - record_channel->write_pos += size; - len = RECORD_SAMPLES_SIZE - write_pos; - now = MIN(len, size); - size -= now; - memcpy(record_channel->samples + write_pos, data, now << 2); - - if (size) { - memcpy(record_channel->samples, data + now, size << 2); - } - - if (record_channel->write_pos - record_channel->read_pos > RECORD_SAMPLES_SIZE) { - record_channel->read_pos = record_channel->write_pos - RECORD_SAMPLES_SIZE; - } - return TRUE; -} - -static int snd_playback_handle_message(SndChannel *channel, size_t size, uint32_t type, void *message) -{ - if (!channel) { - return FALSE; - } - - switch (type) { - case SPICE_MSGC_DISCONNECTING: - break; - default: - spice_printerr("invalid message type %u", type); - return FALSE; - } - return TRUE; -} - -static int snd_record_handle_message(SndChannel *channel, size_t size, uint32_t type, void *message) -{ - RecordChannel *record_channel = (RecordChannel *)channel; - - if (!channel) { - return FALSE; - } - switch (type) { - case SPICE_MSGC_RECORD_DATA: - return snd_record_handle_write((RecordChannel *)channel, size, message); - case SPICE_MSGC_RECORD_MODE: { - SpiceMsgcRecordMode *mode = (SpiceMsgcRecordMode *)message; - SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker); - record_channel->mode_time = mode->time; - if (mode->mode != SPICE_AUDIO_DATA_MODE_RAW) { - if (snd_codec_is_capable(mode->mode, st->frequency)) { - if (snd_codec_create(&record_channel->codec, mode->mode, st->frequency, SND_CODEC_DECODE) == SND_CODEC_OK) { - record_channel->mode = mode->mode; - } else { - spice_printerr("create decoder failed"); - return FALSE; - } - } - else { - spice_printerr("unsupported mode %d", record_channel->mode); - return FALSE; - } - } - else - record_channel->mode = mode->mode; - break; - } - - case SPICE_MSGC_RECORD_START_MARK: { - SpiceMsgcRecordStartMark *mark = (SpiceMsgcRecordStartMark *)message; - record_channel->start_time = mark->time; - break; - } - case SPICE_MSGC_DISCONNECTING: - break; - default: - spice_printerr("invalid message type %u", type); - return FALSE; - } - return TRUE; -} - -static void snd_receive(void* data) -{ - SndChannel *channel = (SndChannel*)data; - SpiceDataHeaderOpaque *header; - - if (!channel) { - return; - } - - header = &channel->channel_client->incoming.header; - - for (;;) { - ssize_t n; - n = channel->receive_data.end - channel->receive_data.now; - spice_warn_if(n <= 0); - n = reds_stream_read(channel->stream, channel->receive_data.now, n); - if (n <= 0) { - if (n == 0) { - snd_disconnect_channel(channel); - return; - } - spice_assert(n == -1); - switch (errno) { - case EAGAIN: - return; - case EINTR: - break; - case EPIPE: - snd_disconnect_channel(channel); - return; - default: - spice_printerr("%s", strerror(errno)); - snd_disconnect_channel(channel); - return; - } - } else { - channel->receive_data.now += n; - for (;;) { - uint8_t *msg_start = channel->receive_data.message_start; - uint8_t *data = msg_start + header->header_size; - size_t parsed_size; - uint8_t *parsed; - message_destructor_t parsed_free; - - header->data = msg_start; - n = channel->receive_data.now - msg_start; - - if (n < header->header_size || - n < header->header_size + header->get_msg_size(header)) { - break; - } - parsed = channel->parser((void *)data, data + header->get_msg_size(header), - header->get_msg_type(header), - SPICE_VERSION_MINOR, &parsed_size, &parsed_free); - if (parsed == NULL) { - spice_printerr("failed to parse message type %d", header->get_msg_type(header)); - snd_disconnect_channel(channel); - return; - } - if (!channel->handle_message(channel, parsed_size, - header->get_msg_type(header), parsed)) { - free(parsed); - snd_disconnect_channel(channel); - return; - } - parsed_free(parsed); - channel->receive_data.message_start = msg_start + header->header_size + - header->get_msg_size(header); - } - if (channel->receive_data.now == channel->receive_data.message_start) { - channel->receive_data.now = channel->receive_data.buf; - channel->receive_data.message_start = channel->receive_data.buf; - } else if (channel->receive_data.now == channel->receive_data.end) { - memcpy(channel->receive_data.buf, channel->receive_data.message_start, n); - channel->receive_data.now = channel->receive_data.buf + n; - channel->receive_data.message_start = channel->receive_data.buf; - } - } - } -} - -static void snd_event(int fd, int event, void *data) -{ - SndChannel *channel = data; - - if (event & SPICE_WATCH_EVENT_READ) { - snd_receive(channel); - } - if (event & SPICE_WATCH_EVENT_WRITE) { - channel->send_messages(channel); - } -} - -static inline int snd_reset_send_data(SndChannel *channel, uint16_t verb) -{ - SpiceDataHeaderOpaque *header; - - if (!channel) { - return FALSE; - } - - header = &channel->channel_client->send_data.header; - spice_marshaller_reset(channel->send_data.marshaller); - header->data = spice_marshaller_reserve_space(channel->send_data.marshaller, - header->header_size); - spice_marshaller_set_base(channel->send_data.marshaller, - header->header_size); - channel->send_data.pos = 0; - header->set_msg_size(header, 0); - header->set_msg_type(header, verb); - channel->send_data.serial++; - if (!channel->channel_client->is_mini_header) { - header->set_msg_serial(header, channel->send_data.serial); - header->set_msg_sub_list(header, 0); - } - - return TRUE; -} - -static int snd_begin_send_message(SndChannel *channel) -{ - SpiceDataHeaderOpaque *header = &channel->channel_client->send_data.header; - - spice_marshaller_flush(channel->send_data.marshaller); - channel->send_data.size = spice_marshaller_get_total_size(channel->send_data.marshaller); - header->set_msg_size(header, channel->send_data.size - header->header_size); - return snd_send_data(channel); -} - -static int snd_channel_send_migrate(SndChannel *channel) -{ - SpiceMsgMigrate migrate; - - if (!snd_reset_send_data(channel, SPICE_MSG_MIGRATE)) { - return FALSE; - } - spice_debug(NULL); - migrate.flags = 0; - spice_marshall_msg_migrate(channel->send_data.marshaller, &migrate); - - return snd_begin_send_message(channel); -} - -static int snd_playback_send_migrate(PlaybackChannel *channel) -{ - return snd_channel_send_migrate(&channel->base); -} - -static int snd_send_volume(SndChannel *channel, SpiceVolumeState *st, int msg) -{ - SpiceMsgAudioVolume *vol; - uint8_t c; - - vol = alloca(sizeof (SpiceMsgAudioVolume) + - st->volume_nchannels * sizeof (uint16_t)); - if (!snd_reset_send_data(channel, msg)) { - return FALSE; - } - vol->nchannels = st->volume_nchannels; - for (c = 0; c < st->volume_nchannels; ++c) { - vol->volume[c] = st->volume[c]; - } - spice_marshall_SpiceMsgAudioVolume(channel->send_data.marshaller, vol); - - return snd_begin_send_message(channel); -} - -static int snd_playback_send_volume(PlaybackChannel *playback_channel) -{ - SndChannel *channel = &playback_channel->base; - SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker); - - if (!red_channel_client_test_remote_cap(channel->channel_client, - SPICE_PLAYBACK_CAP_VOLUME)) { - return TRUE; - } - - return snd_send_volume(channel, &st->volume, SPICE_MSG_PLAYBACK_VOLUME); -} - -static int snd_send_mute(SndChannel *channel, SpiceVolumeState *st, int msg) -{ - SpiceMsgAudioMute mute; - - if (!snd_reset_send_data(channel, msg)) { - return FALSE; - } - mute.mute = st->mute; - spice_marshall_SpiceMsgAudioMute(channel->send_data.marshaller, &mute); - - return snd_begin_send_message(channel); -} - -static int snd_playback_send_mute(PlaybackChannel *playback_channel) -{ - SndChannel *channel = &playback_channel->base; - SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker); - - if (!red_channel_client_test_remote_cap(channel->channel_client, - SPICE_PLAYBACK_CAP_VOLUME)) { - return TRUE; - } - - return snd_send_mute(channel, &st->volume, SPICE_MSG_PLAYBACK_MUTE); -} - -static int snd_playback_send_latency(PlaybackChannel *playback_channel) -{ - SndChannel *channel = &playback_channel->base; - SpiceMsgPlaybackLatency latency_msg; - - spice_debug("latency %u", playback_channel->latency); - if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_LATENCY)) { - return FALSE; - } - latency_msg.latency_ms = playback_channel->latency; - spice_marshall_msg_playback_latency(channel->send_data.marshaller, &latency_msg); - - return snd_begin_send_message(channel); -} -static int snd_playback_send_start(PlaybackChannel *playback_channel) -{ - SndChannel *channel = (SndChannel *)playback_channel; - SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker); - SpiceMsgPlaybackStart start; - - if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_START)) { - return FALSE; - } - - start.channels = SPICE_INTERFACE_PLAYBACK_CHAN; - start.frequency = st->frequency; - spice_assert(SPICE_INTERFACE_PLAYBACK_FMT == SPICE_INTERFACE_AUDIO_FMT_S16); - start.format = SPICE_AUDIO_FMT_S16; - start.time = reds_get_mm_time(); - spice_marshall_msg_playback_start(channel->send_data.marshaller, &start); - - return snd_begin_send_message(channel); -} - -static int snd_playback_send_stop(PlaybackChannel *playback_channel) -{ - SndChannel *channel = (SndChannel *)playback_channel; - - if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_STOP)) { - return FALSE; - } - - return snd_begin_send_message(channel); -} - -static int snd_playback_send_ctl(PlaybackChannel *playback_channel) -{ - SndChannel *channel = (SndChannel *)playback_channel; - - if ((channel->client_active = channel->active)) { - return snd_playback_send_start(playback_channel); - } else { - return snd_playback_send_stop(playback_channel); - } -} - -static int snd_record_send_start(RecordChannel *record_channel) -{ - SndChannel *channel = (SndChannel *)record_channel; - SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker); - SpiceMsgRecordStart start; - - if (!snd_reset_send_data(channel, SPICE_MSG_RECORD_START)) { - return FALSE; - } - - start.channels = SPICE_INTERFACE_RECORD_CHAN; - start.frequency = st->frequency; - spice_assert(SPICE_INTERFACE_RECORD_FMT == SPICE_INTERFACE_AUDIO_FMT_S16); - start.format = SPICE_AUDIO_FMT_S16; - spice_marshall_msg_record_start(channel->send_data.marshaller, &start); - - return snd_begin_send_message(channel); -} - -static int snd_record_send_stop(RecordChannel *record_channel) -{ - SndChannel *channel = (SndChannel *)record_channel; - - if (!snd_reset_send_data(channel, SPICE_MSG_RECORD_STOP)) { - return FALSE; - } - - return snd_begin_send_message(channel); -} - -static int snd_record_send_ctl(RecordChannel *record_channel) -{ - SndChannel *channel = (SndChannel *)record_channel; - - if ((channel->client_active = channel->active)) { - return snd_record_send_start(record_channel); - } else { - return snd_record_send_stop(record_channel); - } -} - -static int snd_record_send_volume(RecordChannel *record_channel) -{ - SndChannel *channel = &record_channel->base; - SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker); - - if (!red_channel_client_test_remote_cap(channel->channel_client, - SPICE_RECORD_CAP_VOLUME)) { - return TRUE; - } - - return snd_send_volume(channel, &st->volume, SPICE_MSG_RECORD_VOLUME); -} - -static int snd_record_send_mute(RecordChannel *record_channel) -{ - SndChannel *channel = &record_channel->base; - SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker); - - if (!red_channel_client_test_remote_cap(channel->channel_client, - SPICE_RECORD_CAP_VOLUME)) { - return TRUE; - } - - return snd_send_mute(channel, &st->volume, SPICE_MSG_RECORD_MUTE); -} - -static int snd_record_send_migrate(RecordChannel *record_channel) -{ - /* No need for migration data: if recording has started before migration, - * the client receives RECORD_STOP from the src before the migration completion - * notification (when the vm is stopped). - * Afterwards, when the vm starts on the dest, the client receives RECORD_START. */ - return snd_channel_send_migrate(&record_channel->base); -} - -static int snd_playback_send_write(PlaybackChannel *playback_channel) -{ - SndChannel *channel = (SndChannel *)playback_channel; - AudioFrame *frame; - SpiceMsgPlaybackPacket msg; - - if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_DATA)) { - return FALSE; - } - - frame = playback_channel->in_progress; - msg.time = frame->time; - - spice_marshall_msg_playback_data(channel->send_data.marshaller, &msg); - - if (playback_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) { - spice_marshaller_add_ref(channel->send_data.marshaller, - (uint8_t *)frame->samples, - snd_codec_frame_size(playback_channel->codec) * sizeof(frame->samples[0])); - } - else { - int n = sizeof(playback_channel->encode_buf); - if (snd_codec_encode(playback_channel->codec, (uint8_t *) frame->samples, - snd_codec_frame_size(playback_channel->codec) * sizeof(frame->samples[0]), - playback_channel->encode_buf, &n) != SND_CODEC_OK) { - spice_printerr("encode failed"); - snd_disconnect_channel(channel); - return FALSE; - } - spice_marshaller_add_ref(channel->send_data.marshaller, playback_channel->encode_buf, n); - } - - return snd_begin_send_message(channel); -} - -static int playback_send_mode(PlaybackChannel *playback_channel) -{ - SndChannel *channel = (SndChannel *)playback_channel; - SpiceMsgPlaybackMode mode; - - if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_MODE)) { - return FALSE; - } - mode.time = reds_get_mm_time(); - mode.mode = playback_channel->mode; - spice_marshall_msg_playback_mode(channel->send_data.marshaller, &mode); - - return snd_begin_send_message(channel); -} - -static void snd_playback_send(void* data) -{ - PlaybackChannel *playback_channel = (PlaybackChannel*)data; - SndChannel *channel = (SndChannel*)playback_channel; - - if (!playback_channel || !snd_send_data(data)) { - return; - } - - while (channel->command) { - if (channel->command & SND_PLAYBACK_MODE_MASK) { - if (!playback_send_mode(playback_channel)) { - return; - } - channel->command &= ~SND_PLAYBACK_MODE_MASK; - } - if (channel->command & SND_PLAYBACK_PCM_MASK) { - spice_assert(!playback_channel->in_progress && playback_channel->pending_frame); - playback_channel->in_progress = playback_channel->pending_frame; - playback_channel->pending_frame = NULL; - channel->command &= ~SND_PLAYBACK_PCM_MASK; - if (!snd_playback_send_write(playback_channel)) { - spice_printerr("snd_send_playback_write failed"); - return; - } - } - if (channel->command & SND_PLAYBACK_CTRL_MASK) { - if (!snd_playback_send_ctl(playback_channel)) { - return; - } - channel->command &= ~SND_PLAYBACK_CTRL_MASK; - } - if (channel->command & SND_PLAYBACK_VOLUME_MASK) { - if (!snd_playback_send_volume(playback_channel) || - !snd_playback_send_mute(playback_channel)) { - return; - } - channel->command &= ~SND_PLAYBACK_VOLUME_MASK; - } - if (channel->command & SND_PLAYBACK_MIGRATE_MASK) { - if (!snd_playback_send_migrate(playback_channel)) { - return; - } - channel->command &= ~SND_PLAYBACK_MIGRATE_MASK; - } - if (channel->command & SND_PLAYBACK_LATENCY_MASK) { - if (!snd_playback_send_latency(playback_channel)) { - return; - } - channel->command &= ~SND_PLAYBACK_LATENCY_MASK; - } - } -} - -static void snd_record_send(void* data) -{ - RecordChannel *record_channel = (RecordChannel*)data; - SndChannel *channel = (SndChannel*)record_channel; - - if (!record_channel || !snd_send_data(data)) { - return; - } - - while (channel->command) { - if (channel->command & SND_RECORD_CTRL_MASK) { - if (!snd_record_send_ctl(record_channel)) { - return; - } - channel->command &= ~SND_RECORD_CTRL_MASK; - } - if (channel->command & SND_RECORD_VOLUME_MASK) { - if (!snd_record_send_volume(record_channel) || - !snd_record_send_mute(record_channel)) { - return; - } - channel->command &= ~SND_RECORD_VOLUME_MASK; - } - if (channel->command & SND_RECORD_MIGRATE_MASK) { - if (!snd_record_send_migrate(record_channel)) { - return; - } - channel->command &= ~SND_RECORD_MIGRATE_MASK; - } - } -} - -static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_id, - RedClient *client, - RedsStream *stream, - int migrate, - snd_channel_send_messages_proc send_messages, - snd_channel_handle_message_proc handle_message, - snd_channel_on_message_done_proc on_message_done, - snd_channel_cleanup_channel_proc cleanup, - uint32_t *common_caps, int num_common_caps, - uint32_t *caps, int num_caps) -{ - SndChannel *channel; - int delay_val; - int flags; -#ifdef SO_PRIORITY - int priority; -#endif - int tos; - MainChannelClient *mcc = red_client_get_main(client); - - spice_assert(stream); - if ((flags = fcntl(stream->socket, F_GETFL)) == -1) { - spice_printerr("accept failed, %s", strerror(errno)); - goto error1; - } - -#ifdef SO_PRIORITY - priority = 6; - if (setsockopt(stream->socket, SOL_SOCKET, SO_PRIORITY, (void*)&priority, - sizeof(priority)) == -1) { - if (errno != ENOTSUP) { - spice_printerr("setsockopt failed, %s", strerror(errno)); - } - } -#endif - - tos = IPTOS_LOWDELAY; - if (setsockopt(stream->socket, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) == -1) { - if (errno != ENOTSUP) { - spice_printerr("setsockopt failed, %s", strerror(errno)); - } - } - - delay_val = main_channel_client_is_low_bandwidth(mcc) ? 0 : 1; - if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) { - if (errno != ENOTSUP) { - spice_printerr("setsockopt failed, %s", strerror(errno)); - } - } - - if (fcntl(stream->socket, F_SETFL, flags | O_NONBLOCK) == -1) { - spice_printerr("accept failed, %s", strerror(errno)); - goto error1; - } - - spice_assert(size >= sizeof(*channel)); - channel = spice_malloc0(size); - channel->refs = 1; - channel->parser = spice_get_client_channel_parser(channel_id, NULL); - channel->stream = stream; - channel->worker = worker; - channel->receive_data.message_start = channel->receive_data.buf; - channel->receive_data.now = channel->receive_data.buf; - channel->receive_data.end = channel->receive_data.buf + sizeof(channel->receive_data.buf); - channel->send_data.marshaller = spice_marshaller_new(); - - stream->watch = core->watch_add(stream->socket, SPICE_WATCH_EVENT_READ, - snd_event, channel); - if (stream->watch == NULL) { - spice_printerr("watch_add failed, %s", strerror(errno)); - goto error2; - } - - channel->send_messages = send_messages; - channel->handle_message = handle_message; - channel->on_message_done = on_message_done; - channel->cleanup = cleanup; - - channel->channel_client = red_channel_client_create_dummy(sizeof(RedChannelClient), - worker->base_channel, - client, - num_common_caps, common_caps, - num_caps, caps); - if (!channel->channel_client) { - goto error2; - } - return channel; - -error2: - free(channel); - -error1: - reds_stream_free(stream); - return NULL; -} - -static void snd_disconnect_channel_client(RedChannelClient *rcc) -{ - SndWorker *worker; - - spice_assert(rcc->channel); - spice_assert(rcc->channel->data); - worker = (SndWorker *)rcc->channel->data; - - spice_debug("channel-type=%d", rcc->channel->type); - if (worker->connection) { - spice_assert(worker->connection->channel_client == rcc); - snd_disconnect_channel(worker->connection); - } -} - -static void snd_set_command(SndChannel *channel, uint32_t command) -{ - if (!channel) { - return; - } - channel->command |= command; -} - -SPICE_GNUC_VISIBLE void spice_server_playback_set_volume(SpicePlaybackInstance *sin, - uint8_t nchannels, - uint16_t *volume) -{ - SpiceVolumeState *st = &sin->st->volume; - SndChannel *channel = sin->st->worker.connection; - PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base); - - st->volume_nchannels = nchannels; - free(st->volume); - st->volume = spice_memdup(volume, sizeof(uint16_t) * nchannels); - - if (!channel || nchannels == 0) - return; - - snd_playback_send_volume(playback_channel); -} - -SPICE_GNUC_VISIBLE void spice_server_playback_set_mute(SpicePlaybackInstance *sin, uint8_t mute) -{ - SpiceVolumeState *st = &sin->st->volume; - SndChannel *channel = sin->st->worker.connection; - PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base); - - st->mute = mute; - - if (!channel) - return; - - snd_playback_send_mute(playback_channel); -} - -SPICE_GNUC_VISIBLE void spice_server_playback_start(SpicePlaybackInstance *sin) -{ - SndChannel *channel = sin->st->worker.connection; - PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base); - - sin->st->worker.active = 1; - if (!channel) - return; - spice_assert(!playback_channel->base.active); - reds_disable_mm_timer(); - playback_channel->base.active = TRUE; - if (!playback_channel->base.client_active) { - snd_set_command(&playback_channel->base, SND_PLAYBACK_CTRL_MASK); - snd_playback_send(&playback_channel->base); - } else { - playback_channel->base.command &= ~SND_PLAYBACK_CTRL_MASK; - } -} - -SPICE_GNUC_VISIBLE void spice_server_playback_stop(SpicePlaybackInstance *sin) -{ - SndChannel *channel = sin->st->worker.connection; - PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base); - - sin->st->worker.active = 0; - if (!channel) - return; - spice_assert(playback_channel->base.active); - reds_enable_mm_timer(); - playback_channel->base.active = FALSE; - if (playback_channel->base.client_active) { - snd_set_command(&playback_channel->base, SND_PLAYBACK_CTRL_MASK); - snd_playback_send(&playback_channel->base); - } else { - playback_channel->base.command &= ~SND_PLAYBACK_CTRL_MASK; - playback_channel->base.command &= ~SND_PLAYBACK_PCM_MASK; - - if (playback_channel->pending_frame) { - spice_assert(!playback_channel->in_progress); - snd_playback_free_frame(playback_channel, - playback_channel->pending_frame); - playback_channel->pending_frame = NULL; - } - } -} - -SPICE_GNUC_VISIBLE void spice_server_playback_get_buffer(SpicePlaybackInstance *sin, - uint32_t **frame, uint32_t *num_samples) -{ - SndChannel *channel = sin->st->worker.connection; - PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base); - - if (!channel || !playback_channel->free_frames) { - *frame = NULL; - *num_samples = 0; - return; - } - spice_assert(playback_channel->base.active); - snd_channel_get(channel); - - *frame = playback_channel->free_frames->samples; - playback_channel->free_frames = playback_channel->free_frames->next; - *num_samples = snd_codec_frame_size(playback_channel->codec); -} - -SPICE_GNUC_VISIBLE void spice_server_playback_put_samples(SpicePlaybackInstance *sin, uint32_t *samples) -{ - PlaybackChannel *playback_channel; - AudioFrame *frame; - - frame = SPICE_CONTAINEROF(samples, AudioFrame, samples); - playback_channel = frame->channel; - spice_assert(playback_channel); - if (!snd_channel_put(&playback_channel->base) || - sin->st->worker.connection != &playback_channel->base) { - /* lost last reference, channel has been destroyed previously */ - spice_info("audio samples belong to a disconnected channel"); - return; - } - spice_assert(playback_channel->base.active); - - if (playback_channel->pending_frame) { - snd_playback_free_frame(playback_channel, playback_channel->pending_frame); - } - frame->time = reds_get_mm_time(); - red_dispatcher_set_mm_time(frame->time); - playback_channel->pending_frame = frame; - snd_set_command(&playback_channel->base, SND_PLAYBACK_PCM_MASK); - snd_playback_send(&playback_channel->base); -} - -void snd_set_playback_latency(RedClient *client, uint32_t latency) -{ - SndWorker *now = workers; - - for (; now; now = now->next) { - if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection && - now->connection->channel_client->client == client) { - - if (red_channel_client_test_remote_cap(now->connection->channel_client, - SPICE_PLAYBACK_CAP_LATENCY)) { - PlaybackChannel* playback = (PlaybackChannel*)now->connection; - - playback->latency = latency; - snd_set_command(now->connection, SND_PLAYBACK_LATENCY_MASK); - snd_playback_send(now->connection); - } else { - spice_debug("client doesn't not support SPICE_PLAYBACK_CAP_LATENCY"); - } - } - } -} - -static int snd_desired_audio_mode(int frequency, int client_can_celt, int client_can_opus) -{ - if (! playback_compression) - return SPICE_AUDIO_DATA_MODE_RAW; - - if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency)) - return SPICE_AUDIO_DATA_MODE_OPUS; - - if (client_can_celt && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, frequency)) - return SPICE_AUDIO_DATA_MODE_CELT_0_5_1; - - return SPICE_AUDIO_DATA_MODE_RAW; -} - -static void on_new_playback_channel(SndWorker *worker) -{ - PlaybackChannel *playback_channel = - SPICE_CONTAINEROF(worker->connection, PlaybackChannel, base); - SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker); - - spice_assert(playback_channel); - - snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_MODE_MASK); - if (playback_channel->base.active) { - snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_CTRL_MASK); - } - if (st->volume.volume_nchannels) { - snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_VOLUME_MASK); - } - if (playback_channel->base.active) { - reds_disable_mm_timer(); - } -} - -static void snd_playback_cleanup(SndChannel *channel) -{ - PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base); - - if (playback_channel->base.active) { - reds_enable_mm_timer(); - } - - snd_codec_destroy(&playback_channel->codec); -} - -static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsStream *stream, - int migration, int num_common_caps, uint32_t *common_caps, - int num_caps, uint32_t *caps) -{ - SndWorker *worker = channel->data; - PlaybackChannel *playback_channel; - SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker); - - snd_disconnect_channel(worker->connection); - - if (!(playback_channel = (PlaybackChannel *)__new_channel(worker, - sizeof(*playback_channel), - SPICE_CHANNEL_PLAYBACK, - client, - stream, - migration, - snd_playback_send, - snd_playback_handle_message, - snd_playback_on_message_done, - snd_playback_cleanup, - common_caps, num_common_caps, - caps, num_caps))) { - return; - } - worker->connection = &playback_channel->base; - snd_playback_free_frame(playback_channel, &playback_channel->frames[0]); - snd_playback_free_frame(playback_channel, &playback_channel->frames[1]); - snd_playback_free_frame(playback_channel, &playback_channel->frames[2]); - - int client_can_celt = red_channel_client_test_remote_cap(playback_channel->base.channel_client, - SPICE_PLAYBACK_CAP_CELT_0_5_1); - int client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client, - SPICE_PLAYBACK_CAP_OPUS); - int desired_mode = snd_desired_audio_mode(st->frequency, client_can_celt, client_can_opus); - playback_channel->mode = SPICE_AUDIO_DATA_MODE_RAW; - if (desired_mode != SPICE_AUDIO_DATA_MODE_RAW) { - if (snd_codec_create(&playback_channel->codec, desired_mode, st->frequency, SND_CODEC_ENCODE) == SND_CODEC_OK) { - playback_channel->mode = desired_mode; - } else { - spice_printerr("create encoder failed"); - } - } - - on_new_playback_channel(worker); - if (worker->active) { - spice_server_playback_start(st->sin); - } - snd_playback_send(worker->connection); -} - -static void snd_record_migrate_channel_client(RedChannelClient *rcc) -{ - SndWorker *worker; - - spice_debug(NULL); - spice_assert(rcc->channel); - spice_assert(rcc->channel->data); - worker = (SndWorker *)rcc->channel->data; - - if (worker->connection) { - spice_assert(worker->connection->channel_client == rcc); - snd_set_command(worker->connection, SND_RECORD_MIGRATE_MASK); - snd_record_send(worker->connection); - } -} - -SPICE_GNUC_VISIBLE void spice_server_record_set_volume(SpiceRecordInstance *sin, - uint8_t nchannels, - uint16_t *volume) -{ - SpiceVolumeState *st = &sin->st->volume; - SndChannel *channel = sin->st->worker.connection; - RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base); - - st->volume_nchannels = nchannels; - free(st->volume); - st->volume = spice_memdup(volume, sizeof(uint16_t) * nchannels); - - if (!channel || nchannels == 0) - return; - - snd_record_send_volume(record_channel); -} - -SPICE_GNUC_VISIBLE void spice_server_record_set_mute(SpiceRecordInstance *sin, uint8_t mute) -{ - SpiceVolumeState *st = &sin->st->volume; - SndChannel *channel = sin->st->worker.connection; - RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base); - - st->mute = mute; - - if (!channel) - return; - - snd_record_send_mute(record_channel); -} - -SPICE_GNUC_VISIBLE void spice_server_record_start(SpiceRecordInstance *sin) -{ - SndChannel *channel = sin->st->worker.connection; - RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base); - - sin->st->worker.active = 1; - if (!channel) - return; - spice_assert(!record_channel->base.active); - record_channel->base.active = TRUE; - record_channel->read_pos = record_channel->write_pos = 0; //todo: improve by - //stream generation - if (!record_channel->base.client_active) { - snd_set_command(&record_channel->base, SND_RECORD_CTRL_MASK); - snd_record_send(&record_channel->base); - } else { - record_channel->base.command &= ~SND_RECORD_CTRL_MASK; - } -} - -SPICE_GNUC_VISIBLE void spice_server_record_stop(SpiceRecordInstance *sin) -{ - SndChannel *channel = sin->st->worker.connection; - RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base); - - sin->st->worker.active = 0; - if (!channel) - return; - spice_assert(record_channel->base.active); - record_channel->base.active = FALSE; - if (record_channel->base.client_active) { - snd_set_command(&record_channel->base, SND_RECORD_CTRL_MASK); - snd_record_send(&record_channel->base); - } else { - record_channel->base.command &= ~SND_RECORD_CTRL_MASK; - } -} - -SPICE_GNUC_VISIBLE uint32_t spice_server_record_get_samples(SpiceRecordInstance *sin, - uint32_t *samples, uint32_t bufsize) -{ - SndChannel *channel = sin->st->worker.connection; - RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base); - uint32_t read_pos; - uint32_t now; - uint32_t len; - - if (!channel) - return 0; - spice_assert(record_channel->base.active); - - if (record_channel->write_pos < RECORD_SAMPLES_SIZE / 2) { - return 0; - } - - len = MIN(record_channel->write_pos - record_channel->read_pos, bufsize); - - if (len < bufsize) { - SndWorker *worker = record_channel->base.worker; - snd_receive(record_channel); - if (!worker->connection) { - return 0; - } - len = MIN(record_channel->write_pos - record_channel->read_pos, bufsize); - } - - read_pos = record_channel->read_pos % RECORD_SAMPLES_SIZE; - record_channel->read_pos += len; - now = MIN(len, RECORD_SAMPLES_SIZE - read_pos); - memcpy(samples, &record_channel->samples[read_pos], now * 4); - if (now < len) { - memcpy(samples + now, record_channel->samples, (len - now) * 4); - } - return len; -} - -SPICE_GNUC_VISIBLE uint32_t spice_server_get_best_playback_rate(SpicePlaybackInstance *sin) -{ - int client_can_opus = TRUE; - if (sin && sin->st->worker.connection) { - SndChannel *channel = sin->st->worker.connection; - PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base); - client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client, - SPICE_PLAYBACK_CAP_OPUS); - } - - if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY)) - return SND_CODEC_OPUS_PLAYBACK_FREQ; - - return SND_CODEC_CELT_PLAYBACK_FREQ; -} - -SPICE_GNUC_VISIBLE void spice_server_set_playback_rate(SpicePlaybackInstance *sin, uint32_t frequency) -{ - RedChannel *channel = sin->st->worker.base_channel; - sin->st->frequency = frequency; - if (channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency)) - red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_OPUS); -} - -SPICE_GNUC_VISIBLE uint32_t spice_server_get_best_record_rate(SpiceRecordInstance *sin) -{ - int client_can_opus = TRUE; - if (sin && sin->st->worker.connection) { - SndChannel *channel = sin->st->worker.connection; - RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base); - client_can_opus = red_channel_client_test_remote_cap(record_channel->base.channel_client, - SPICE_RECORD_CAP_OPUS); - } - - if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY)) - return SND_CODEC_OPUS_PLAYBACK_FREQ; - - return SND_CODEC_CELT_PLAYBACK_FREQ; -} - -SPICE_GNUC_VISIBLE void spice_server_set_record_rate(SpiceRecordInstance *sin, uint32_t frequency) -{ - RedChannel *channel = sin->st->worker.base_channel; - sin->st->frequency = frequency; - if (channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency)) - red_channel_set_cap(channel, SPICE_RECORD_CAP_OPUS); -} - -static void on_new_record_channel(SndWorker *worker) -{ - RecordChannel *record_channel = (RecordChannel *)worker->connection; - SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker); - - spice_assert(record_channel); - - if (st->volume.volume_nchannels) { - snd_set_command((SndChannel *)record_channel, SND_RECORD_VOLUME_MASK); - } - if (record_channel->base.active) { - snd_set_command((SndChannel *)record_channel, SND_RECORD_CTRL_MASK); - } -} - -static void snd_record_cleanup(SndChannel *channel) -{ - RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base); - snd_codec_destroy(&record_channel->codec); -} - -static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStream *stream, - int migration, int num_common_caps, uint32_t *common_caps, - int num_caps, uint32_t *caps) -{ - SndWorker *worker = channel->data; - RecordChannel *record_channel; - SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker); - - snd_disconnect_channel(worker->connection); - - if (!(record_channel = (RecordChannel *)__new_channel(worker, - sizeof(*record_channel), - SPICE_CHANNEL_RECORD, - client, - stream, - migration, - snd_record_send, - snd_record_handle_message, - snd_record_on_message_done, - snd_record_cleanup, - common_caps, num_common_caps, - caps, num_caps))) { - return; - } - - record_channel->mode = SPICE_AUDIO_DATA_MODE_RAW; - - worker->connection = &record_channel->base; - - on_new_record_channel(worker); - if (worker->active) { - spice_server_record_start(st->sin); - } - snd_record_send(worker->connection); -} - -static void snd_playback_migrate_channel_client(RedChannelClient *rcc) -{ - SndWorker *worker; - - spice_assert(rcc->channel); - spice_assert(rcc->channel->data); - worker = (SndWorker *)rcc->channel->data; - spice_debug(NULL); - - if (worker->connection) { - spice_assert(worker->connection->channel_client == rcc); - snd_set_command(worker->connection, SND_PLAYBACK_MIGRATE_MASK); - snd_playback_send(worker->connection); - } -} - -static void add_worker(SndWorker *worker) -{ - worker->next = workers; - workers = worker; -} - -static void remove_worker(SndWorker *worker) -{ - SndWorker **now = &workers; - while (*now) { - if (*now == worker) { - *now = worker->next; - return; - } - now = &(*now)->next; - } - spice_printerr("not found"); -} - -void snd_attach_playback(SpicePlaybackInstance *sin) -{ - SndWorker *playback_worker; - RedChannel *channel; - ClientCbs client_cbs = { NULL, }; - - sin->st = spice_new0(SpicePlaybackState, 1); - sin->st->sin = sin; - playback_worker = &sin->st->worker; - sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */ - - // TODO: Make RedChannel base of worker? instead of assigning it to channel->data - channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_PLAYBACK, 0); - - channel->data = playback_worker; - client_cbs.connect = snd_set_playback_peer; - client_cbs.disconnect = snd_disconnect_channel_client; - client_cbs.migrate = snd_playback_migrate_channel_client; - red_channel_register_client_cbs(channel, &client_cbs); - red_channel_set_data(channel, playback_worker); - - if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY)) - red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_CELT_0_5_1); - - red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_VOLUME); - - playback_worker->base_channel = channel; - add_worker(playback_worker); - reds_register_channel(playback_worker->base_channel); -} - -void snd_attach_record(SpiceRecordInstance *sin) -{ - SndWorker *record_worker; - RedChannel *channel; - ClientCbs client_cbs = { NULL, }; - - sin->st = spice_new0(SpiceRecordState, 1); - sin->st->sin = sin; - record_worker = &sin->st->worker; - sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */ - - // TODO: Make RedChannel base of worker? instead of assigning it to channel->data - channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_RECORD, 0); - - channel->data = record_worker; - client_cbs.connect = snd_set_record_peer; - client_cbs.disconnect = snd_disconnect_channel_client; - client_cbs.migrate = snd_record_migrate_channel_client; - red_channel_register_client_cbs(channel, &client_cbs); - red_channel_set_data(channel, record_worker); - if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY)) - red_channel_set_cap(channel, SPICE_RECORD_CAP_CELT_0_5_1); - red_channel_set_cap(channel, SPICE_RECORD_CAP_VOLUME); - - record_worker->base_channel = channel; - add_worker(record_worker); - reds_register_channel(record_worker->base_channel); -} - -static void snd_detach_common(SndWorker *worker) -{ - if (!worker) { - return; - } - remove_worker(worker); - snd_disconnect_channel(worker->connection); - reds_unregister_channel(worker->base_channel); - red_channel_destroy(worker->base_channel); -} - -static void spice_playback_state_free(SpicePlaybackState *st) -{ - free(st->volume.volume); - free(st); -} - -void snd_detach_playback(SpicePlaybackInstance *sin) -{ - snd_detach_common(&sin->st->worker); - spice_playback_state_free(sin->st); -} - -static void spice_record_state_free(SpiceRecordState *st) -{ - free(st->volume.volume); - free(st); -} - -void snd_detach_record(SpiceRecordInstance *sin) -{ - snd_detach_common(&sin->st->worker); - spice_record_state_free(sin->st); -} - -void snd_set_playback_compression(int on) -{ - SndWorker *now = workers; - - playback_compression = !!on; - - for (; now; now = now->next) { - if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) { - PlaybackChannel* playback = (PlaybackChannel*)now->connection; - SpicePlaybackState *st = SPICE_CONTAINEROF(now, SpicePlaybackState, worker); - int client_can_celt = red_channel_client_test_remote_cap(playback->base.channel_client, - SPICE_PLAYBACK_CAP_CELT_0_5_1); - int client_can_opus = red_channel_client_test_remote_cap(playback->base.channel_client, - SPICE_PLAYBACK_CAP_OPUS); - int desired_mode = snd_desired_audio_mode(st->frequency, client_can_opus, client_can_celt); - if (playback->mode != desired_mode) { - playback->mode = desired_mode; - snd_set_command(now->connection, SND_PLAYBACK_MODE_MASK); - } - } - } -} - -int snd_get_playback_compression(void) -{ - return playback_compression; -} |