| Index: webrtc/sound/alsasoundsystem.cc
|
| diff --git a/webrtc/sound/alsasoundsystem.cc b/webrtc/sound/alsasoundsystem.cc
|
| deleted file mode 100644
|
| index 696ff1e45059aa00857a2a35705f2f7665916545..0000000000000000000000000000000000000000
|
| --- a/webrtc/sound/alsasoundsystem.cc
|
| +++ /dev/null
|
| @@ -1,741 +0,0 @@
|
| -/*
|
| - * Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
| - *
|
| - * Use of this source code is governed by a BSD-style license
|
| - * that can be found in the LICENSE file in the root of the source
|
| - * tree. An additional intellectual property rights grant can be found
|
| - * in the file PATENTS. All contributing project authors may
|
| - * be found in the AUTHORS file in the root of the source tree.
|
| - */
|
| -
|
| -#include "webrtc/sound/alsasoundsystem.h"
|
| -
|
| -#include <algorithm>
|
| -#include <string>
|
| -
|
| -#include "webrtc/base/arraysize.h"
|
| -#include "webrtc/base/common.h"
|
| -#include "webrtc/base/logging.h"
|
| -#include "webrtc/base/scoped_ptr.h"
|
| -#include "webrtc/base/stringutils.h"
|
| -#include "webrtc/base/timeutils.h"
|
| -#include "webrtc/base/worker.h"
|
| -#include "webrtc/sound/sounddevicelocator.h"
|
| -#include "webrtc/sound/soundinputstreaminterface.h"
|
| -#include "webrtc/sound/soundoutputstreaminterface.h"
|
| -
|
| -namespace rtc {
|
| -
|
| -// Lookup table from the rtc format enum in soundsysteminterface.h to
|
| -// ALSA's enums.
|
| -static const snd_pcm_format_t kCricketFormatToAlsaFormatTable[] = {
|
| - // The order here must match the order in soundsysteminterface.h
|
| - SND_PCM_FORMAT_S16_LE,
|
| -};
|
| -
|
| -// Lookup table for the size of a single sample of a given format.
|
| -static const size_t kCricketFormatToSampleSizeTable[] = {
|
| - // The order here must match the order in soundsysteminterface.h
|
| - sizeof(int16_t), // 2
|
| -};
|
| -
|
| -// Minimum latency we allow, in microseconds. This is more or less arbitrary,
|
| -// but it has to be at least large enough to be able to buffer data during a
|
| -// missed context switch, and the typical Linux scheduling quantum is 10ms.
|
| -static const int kMinimumLatencyUsecs = 20 * 1000;
|
| -
|
| -// The latency we'll use for kNoLatencyRequirements (chosen arbitrarily).
|
| -static const int kDefaultLatencyUsecs = kMinimumLatencyUsecs * 2;
|
| -
|
| -// We translate newlines in ALSA device descriptions to hyphens.
|
| -static const char kAlsaDescriptionSearch[] = "\n";
|
| -static const char kAlsaDescriptionReplace[] = " - ";
|
| -
|
| -class AlsaDeviceLocator : public SoundDeviceLocator {
|
| - public:
|
| - AlsaDeviceLocator(const std::string &name,
|
| - const std::string &device_name)
|
| - : SoundDeviceLocator(name, device_name) {
|
| - // The ALSA descriptions have newlines in them, which won't show up in
|
| - // a drop-down box. Replace them with hyphens.
|
| - rtc::replace_substrs(kAlsaDescriptionSearch,
|
| - sizeof(kAlsaDescriptionSearch) - 1,
|
| - kAlsaDescriptionReplace,
|
| - sizeof(kAlsaDescriptionReplace) - 1,
|
| - &name_);
|
| - }
|
| -
|
| - SoundDeviceLocator *Copy() const override {
|
| - return new AlsaDeviceLocator(*this);
|
| - }
|
| -};
|
| -
|
| -// Functionality that is common to both AlsaInputStream and AlsaOutputStream.
|
| -class AlsaStream {
|
| - public:
|
| - AlsaStream(AlsaSoundSystem *alsa,
|
| - snd_pcm_t *handle,
|
| - size_t frame_size,
|
| - int wait_timeout_ms,
|
| - int flags,
|
| - int freq)
|
| - : alsa_(alsa),
|
| - handle_(handle),
|
| - frame_size_(frame_size),
|
| - wait_timeout_ms_(wait_timeout_ms),
|
| - flags_(flags),
|
| - freq_(freq) {
|
| - }
|
| -
|
| - ~AlsaStream() {
|
| - Close();
|
| - }
|
| -
|
| - // Waits for the stream to be ready to accept/return more data, and returns
|
| - // how much can be written/read, or 0 if we need to Wait() again.
|
| - snd_pcm_uframes_t Wait() {
|
| - snd_pcm_sframes_t frames;
|
| - // Ideally we would not use snd_pcm_wait() and instead hook snd_pcm_poll_*
|
| - // into PhysicalSocketServer, but PhysicalSocketServer is nasty enough
|
| - // already and the current clients of SoundSystemInterface do not run
|
| - // anything else on their worker threads, so snd_pcm_wait() is good enough.
|
| - frames = symbol_table()->snd_pcm_avail_update()(handle_);
|
| - if (frames < 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_avail_update(): " << GetError(frames);
|
| - Recover(frames);
|
| - return 0;
|
| - } else if (frames > 0) {
|
| - // Already ready, so no need to wait.
|
| - return frames;
|
| - }
|
| - // Else no space/data available, so must wait.
|
| - int ready = symbol_table()->snd_pcm_wait()(handle_, wait_timeout_ms_);
|
| - if (ready < 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_wait(): " << GetError(ready);
|
| - Recover(ready);
|
| - return 0;
|
| - } else if (ready == 0) {
|
| - // Timeout, so nothing can be written/read right now.
|
| - // We set the timeout to twice the requested latency, so continuous
|
| - // timeouts are indicative of a problem, so log as a warning.
|
| - LOG(LS_WARNING) << "Timeout while waiting on stream";
|
| - return 0;
|
| - }
|
| - // Else ready > 0 (i.e., 1), so it's ready. Get count.
|
| - frames = symbol_table()->snd_pcm_avail_update()(handle_);
|
| - if (frames < 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_avail_update(): " << GetError(frames);
|
| - Recover(frames);
|
| - return 0;
|
| - } else if (frames == 0) {
|
| - // wait() said we were ready, so this ought to have been positive. Has
|
| - // been observed to happen in practice though.
|
| - LOG(LS_WARNING) << "Spurious wake-up";
|
| - }
|
| - return frames;
|
| - }
|
| -
|
| - int CurrentDelayUsecs() {
|
| - if (!(flags_ & SoundSystemInterface::FLAG_REPORT_LATENCY)) {
|
| - return 0;
|
| - }
|
| -
|
| - snd_pcm_sframes_t delay;
|
| - int err = symbol_table()->snd_pcm_delay()(handle_, &delay);
|
| - if (err != 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_delay(): " << GetError(err);
|
| - Recover(err);
|
| - // We'd rather continue playout/capture with an incorrect delay than stop
|
| - // it altogether, so return a valid value.
|
| - return 0;
|
| - }
|
| - // The delay is in frames. Convert to microseconds.
|
| - return delay * rtc::kNumMicrosecsPerSec / freq_;
|
| - }
|
| -
|
| - // Used to recover from certain recoverable errors, principally buffer overrun
|
| - // or underrun (identified as EPIPE). Without calling this the stream stays
|
| - // in the error state forever.
|
| - bool Recover(int error) {
|
| - int err;
|
| - err = symbol_table()->snd_pcm_recover()(
|
| - handle_,
|
| - error,
|
| - // Silent; i.e., no logging on stderr.
|
| - 1);
|
| - if (err != 0) {
|
| - // Docs say snd_pcm_recover returns the original error if it is not one
|
| - // of the recoverable ones, so this log message will probably contain the
|
| - // same error twice.
|
| - LOG(LS_ERROR) << "Unable to recover from \"" << GetError(error) << "\": "
|
| - << GetError(err);
|
| - return false;
|
| - }
|
| - if (error == -EPIPE && // Buffer underrun/overrun.
|
| - symbol_table()->snd_pcm_stream()(handle_) == SND_PCM_STREAM_CAPTURE) {
|
| - // For capture streams we also have to repeat the explicit start() to get
|
| - // data flowing again.
|
| - err = symbol_table()->snd_pcm_start()(handle_);
|
| - if (err != 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_start(): " << GetError(err);
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool Close() {
|
| - if (handle_) {
|
| - int err;
|
| - err = symbol_table()->snd_pcm_drop()(handle_);
|
| - if (err != 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_drop(): " << GetError(err);
|
| - // Continue anyways.
|
| - }
|
| - err = symbol_table()->snd_pcm_close()(handle_);
|
| - if (err != 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_close(): " << GetError(err);
|
| - // Continue anyways.
|
| - }
|
| - handle_ = NULL;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - AlsaSymbolTable *symbol_table() {
|
| - return &alsa_->symbol_table_;
|
| - }
|
| -
|
| - snd_pcm_t *handle() {
|
| - return handle_;
|
| - }
|
| -
|
| - const char *GetError(int err) {
|
| - return alsa_->GetError(err);
|
| - }
|
| -
|
| - size_t frame_size() {
|
| - return frame_size_;
|
| - }
|
| -
|
| - private:
|
| - AlsaSoundSystem *alsa_;
|
| - snd_pcm_t *handle_;
|
| - size_t frame_size_;
|
| - int wait_timeout_ms_;
|
| - int flags_;
|
| - int freq_;
|
| -
|
| - RTC_DISALLOW_COPY_AND_ASSIGN(AlsaStream);
|
| -};
|
| -
|
| -// Implementation of an input stream. See soundinputstreaminterface.h regarding
|
| -// thread-safety.
|
| -class AlsaInputStream :
|
| - public SoundInputStreamInterface,
|
| - private rtc::Worker {
|
| - public:
|
| - AlsaInputStream(AlsaSoundSystem *alsa,
|
| - snd_pcm_t *handle,
|
| - size_t frame_size,
|
| - int wait_timeout_ms,
|
| - int flags,
|
| - int freq)
|
| - : stream_(alsa, handle, frame_size, wait_timeout_ms, flags, freq),
|
| - buffer_size_(0) {
|
| - }
|
| -
|
| - ~AlsaInputStream() override {
|
| - bool success = StopReading();
|
| - // We need that to live.
|
| - VERIFY(success);
|
| - }
|
| -
|
| - bool StartReading() override {
|
| - return StartWork();
|
| - }
|
| -
|
| - bool StopReading() override {
|
| - return StopWork();
|
| - }
|
| -
|
| - bool GetVolume(int *volume) override {
|
| - // TODO(henrika): Implement this.
|
| - return false;
|
| - }
|
| -
|
| - bool SetVolume(int volume) override {
|
| - // TODO(henrika): Implement this.
|
| - return false;
|
| - }
|
| -
|
| - bool Close() override {
|
| - return StopReading() && stream_.Close();
|
| - }
|
| -
|
| - int LatencyUsecs() override {
|
| - return stream_.CurrentDelayUsecs();
|
| - }
|
| -
|
| - private:
|
| - // Inherited from Worker.
|
| - void OnStart() override {
|
| - HaveWork();
|
| - }
|
| -
|
| - // Inherited from Worker.
|
| - void OnHaveWork() override {
|
| - // Block waiting for data.
|
| - snd_pcm_uframes_t avail = stream_.Wait();
|
| - if (avail > 0) {
|
| - // Data is available.
|
| - size_t size = avail * stream_.frame_size();
|
| - if (size > buffer_size_) {
|
| - // Must increase buffer size.
|
| - buffer_.reset(new char[size]);
|
| - buffer_size_ = size;
|
| - }
|
| - // Read all the data.
|
| - snd_pcm_sframes_t read = stream_.symbol_table()->snd_pcm_readi()(
|
| - stream_.handle(),
|
| - buffer_.get(),
|
| - avail);
|
| - if (read < 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_readi(): " << GetError(read);
|
| - stream_.Recover(read);
|
| - } else if (read == 0) {
|
| - // Docs say this shouldn't happen.
|
| - ASSERT(false);
|
| - LOG(LS_ERROR) << "No data?";
|
| - } else {
|
| - // Got data. Pass it off to the app.
|
| - SignalSamplesRead(buffer_.get(),
|
| - read * stream_.frame_size(),
|
| - this);
|
| - }
|
| - }
|
| - // Check for more data with no delay, after any pending messages are
|
| - // dispatched.
|
| - HaveWork();
|
| - }
|
| -
|
| - // Inherited from Worker.
|
| - void OnStop() override {
|
| - // Nothing to do.
|
| - }
|
| -
|
| - const char *GetError(int err) {
|
| - return stream_.GetError(err);
|
| - }
|
| -
|
| - AlsaStream stream_;
|
| - rtc::scoped_ptr<char[]> buffer_;
|
| - size_t buffer_size_;
|
| -
|
| - RTC_DISALLOW_COPY_AND_ASSIGN(AlsaInputStream);
|
| -};
|
| -
|
| -// Implementation of an output stream. See soundoutputstreaminterface.h
|
| -// regarding thread-safety.
|
| -class AlsaOutputStream : public SoundOutputStreamInterface,
|
| - private rtc::Worker {
|
| - public:
|
| - AlsaOutputStream(AlsaSoundSystem *alsa,
|
| - snd_pcm_t *handle,
|
| - size_t frame_size,
|
| - int wait_timeout_ms,
|
| - int flags,
|
| - int freq)
|
| - : stream_(alsa, handle, frame_size, wait_timeout_ms, flags, freq) {
|
| - }
|
| -
|
| - ~AlsaOutputStream() override {
|
| - bool success = DisableBufferMonitoring();
|
| - // We need that to live.
|
| - VERIFY(success);
|
| - }
|
| -
|
| - bool EnableBufferMonitoring() override {
|
| - return StartWork();
|
| - }
|
| -
|
| - bool DisableBufferMonitoring() override {
|
| - return StopWork();
|
| - }
|
| -
|
| - bool WriteSamples(const void *sample_data, size_t size) override {
|
| - if (size % stream_.frame_size() != 0) {
|
| - // No client of SoundSystemInterface does this, so let's not support it.
|
| - // (If we wanted to support it, we'd basically just buffer the fractional
|
| - // frame until we get more data.)
|
| - ASSERT(false);
|
| - LOG(LS_ERROR) << "Writes with fractional frames are not supported";
|
| - return false;
|
| - }
|
| - snd_pcm_uframes_t frames = size / stream_.frame_size();
|
| - snd_pcm_sframes_t written = stream_.symbol_table()->snd_pcm_writei()(
|
| - stream_.handle(),
|
| - sample_data,
|
| - frames);
|
| - if (written < 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_writei(): " << GetError(written);
|
| - stream_.Recover(written);
|
| - return false;
|
| - } else if (static_cast<snd_pcm_uframes_t>(written) < frames) {
|
| - // Shouldn't happen. Drop the rest of the data.
|
| - LOG(LS_ERROR) << "Stream wrote only " << written << " of " << frames
|
| - << " frames!";
|
| - return false;
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - bool GetVolume(int *volume) override {
|
| - // TODO(henrika): Implement this.
|
| - return false;
|
| - }
|
| -
|
| - bool SetVolume(int volume) override {
|
| - // TODO(henrika): Implement this.
|
| - return false;
|
| - }
|
| -
|
| - bool Close() override {
|
| - return DisableBufferMonitoring() && stream_.Close();
|
| - }
|
| -
|
| - int LatencyUsecs() override {
|
| - return stream_.CurrentDelayUsecs();
|
| - }
|
| -
|
| - private:
|
| - // Inherited from Worker.
|
| - void OnStart() override {
|
| - HaveWork();
|
| - }
|
| -
|
| - // Inherited from Worker.
|
| - void OnHaveWork() override {
|
| - snd_pcm_uframes_t avail = stream_.Wait();
|
| - if (avail > 0) {
|
| - size_t space = avail * stream_.frame_size();
|
| - SignalBufferSpace(space, this);
|
| - }
|
| - HaveWork();
|
| - }
|
| -
|
| - // Inherited from Worker.
|
| - void OnStop() override {
|
| - // Nothing to do.
|
| - }
|
| -
|
| - const char *GetError(int err) {
|
| - return stream_.GetError(err);
|
| - }
|
| -
|
| - AlsaStream stream_;
|
| -
|
| - RTC_DISALLOW_COPY_AND_ASSIGN(AlsaOutputStream);
|
| -};
|
| -
|
| -AlsaSoundSystem::AlsaSoundSystem() : initialized_(false) {}
|
| -
|
| -AlsaSoundSystem::~AlsaSoundSystem() {
|
| - // Not really necessary, because Terminate() doesn't really do anything.
|
| - Terminate();
|
| -}
|
| -
|
| -bool AlsaSoundSystem::Init() {
|
| - if (IsInitialized()) {
|
| - return true;
|
| - }
|
| -
|
| - // Load libasound.
|
| - if (!symbol_table_.Load()) {
|
| - // Very odd for a Linux machine to not have a working libasound ...
|
| - LOG(LS_ERROR) << "Failed to load symbol table";
|
| - return false;
|
| - }
|
| -
|
| - initialized_ = true;
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void AlsaSoundSystem::Terminate() {
|
| - if (!IsInitialized()) {
|
| - return;
|
| - }
|
| -
|
| - initialized_ = false;
|
| -
|
| - // We do not unload the symbol table because we may need it again soon if
|
| - // Init() is called again.
|
| -}
|
| -
|
| -bool AlsaSoundSystem::EnumeratePlaybackDevices(
|
| - SoundDeviceLocatorList *devices) {
|
| - return EnumerateDevices(devices, false);
|
| -}
|
| -
|
| -bool AlsaSoundSystem::EnumerateCaptureDevices(
|
| - SoundDeviceLocatorList *devices) {
|
| - return EnumerateDevices(devices, true);
|
| -}
|
| -
|
| -bool AlsaSoundSystem::GetDefaultPlaybackDevice(SoundDeviceLocator **device) {
|
| - return GetDefaultDevice(device);
|
| -}
|
| -
|
| -bool AlsaSoundSystem::GetDefaultCaptureDevice(SoundDeviceLocator **device) {
|
| - return GetDefaultDevice(device);
|
| -}
|
| -
|
| -SoundOutputStreamInterface *AlsaSoundSystem::OpenPlaybackDevice(
|
| - const SoundDeviceLocator *device,
|
| - const OpenParams ¶ms) {
|
| - return OpenDevice<SoundOutputStreamInterface>(
|
| - device,
|
| - params,
|
| - SND_PCM_STREAM_PLAYBACK,
|
| - &AlsaSoundSystem::StartOutputStream);
|
| -}
|
| -
|
| -SoundInputStreamInterface *AlsaSoundSystem::OpenCaptureDevice(
|
| - const SoundDeviceLocator *device,
|
| - const OpenParams ¶ms) {
|
| - return OpenDevice<SoundInputStreamInterface>(
|
| - device,
|
| - params,
|
| - SND_PCM_STREAM_CAPTURE,
|
| - &AlsaSoundSystem::StartInputStream);
|
| -}
|
| -
|
| -const char *AlsaSoundSystem::GetName() const {
|
| - return "ALSA";
|
| -}
|
| -
|
| -bool AlsaSoundSystem::EnumerateDevices(
|
| - SoundDeviceLocatorList *devices,
|
| - bool capture_not_playback) {
|
| - ClearSoundDeviceLocatorList(devices);
|
| -
|
| - if (!IsInitialized()) {
|
| - return false;
|
| - }
|
| -
|
| - const char *type = capture_not_playback ? "Input" : "Output";
|
| - // dmix and dsnoop are only for playback and capture, respectively, but ALSA
|
| - // stupidly includes them in both lists.
|
| - const char *ignore_prefix = capture_not_playback ? "dmix:" : "dsnoop:";
|
| - // (ALSA lists many more "devices" of questionable interest, but we show them
|
| - // just in case the weird devices may actually be desirable for some
|
| - // users/systems.)
|
| - const char *ignore_default = "default";
|
| - const char *ignore_null = "null";
|
| - const char *ignore_pulse = "pulse";
|
| - // The 'pulse' entry has a habit of mysteriously disappearing when you query
|
| - // a second time. Remove it from our list. (GIPS lib did the same thing.)
|
| - int err;
|
| -
|
| - void **hints;
|
| - err = symbol_table_.snd_device_name_hint()(-1, // All cards
|
| - "pcm", // Only PCM devices
|
| - &hints);
|
| - if (err != 0) {
|
| - LOG(LS_ERROR) << "snd_device_name_hint(): " << GetError(err);
|
| - return false;
|
| - }
|
| -
|
| - for (void **list = hints; *list != NULL; ++list) {
|
| - char *actual_type = symbol_table_.snd_device_name_get_hint()(*list, "IOID");
|
| - if (actual_type) { // NULL means it's both.
|
| - bool wrong_type = (strcmp(actual_type, type) != 0);
|
| - free(actual_type);
|
| - if (wrong_type) {
|
| - // Wrong type of device (i.e., input vs. output).
|
| - continue;
|
| - }
|
| - }
|
| -
|
| - char *name = symbol_table_.snd_device_name_get_hint()(*list, "NAME");
|
| - if (!name) {
|
| - LOG(LS_ERROR) << "Device has no name???";
|
| - // Skip it.
|
| - continue;
|
| - }
|
| -
|
| - // Now check if we actually want to show this device.
|
| - if (strcmp(name, ignore_default) != 0 &&
|
| - strcmp(name, ignore_null) != 0 &&
|
| - strcmp(name, ignore_pulse) != 0 &&
|
| - !rtc::starts_with(name, ignore_prefix)) {
|
| - // Yes, we do.
|
| - char *desc = symbol_table_.snd_device_name_get_hint()(*list, "DESC");
|
| - if (!desc) {
|
| - // Virtual devices don't necessarily have descriptions. Use their names
|
| - // instead (not pretty!).
|
| - desc = name;
|
| - }
|
| -
|
| - AlsaDeviceLocator *device = new AlsaDeviceLocator(desc, name);
|
| -
|
| - devices->push_back(device);
|
| -
|
| - if (desc != name) {
|
| - free(desc);
|
| - }
|
| - }
|
| -
|
| - free(name);
|
| - }
|
| -
|
| - err = symbol_table_.snd_device_name_free_hint()(hints);
|
| - if (err != 0) {
|
| - LOG(LS_ERROR) << "snd_device_name_free_hint(): " << GetError(err);
|
| - // Continue and return true anyways, since we did get the whole list.
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -bool AlsaSoundSystem::GetDefaultDevice(SoundDeviceLocator **device) {
|
| - if (!IsInitialized()) {
|
| - return false;
|
| - }
|
| - *device = new AlsaDeviceLocator("Default device", "default");
|
| - return true;
|
| -}
|
| -
|
| -inline size_t AlsaSoundSystem::FrameSize(const OpenParams ¶ms) {
|
| - return kCricketFormatToSampleSizeTable[params.format] * params.channels;
|
| -}
|
| -
|
| -template <typename StreamInterface>
|
| -StreamInterface *AlsaSoundSystem::OpenDevice(
|
| - const SoundDeviceLocator *device,
|
| - const OpenParams ¶ms,
|
| - snd_pcm_stream_t type,
|
| - StreamInterface *(AlsaSoundSystem::*start_fn)(
|
| - snd_pcm_t *handle,
|
| - size_t frame_size,
|
| - int wait_timeout_ms,
|
| - int flags,
|
| - int freq)) {
|
| - if (!IsInitialized()) {
|
| - return NULL;
|
| - }
|
| -
|
| - StreamInterface *stream;
|
| - int err;
|
| -
|
| - const char *dev = static_cast<const AlsaDeviceLocator *>(device)->
|
| - device_name().c_str();
|
| -
|
| - snd_pcm_t *handle = NULL;
|
| - err = symbol_table_.snd_pcm_open()(
|
| - &handle,
|
| - dev,
|
| - type,
|
| - // No flags.
|
| - 0);
|
| - if (err != 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_open(" << dev << "): " << GetError(err);
|
| - return NULL;
|
| - }
|
| - LOG(LS_VERBOSE) << "Opening " << dev;
|
| - ASSERT(handle); // If open succeeded, handle ought to be valid
|
| -
|
| - // Compute requested latency in microseconds.
|
| - int latency;
|
| - if (params.latency == kNoLatencyRequirements) {
|
| - latency = kDefaultLatencyUsecs;
|
| - } else {
|
| - // kLowLatency is 0, so we treat it the same as a request for zero latency.
|
| - // Compute what the user asked for.
|
| - latency = rtc::kNumMicrosecsPerSec *
|
| - params.latency /
|
| - params.freq /
|
| - FrameSize(params);
|
| - // And this is what we'll actually use.
|
| - latency = std::max(latency, kMinimumLatencyUsecs);
|
| - }
|
| -
|
| - ASSERT(params.format < arraysize(kCricketFormatToAlsaFormatTable));
|
| -
|
| - err = symbol_table_.snd_pcm_set_params()(
|
| - handle,
|
| - kCricketFormatToAlsaFormatTable[params.format],
|
| - // SoundSystemInterface only supports interleaved audio.
|
| - SND_PCM_ACCESS_RW_INTERLEAVED,
|
| - params.channels,
|
| - params.freq,
|
| - 1, // Allow ALSA to resample.
|
| - latency);
|
| - if (err != 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_set_params(): " << GetError(err);
|
| - goto fail;
|
| - }
|
| -
|
| - err = symbol_table_.snd_pcm_prepare()(handle);
|
| - if (err != 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_prepare(): " << GetError(err);
|
| - goto fail;
|
| - }
|
| -
|
| - stream = (this->*start_fn)(
|
| - handle,
|
| - FrameSize(params),
|
| - // We set the wait time to twice the requested latency, so that wait
|
| - // timeouts should be rare.
|
| - 2 * latency / rtc::kNumMicrosecsPerMillisec,
|
| - params.flags,
|
| - params.freq);
|
| - if (stream) {
|
| - return stream;
|
| - }
|
| - // Else fall through.
|
| -
|
| - fail:
|
| - err = symbol_table_.snd_pcm_close()(handle);
|
| - if (err != 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_close(): " << GetError(err);
|
| - }
|
| - return NULL;
|
| -}
|
| -
|
| -SoundOutputStreamInterface *AlsaSoundSystem::StartOutputStream(
|
| - snd_pcm_t *handle,
|
| - size_t frame_size,
|
| - int wait_timeout_ms,
|
| - int flags,
|
| - int freq) {
|
| - // Nothing to do here but instantiate the stream.
|
| - return new AlsaOutputStream(
|
| - this, handle, frame_size, wait_timeout_ms, flags, freq);
|
| -}
|
| -
|
| -SoundInputStreamInterface *AlsaSoundSystem::StartInputStream(
|
| - snd_pcm_t *handle,
|
| - size_t frame_size,
|
| - int wait_timeout_ms,
|
| - int flags,
|
| - int freq) {
|
| - // Output streams start automatically once enough data has been written, but
|
| - // input streams must be started manually or else snd_pcm_wait() will never
|
| - // return true.
|
| - int err;
|
| - err = symbol_table_.snd_pcm_start()(handle);
|
| - if (err != 0) {
|
| - LOG(LS_ERROR) << "snd_pcm_start(): " << GetError(err);
|
| - return NULL;
|
| - }
|
| - return new AlsaInputStream(
|
| - this, handle, frame_size, wait_timeout_ms, flags, freq);
|
| -}
|
| -
|
| -inline const char *AlsaSoundSystem::GetError(int err) {
|
| - return symbol_table_.snd_strerror()(err);
|
| -}
|
| -
|
| -} // namespace rtc
|
|
|