Index: head/www/chromium/files/sndio_input.cc =================================================================== --- head/www/chromium/files/sndio_input.cc (revision 506265) +++ head/www/chromium/files/sndio_input.cc (revision 506266) @@ -1,170 +1,201 @@ // Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "media/audio/sndio/sndio_input.h" - -#include - #include "base/bind.h" #include "base/logging.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" +#include "media/base/audio_timestamp_helper.h" #include "media/audio/openbsd/audio_manager_openbsd.h" #include "media/audio/audio_manager.h" +#include "media/audio/sndio/sndio_input.h" namespace media { static const SampleFormat kSampleFormat = kSampleFormatS16; -void sndio_in_onmove(void *arg, int delta) { - NOTIMPLEMENTED(); +void SndioAudioInputStream::OnMoveCallback(void *arg, int delta) +{ SndioAudioInputStream* self = static_cast(arg); - self->hw_delay_ = delta - self->params_.GetBytesPerFrame(kSampleFormat); + self->hw_delay += delta; } -void *sndio_in_threadstart(void *arg) { - NOTIMPLEMENTED(); +void *SndioAudioInputStream::ThreadEntry(void *arg) { SndioAudioInputStream* self = static_cast(arg); - self->ReadAudio(); + self->ThreadLoop(); return NULL; } -SndioAudioInputStream::SndioAudioInputStream(AudioManagerBase* audio_manager, - const std::string& device_name, - const AudioParameters& params) - : audio_manager_(audio_manager), - device_name_(device_name), - params_(params), - bytes_per_buffer_(params.GetBytesPerBuffer(kSampleFormat)), - buffer_duration_(base::TimeDelta::FromMicroseconds( - params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / - static_cast(params.sample_rate()))), - callback_(NULL), - device_handle_(NULL), - read_callback_behind_schedule_(false), - audio_bus_(AudioBus::Create(params)) { +SndioAudioInputStream::SndioAudioInputStream(AudioManagerBase* manager, + const std::string& device_name, + const AudioParameters& params) + : manager(manager), + params(params), + audio_bus(AudioBus::Create(params)), + state(kClosed) { } -SndioAudioInputStream::~SndioAudioInputStream() {} +SndioAudioInputStream::~SndioAudioInputStream() { + if (state != kClosed) + Close(); +} bool SndioAudioInputStream::Open() { struct sio_par par; int sig; - if (device_handle_) - return false; // Already open. + if (state != kClosed) + return false; - if (params_.format() != AudioParameters::AUDIO_PCM_LINEAR && - params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY) { + if (params.format() != AudioParameters::AUDIO_PCM_LINEAR && + params.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY) { LOG(WARNING) << "Unsupported audio format."; return false; } sio_initpar(&par); - par.rate = params_.sample_rate(); - par.pchan = params_.channels(); - par.bits = SampleFormatToBytesPerChannel(kSampleFormat); + par.rate = params.sample_rate(); + par.rchan = params.channels(); + par.bits = SampleFormatToBitsPerChannel(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; + par.appbufsz = params.frames_per_buffer(); - device_handle_ = sio_open(SIO_DEVANY, SIO_REC, 0); + hdl = sio_open(SIO_DEVANY, SIO_REC, 0); - if (device_handle_ == NULL) { + if (hdl == NULL) { LOG(ERROR) << "Couldn't open audio device."; return false; } - if (!sio_setpar(device_handle_, &par) || !sio_getpar(device_handle_, &par)) { + if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &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) || + if (par.rate != (unsigned int)params.sample_rate() || + par.rchan != (unsigned int)params.channels() || + par.bits != (unsigned int)SampleFormatToBitsPerChannel(kSampleFormat) || par.sig != (unsigned int)sig || (par.bps > 1 && par.le != SIO_LE_NATIVE) || (par.bits != par.bps * 8)) { LOG(ERROR) << "Unsupported audio parameters."; goto bad_close; } - sio_onmove(device_handle_, sndio_in_onmove, this); - - audio_buffer_.reset(new uint8_t[bytes_per_buffer_]); - + state = kStopped; + buffer = new char[audio_bus->frames() * params.GetBytesPerFrame(kSampleFormat)]; + sio_onmove(hdl, &OnMoveCallback, this); return true; bad_close: - sio_close(device_handle_); + sio_close(hdl); return false; } -void SndioAudioInputStream::Start(AudioInputCallback* callback) { - DCHECK(!callback_ && callback); - callback_ = callback; +void SndioAudioInputStream::Start(AudioInputCallback* cb) { + StartAgc(); - // 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) - LOG(ERROR) << "Failed to create real-time thread."; + state = kRunning; + hw_delay = 0; + callback = cb; + sio_start(hdl); + if (pthread_create(&thread, NULL, &ThreadEntry, this) != 0) { + LOG(ERROR) << "Failed to create real-time thread for recording."; + sio_stop(hdl); + state = kStopped; + } } -void SndioAudioInputStream::ReadAudio() { - NOTIMPLEMENTED(); -} - void SndioAudioInputStream::Stop() { - if (!device_handle_ || !callback_) + + if (state == kStopped) return; - StopAgc(); + state = kStopWait; + pthread_join(thread, NULL); + sio_stop(hdl); + state = kStopped; - pthread_join(thread_, NULL); - sio_stop(device_handle_); - - callback_ = NULL; + StopAgc(); } void SndioAudioInputStream::Close() { - if (device_handle_) { - sio_close(device_handle_); - audio_buffer_.reset(); - device_handle_ = NULL; - } - audio_manager_->ReleaseInputStream(this); + if (state == kClosed) + return; + + if (state == kRunning) + Stop(); + + state = kClosed; + delete [] buffer; + sio_close(hdl); + + manager->ReleaseInputStream(this); } double SndioAudioInputStream::GetMaxVolume() { - return static_cast(SIO_MAXVOL); + // Not supported + return 0.0; } void SndioAudioInputStream::SetVolume(double volume) { - NOTIMPLEMENTED(); + // Not supported. Do nothing. } double SndioAudioInputStream::GetVolume() { - long current_volume = 0; - return static_cast(current_volume); + // Not supported. + return 0.0; } bool SndioAudioInputStream::IsMuted() { + // Not supported. return false; } void SndioAudioInputStream::SetOutputDeviceForAec( const std::string& output_device_id) { -// Not supported. Do nothing. + // Not supported. +} + +void SndioAudioInputStream::ThreadLoop(void) { + size_t todo, n; + char *data; + unsigned int nframes; + double normalized_volume = 0.0; + + nframes = audio_bus->frames(); + + while (state == kRunning && !sio_eof(hdl)) { + + GetAgcVolume(&normalized_volume); + + // read one block + todo = nframes * params.GetBytesPerFrame(kSampleFormat); + data = buffer; + while (todo > 0) { + n = sio_read(hdl, data, todo); + if (n == 0) + return; // unrecoverable I/O error + todo -= n; + data += n; + } + hw_delay -= nframes; + + // convert frames count to TimeDelta + const base::TimeDelta delay = AudioTimestampHelper::FramesToTime(hw_delay, + params.sample_rate() * 1000); + + // push into bus + audio_bus->FromInterleaved(buffer, nframes, SampleFormatToBytesPerChannel(kSampleFormat)); + + // invoke callback + callback->OnData(audio_bus.get(), base::TimeTicks::Now() - delay, 1.); + } } } // namespace media Index: head/www/chromium/files/sndio_input.h =================================================================== --- head/www/chromium/files/sndio_input.h (revision 506265) +++ head/www/chromium/files/sndio_input.h (revision 506266) @@ -1,109 +1,91 @@ // Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef MEDIA_AUDIO_SNDIO_SNDIO_INPUT_H_ #define MEDIA_AUDIO_SNDIO_SNDIO_INPUT_H_ #include #include #include #include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "media/audio/agc_audio_stream.h" #include "media/audio/audio_io.h" #include "media/audio/audio_device_description.h" #include "media/base/audio_parameters.h" namespace media { class AudioManagerBase; -// call-backs invoked from C libraries, thus requiring C linkage -extern "C" { - // Invoked (on the real-time thread) at each sound card clock tick - void sndio_in_onmove(void *arg, int delta); - // Invoked (on the real-time thread) whenever the volume changes - void sndio_in_onvol(void *arg, unsigned int vol); - // Real-time thread entry point - void *sndio_in_threadstart(void *arg); -} - -// Provides an input stream for audio capture based on the SNDIO PCM interface. -// This object is not thread safe and all methods should be invoked in the -// thread that created the object. +// Implementation of AudioOutputStream using sndio(7) class SndioAudioInputStream : public AgcAudioStream { public: // Pass this to the constructor if you want to attempt auto-selection // of the audio recording device. static const char kAutoSelectDevice[]; // Create a PCM Output stream for the SNDIO device identified by // |device_name|. If unsure of what to use for |device_name|, use // |kAutoSelectDevice|. SndioAudioInputStream(AudioManagerBase* audio_manager, const std::string& device_name, const AudioParameters& params); ~SndioAudioInputStream() override; // Implementation of AudioInputStream. bool Open() override; void Start(AudioInputCallback* callback) override; void Stop() override; void Close() override; double GetMaxVolume() override; void SetVolume(double volume) override; double GetVolume() override; bool IsMuted() override; void SetOutputDeviceForAec(const std::string& output_device_id) override; - // C-linkage call-backs are friends to access private data - friend void sndio_in_onmove(void *arg, int delta); - friend void sndio_in_onvol(void *arg, unsigned int vol); - friend void *sndio_in_threadstart(void *arg); - private: - // Logs the error and invokes any registered callbacks. - void HandleError(const char* method, int error); - // Reads one or more buffers of audio from the device, passes on to the - // registered callback and schedules the next read. - void ReadAudio(); + 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 + }; - // Recovers from any device errors if possible. - bool Recover(int error); + // C-style call-backs + static void OnMoveCallback(void *arg, int delta); + static void* ThreadEntry(void *arg); - // Non-refcounted pointer back to the audio manager. - // The AudioManager indirectly holds on to stream objects, so we don't - // want circular references. Additionally, stream objects live on the audio - // thread, which is owned by the audio manager and we don't want to addref - // the manager from that thread. - AudioManagerBase* audio_manager_; - std::string device_name_; - AudioParameters params_; - int bytes_per_buffer_; - base::TimeDelta buffer_duration_; // Length of each recorded buffer. - AudioInputCallback* callback_; // Valid during a recording session. - base::TimeTicks next_read_time_; // Scheduled time for next read callback. - 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_; - - int hw_delay_; - int sndio_rec_bufsize_; - int sndio_rec_bufsz_; - - // High priority thread running RealTimeThread() - pthread_t thread_; + // Continuously moves data from the device to the consumer + void ThreadLoop(); + // Our creator, the audio manager needs to be notified when we close. + AudioManagerBase* manager; + // Parameters of the source + AudioParameters params; + // We store data here for consumer + std::unique_ptr audio_bus; + // Call-back that consumes recorded data + AudioInputCallback* callback; // Valid during a recording session. + // Handle of the audio device + struct sio_hdl* hdl; + // Current state of the stream + enum StreamState state; + // High priority thread running ThreadLoop() + pthread_t thread; + // Number of frames buffered in the hardware + int hw_delay; + // Temporary buffer where data is stored sndio-compatible format + char* buffer; DISALLOW_COPY_AND_ASSIGN(SndioAudioInputStream); }; } // namespace media #endif // MEDIA_AUDIO_SNDIO_SNDIO_INPUT_H_ Index: head/www/chromium/files/sndio_output.cc =================================================================== --- head/www/chromium/files/sndio_output.cc (revision 506265) +++ head/www/chromium/files/sndio_output.cc (revision 506266) @@ -1,179 +1,178 @@ // Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/logging.h" #include "base/time/time.h" #include "base/time/default_tick_clock.h" #include "media/audio/audio_manager_base.h" #include "media/base/audio_timestamp_helper.h" #include "media/audio/sndio/sndio_output.h" namespace media { static const SampleFormat kSampleFormat = kSampleFormatS16; -void sndio_onmove(void *arg, int delta) { +void SndioAudioOutputStream::OnMoveCallback(void *arg, int delta) { SndioAudioOutputStream* self = static_cast(arg); - self->hw_delay = delta; + self->hw_delay -= delta; } -void sndio_onvol(void *arg, unsigned int vol) { +void SndioAudioOutputStream::OnVolCallback(void *arg, unsigned int vol) { SndioAudioOutputStream* self = static_cast(arg); self->vol = vol; } -void *sndio_threadstart(void *arg) { +void *SndioAudioOutputStream::ThreadEntry(void *arg) { SndioAudioOutputStream* self = static_cast(arg); - self->RealTimeThread(); + self->ThreadLoop(); return NULL; } SndioAudioOutputStream::SndioAudioOutputStream(const AudioParameters& params, AudioManagerBase* manager) : manager(manager), params(params), audio_bus(AudioBus::Create(params)), - bytes_per_frame(params.GetBytesPerFrame(kSampleFormat)), state(kClosed), mutex(PTHREAD_MUTEX_INITIALIZER) { } SndioAudioOutputStream::~SndioAudioOutputStream() { if (state != kClosed) Close(); } bool SndioAudioOutputStream::Open() { struct sio_par par; int sig; if (params.format() != AudioParameters::AUDIO_PCM_LINEAR && params.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY) { LOG(WARNING) << "Unsupported audio format."; return false; } sio_initpar(&par); par.rate = params.sample_rate(); par.pchan = params.channels(); par.bits = SampleFormatToBitsPerChannel(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(); hdl = sio_open(SIO_DEVANY, SIO_PLAY, 0); if (hdl == NULL) { LOG(ERROR) << "Couldn't open audio device."; return false; } if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &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)SampleFormatToBitsPerChannel(kSampleFormat) || par.sig != (unsigned int)sig || (par.bps > 1 && par.le != SIO_LE_NATIVE) || (par.bits != par.bps * 8)) { LOG(ERROR) << "Unsupported audio parameters."; goto bad_close; } state = kStopped; volpending = 0; vol = 0; buffer = new char[audio_bus->frames() * params.GetBytesPerFrame(kSampleFormat)]; - sio_onmove(hdl, sndio_onmove, this); - sio_onvol(hdl, sndio_onvol, this); + sio_onmove(hdl, &OnMoveCallback, this); + sio_onvol(hdl, &OnVolCallback, this); return true; bad_close: sio_close(hdl); return false; } void SndioAudioOutputStream::Close() { if (state == kClosed) return; if (state == kRunning) Stop(); state = kClosed; delete [] buffer; sio_close(hdl); manager->ReleaseOutputStream(this); // Calls the destructor } void SndioAudioOutputStream::Start(AudioSourceCallback* callback) { state = kRunning; hw_delay = 0; source = callback; sio_start(hdl); - if (pthread_create(&thread, NULL, sndio_threadstart, this) != 0) { + if (pthread_create(&thread, NULL, &ThreadEntry, this) != 0) { LOG(ERROR) << "Failed to create real-time thread."; sio_stop(hdl); state = kStopped; } } void SndioAudioOutputStream::Stop() { if (state == kStopped) return; state = kStopWait; pthread_join(thread, NULL); sio_stop(hdl); state = kStopped; } void SndioAudioOutputStream::SetVolume(double v) { pthread_mutex_lock(&mutex); vol = v * SIO_MAXVOL; volpending = 1; pthread_mutex_unlock(&mutex); } void SndioAudioOutputStream::GetVolume(double* v) { pthread_mutex_lock(&mutex); *v = vol * (1. / SIO_MAXVOL); pthread_mutex_unlock(&mutex); } -void SndioAudioOutputStream::RealTimeThread(void) { +void SndioAudioOutputStream::ThreadLoop(void) { int avail, count; while (state == kRunning) { // Update volume if needed pthread_mutex_lock(&mutex); if (volpending) { volpending = 0; sio_setvol(hdl, vol); } pthread_mutex_unlock(&mutex); // Get data to play const base::TimeDelta delay = AudioTimestampHelper::FramesToTime(hw_delay, params.sample_rate() * 1000); count = source->OnMoreData(delay, base::TimeTicks::Now(), 0, audio_bus.get()); audio_bus->ToInterleaved(count, SampleFormatToBytesPerChannel(kSampleFormat), buffer); if (count == 0) { // We have to submit something to the device count = audio_bus->frames(); memset(buffer, 0, count * params.GetBytesPerFrame(kSampleFormat)); LOG(WARNING) << "No data to play, running empty cycle."; } // Submit data to the device avail = count * params.GetBytesPerFrame(kSampleFormat); count = sio_write(hdl, buffer, avail); if (count == 0) { LOG(WARNING) << "Audio device disconnected."; break; } // Update hardware pointer hw_delay += count; } } } // namespace media Index: head/www/chromium/files/sndio_output.h =================================================================== --- head/www/chromium/files/sndio_output.h (revision 506265) +++ head/www/chromium/files/sndio_output.h (revision 506266) @@ -1,92 +1,85 @@ // Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef MEDIA_AUDIO_SNDIO_SNDIO_OUTPUT_H_ #define MEDIA_AUDIO_SNDIO_SNDIO_OUTPUT_H_ #include #include #include "base/time/tick_clock.h" #include "base/time/time.h" #include "media/audio/audio_io.h" - namespace media { -class AudioParameters; class AudioManagerBase; -// call-backs invoked from C libraries, thus requiring C linkage -extern "C" { - // Invoked (on the real-time thread) at each sound card clock tick - void sndio_onmove(void *arg, int delta); - // Invoked (on the real-time thread) whenever the volume changes - void sndio_onvol(void *arg, unsigned int vol); - // Real-time thread entry point - void *sndio_threadstart(void *arg); -} - // Implementation of AudioOutputStream using sndio(7) class SndioAudioOutputStream : public AudioOutputStream { public: // The manager is creating this object SndioAudioOutputStream(const AudioParameters& params, AudioManagerBase* manager); virtual ~SndioAudioOutputStream(); // Implementation of AudioOutputStream. - virtual bool Open() override; - virtual void Close() override; - virtual void Start(AudioSourceCallback* callback) override; - virtual void Stop() override; - virtual void SetVolume(double volume) override; - virtual void GetVolume(double* volume) override; + bool Open() override; + void Close() override; + void Start(AudioSourceCallback* callback) override; + void Stop() override; + void SetVolume(double volume) override; + void GetVolume(double* volume) override; - // C-linkage call-backs are friends to access private data friend void sndio_onmove(void *arg, int delta); friend void sndio_onvol(void *arg, unsigned int vol); friend void *sndio_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 }; - // Continuously moves data from the audio bus to the device - void RealTimeThread(void); + + // C-style call-backs + static void OnMoveCallback(void *arg, int delta); + static void OnVolCallback(void *arg, unsigned int vol); + static void* ThreadEntry(void *arg); + + // Continuously moves data from the producer to the device + void ThreadLoop(void); + // Our creator, the audio manager needs to be notified when we close. AudioManagerBase* manager; // Parameters of the source AudioParameters params; // Source stores data here std::unique_ptr audio_bus; - int bytes_per_frame; // Call-back that produces data to play AudioSourceCallback* source; // Handle of the audio device struct sio_hdl* hdl; // Current state of the stream enum StreamState state; - // High priority thread running RealTimeThread() + // High priority thread running ThreadLoop() pthread_t thread; // Protects vol, volpending and hw_delay pthread_mutex_t mutex; // Current volume in the 0..SIO_MAXVOL range int vol; // Set to 1 if volumes must be refreshed in the realtime thread int volpending; - // Number of bytes buffered in the hardware + // Number of frames buffered in the hardware int hw_delay; // Temporary buffer where data is stored sndio-compatible format char* buffer; DISALLOW_COPY_AND_ASSIGN(SndioAudioOutputStream); }; } // namespace media #endif // MEDIA_AUDIO_SNDIO_SNDIO_OUTPUT_H_