/* 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 . */ #include "common.h" #include "red_client.h" #include "audio_channels.h" #include "audio_devices.h" #define NUM_SAMPLES_MESSAGES 4 static uint32_t get_mm_time() { return uint32_t(Platform::get_monolithic_time() / (1000 * 1000)); } class RecordSamplesMessage: public RedChannel::OutMessage { public: RecordSamplesMessage(RecordChannel& channel); virtual ~RecordSamplesMessage(); virtual RedPeer::OutMessage& peer_message() { return *_massage;} virtual void release(); private: RecordChannel& _channel; RedPeer::OutMessage *_massage; }; RecordSamplesMessage::RecordSamplesMessage(RecordChannel& channel) : _channel (channel) , _massage (new Message(SPICE_MSGC_RECORD_DATA, sizeof(SpiceMsgcRecordPacket) + 4096)) { } RecordSamplesMessage::~RecordSamplesMessage() { delete _massage; } void RecordSamplesMessage::release() { _channel.release_message(this); } int RecordChannel::data_mode = SPICE_AUDIO_DATA_MODE_CELT_0_5_1; class RecordHandler: public MessageHandlerImp { public: RecordHandler(RecordChannel& channel) : MessageHandlerImp(channel) {} }; RecordChannel::RecordChannel(RedClient& client, uint32_t id) : RedChannel(client, SPICE_CHANNEL_RECORD, id, new RecordHandler(*this)) , _wave_recorder (NULL) , _mode (SPICE_AUDIO_DATA_MODE_INVALD) , _celt_mode (NULL) , _celt_encoder (NULL) { for (int i = 0; i < NUM_SAMPLES_MESSAGES; i++) { _messages.push_front(new RecordSamplesMessage(*this)); } RecordHandler* handler = static_cast(get_message_handler()); handler->set_handler(SPICE_MSG_MIGRATE, &RecordChannel::handle_migrate, 0); handler->set_handler(SPICE_MSG_SET_ACK, &RecordChannel::handle_set_ack, sizeof(SpiceMsgSetAck)); handler->set_handler(SPICE_MSG_PING, &RecordChannel::handle_ping, sizeof(SpiceMsgPing)); handler->set_handler(SPICE_MSG_WAIT_FOR_CHANNELS, &RecordChannel::handle_wait_for_channels, sizeof(SpiceMsgWaitForChannels)); handler->set_handler(SPICE_MSG_DISCONNECTING, &RecordChannel::handle_disconnect, sizeof(SpiceMsgDisconnect)); handler->set_handler(SPICE_MSG_NOTIFY, &RecordChannel::handle_notify, sizeof(SpiceMsgNotify)); handler->set_handler(SPICE_MSG_RECORD_START, &RecordChannel::handle_start, sizeof(SpiceMsgRecordStart)); set_capability(SPICE_RECORD_CAP_CELT_0_5_1); } RecordChannel::~RecordChannel(void) { while (!_messages.empty()) { RecordSamplesMessage *mes; mes = *_messages.begin(); _messages.pop_front(); delete mes; } delete _wave_recorder; if (_celt_encoder) { celt051_encoder_destroy(_celt_encoder); } if (_celt_mode) { celt051_mode_destroy(_celt_mode); } } bool RecordChannel::abort(void) { return (!_wave_recorder || _wave_recorder->abort()) && RedChannel::abort(); } void RecordChannel::on_connect() { Message* message = new Message(SPICE_MSGC_RECORD_MODE, sizeof(SpiceMsgcRecordMode)); SpiceMsgcRecordMode *mode = (SpiceMsgcRecordMode *)message->data(); mode->time = get_mm_time(); mode->mode = _mode = test_capability(SPICE_RECORD_CAP_CELT_0_5_1) ? RecordChannel::data_mode : SPICE_AUDIO_DATA_MODE_RAW; post_message(message); } void RecordChannel::send_start_mark() { Message* message = new Message(SPICE_MSGC_RECORD_START_MARK, sizeof(SpiceMsgcRecordStartMark)); SpiceMsgcRecordStartMark *start_mark = (SpiceMsgcRecordStartMark *)message->data(); start_mark->time = get_mm_time(); post_message(message); } void RecordChannel::handle_start(RedPeer::InMessage* message) { RecordHandler* handler = static_cast(get_message_handler()); SpiceMsgRecordStart* start = (SpiceMsgRecordStart*)message->data(); handler->set_handler(SPICE_MSG_RECORD_START, NULL, 0); handler->set_handler(SPICE_MSG_RECORD_STOP, &RecordChannel::handle_stop, 0); ASSERT(!_wave_recorder && !_celt_mode && !_celt_encoder); // for now support only one setting if (start->format != SPICE_AUDIO_FMT_S16) { THROW("unexpected format"); } 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; } int frame_size = 256; int celt_mode_err; _frame_bytes = frame_size * bits_per_sample * start->channels / 8; if (!(_celt_mode = celt051_mode_create(start->frequency, start->channels, frame_size, &celt_mode_err))) { THROW("create celt mode failed %d", celt_mode_err); } if (!(_celt_encoder = celt051_encoder_create(_celt_mode))) { THROW("create celt encoder failed"); } send_start_mark(); _wave_recorder->start(); } void RecordChannel::handle_stop(RedPeer::InMessage* message) { RecordHandler* handler = static_cast(get_message_handler()); handler->set_handler(SPICE_MSG_RECORD_START, &RecordChannel::handle_start, sizeof(SpiceMsgRecordStart)); handler->set_handler(SPICE_MSG_RECORD_STOP, NULL, 0); if (!_wave_recorder) { return; } ASSERT(_celt_mode && _celt_encoder); _wave_recorder->stop(); celt051_encoder_destroy(_celt_encoder); _celt_encoder = NULL; celt051_mode_destroy(_celt_mode); _celt_mode = NULL; delete _wave_recorder; _wave_recorder = NULL; } RecordSamplesMessage* RecordChannel::get_message() { Lock lock(_messages_lock); if (_messages.empty()) { return NULL; } RecordSamplesMessage* ret = *_messages.begin(); _messages.pop_front(); return ret; } void RecordChannel::release_message(RecordSamplesMessage *message) { Lock lock(_messages_lock); _messages.push_front(message); } void RecordChannel::add_event_source(EventSources::File& event_source) { get_process_loop().add_file(event_source); } void RecordChannel::remove_event_source(EventSources::File& event_source) { get_process_loop().remove_file(event_source); } void RecordChannel::add_event_source(EventSources::Trigger& event_source) { get_process_loop().add_trigger(event_source); } void RecordChannel::remove_event_source(EventSources::Trigger& event_source) { get_process_loop().remove_trigger(event_source); } #define FRAME_SIZE 256 #define CELT_BIT_RATE (64 * 1024) #define CELT_COMPRESSED_FRAME_BYTES (FRAME_SIZE * CELT_BIT_RATE / 44100 / 8) void RecordChannel::push_frame(uint8_t *frame) { RecordSamplesMessage *message; ASSERT(_frame_bytes == FRAME_SIZE * 4); if (!(message = get_message())) { DBG(0, "blocked"); return; } uint8_t celt_buf[CELT_COMPRESSED_FRAME_BYTES]; int n; if (_mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1) { n = celt051_encode(_celt_encoder, (celt_int16_t *)frame, NULL, celt_buf, CELT_COMPRESSED_FRAME_BYTES); if (n < 0) { THROW("celt encode failed"); } frame = celt_buf; } else { n = _frame_bytes; } RedPeer::OutMessage& peer_message = message->peer_message(); peer_message.resize(n + sizeof(SpiceMsgcRecordPacket)); SpiceMsgcRecordPacket* packet = (SpiceMsgcRecordPacket*)peer_message.data(); packet->time = get_mm_time(); memcpy(packet->data, frame, n); post_message(message); } class RecordFactory: public ChannelFactory { public: RecordFactory() : ChannelFactory(SPICE_CHANNEL_RECORD) {} virtual RedChannel* construct(RedClient& client, uint32_t id) { return new RecordChannel(client, id); } }; static RecordFactory factory; ChannelFactory& RecordChannel::Factory() { return factory; }