Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 17 Jul 2019 18:47:02 +0000 (UTC)
From:      "Carlos J. Puga Medina" <cpm@FreeBSD.org>
To:        ports-committers@freebsd.org, svn-ports-all@freebsd.org, svn-ports-branches@freebsd.org
Subject:   svn commit: r506812 - branches/2019Q3/www/chromium/files
Message-ID:  <201907171847.x6HIl2se070334@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: cpm
Date: Wed Jul 17 18:47:02 2019
New Revision: 506812
URL: https://svnweb.freebsd.org/changeset/ports/506812

Log:
  MFH: r506266
  
  www/chromium: Add support for audio recording using sndio
  
  PR:		238869
  Submitted by:	Zielonka Michal <zielonka.michal@gmail.com>, tobik
  Obtained from:	https://github.com/t6/freebsd-ports-sndio/commit/f213ba25a3460ed6f8e858f04f5592fca8edb7d8
  
  Approved by:	ports-secteam (joneum)

Modified:
  branches/2019Q3/www/chromium/files/sndio_input.cc
  branches/2019Q3/www/chromium/files/sndio_input.h
  branches/2019Q3/www/chromium/files/sndio_output.cc
  branches/2019Q3/www/chromium/files/sndio_output.h
Directory Properties:
  branches/2019Q3/   (props changed)

Modified: branches/2019Q3/www/chromium/files/sndio_input.cc
==============================================================================
--- branches/2019Q3/www/chromium/files/sndio_input.cc	Wed Jul 17 18:46:15 2019	(r506811)
+++ branches/2019Q3/www/chromium/files/sndio_input.cc	Wed Jul 17 18:47:02 2019	(r506812)
@@ -2,169 +2,200 @@
 // 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 <stddef.h>
-
 #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<SndioAudioInputStream*>(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<SndioAudioInputStream*>(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<float>(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<double>(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<double>(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

Modified: branches/2019Q3/www/chromium/files/sndio_input.h
==============================================================================
--- branches/2019Q3/www/chromium/files/sndio_input.h	Wed Jul 17 18:46:15 2019	(r506811)
+++ branches/2019Q3/www/chromium/files/sndio_input.h	Wed Jul 17 18:47:02 2019	(r506812)
@@ -22,19 +22,7 @@ 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<AudioInputStream> {
  public:
   // Pass this to the constructor if you want to attempt auto-selection
@@ -61,45 +49,39 @@ class SndioAudioInputStream : public AgcAudioStream<Au
   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<uint8_t[]> audio_buffer_;  // Buffer used for reading audio data.
-  bool read_callback_behind_schedule_;
-  std::unique_ptr<AudioBus> 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<AudioBus> 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);
 };

Modified: branches/2019Q3/www/chromium/files/sndio_output.cc
==============================================================================
--- branches/2019Q3/www/chromium/files/sndio_output.cc	Wed Jul 17 18:46:15 2019	(r506811)
+++ branches/2019Q3/www/chromium/files/sndio_output.cc	Wed Jul 17 18:47:02 2019	(r506812)
@@ -13,22 +13,22 @@ 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<SndioAudioOutputStream*>(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<SndioAudioOutputStream*>(arg);
 
   self->vol = vol;
 }
 
-void *sndio_threadstart(void *arg) {
+void *SndioAudioOutputStream::ThreadEntry(void *arg) {
   SndioAudioOutputStream* self = static_cast<SndioAudioOutputStream*>(arg);
 
-  self->RealTimeThread();
+  self->ThreadLoop();
   return NULL;
 }
 
@@ -37,7 +37,6 @@ SndioAudioOutputStream::SndioAudioOutputStream(const A
     : manager(manager),
       params(params),
       audio_bus(AudioBus::Create(params)),
-      bytes_per_frame(params.GetBytesPerFrame(kSampleFormat)),
       state(kClosed),
       mutex(PTHREAD_MUTEX_INITIALIZER) {
 }
@@ -87,8 +86,8 @@ bool SndioAudioOutputStream::Open() {
   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);
@@ -111,7 +110,7 @@ void SndioAudioOutputStream::Start(AudioSourceCallback
   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;
@@ -140,7 +139,7 @@ void SndioAudioOutputStream::GetVolume(double* v) {
   pthread_mutex_unlock(&mutex);
 }
 
-void SndioAudioOutputStream::RealTimeThread(void) {
+void SndioAudioOutputStream::ThreadLoop(void) {
   int avail, count;
 
   while (state == kRunning) {

Modified: branches/2019Q3/www/chromium/files/sndio_output.h
==============================================================================
--- branches/2019Q3/www/chromium/files/sndio_output.h	Wed Jul 17 18:46:15 2019	(r506811)
+++ branches/2019Q3/www/chromium/files/sndio_output.h	Wed Jul 17 18:47:02 2019	(r506812)
@@ -12,22 +12,10 @@
 #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:
@@ -37,14 +25,13 @@ class SndioAudioOutputStream : public AudioOutputStrea
   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);
@@ -56,22 +43,28 @@ class SndioAudioOutputStream : public AudioOutputStrea
     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<AudioBus> 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;
@@ -79,7 +72,7 @@ class SndioAudioOutputStream : public AudioOutputStrea
   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;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201907171847.x6HIl2se070334>