mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-27 07:29:32 +00:00
Add support for the Opus codec
Signed-off-by: Jeremy White <jwhite@codeweavers.com>
This commit is contained in:
parent
4c7c0ef3a7
commit
ce9b714137
@ -72,7 +72,6 @@ public:
|
||||
static ChannelFactory& Factory();
|
||||
|
||||
protected:
|
||||
virtual void on_connect();
|
||||
virtual void on_disconnect();
|
||||
|
||||
private:
|
||||
@ -85,6 +84,8 @@ private:
|
||||
virtual void remove_event_source(EventSources::Trigger& event_source);
|
||||
virtual void push_frame(uint8_t *frame);
|
||||
|
||||
void set_desired_mode(int frequency);
|
||||
void send_record_mode();
|
||||
void send_start_mark();
|
||||
void release_message(RecordSamplesMessage *message);
|
||||
RecordSamplesMessage * get_message();
|
||||
|
||||
@ -27,10 +27,6 @@ public:
|
||||
virtual bool abort() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual uint32_t get_delay_ms() = 0;
|
||||
|
||||
enum {
|
||||
FRAME_SIZE = 256,
|
||||
};
|
||||
};
|
||||
|
||||
class WaveRecordAbstract {
|
||||
@ -42,10 +38,6 @@ public:
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual bool abort() = 0;
|
||||
|
||||
enum {
|
||||
FRAME_SIZE = 256,
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -68,10 +68,12 @@ public:
|
||||
static WaveRecordAbstract* create_recorder(RecordClient& client,
|
||||
uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels);
|
||||
uint32_t channels,
|
||||
uint32_t frame_size);
|
||||
static WavePlaybackAbstract* create_player(uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels);
|
||||
uint32_t channels,
|
||||
uint32_t frame_size);
|
||||
|
||||
enum {
|
||||
SCROLL_LOCK_MODIFIER_SHIFT,
|
||||
|
||||
@ -168,8 +168,10 @@ PlaybackChannel::PlaybackChannel(RedClient& client, uint32_t id)
|
||||
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_MODE, &PlaybackChannel::handle_mode);
|
||||
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
|
||||
set_capability(SPICE_PLAYBACK_CAP_CELT_0_5_1);
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
|
||||
set_capability(SPICE_PLAYBACK_CAP_OPUS);
|
||||
}
|
||||
|
||||
void PlaybackChannel::clear()
|
||||
@ -206,7 +208,7 @@ void PlaybackChannel::set_data_handler()
|
||||
|
||||
if (_mode == SPICE_AUDIO_DATA_MODE_RAW) {
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_DATA, &PlaybackChannel::handle_raw_data);
|
||||
} else if (snd_codec_is_capable(_mode)) {
|
||||
} else if (snd_codec_is_capable(_mode, SND_CODEC_ANY_FREQUENCY)) {
|
||||
handler->set_handler(SPICE_MSG_PLAYBACK_DATA, &PlaybackChannel::handle_compressed_data);
|
||||
} else {
|
||||
THROW("invalid mode");
|
||||
@ -218,7 +220,7 @@ void PlaybackChannel::handle_mode(RedPeer::InMessage* message)
|
||||
{
|
||||
SpiceMsgPlaybackMode* playback_mode = (SpiceMsgPlaybackMode*)message->data();
|
||||
if (playback_mode->mode != SPICE_AUDIO_DATA_MODE_RAW
|
||||
&& !snd_codec_is_capable(playback_mode->mode) ) {
|
||||
&& !snd_codec_is_capable(playback_mode->mode, SND_CODEC_ANY_FREQUENCY) ) {
|
||||
THROW("invalid mode");
|
||||
}
|
||||
|
||||
@ -266,15 +268,6 @@ void PlaybackChannel::handle_start(RedPeer::InMessage* message)
|
||||
}
|
||||
int bits_per_sample = 16;
|
||||
int frame_size = SND_CODEC_MAX_FRAME_SIZE;
|
||||
try {
|
||||
_wave_player = Platform::create_player(start->frequency, bits_per_sample,
|
||||
start->channels);
|
||||
} catch (...) {
|
||||
LOG_WARN("create player failed");
|
||||
//todo: support disconnecting single channel
|
||||
disable();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_mode != SPICE_AUDIO_DATA_MODE_RAW) {
|
||||
if (snd_codec_create(&_codec, _mode, start->frequency, SND_CODEC_DECODE) != SND_CODEC_OK)
|
||||
@ -282,6 +275,16 @@ void PlaybackChannel::handle_start(RedPeer::InMessage* message)
|
||||
frame_size = snd_codec_frame_size(_codec);
|
||||
}
|
||||
|
||||
try {
|
||||
_wave_player = Platform::create_player(start->frequency, bits_per_sample,
|
||||
start->channels, frame_size);
|
||||
} catch (...) {
|
||||
LOG_WARN("create player failed");
|
||||
//todo: support disconnecting single channel
|
||||
disable();
|
||||
return;
|
||||
}
|
||||
|
||||
_frame_bytes = frame_size * start->channels * bits_per_sample / 8;
|
||||
}
|
||||
_playing = true;
|
||||
|
||||
@ -87,8 +87,10 @@ RecordChannel::RecordChannel(RedClient& client, uint32_t id)
|
||||
|
||||
handler->set_handler(SPICE_MSG_RECORD_START, &RecordChannel::handle_start);
|
||||
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
|
||||
set_capability(SPICE_RECORD_CAP_CELT_0_5_1);
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
|
||||
set_capability(SPICE_RECORD_CAP_OPUS);
|
||||
}
|
||||
|
||||
RecordChannel::~RecordChannel(void)
|
||||
@ -107,16 +109,23 @@ bool RecordChannel::abort(void)
|
||||
return (!_wave_recorder || _wave_recorder->abort()) && RedChannel::abort();
|
||||
}
|
||||
|
||||
void RecordChannel::on_connect()
|
||||
void RecordChannel::set_desired_mode(int frequency)
|
||||
{
|
||||
Message* message = new Message(SPICE_MSGC_RECORD_MODE);
|
||||
SpiceMsgcRecordMode mode;
|
||||
mode.time = get_mm_time();
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1) &&
|
||||
if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency) &&
|
||||
test_capability(SPICE_RECORD_CAP_OPUS))
|
||||
_mode = SPICE_AUDIO_DATA_MODE_OPUS;
|
||||
else if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, frequency) &&
|
||||
test_capability(SPICE_RECORD_CAP_CELT_0_5_1))
|
||||
_mode = SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
|
||||
else
|
||||
_mode = SPICE_AUDIO_DATA_MODE_RAW;
|
||||
}
|
||||
|
||||
void RecordChannel::send_record_mode()
|
||||
{
|
||||
Message* message = new Message(SPICE_MSGC_RECORD_MODE);
|
||||
SpiceMsgcRecordMode mode;
|
||||
mode.time = get_mm_time();
|
||||
mode.mode = _mode;
|
||||
_marshallers->msgc_record_mode(message->marshaller(), &mode);
|
||||
post_message(message);
|
||||
@ -153,15 +162,7 @@ void RecordChannel::handle_start(RedPeer::InMessage* message)
|
||||
THROW("unexpected number of channels");
|
||||
}
|
||||
|
||||
int bits_per_sample = 16;
|
||||
try {
|
||||
_wave_recorder = Platform::create_recorder(*this, start->frequency,
|
||||
bits_per_sample,
|
||||
start->channels);
|
||||
} catch (...) {
|
||||
LOG_WARN("create recorder failed");
|
||||
return;
|
||||
}
|
||||
set_desired_mode(start->frequency);
|
||||
|
||||
int frame_size = SND_CODEC_MAX_FRAME_SIZE;
|
||||
if (_mode != SPICE_AUDIO_DATA_MODE_RAW) {
|
||||
@ -169,8 +170,21 @@ void RecordChannel::handle_start(RedPeer::InMessage* message)
|
||||
THROW("create encoder failed");
|
||||
frame_size = snd_codec_frame_size(_codec);
|
||||
}
|
||||
|
||||
int bits_per_sample = 16;
|
||||
try {
|
||||
_wave_recorder = Platform::create_recorder(*this, start->frequency,
|
||||
bits_per_sample,
|
||||
start->channels,
|
||||
frame_size);
|
||||
} catch (...) {
|
||||
LOG_WARN("create recorder failed");
|
||||
return;
|
||||
}
|
||||
|
||||
_frame_bytes = frame_size * bits_per_sample * start->channels / 8;
|
||||
|
||||
send_record_mode();
|
||||
send_start_mark();
|
||||
_wave_recorder->start();
|
||||
}
|
||||
@ -237,7 +251,6 @@ void RecordChannel::remove_event_source(EventSources::Trigger& event_source)
|
||||
void RecordChannel::push_frame(uint8_t *frame)
|
||||
{
|
||||
RecordSamplesMessage *message;
|
||||
ASSERT(_frame_bytes == FRAME_SIZE * 4);
|
||||
if (!(message = get_message())) {
|
||||
DBG(0, "blocked");
|
||||
return;
|
||||
|
||||
@ -3433,16 +3433,18 @@ void Platform::reset_cursor_pos()
|
||||
WaveRecordAbstract* Platform::create_recorder(RecordClient& client,
|
||||
uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels)
|
||||
uint32_t channels,
|
||||
uint32_t frame_size)
|
||||
{
|
||||
return new WaveRecorder(client, sampels_per_sec, bits_per_sample, channels);
|
||||
return new WaveRecorder(client, sampels_per_sec, bits_per_sample, channels, frame_size);
|
||||
}
|
||||
|
||||
WavePlaybackAbstract* Platform::create_player(uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels)
|
||||
uint32_t channels,
|
||||
uint32_t frame_size)
|
||||
{
|
||||
return new WavePlayer(sampels_per_sec, bits_per_sample, channels);
|
||||
return new WavePlayer(sampels_per_sec, bits_per_sample, channels, frame_size);
|
||||
}
|
||||
|
||||
void XPlatform::on_focus_in()
|
||||
|
||||
@ -24,12 +24,12 @@
|
||||
|
||||
#define REING_SIZE_MS 300
|
||||
|
||||
WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels)
|
||||
WavePlayer::WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels, uint32_t frame_size)
|
||||
: _pcm (NULL)
|
||||
, _hw_params (NULL)
|
||||
, _sw_params (NULL)
|
||||
{
|
||||
if (!init(sampels_per_sec, bits_per_sample, channels)) {
|
||||
if (!init(sampels_per_sec, bits_per_sample, channels, frame_size)) {
|
||||
cleanup();
|
||||
THROW("failed");
|
||||
}
|
||||
@ -57,9 +57,9 @@ WavePlayer::~WavePlayer()
|
||||
|
||||
bool WavePlayer::init(uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels)
|
||||
uint32_t channels,
|
||||
uint32_t frame_size)
|
||||
{
|
||||
const int frame_size = WavePlaybackAbstract::FRAME_SIZE;
|
||||
const char* pcm_device = "default";
|
||||
snd_pcm_format_t format;
|
||||
int err;
|
||||
@ -75,6 +75,7 @@ bool WavePlayer::init(uint32_t sampels_per_sec,
|
||||
return false;
|
||||
}
|
||||
_sampels_per_ms = sampels_per_sec / 1000;
|
||||
_frame_size = frame_size;
|
||||
|
||||
if ((err = snd_pcm_open(&_pcm, pcm_device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
|
||||
LOG_ERROR("cannot open audio playback device %s %s", pcm_device, snd_strerror(err));
|
||||
@ -183,14 +184,14 @@ bool WavePlayer::init(uint32_t sampels_per_sec,
|
||||
|
||||
bool WavePlayer::write(uint8_t* frame)
|
||||
{
|
||||
snd_pcm_sframes_t ret = snd_pcm_writei(_pcm, frame, WavePlaybackAbstract::FRAME_SIZE);
|
||||
snd_pcm_sframes_t ret = snd_pcm_writei(_pcm, frame, _frame_size);
|
||||
if (ret < 0) {
|
||||
if (ret == -EAGAIN) {
|
||||
return false;
|
||||
}
|
||||
DBG(0, "err %s", snd_strerror(-ret));
|
||||
if (snd_pcm_recover(_pcm, ret, 1) == 0) {
|
||||
snd_pcm_writei(_pcm, frame, WavePlaybackAbstract::FRAME_SIZE);
|
||||
snd_pcm_writei(_pcm, frame, _frame_size);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
|
||||
class WavePlayer: public WavePlaybackAbstract {
|
||||
public:
|
||||
WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels);
|
||||
WavePlayer(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channels, uint32_t frame_size);
|
||||
virtual ~WavePlayer();
|
||||
|
||||
virtual bool write(uint8_t* frame);
|
||||
@ -34,7 +34,7 @@ public:
|
||||
virtual uint32_t get_delay_ms();
|
||||
|
||||
private:
|
||||
bool init(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channel);
|
||||
bool init(uint32_t sampels_per_sec, uint32_t bits_per_sample, uint32_t channel, uint32_t frame_size);
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
@ -42,6 +42,7 @@ private:
|
||||
snd_pcm_hw_params_t* _hw_params;
|
||||
snd_pcm_sw_params_t* _sw_params;
|
||||
uint32_t _sampels_per_ms;
|
||||
uint32_t _frame_size;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -48,18 +48,19 @@ void WaveRecorder::EventTrigger::on_event()
|
||||
WaveRecorder::WaveRecorder(Platform::RecordClient& client,
|
||||
uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels)
|
||||
uint32_t channels,
|
||||
uint32_t frame_size)
|
||||
: _client (client)
|
||||
, _pcm (NULL)
|
||||
, _hw_params (NULL)
|
||||
, _sw_params (NULL)
|
||||
, _sample_bytes (bits_per_sample * channels / 8)
|
||||
, _frame (new uint8_t[_sample_bytes * WaveRecordAbstract::FRAME_SIZE])
|
||||
, _frame (new uint8_t[_sample_bytes * frame_size])
|
||||
, _frame_pos (_frame)
|
||||
, _frame_end (_frame + _sample_bytes * WaveRecordAbstract::FRAME_SIZE)
|
||||
, _frame_end (_frame + _sample_bytes * frame_size)
|
||||
, _event_trigger (NULL)
|
||||
{
|
||||
if (!init(sampels_per_sec, bits_per_sample, channels)) {
|
||||
if (!init(sampels_per_sec, bits_per_sample, channels, frame_size)) {
|
||||
cleanup();
|
||||
THROW("failed");
|
||||
}
|
||||
@ -93,13 +94,15 @@ void WaveRecorder::cleanup()
|
||||
|
||||
bool WaveRecorder::init(uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels)
|
||||
uint32_t channels,
|
||||
uint32_t frame_size)
|
||||
{
|
||||
const int frame_size = WaveRecordAbstract::FRAME_SIZE;
|
||||
const char* pcm_device = "default";
|
||||
snd_pcm_format_t format;
|
||||
int err;
|
||||
|
||||
_frame_size = frame_size;
|
||||
|
||||
switch (bits_per_sample) {
|
||||
case 8:
|
||||
format = SND_PCM_FORMAT_S8;
|
||||
|
||||
@ -29,7 +29,8 @@ public:
|
||||
WaveRecorder(Platform::RecordClient& client,
|
||||
uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels);
|
||||
uint32_t channels,
|
||||
uint32_t frame_size);
|
||||
virtual ~WaveRecorder();
|
||||
|
||||
virtual void start();
|
||||
@ -39,7 +40,8 @@ public:
|
||||
private:
|
||||
bool init(uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels);
|
||||
uint32_t channels,
|
||||
uint32_t frame_size);
|
||||
void cleanup();
|
||||
void on_event();
|
||||
|
||||
@ -49,6 +51,7 @@ private:
|
||||
snd_pcm_hw_params_t* _hw_params;
|
||||
snd_pcm_sw_params_t* _sw_params;
|
||||
uint32_t _sample_bytes;
|
||||
uint32_t _frame_size;
|
||||
uint8_t* _frame;
|
||||
uint8_t* _frame_pos;
|
||||
uint8_t* _frame_end;
|
||||
|
||||
@ -156,12 +156,14 @@ 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 {
|
||||
@ -370,14 +372,27 @@ static int snd_record_handle_message(SndChannel *channel, size_t size, uint32_t
|
||||
return snd_record_handle_write((RecordChannel *)channel, size, message);
|
||||
case SPICE_MSGC_RECORD_MODE: {
|
||||
SpiceMsgcRecordMode *mode = (SpiceMsgcRecordMode *)message;
|
||||
record_channel->mode = mode->mode;
|
||||
SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
|
||||
record_channel->mode_time = mode->time;
|
||||
if (record_channel->mode != SPICE_AUDIO_DATA_MODE_RAW &&
|
||||
! snd_codec_is_capable(record_channel->mode)) {
|
||||
spice_printerr("unsupported mode %d", record_channel->mode);
|
||||
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;
|
||||
@ -615,6 +630,7 @@ static int snd_playback_send_latency(PlaybackChannel *playback_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)) {
|
||||
@ -622,7 +638,7 @@ static int snd_playback_send_start(PlaybackChannel *playback_channel)
|
||||
}
|
||||
|
||||
start.channels = SPICE_INTERFACE_PLAYBACK_CHAN;
|
||||
start.frequency = SPICE_INTERFACE_PLAYBACK_FREQ;
|
||||
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();
|
||||
@ -656,6 +672,7 @@ static int snd_playback_send_ctl(PlaybackChannel *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)) {
|
||||
@ -663,7 +680,7 @@ static int snd_record_send_start(RecordChannel *record_channel)
|
||||
}
|
||||
|
||||
start.channels = SPICE_INTERFACE_RECORD_CHAN;
|
||||
start.frequency = SPICE_INTERFACE_RECORD_FREQ;
|
||||
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);
|
||||
@ -745,11 +762,13 @@ static int snd_playback_send_write(PlaybackChannel *playback_channel)
|
||||
|
||||
if (playback_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
|
||||
spice_marshaller_add_ref(channel->send_data.marshaller,
|
||||
(uint8_t *)frame->samples, sizeof(frame->samples));
|
||||
(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, sizeof(frame->samples),
|
||||
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);
|
||||
@ -1126,12 +1145,15 @@ void snd_set_playback_latency(RedClient *client, uint32_t latency)
|
||||
}
|
||||
}
|
||||
|
||||
static int snd_desired_audio_mode(int client_can_celt)
|
||||
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_celt && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1))
|
||||
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;
|
||||
@ -1199,10 +1221,12 @@ static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsSt
|
||||
|
||||
int client_can_celt = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
|
||||
SPICE_PLAYBACK_CAP_CELT_0_5_1);
|
||||
int desired_mode = snd_desired_audio_mode(client_can_celt);
|
||||
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, SPICE_INTERFACE_PLAYBACK_FREQ, SND_CODEC_ENCODE) == SND_CODEC_OK) {
|
||||
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");
|
||||
@ -1340,6 +1364,54 @@ SPICE_GNUC_VISIBLE uint32_t spice_server_record_get_samples(SpiceRecordInstance
|
||||
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;
|
||||
@ -1386,17 +1458,7 @@ static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStre
|
||||
return;
|
||||
}
|
||||
|
||||
int client_can_celt = red_channel_client_test_remote_cap(record_channel->base.channel_client,
|
||||
SPICE_RECORD_CAP_CELT_0_5_1);
|
||||
int desired_mode = snd_desired_audio_mode(client_can_celt);
|
||||
record_channel->mode = SPICE_AUDIO_DATA_MODE_RAW;
|
||||
if (desired_mode != SPICE_AUDIO_DATA_MODE_RAW) {
|
||||
if (snd_codec_create(&record_channel->codec, desired_mode, SPICE_INTERFACE_RECORD_FREQ, SND_CODEC_DECODE) == SND_CODEC_OK) {
|
||||
record_channel->mode = desired_mode;
|
||||
} else {
|
||||
spice_printerr("create decoder failed");
|
||||
}
|
||||
}
|
||||
|
||||
worker->connection = &record_channel->base;
|
||||
|
||||
@ -1451,6 +1513,7 @@ void snd_attach_playback(SpicePlaybackInstance *sin)
|
||||
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);
|
||||
@ -1462,7 +1525,7 @@ void snd_attach_playback(SpicePlaybackInstance *sin)
|
||||
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))
|
||||
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);
|
||||
@ -1481,6 +1544,7 @@ void snd_attach_record(SpiceRecordInstance *sin)
|
||||
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);
|
||||
@ -1491,7 +1555,7 @@ void snd_attach_record(SpiceRecordInstance *sin)
|
||||
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))
|
||||
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);
|
||||
|
||||
@ -1544,9 +1608,12 @@ void snd_set_playback_compression(int on)
|
||||
for (; now; now = now->next) {
|
||||
if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) {
|
||||
PlaybackChannel* playback = (PlaybackChannel*)now->connection;
|
||||
int desired_mode = snd_desired_audio_mode(
|
||||
red_channel_client_test_remote_cap(now->connection->channel_client, SPICE_PLAYBACK_CAP_CELT_0_5_1)
|
||||
);
|
||||
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);
|
||||
|
||||
@ -145,3 +145,11 @@ SPICE_SERVER_0.12.4 {
|
||||
global:
|
||||
spice_server_set_agent_file_xfer;
|
||||
} SPICE_SERVER_0.12.3;
|
||||
|
||||
SPICE_SERVER_0.12.5 {
|
||||
global:
|
||||
spice_server_get_best_playback_rate;
|
||||
spice_server_set_playback_rate;
|
||||
spice_server_get_best_record_rate;
|
||||
spice_server_set_record_rate;
|
||||
} SPICE_SERVER_0.12.4;
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
#include <spice/vd_agent.h>
|
||||
#include <spice/macros.h>
|
||||
|
||||
#define SPICE_SERVER_VERSION 0x000c04 /* release 0.12.4 */
|
||||
#define SPICE_SERVER_VERSION 0x000c05 /* release 0.12.5 */
|
||||
|
||||
#ifdef SPICE_SERVER_INTERNAL
|
||||
#undef SPICE_GNUC_DEPRECATED
|
||||
@ -333,7 +333,7 @@ struct SpiceTabletInstance {
|
||||
|
||||
#define SPICE_INTERFACE_PLAYBACK "playback"
|
||||
#define SPICE_INTERFACE_PLAYBACK_MAJOR 1
|
||||
#define SPICE_INTERFACE_PLAYBACK_MINOR 2
|
||||
#define SPICE_INTERFACE_PLAYBACK_MINOR 3
|
||||
typedef struct SpicePlaybackInterface SpicePlaybackInterface;
|
||||
typedef struct SpicePlaybackInstance SpicePlaybackInstance;
|
||||
typedef struct SpicePlaybackState SpicePlaybackState;
|
||||
@ -342,7 +342,7 @@ enum {
|
||||
SPICE_INTERFACE_AUDIO_FMT_S16 = 1,
|
||||
};
|
||||
|
||||
#define SPICE_INTERFACE_PLAYBACK_FREQ 44100
|
||||
#define SPICE_INTERFACE_PLAYBACK_FREQ 48000
|
||||
#define SPICE_INTERFACE_PLAYBACK_CHAN 2
|
||||
#define SPICE_INTERFACE_PLAYBACK_FMT SPICE_INTERFACE_AUDIO_FMT_S16
|
||||
|
||||
@ -367,12 +367,12 @@ void spice_server_playback_set_mute(SpicePlaybackInstance *sin, uint8_t mute);
|
||||
|
||||
#define SPICE_INTERFACE_RECORD "record"
|
||||
#define SPICE_INTERFACE_RECORD_MAJOR 2
|
||||
#define SPICE_INTERFACE_RECORD_MINOR 2
|
||||
#define SPICE_INTERFACE_RECORD_MINOR 3
|
||||
typedef struct SpiceRecordInterface SpiceRecordInterface;
|
||||
typedef struct SpiceRecordInstance SpiceRecordInstance;
|
||||
typedef struct SpiceRecordState SpiceRecordState;
|
||||
|
||||
#define SPICE_INTERFACE_RECORD_FREQ 44100
|
||||
#define SPICE_INTERFACE_RECORD_FREQ 48000
|
||||
#define SPICE_INTERFACE_RECORD_CHAN 2
|
||||
#define SPICE_INTERFACE_RECORD_FMT SPICE_INTERFACE_AUDIO_FMT_S16
|
||||
|
||||
@ -393,6 +393,11 @@ void spice_server_record_set_volume(SpiceRecordInstance *sin,
|
||||
uint8_t nchannels, uint16_t *volume);
|
||||
void spice_server_record_set_mute(SpiceRecordInstance *sin, uint8_t mute);
|
||||
|
||||
uint32_t spice_server_get_best_playback_rate(SpicePlaybackInstance *sin);
|
||||
void spice_server_set_playback_rate(SpicePlaybackInstance *sin, uint32_t frequency);
|
||||
uint32_t spice_server_get_best_record_rate(SpiceRecordInstance *sin);
|
||||
void spice_server_set_record_rate(SpiceRecordInstance *sin, uint32_t frequency);
|
||||
|
||||
/* char device interfaces */
|
||||
|
||||
#define SPICE_INTERFACE_CHAR_DEVICE "char_device"
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit c108e4ee8cb33218d9a64e25de9e79b63d23a8e7
|
||||
Subproject commit 57ce430ccd66bd1ca2447c14503234cfb88e2365
|
||||
Loading…
Reference in New Issue
Block a user