diff --git a/www/chromium/files/sndio_input.cc b/www/chromium/files/sndio_input.cc index 4a00d8ac1083..e4f6e6204f2b 100644 --- a/www/chromium/files/sndio_input.cc +++ b/www/chromium/files/sndio_input.cc @@ -12,20 +12,29 @@ #include "base/message_loop/message_loop.h" #include "media/audio/openbsd/audio_manager_openbsd.h" #include "media/audio/audio_manager.h" +#include "media/base/audio_timestamp_helper.h" namespace media { static const SampleFormat kSampleFormat = kSampleFormatS16; +// Number of blocks of buffers used in the |fifo_|. +const int kNumberOfBlocksBufferInFifo = 2; + void sndio_in_onmove(void *arg, int delta) { - NOTIMPLEMENTED(); SndioAudioInputStream* self = static_cast(arg); - self->hw_delay_ = delta - self->params_.GetBytesPerFrame(kSampleFormat); + self->hw_delay_ = delta; +} + +void sndio_in_onvol(void *arg, unsigned int volume) { + SndioAudioInputStream* self = static_cast(arg); + pthread_mutex_lock(&(self->mutex)); + self->vol = volume; + pthread_mutex_unlock(&(self->mutex)); } void *sndio_in_threadstart(void *arg) { - NOTIMPLEMENTED(); SndioAudioInputStream* self = static_cast(arg); self->ReadAudio(); @@ -45,10 +54,17 @@ SndioAudioInputStream::SndioAudioInputStream(AudioManagerBase* audio_manager, callback_(NULL), device_handle_(NULL), read_callback_behind_schedule_(false), - audio_bus_(AudioBus::Create(params)) { + state(kClosed), + mutex(PTHREAD_MUTEX_INITIALIZER), + fifo_(params.channels(), + params.frames_per_buffer(), + kNumberOfBlocksBufferInFifo) { } -SndioAudioInputStream::~SndioAudioInputStream() {} +SndioAudioInputStream::~SndioAudioInputStream() { + if (state != kClosed) + Close(); +} bool SndioAudioInputStream::Open() { struct sio_par par; @@ -59,21 +75,10 @@ bool SndioAudioInputStream::Open() { if (params_.format() != AudioParameters::AUDIO_PCM_LINEAR && params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY) { - LOG(WARNING) << "Unsupported audio format."; + LOG(ERROR) << "Unsupported audio format."; return false; } - sio_initpar(&par); - par.rate = params_.sample_rate(); - par.pchan = params_.channels(); - par.bits = SampleFormatToBytesPerChannel(kSampleFormat); - par.bps = par.bits / 8; - par.sig = sig = par.bits != 8 ? 1 : 0; - par.le = SIO_LE_NATIVE; - par.appbufsz = params_.frames_per_buffer(); - sndio_rec_bufsz_ = par.bufsz; - sndio_rec_bufsize_ = par.round * par.bps * par.rchan; - device_handle_ = sio_open(SIO_DEVANY, SIO_REC, 0); if (device_handle_ == NULL) { @@ -81,14 +86,24 @@ bool SndioAudioInputStream::Open() { return false; } + sio_initpar(&par); + + par.rate = params_.sample_rate(); + par.rchan = params_.channels(); + par.bps = SampleFormatToBytesPerChannel(kSampleFormat); + par.bits = par.bps * 8; + par.sig = sig = par.bits != 8 ? 1 : 0; + par.le = SIO_LE_NATIVE; + par.appbufsz = 10 * params_.channels() * par.bps * params_.frames_per_buffer(); + if (!sio_setpar(device_handle_, &par) || !sio_getpar(device_handle_, &par)) { LOG(ERROR) << "Couldn't set audio parameters."; goto bad_close; } if (par.rate != (unsigned int)params_.sample_rate() || - par.pchan != (unsigned int)params_.channels() || - par.bits != (unsigned int)SampleFormatToBytesPerChannel(kSampleFormat) || + par.rchan != (unsigned int)params_.channels() || + par.bps != (unsigned int)SampleFormatToBytesPerChannel(kSampleFormat) || par.sig != (unsigned int)sig || (par.bps > 1 && par.le != SIO_LE_NATIVE) || (par.bits != par.bps * 8)) { @@ -96,7 +111,16 @@ bool SndioAudioInputStream::Open() { goto bad_close; } sio_onmove(device_handle_, sndio_in_onmove, this); + sio_onvol(device_handle_, sndio_in_onvol, this); + + vol = SIO_MAXVOL; + + if (!sio_start(device_handle_)) { + LOG(ERROR) << "Couldn't start device."; + goto bad_close; + } + state = kStopped; audio_buffer_.reset(new uint8_t[bytes_per_buffer_]); return true; @@ -110,32 +134,80 @@ void SndioAudioInputStream::Start(AudioInputCallback* callback) { callback_ = callback; StartAgc(); + state = kRunning; // We start reading data half |buffer_duration_| later than when the // buffer might have got filled, to accommodate some delays in the audio // driver. This could also give us a smooth read sequence going forward. base::TimeDelta delay = buffer_duration_ + buffer_duration_ / 2; next_read_time_ = base::TimeTicks::Now() + delay; - if (pthread_create(&thread_, NULL, sndio_in_threadstart, this) != 0) + + if (pthread_create(&thread_, NULL, sndio_in_threadstart, this) != 0) { LOG(ERROR) << "Failed to create real-time thread."; + sio_stop(device_handle_); + state = kStopped; + } } void SndioAudioInputStream::ReadAudio() { - NOTIMPLEMENTED(); + DCHECK(callback_); + + double normalized_volume = 1.0; + void* data = audio_buffer_.get(); + int number_of_bytes = 0; + int number_of_frames = 0; + int bpc = SampleFormatToBytesPerChannel(kSampleFormat); + const AudioBus* audio_bus; + base::TimeTicks capture_time = base::TimeTicks::Now(); + + while (state == kRunning) { + normalized_volume = GetVolume()/SIO_MAXVOL; + + capture_time = base::TimeTicks::Now() - base::TimeDelta::FromMicroseconds(hw_delay_ * base::Time::kMicrosecondsPerSecond / static_cast(params_.sample_rate())); + + number_of_bytes = sio_read(device_handle_, data, bytes_per_buffer_); + number_of_frames = number_of_bytes/(bpc * params_.channels()); + + fifo_.Push(data, number_of_frames, bpc); + + while (fifo_.available_blocks()) { + audio_bus = fifo_.Consume(); + + callback_->OnData(audio_bus, capture_time, normalized_volume); + + // Move the capture time forward for each vended block. + capture_time += AudioTimestampHelper::FramesToTime(audio_bus->frames(), + params_.sample_rate()); + } + } } void SndioAudioInputStream::Stop() { + + if (state == kStopped) + return; + if (!device_handle_ || !callback_) return; StopAgc(); + state = kStopWait; pthread_join(thread_, NULL); sio_stop(device_handle_); - + state = kStopped; + fifo_.Clear(); callback_ = NULL; } void SndioAudioInputStream::Close() { + if (state == kClosed) + return; + + if (state == kRunning) + Stop(); + + state = kClosed; + if (device_handle_) { sio_close(device_handle_); audio_buffer_.reset(); @@ -149,13 +221,15 @@ double SndioAudioInputStream::GetMaxVolume() { return static_cast(SIO_MAXVOL); } -void SndioAudioInputStream::SetVolume(double volume) { - NOTIMPLEMENTED(); +void SndioAudioInputStream::SetVolume(double v) { + pthread_mutex_lock(&mutex); + int res = sio_setvol(device_handle_, v); + vol = v; + pthread_mutex_unlock(&mutex); } double SndioAudioInputStream::GetVolume() { - long current_volume = 0; - return static_cast(current_volume); + return vol; } bool SndioAudioInputStream::IsMuted() { diff --git a/www/chromium/files/sndio_input.h b/www/chromium/files/sndio_input.h index 2f3fca97a3a3..eb5906998e8d 100644 --- a/www/chromium/files/sndio_input.h +++ b/www/chromium/files/sndio_input.h @@ -17,6 +17,7 @@ #include "media/audio/audio_io.h" #include "media/audio/audio_device_description.h" #include "media/base/audio_parameters.h" +#include "media/base/audio_block_fifo.h" namespace media { @@ -67,6 +68,13 @@ class SndioAudioInputStream : public AgcAudioStream { friend void *sndio_in_threadstart(void *arg); private: + enum StreamState { + kClosed, // Not opened yet + kStopped, // Device opened, but not started yet + kRunning, // Started, device playing + kStopWait // Stopping, waiting for the real-time thread to exit + }; + // Logs the error and invokes any registered callbacks. void HandleError(const char* method, int error); @@ -92,14 +100,22 @@ class SndioAudioInputStream : public AgcAudioStream { struct sio_hdl* device_handle_; // Handle to the SNDIO PCM recording device. std::unique_ptr audio_buffer_; // Buffer used for reading audio data. bool read_callback_behind_schedule_; - std::unique_ptr audio_bus_; + // Current state of the stream + enum StreamState state; int hw_delay_; int sndio_rec_bufsize_; int sndio_rec_bufsz_; + // Current volume in the 0..SIO_MAXVOL range + int vol; // High priority thread running RealTimeThread() pthread_t thread_; + // Protects vol, volpending and hw_delay + pthread_mutex_t mutex; + + // Holds the data from the OS. + AudioBlockFifo fifo_; DISALLOW_COPY_AND_ASSIGN(SndioAudioInputStream); };