/* Copyright (C) 2009 Red Hat, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "common.h" #include "red_client.h" #include "audio_channels.h" #include "audio_devices.h" //#define WAVE_CAPTURE #ifdef WAVE_CAPTURE #include #define WAVE_BUF_SIZE (1024 * 1024 * 20) typedef struct __attribute__ ((__packed__)) ChunkHeader { uint32_t id; uint32_t size; } ChunkHeader; typedef struct __attribute__ ((__packed__)) FormatInfo { uint16_t compression_code; uint16_t num_channels; uint32_t sample_rate; uint32_t average_bytes_per_second; uint16_t block_align; uint16_t bits_per_sample; //uint16_t extra_format_bytes; //uint8_t extra[0]; } FormatInfo; static uint8_t* wave_buf = NULL; static uint8_t* wave_now = NULL; static uint8_t* wave_end = NULL; static bool wave_blocked = false; static void write_all(int fd, uint8_t* data, uint32_t size) { while (size) { int n = write(fd, data, size); if (n == -1) { if (errno != EINTR) { throw Exception(fmt("%s: failed") % __FUNCTION__); } } else { data += n; size -= n; } } } static void write_wave() { static uint32_t file_id = 0; char file_name[100]; ChunkHeader header; FormatInfo format; if (wave_buf == wave_now) { return; } sprintf(file_name, "/tmp/%u.wav", ++file_id); int fd = open(file_name, O_CREAT | O_TRUNC | O_WRONLY, 0644); if (fd == -1) { DBG(0, fmt("open file %s failed") % file_name); return; } memcpy((char *)&header.id, "RIFF", 4); header.size = 4; write_all(fd, (uint8_t *)&header, sizeof(header)); write_all(fd, (uint8_t *)"WAVE", 4); memcpy((char *)&header.id, "fmt ", 4); header.size = sizeof(format); write_all(fd, (uint8_t *)&header, sizeof(header)); format.compression_code = 1; format.num_channels = 2; format.sample_rate = 44100; format.average_bytes_per_second = format.sample_rate * 4; format.block_align = 4; format.bits_per_sample = 16; write_all(fd, (uint8_t *)&format, sizeof(format)); memcpy((char *)&header.id, "data", 4); header.size = wave_now - wave_buf; write_all(fd, (uint8_t *)&header, sizeof(header)); write_all(fd, wave_buf, header.size); close(fd); } static void init_wave() { if (!wave_buf) { wave_buf = new uint8_t[WAVE_BUF_SIZE]; } wave_now = wave_buf; wave_end = wave_buf + WAVE_BUF_SIZE; } static void start_wave() { wave_blocked = false; wave_now = wave_buf; } static void put_wave_data(uint8_t *data, uint32_t size) { if (wave_blocked || size > wave_end - wave_now) { wave_blocked = true; return; } memcpy((void *)wave_now, (void *)data, size); wave_now += size; } static void end_wave() { write_wave(); } #endif class PlaybackHandler: public MessageHandlerImp { public: PlaybackHandler(PlaybackChannel& channel) : MessageHandlerImp(channel) {} }; PlaybackChannel::PlaybackChannel(RedClient& client, uint32_t id) : RedChannel(client, RED_CHANNEL_PLAYBACK, id, new PlaybackHandler(*this), Platform::PRIORITY_HIGH) , _wave_player (NULL) , _mode (RED_AUDIO_DATA_MODE_INVALD) , _celt_mode (NULL) , _celt_decoder (NULL) , _playing (false) { #ifdef WAVE_CAPTURE init_wave(); #endif PlaybackHandler* handler = static_cast(get_message_handler()); handler->set_handler(RED_MIGRATE, &PlaybackChannel::handle_migrate, 0); handler->set_handler(RED_SET_ACK, &PlaybackChannel::handle_set_ack, sizeof(RedSetAck)); handler->set_handler(RED_PING, &PlaybackChannel::handle_ping, sizeof(RedPing)); handler->set_handler(RED_WAIT_FOR_CHANNELS, &PlaybackChannel::handle_wait_for_channels, sizeof(RedWaitForChannels)); handler->set_handler(RED_DISCONNECTING, &PlaybackChannel::handle_disconnect, sizeof(RedDisconnect)); handler->set_handler(RED_NOTIFY, &PlaybackChannel::handle_notify, sizeof(RedNotify)); handler->set_handler(RED_PLAYBACK_MODE, &PlaybackChannel::handle_mode, sizeof(RedPlaybackMode)); set_capability(RED_PLAYBACK_CAP_CELT_0_5_1); } PlaybackChannel::~PlaybackChannel(void) { delete _wave_player; if (_celt_decoder) { celt051_decoder_destroy(_celt_decoder); } if (_celt_mode) { celt051_mode_destroy(_celt_mode); } } bool PlaybackChannel::abort(void) { return (!_wave_player || _wave_player->abort()) && RedChannel::abort(); } void PlaybackChannel::set_data_handler() { PlaybackHandler* handler = static_cast(get_message_handler()); if (_mode == RED_AUDIO_DATA_MODE_RAW) { handler->set_handler(RED_PLAYBACK_DATA, &PlaybackChannel::handle_raw_data, 0); } else if (_mode == RED_AUDIO_DATA_MODE_CELT_0_5_1) { handler->set_handler(RED_PLAYBACK_DATA, &PlaybackChannel::handle_celt_data, 0); } else { THROW("invalid mode"); } } void PlaybackChannel::handle_mode(RedPeer::InMessage* message) { RedPlaybackMode* playbacke_mode = (RedPlaybackMode*)message->data(); if (playbacke_mode->mode != RED_AUDIO_DATA_MODE_RAW && playbacke_mode->mode != RED_AUDIO_DATA_MODE_CELT_0_5_1) { THROW("invalid mode"); } _mode = playbacke_mode->mode; if (_playing) { set_data_handler(); return; } PlaybackHandler* handler = static_cast(get_message_handler()); handler->set_handler(RED_PLAYBACK_START, &PlaybackChannel::handle_start, sizeof(RedPlaybackStart)); } void PlaybackChannel::null_handler(RedPeer::InMessage* message) { } void PlaybackChannel::disable() { PlaybackHandler* handler = static_cast(get_message_handler()); handler->set_handler(RED_PLAYBACK_START, &PlaybackChannel::null_handler, 0); handler->set_handler(RED_PLAYBACK_STOP, &PlaybackChannel::null_handler, 0); handler->set_handler(RED_PLAYBACK_MODE, &PlaybackChannel::null_handler, 0); handler->set_handler(RED_PLAYBACK_DATA, &PlaybackChannel::null_handler, 0); } void PlaybackChannel::handle_start(RedPeer::InMessage* message) { PlaybackHandler* handler = static_cast(get_message_handler()); RedPlaybackStart* start = (RedPlaybackStart*)message->data(); handler->set_handler(RED_PLAYBACK_START, NULL, 0); handler->set_handler(RED_PLAYBACK_STOP, &PlaybackChannel::handle_stop, 0); #ifdef WAVE_CAPTURE start_wave(); #endif if (!_wave_player) { // for now support only one setting int celt_mode_err; if (start->format != RED_AUDIO_FMT_S16) { THROW("unexpected format"); } int bits_per_sample = 16; int frame_size = 256; _frame_bytes = frame_size * start->channels * bits_per_sample / 8; try { _wave_player = Platform::create_player(start->frequency, bits_per_sample, start->channels); } catch (...) { LOG_WARN("create player failed"); //todo: support disconnecting singel channel disable(); return; } 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_decoder = celt051_decoder_create(_celt_mode))) { THROW("create celt decoder"); } } _playing = true; _frame_count = 0; set_data_handler(); } void PlaybackChannel::handle_stop(RedPeer::InMessage* message) { PlaybackHandler* handler = static_cast(get_message_handler()); handler->set_handler(RED_PLAYBACK_STOP, NULL, 0); handler->set_handler(RED_PLAYBACK_DATA, NULL, 0); handler->set_handler(RED_PLAYBACK_START, &PlaybackChannel::handle_start, sizeof(RedPlaybackStart)); #ifdef WAVE_CAPTURE end_wave(); #endif _wave_player->stop(); _playing = false; } void PlaybackChannel::handle_raw_data(RedPeer::InMessage* message) { RedPlaybackPacket* packet = (RedPlaybackPacket*)message->data(); uint8_t* data = (uint8_t*)(packet + 1); uint32_t size = message->size() - sizeof(*packet); #ifdef WAVE_CAPTURE put_wave_data(data, size); return; #endif if (size != _frame_bytes) { //for now throw on unexpected size (based on current server imp). // will probably be replaced by supporting flexible data size in the player imp THROW("unexpected frame size"); } if ((_frame_count++ % 1000) == 0) { get_client().set_mm_time(packet->time - _wave_player->get_delay_ms()); } _wave_player->write(data); } void PlaybackChannel::handle_celt_data(RedPeer::InMessage* message) { RedPlaybackPacket* packet = (RedPlaybackPacket*)message->data(); uint8_t* data = (uint8_t*)(packet + 1); uint32_t size = message->size() - sizeof(*packet); celt_int16_t pcm[256 * 2]; if (celt051_decode(_celt_decoder, data, size, pcm) != CELT_OK) { THROW("celt decode failed"); } #ifdef WAVE_CAPTURE put_wave_data(pcm, _frame_bytes); return; #endif if ((_frame_count++ % 1000) == 0) { get_client().set_mm_time(packet->time - _wave_player->get_delay_ms()); } _wave_player->write((uint8_t *)pcm); } class PlaybackFactory: public ChannelFactory { public: PlaybackFactory() : ChannelFactory(RED_CHANNEL_PLAYBACK) {} virtual RedChannel* construct(RedClient& client, uint32_t id) { return new PlaybackChannel(client, id); } }; static PlaybackFactory factory; ChannelFactory& PlaybackChannel::Factory() { return factory; }