~ubuntu-branches/debian/experimental/kopete/experimental

« back to all changes in this revision

Viewing changes to protocols/jabber/libjingle/talk/sound/alsasoundsystem.cc

  • Committer: Package Import Robot
  • Author(s): Maximiliano Curia
  • Date: 2015-02-24 11:32:57 UTC
  • mfrom: (1.1.41 vivid)
  • Revision ID: package-import@ubuntu.com-20150224113257-gnupg4v7lzz18ij0
Tags: 4:14.12.2-1
* New upstream release (14.12.2).
* Bump Standards-Version to 3.9.6, no changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * libjingle
 
3
 * Copyright 2004--2010, Google Inc.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions are met:
 
7
 *
 
8
 *  1. Redistributions of source code must retain the above copyright notice,
 
9
 *     this list of conditions and the following disclaimer.
 
10
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 
11
 *     this list of conditions and the following disclaimer in the documentation
 
12
 *     and/or other materials provided with the distribution.
 
13
 *  3. The name of the author may not be used to endorse or promote products
 
14
 *     derived from this software without specific prior written permission.
 
15
 *
 
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 
17
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 
18
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 
19
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
20
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
21
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 
22
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 
23
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 
24
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
25
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
26
 */
 
27
 
 
28
#include "talk/sound/alsasoundsystem.h"
 
29
 
 
30
#include "talk/base/common.h"
 
31
#include "talk/base/logging.h"
 
32
#include "talk/base/scoped_ptr.h"
 
33
#include "talk/base/stringutils.h"
 
34
#include "talk/base/timeutils.h"
 
35
#include "talk/base/worker.h"
 
36
#include "talk/sound/sounddevicelocator.h"
 
37
#include "talk/sound/soundinputstreaminterface.h"
 
38
#include "talk/sound/soundoutputstreaminterface.h"
 
39
 
 
40
namespace cricket {
 
41
 
 
42
// Lookup table from the cricket format enum in soundsysteminterface.h to
 
43
// ALSA's enums.
 
44
static const snd_pcm_format_t kCricketFormatToAlsaFormatTable[] = {
 
45
  // The order here must match the order in soundsysteminterface.h
 
46
  SND_PCM_FORMAT_S16_LE,
 
47
};
 
48
 
 
49
// Lookup table for the size of a single sample of a given format.
 
50
static const size_t kCricketFormatToSampleSizeTable[] = {
 
51
  // The order here must match the order in soundsysteminterface.h
 
52
  sizeof(int16_t),  // 2
 
53
};
 
54
 
 
55
// Minimum latency we allow, in microseconds. This is more or less arbitrary,
 
56
// but it has to be at least large enough to be able to buffer data during a
 
57
// missed context switch, and the typical Linux scheduling quantum is 10ms.
 
58
static const int kMinimumLatencyUsecs = 20 * 1000;
 
59
 
 
60
// The latency we'll use for kNoLatencyRequirements (chosen arbitrarily).
 
61
static const int kDefaultLatencyUsecs = kMinimumLatencyUsecs * 2;
 
62
 
 
63
// We translate newlines in ALSA device descriptions to hyphens.
 
64
static const char kAlsaDescriptionSearch[] = "\n";
 
65
static const char kAlsaDescriptionReplace[] = " - ";
 
66
 
 
67
class AlsaDeviceLocator : public SoundDeviceLocator {
 
68
 public:
 
69
  AlsaDeviceLocator(const std::string &name,
 
70
                    const std::string &device_name)
 
71
      : SoundDeviceLocator(name, device_name) {
 
72
    // The ALSA descriptions have newlines in them, which won't show up in
 
73
    // a drop-down box. Replace them with hyphens.
 
74
    talk_base::replace_substrs(kAlsaDescriptionSearch,
 
75
                               sizeof(kAlsaDescriptionSearch) - 1,
 
76
                               kAlsaDescriptionReplace,
 
77
                               sizeof(kAlsaDescriptionReplace) - 1,
 
78
                               &name_);
 
79
  }
 
80
 
 
81
  virtual SoundDeviceLocator *Copy() const {
 
82
    return new AlsaDeviceLocator(*this);
 
83
  }
 
84
};
 
85
 
 
86
// Accesses ALSA functions through our late-binding symbol table instead of
 
87
// directly. This way we don't have to link to libasound, which means our binary
 
88
// will load faster and we can run on strange systems that may not have
 
89
// libasound
 
90
#define LATE(sym) \
 
91
  LATESYM_GET(AlsaSymbolTable, &alsa_->symbol_table_, sym)
 
92
 
 
93
// Functionality that is common to both AlsaInputStream and AlsaOutputStream.
 
94
class AlsaStream {
 
95
 public:
 
96
  AlsaStream(AlsaSoundSystem *alsa,
 
97
             snd_pcm_t *handle,
 
98
             size_t frame_size,
 
99
             int wait_timeout_ms,
 
100
             int flags,
 
101
             int freq)
 
102
      : alsa_(alsa),
 
103
        handle_(handle),
 
104
        frame_size_(frame_size),
 
105
        wait_timeout_ms_(wait_timeout_ms),
 
106
        flags_(flags),
 
107
        freq_(freq) {
 
108
  }
 
109
 
 
110
  ~AlsaStream() {
 
111
    Close();
 
112
  }
 
113
 
 
114
  // Waits for the stream to be ready to accept/return more data, and returns
 
115
  // how much can be written/read, or 0 if we need to Wait() again.
 
116
  snd_pcm_uframes_t Wait() {
 
117
    snd_pcm_sframes_t frames;
 
118
    // Ideally we would not use snd_pcm_wait() and instead hook snd_pcm_poll_*
 
119
    // into PhysicalSocketServer, but PhysicalSocketServer is nasty enough
 
120
    // already and the current clients of SoundSystemInterface do not run
 
121
    // anything else on their worker threads, so snd_pcm_wait() is good enough.
 
122
    frames = LATE(snd_pcm_avail_update)(handle_);
 
123
    if (frames < 0) {
 
124
      LOG(LS_ERROR) << "snd_pcm_avail_update(): " << GetError(frames);
 
125
      Recover(frames);
 
126
      return 0;
 
127
    } else if (frames > 0) {
 
128
      // Already ready, so no need to wait.
 
129
      return frames;
 
130
    }
 
131
    // Else no space/data available, so must wait.
 
132
    int ready = LATE(snd_pcm_wait)(handle_, wait_timeout_ms_);
 
133
    if (ready < 0) {
 
134
      LOG(LS_ERROR) << "snd_pcm_wait(): " << GetError(ready);
 
135
      Recover(ready);
 
136
      return 0;
 
137
    } else if (ready == 0) {
 
138
      // Timeout, so nothing can be written/read right now.
 
139
      // We set the timeout to twice the requested latency, so continuous
 
140
      // timeouts are indicative of a problem, so log as a warning.
 
141
      LOG(LS_WARNING) << "Timeout while waiting on stream";
 
142
      return 0;
 
143
    }
 
144
    // Else ready > 0 (i.e., 1), so it's ready. Get count.
 
145
    frames = LATE(snd_pcm_avail_update)(handle_);
 
146
    if (frames < 0) {
 
147
      LOG(LS_ERROR) << "snd_pcm_avail_update(): " << GetError(frames);
 
148
      Recover(frames);
 
149
      return 0;
 
150
    } else if (frames == 0) {
 
151
      // wait() said we were ready, so this ought to have been positive. Has
 
152
      // been observed to happen in practice though.
 
153
      LOG(LS_WARNING) << "Spurious wake-up";
 
154
    }
 
155
    return frames;
 
156
  }
 
157
 
 
158
  int CurrentDelayUsecs() {
 
159
    if (!(flags_ & SoundSystemInterface::FLAG_REPORT_LATENCY)) {
 
160
      return 0;
 
161
    }
 
162
 
 
163
    snd_pcm_sframes_t delay;
 
164
    int err = LATE(snd_pcm_delay)(handle_, &delay);
 
165
    if (err != 0) {
 
166
      LOG(LS_ERROR) << "snd_pcm_delay(): " << GetError(err);
 
167
      Recover(err);
 
168
      // We'd rather continue playout/capture with an incorrect delay than stop
 
169
      // it altogether, so return a valid value.
 
170
      return 0;
 
171
    }
 
172
    // The delay is in frames. Convert to microseconds.
 
173
    return delay * talk_base::kNumMicrosecsPerSec / freq_;
 
174
  }
 
175
 
 
176
  // Used to recover from certain recoverable errors, principally buffer overrun
 
177
  // or underrun (identified as EPIPE). Without calling this the stream stays
 
178
  // in the error state forever.
 
179
  bool Recover(int error) {
 
180
    int err;
 
181
    err = LATE(snd_pcm_recover)(handle_,
 
182
                                error,
 
183
                                // Silent; i.e., no logging on stderr.
 
184
                                1);
 
185
    if (err != 0) {
 
186
      // Docs say snd_pcm_recover returns the original error if it is not one
 
187
      // of the recoverable ones, so this log message will probably contain the
 
188
      // same error twice.
 
189
      LOG(LS_ERROR) << "Unable to recover from \"" << GetError(error) << "\": "
 
190
                    << GetError(err);
 
191
      return false;
 
192
    }
 
193
    if (error == -EPIPE &&  // Buffer underrun/overrun.
 
194
        LATE(snd_pcm_stream)(handle_) == SND_PCM_STREAM_CAPTURE) {
 
195
      // For capture streams we also have to repeat the explicit start() to get
 
196
      // data flowing again.
 
197
      err = LATE(snd_pcm_start)(handle_);
 
198
      if (err != 0) {
 
199
        LOG(LS_ERROR) << "snd_pcm_start(): " << GetError(err);
 
200
        return false;
 
201
      }
 
202
    }
 
203
    return true;
 
204
  }
 
205
 
 
206
  bool Close() {
 
207
    if (handle_) {
 
208
      int err;
 
209
      err = LATE(snd_pcm_drop)(handle_);
 
210
      if (err != 0) {
 
211
        LOG(LS_ERROR) << "snd_pcm_drop(): " << GetError(err);
 
212
        // Continue anyways.
 
213
      }
 
214
      err = LATE(snd_pcm_close)(handle_);
 
215
      if (err != 0) {
 
216
        LOG(LS_ERROR) << "snd_pcm_close(): " << GetError(err);
 
217
        // Continue anyways.
 
218
      }
 
219
      handle_ = NULL;
 
220
    }
 
221
    return true;
 
222
  }
 
223
 
 
224
  AlsaSoundSystem *alsa() {
 
225
    return alsa_;
 
226
  }
 
227
 
 
228
  snd_pcm_t *handle() {
 
229
    return handle_;
 
230
  }
 
231
 
 
232
  const char *GetError(int err) {
 
233
    return alsa_->GetError(err);
 
234
  }
 
235
 
 
236
  size_t frame_size() {
 
237
    return frame_size_;
 
238
  }
 
239
 
 
240
 private:
 
241
  AlsaSoundSystem *alsa_;
 
242
  snd_pcm_t *handle_;
 
243
  size_t frame_size_;
 
244
  int wait_timeout_ms_;
 
245
  int flags_;
 
246
  int freq_;
 
247
 
 
248
  DISALLOW_COPY_AND_ASSIGN(AlsaStream);
 
249
};
 
250
 
 
251
// Redefine for the next two classes.
 
252
#undef LATE
 
253
#define LATE(sym) \
 
254
  LATESYM_GET(AlsaSymbolTable, &stream_.alsa()->symbol_table_, sym)
 
255
 
 
256
// Implementation of an input stream. See soundinputstreaminterface.h regarding
 
257
// thread-safety.
 
258
class AlsaInputStream :
 
259
    public SoundInputStreamInterface,
 
260
    private talk_base::Worker {
 
261
 public:
 
262
  AlsaInputStream(AlsaSoundSystem *alsa,
 
263
                  snd_pcm_t *handle,
 
264
                  size_t frame_size,
 
265
                  int wait_timeout_ms,
 
266
                  int flags,
 
267
                  int freq)
 
268
      : stream_(alsa, handle, frame_size, wait_timeout_ms, flags, freq),
 
269
        buffer_size_(0) {
 
270
  }
 
271
 
 
272
  virtual ~AlsaInputStream() {
 
273
    bool success = StopReading();
 
274
    // We need that to live.
 
275
    VERIFY(success);
 
276
  }
 
277
 
 
278
  virtual bool StartReading() {
 
279
    return StartWork();
 
280
  }
 
281
 
 
282
  virtual bool StopReading() {
 
283
    return StopWork();
 
284
  }
 
285
 
 
286
  virtual bool GetVolume(int *volume) {
 
287
    // TODO: Implement this.
 
288
    return false;
 
289
  }
 
290
 
 
291
  virtual bool SetVolume(int volume) {
 
292
    // TODO: Implement this.
 
293
    return false;
 
294
  }
 
295
 
 
296
  virtual bool Close() {
 
297
    return StopReading() && stream_.Close();
 
298
  }
 
299
 
 
300
  virtual int LatencyUsecs() {
 
301
    return stream_.CurrentDelayUsecs();
 
302
  }
 
303
 
 
304
 private:
 
305
  // Inherited from Worker.
 
306
  virtual void OnStart() {
 
307
    HaveWork();
 
308
  }
 
309
 
 
310
  // Inherited from Worker.
 
311
  virtual void OnHaveWork() {
 
312
    // Block waiting for data.
 
313
    snd_pcm_uframes_t avail = stream_.Wait();
 
314
    if (avail > 0) {
 
315
      // Data is available.
 
316
      size_t size = avail * stream_.frame_size();
 
317
      if (size > buffer_size_) {
 
318
        // Must increase buffer size.
 
319
        buffer_.reset(new char[size]);
 
320
        buffer_size_ = size;
 
321
      }
 
322
      // Read all the data.
 
323
      snd_pcm_sframes_t read = LATE(snd_pcm_readi)(stream_.handle(),
 
324
                                                   buffer_.get(),
 
325
                                                   avail);
 
326
      if (read < 0) {
 
327
        LOG(LS_ERROR) << "snd_pcm_readi(): " << GetError(read);
 
328
        stream_.Recover(read);
 
329
      } else if (read == 0) {
 
330
        // Docs say this shouldn't happen.
 
331
        ASSERT(false);
 
332
        LOG(LS_ERROR) << "No data?";
 
333
      } else {
 
334
        // Got data. Pass it off to the app.
 
335
        SignalSamplesRead(buffer_.get(),
 
336
                          read * stream_.frame_size(),
 
337
                          this);
 
338
      }
 
339
    }
 
340
    // Check for more data with no delay, after any pending messages are
 
341
    // dispatched.
 
342
    HaveWork();
 
343
  }
 
344
 
 
345
  // Inherited from Worker.
 
346
  virtual void OnStop() {
 
347
    // Nothing to do.
 
348
  }
 
349
 
 
350
  const char *GetError(int err) {
 
351
    return stream_.GetError(err);
 
352
  }
 
353
 
 
354
  AlsaStream stream_;
 
355
  talk_base::scoped_array<char> buffer_;
 
356
  size_t buffer_size_;
 
357
 
 
358
  DISALLOW_COPY_AND_ASSIGN(AlsaInputStream);
 
359
};
 
360
 
 
361
// Implementation of an output stream. See soundoutputstreaminterface.h
 
362
// regarding thread-safety.
 
363
class AlsaOutputStream :
 
364
    public SoundOutputStreamInterface,
 
365
    private talk_base::Worker {
 
366
 public:
 
367
  AlsaOutputStream(AlsaSoundSystem *alsa,
 
368
                   snd_pcm_t *handle,
 
369
                   size_t frame_size,
 
370
                   int wait_timeout_ms,
 
371
                   int flags,
 
372
                   int freq)
 
373
      : stream_(alsa, handle, frame_size, wait_timeout_ms, flags, freq) {
 
374
  }
 
375
 
 
376
  virtual ~AlsaOutputStream() {
 
377
    bool success = DisableBufferMonitoring();
 
378
    // We need that to live.
 
379
    VERIFY(success);
 
380
  }
 
381
 
 
382
  virtual bool EnableBufferMonitoring() {
 
383
    return StartWork();
 
384
  }
 
385
 
 
386
  virtual bool DisableBufferMonitoring() {
 
387
    return StopWork();
 
388
  }
 
389
 
 
390
  virtual bool WriteSamples(const void *sample_data,
 
391
                            size_t size) {
 
392
    if (size % stream_.frame_size() != 0) {
 
393
      // No client of SoundSystemInterface does this, so let's not support it.
 
394
      // (If we wanted to support it, we'd basically just buffer the fractional
 
395
      // frame until we get more data.)
 
396
      ASSERT(false);
 
397
      LOG(LS_ERROR) << "Writes with fractional frames are not supported";
 
398
      return false;
 
399
    }
 
400
    snd_pcm_uframes_t frames = size / stream_.frame_size();
 
401
    snd_pcm_sframes_t written = LATE(snd_pcm_writei)(stream_.handle(),
 
402
                                                     sample_data,
 
403
                                                     frames);
 
404
    if (written < 0) {
 
405
      LOG(LS_ERROR) << "snd_pcm_writei(): " << GetError(written);
 
406
      stream_.Recover(written);
 
407
      return false;
 
408
    } else if (static_cast<snd_pcm_uframes_t>(written) < frames) {
 
409
      // Shouldn't happen. Drop the rest of the data.
 
410
      LOG(LS_ERROR) << "Stream wrote only " << written << " of " << frames
 
411
                    << " frames!";
 
412
      return false;
 
413
    }
 
414
    return true;
 
415
  }
 
416
 
 
417
  virtual bool GetVolume(int *volume) {
 
418
    // TODO: Implement this.
 
419
    return false;
 
420
  }
 
421
 
 
422
  virtual bool SetVolume(int volume) {
 
423
    // TODO: Implement this.
 
424
    return false;
 
425
  }
 
426
 
 
427
  virtual bool Close() {
 
428
    return DisableBufferMonitoring() && stream_.Close();
 
429
  }
 
430
 
 
431
  virtual int LatencyUsecs() {
 
432
    return stream_.CurrentDelayUsecs();
 
433
  }
 
434
 
 
435
 private:
 
436
  // Inherited from Worker.
 
437
  virtual void OnStart() {
 
438
    HaveWork();
 
439
  }
 
440
 
 
441
  // Inherited from Worker.
 
442
  virtual void OnHaveWork() {
 
443
    snd_pcm_uframes_t avail = stream_.Wait();
 
444
    if (avail > 0) {
 
445
      size_t space = avail * stream_.frame_size();
 
446
      SignalBufferSpace(space, this);
 
447
    }
 
448
    HaveWork();
 
449
  }
 
450
 
 
451
  // Inherited from Worker.
 
452
  virtual void OnStop() {
 
453
    // Nothing to do.
 
454
  }
 
455
 
 
456
  const char *GetError(int err) {
 
457
    return stream_.GetError(err);
 
458
  }
 
459
 
 
460
  AlsaStream stream_;
 
461
 
 
462
  DISALLOW_COPY_AND_ASSIGN(AlsaOutputStream);
 
463
};
 
464
 
 
465
// Redefine for the main class.
 
466
#undef LATE
 
467
#define LATE(sym) LATESYM_GET(AlsaSymbolTable, &symbol_table_, sym)
 
468
 
 
469
AlsaSoundSystem::AlsaSoundSystem() : initialized_(false) {}
 
470
 
 
471
AlsaSoundSystem::~AlsaSoundSystem() {
 
472
  // Not really necessary, because Terminate() doesn't really do anything.
 
473
  Terminate();
 
474
}
 
475
 
 
476
bool AlsaSoundSystem::Init() {
 
477
  if (IsInitialized()) {
 
478
    return true;
 
479
  }
 
480
 
 
481
  // Load libasound.
 
482
  if (!symbol_table_.Load()) {
 
483
    // Very odd for a Linux machine to not have a working libasound ...
 
484
    LOG(LS_ERROR) << "Failed to load symbol table";
 
485
    return false;
 
486
  }
 
487
 
 
488
  initialized_ = true;
 
489
 
 
490
  return true;
 
491
}
 
492
 
 
493
void AlsaSoundSystem::Terminate() {
 
494
  if (!IsInitialized()) {
 
495
    return;
 
496
  }
 
497
 
 
498
  initialized_ = false;
 
499
 
 
500
  // We do not unload the symbol table because we may need it again soon if
 
501
  // Init() is called again.
 
502
}
 
503
 
 
504
bool AlsaSoundSystem::EnumeratePlaybackDevices(
 
505
    SoundDeviceLocatorList *devices) {
 
506
  return EnumerateDevices(devices, false);
 
507
}
 
508
 
 
509
bool AlsaSoundSystem::EnumerateCaptureDevices(
 
510
    SoundDeviceLocatorList *devices) {
 
511
  return EnumerateDevices(devices, true);
 
512
}
 
513
 
 
514
bool AlsaSoundSystem::GetDefaultPlaybackDevice(SoundDeviceLocator **device) {
 
515
  return GetDefaultDevice(device);
 
516
}
 
517
 
 
518
bool AlsaSoundSystem::GetDefaultCaptureDevice(SoundDeviceLocator **device) {
 
519
  return GetDefaultDevice(device);
 
520
}
 
521
 
 
522
SoundOutputStreamInterface *AlsaSoundSystem::OpenPlaybackDevice(
 
523
    const SoundDeviceLocator *device,
 
524
    const OpenParams &params) {
 
525
  return OpenDevice<SoundOutputStreamInterface>(
 
526
      device,
 
527
      params,
 
528
      SND_PCM_STREAM_PLAYBACK,
 
529
      &AlsaSoundSystem::StartOutputStream);
 
530
}
 
531
 
 
532
SoundInputStreamInterface *AlsaSoundSystem::OpenCaptureDevice(
 
533
    const SoundDeviceLocator *device,
 
534
    const OpenParams &params) {
 
535
  return OpenDevice<SoundInputStreamInterface>(
 
536
      device,
 
537
      params,
 
538
      SND_PCM_STREAM_CAPTURE,
 
539
      &AlsaSoundSystem::StartInputStream);
 
540
}
 
541
 
 
542
const char *AlsaSoundSystem::GetName() const {
 
543
  return "ALSA";
 
544
}
 
545
 
 
546
bool AlsaSoundSystem::EnumerateDevices(
 
547
    SoundDeviceLocatorList *devices,
 
548
    bool capture_not_playback) {
 
549
  ClearSoundDeviceLocatorList(devices);
 
550
 
 
551
  if (!IsInitialized()) {
 
552
    return false;
 
553
  }
 
554
 
 
555
  const char *type = capture_not_playback ? "Input" : "Output";
 
556
  // dmix and dsnoop are only for playback and capture, respectively, but ALSA
 
557
  // stupidly includes them in both lists.
 
558
  const char *ignore_prefix = capture_not_playback ? "dmix:" : "dsnoop:";
 
559
  // (ALSA lists many more "devices" of questionable interest, but we show them
 
560
  // just in case the weird devices may actually be desirable for some
 
561
  // users/systems.)
 
562
  const char *ignore_default = "default";
 
563
  const char *ignore_null = "null";
 
564
  const char *ignore_pulse = "pulse";
 
565
  // The 'pulse' entry has a habit of mysteriously disappearing when you query
 
566
  // a second time. Remove it from our list. (GIPS lib did the same thing.)
 
567
  int err;
 
568
 
 
569
  void **hints;
 
570
  err = LATE(snd_device_name_hint)(-1,     // All cards
 
571
                                   "pcm",  // Only PCM devices
 
572
                                   &hints);
 
573
  if (err != 0) {
 
574
    LOG(LS_ERROR) << "snd_device_name_hint(): " << GetError(err);
 
575
    return false;
 
576
  }
 
577
 
 
578
  for (void **list = hints; *list != NULL; ++list) {
 
579
    char *actual_type = LATE(snd_device_name_get_hint)(*list, "IOID");
 
580
    if (actual_type) {  // NULL means it's both.
 
581
      bool wrong_type = (strcmp(actual_type, type) != 0);
 
582
      free(actual_type);
 
583
      if (wrong_type) {
 
584
        // Wrong type of device (i.e., input vs. output).
 
585
        continue;
 
586
      }
 
587
    }
 
588
 
 
589
    char *name = LATE(snd_device_name_get_hint)(*list, "NAME");
 
590
    if (!name) {
 
591
      LOG(LS_ERROR) << "Device has no name???";
 
592
      // Skip it.
 
593
      continue;
 
594
    }
 
595
 
 
596
    // Now check if we actually want to show this device.
 
597
    if (strcmp(name, ignore_default) != 0 &&
 
598
        strcmp(name, ignore_null) != 0 &&
 
599
        strcmp(name, ignore_pulse) != 0 &&
 
600
        !talk_base::starts_with(name, ignore_prefix)) {
 
601
 
 
602
      // Yes, we do.
 
603
      char *desc = LATE(snd_device_name_get_hint)(*list, "DESC");
 
604
      if (!desc) {
 
605
        // Virtual devices don't necessarily have descriptions. Use their names
 
606
        // instead (not pretty!).
 
607
        desc = name;
 
608
      }
 
609
 
 
610
      AlsaDeviceLocator *device = new AlsaDeviceLocator(desc, name);
 
611
 
 
612
      devices->push_back(device);
 
613
 
 
614
      if (desc != name) {
 
615
        free(desc);
 
616
      }
 
617
    }
 
618
 
 
619
    free(name);
 
620
  }
 
621
 
 
622
  err = LATE(snd_device_name_free_hint)(hints);
 
623
  if (err != 0) {
 
624
    LOG(LS_ERROR) << "snd_device_name_free_hint(): " << GetError(err);
 
625
    // Continue and return true anyways, since we did get the whole list.
 
626
  }
 
627
 
 
628
  return true;
 
629
}
 
630
 
 
631
bool AlsaSoundSystem::GetDefaultDevice(SoundDeviceLocator **device) {
 
632
  if (!IsInitialized()) {
 
633
    return false;
 
634
  }
 
635
  *device = new AlsaDeviceLocator("Default device", "default");
 
636
  return true;
 
637
}
 
638
 
 
639
inline size_t AlsaSoundSystem::FrameSize(const OpenParams &params) {
 
640
  ASSERT(params.format < ARRAY_SIZE(kCricketFormatToSampleSizeTable));
 
641
  return kCricketFormatToSampleSizeTable[params.format] * params.channels;
 
642
}
 
643
 
 
644
template <typename StreamInterface>
 
645
StreamInterface *AlsaSoundSystem::OpenDevice(
 
646
    const SoundDeviceLocator *device,
 
647
    const OpenParams &params,
 
648
    snd_pcm_stream_t type,
 
649
    StreamInterface *(AlsaSoundSystem::*start_fn)(
 
650
        snd_pcm_t *handle,
 
651
        size_t frame_size,
 
652
        int wait_timeout_ms,
 
653
        int flags,
 
654
        int freq)) {
 
655
 
 
656
  if (!IsInitialized()) {
 
657
    return NULL;
 
658
  }
 
659
 
 
660
  StreamInterface *stream;
 
661
  int err;
 
662
 
 
663
  const char *dev = static_cast<const AlsaDeviceLocator *>(device)->
 
664
      device_name().c_str();
 
665
 
 
666
  snd_pcm_t *handle = NULL;
 
667
  err = LATE(snd_pcm_open)(
 
668
      &handle,
 
669
      dev,
 
670
      type,
 
671
      // No flags.
 
672
      0);
 
673
  if (err != 0) {
 
674
    LOG(LS_ERROR) << "snd_pcm_open(" << dev << "): " << GetError(err);
 
675
    return NULL;
 
676
  }
 
677
  LOG(LS_VERBOSE) << "Opening " << dev;
 
678
  ASSERT(handle);  // If open succeeded, handle ought to be valid
 
679
 
 
680
  // Compute requested latency in microseconds.
 
681
  int latency;
 
682
  if (params.latency == kNoLatencyRequirements) {
 
683
    latency = kDefaultLatencyUsecs;
 
684
  } else {
 
685
    // kLowLatency is 0, so we treat it the same as a request for zero latency.
 
686
    // Compute what the user asked for.
 
687
    latency = talk_base::kNumMicrosecsPerSec *
 
688
        params.latency /
 
689
        params.freq /
 
690
        FrameSize(params);
 
691
    // And this is what we'll actually use.
 
692
    latency = talk_base::_max(latency, kMinimumLatencyUsecs);
 
693
  }
 
694
 
 
695
  ASSERT(params.format < ARRAY_SIZE(kCricketFormatToAlsaFormatTable));
 
696
 
 
697
  err = LATE(snd_pcm_set_params)(handle,
 
698
                                 kCricketFormatToAlsaFormatTable[params.format],
 
699
                                 // SoundSystemInterface only supports
 
700
                                 // interleaved audio.
 
701
                                 SND_PCM_ACCESS_RW_INTERLEAVED,
 
702
                                 params.channels,
 
703
                                 params.freq,
 
704
                                 1,  // Allow ALSA to resample.
 
705
                                 latency);
 
706
  if (err != 0) {
 
707
    LOG(LS_ERROR) << "snd_pcm_set_params(): " << GetError(err);
 
708
    goto fail;
 
709
  }
 
710
 
 
711
  err = LATE(snd_pcm_prepare)(handle);
 
712
  if (err != 0) {
 
713
    LOG(LS_ERROR) << "snd_pcm_prepare(): " << GetError(err);
 
714
    goto fail;
 
715
  }
 
716
 
 
717
  stream = (this->*start_fn)(
 
718
      handle,
 
719
      FrameSize(params),
 
720
      // We set the wait time to twice the requested latency, so that wait
 
721
      // timeouts should be rare.
 
722
      2 * latency / talk_base::kNumMicrosecsPerMillisec,
 
723
      params.flags,
 
724
      params.freq);
 
725
  if (stream) {
 
726
    return stream;
 
727
  }
 
728
  // Else fall through.
 
729
 
 
730
 fail:
 
731
  err = LATE(snd_pcm_close)(handle);
 
732
  if (err != 0) {
 
733
    LOG(LS_ERROR) << "snd_pcm_close(): " << GetError(err);
 
734
  }
 
735
  return NULL;
 
736
}
 
737
 
 
738
SoundOutputStreamInterface *AlsaSoundSystem::StartOutputStream(
 
739
    snd_pcm_t *handle,
 
740
    size_t frame_size,
 
741
    int wait_timeout_ms,
 
742
    int flags,
 
743
    int freq) {
 
744
  // Nothing to do here but instantiate the stream.
 
745
  return new AlsaOutputStream(
 
746
      this, handle, frame_size, wait_timeout_ms, flags, freq);
 
747
}
 
748
 
 
749
SoundInputStreamInterface *AlsaSoundSystem::StartInputStream(
 
750
    snd_pcm_t *handle,
 
751
    size_t frame_size,
 
752
    int wait_timeout_ms,
 
753
    int flags,
 
754
    int freq) {
 
755
  // Output streams start automatically once enough data has been written, but
 
756
  // input streams must be started manually or else snd_pcm_wait() will never
 
757
  // return true.
 
758
  int err;
 
759
  err = LATE(snd_pcm_start)(handle);
 
760
  if (err != 0) {
 
761
    LOG(LS_ERROR) << "snd_pcm_start(): " << GetError(err);
 
762
    return NULL;
 
763
  }
 
764
  return new AlsaInputStream(
 
765
      this, handle, frame_size, wait_timeout_ms, flags, freq);
 
766
}
 
767
 
 
768
inline const char *AlsaSoundSystem::GetError(int err) {
 
769
  return LATE(snd_strerror)(err);
 
770
}
 
771
 
 
772
}  // namespace cricket