• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "media/audio/alsa/alsa_util.h"
6 
7 #include <string>
8 
9 #include "base/logging.h"
10 #include "media/audio/alsa/alsa_wrapper.h"
11 
12 namespace alsa_util {
13 
OpenDevice(media::AlsaWrapper * wrapper,const char * device_name,snd_pcm_stream_t type,int channels,int sample_rate,snd_pcm_format_t pcm_format,int latency_us)14 static snd_pcm_t* OpenDevice(media::AlsaWrapper* wrapper,
15                              const char* device_name,
16                              snd_pcm_stream_t type,
17                              int channels,
18                              int sample_rate,
19                              snd_pcm_format_t pcm_format,
20                              int latency_us) {
21   snd_pcm_t* handle = NULL;
22   int error = wrapper->PcmOpen(&handle, device_name, type, SND_PCM_NONBLOCK);
23   if (error < 0) {
24     LOG(WARNING) << "PcmOpen: " << device_name << ","
25                  << wrapper->StrError(error);
26     return NULL;
27   }
28 
29   error = wrapper->PcmSetParams(handle, pcm_format,
30                                 SND_PCM_ACCESS_RW_INTERLEAVED, channels,
31                                 sample_rate, 1, latency_us);
32   if (error < 0) {
33     LOG(WARNING) << "PcmSetParams: " << device_name << ", "
34                  << wrapper->StrError(error) << " - Format: " << pcm_format
35                  << " Channels: " << channels << " Latency: " << latency_us;
36     if (alsa_util::CloseDevice(wrapper, handle) < 0) {
37       // TODO(ajwong): Retry on certain errors?
38       LOG(WARNING) << "Unable to close audio device. Leaking handle.";
39     }
40     return NULL;
41   }
42 
43   return handle;
44 }
45 
DeviceNameToControlName(const std::string & device_name)46 static std::string DeviceNameToControlName(const std::string& device_name) {
47   const char kMixerPrefix[] = "hw";
48   std::string control_name;
49   size_t pos1 = device_name.find(':');
50   if (pos1 == std::string::npos) {
51     control_name = device_name;
52   } else {
53     // Examples:
54     // deviceName: "front:CARD=Intel,DEV=0", controlName: "hw:CARD=Intel".
55     // deviceName: "default:CARD=Intel", controlName: "CARD=Intel".
56     size_t pos2 = device_name.find(',');
57     control_name = (pos2 == std::string::npos) ?
58         device_name.substr(pos1) :
59         kMixerPrefix + device_name.substr(pos1, pos2 - pos1);
60   }
61 
62   return control_name;
63 }
64 
BitsToFormat(int bits_per_sample)65 snd_pcm_format_t BitsToFormat(int bits_per_sample) {
66   switch (bits_per_sample) {
67     case 8:
68       return SND_PCM_FORMAT_U8;
69 
70     case 16:
71       return SND_PCM_FORMAT_S16;
72 
73     case 24:
74       return SND_PCM_FORMAT_S24;
75 
76     case 32:
77       return SND_PCM_FORMAT_S32;
78 
79     default:
80       return SND_PCM_FORMAT_UNKNOWN;
81   }
82 }
83 
CloseDevice(media::AlsaWrapper * wrapper,snd_pcm_t * handle)84 int CloseDevice(media::AlsaWrapper* wrapper, snd_pcm_t* handle) {
85   std::string device_name = wrapper->PcmName(handle);
86   int error = wrapper->PcmClose(handle);
87   if (error < 0) {
88     LOG(ERROR) << "PcmClose: " << device_name << ", "
89                << wrapper->StrError(error);
90   }
91 
92   return error;
93 }
94 
OpenCaptureDevice(media::AlsaWrapper * wrapper,const char * device_name,int channels,int sample_rate,snd_pcm_format_t pcm_format,int latency_us)95 snd_pcm_t* OpenCaptureDevice(media::AlsaWrapper* wrapper,
96                              const char* device_name,
97                              int channels,
98                              int sample_rate,
99                              snd_pcm_format_t pcm_format,
100                              int latency_us) {
101   return OpenDevice(wrapper, device_name, SND_PCM_STREAM_CAPTURE, channels,
102                     sample_rate, pcm_format, latency_us);
103 }
104 
OpenPlaybackDevice(media::AlsaWrapper * wrapper,const char * device_name,int channels,int sample_rate,snd_pcm_format_t pcm_format,int latency_us)105 snd_pcm_t* OpenPlaybackDevice(media::AlsaWrapper* wrapper,
106                               const char* device_name,
107                               int channels,
108                               int sample_rate,
109                               snd_pcm_format_t pcm_format,
110                               int latency_us) {
111   return OpenDevice(wrapper, device_name, SND_PCM_STREAM_PLAYBACK, channels,
112                     sample_rate, pcm_format, latency_us);
113 }
114 
OpenMixer(media::AlsaWrapper * wrapper,const std::string & device_name)115 snd_mixer_t* OpenMixer(media::AlsaWrapper* wrapper,
116                        const std::string& device_name) {
117   snd_mixer_t* mixer = NULL;
118 
119   int error = wrapper->MixerOpen(&mixer, 0);
120   if (error < 0) {
121     LOG(ERROR) << "MixerOpen: " << device_name << ", "
122                << wrapper->StrError(error);
123     return NULL;
124   }
125 
126   std::string control_name = DeviceNameToControlName(device_name);
127   error = wrapper->MixerAttach(mixer, control_name.c_str());
128   if (error < 0) {
129     LOG(ERROR) << "MixerAttach, " << control_name << ", "
130                << wrapper->StrError(error);
131     alsa_util::CloseMixer(wrapper, mixer, device_name);
132     return NULL;
133   }
134 
135   error = wrapper->MixerElementRegister(mixer, NULL, NULL);
136   if (error < 0) {
137     LOG(ERROR) << "MixerElementRegister: " << control_name << ", "
138                << wrapper->StrError(error);
139     alsa_util::CloseMixer(wrapper, mixer, device_name);
140     return NULL;
141   }
142 
143   return mixer;
144 }
145 
CloseMixer(media::AlsaWrapper * wrapper,snd_mixer_t * mixer,const std::string & device_name)146 void CloseMixer(media::AlsaWrapper* wrapper, snd_mixer_t* mixer,
147                 const std::string& device_name) {
148   if (!mixer)
149     return;
150 
151   wrapper->MixerFree(mixer);
152 
153   int error = 0;
154   if (!device_name.empty()) {
155     std::string control_name = DeviceNameToControlName(device_name);
156     error = wrapper->MixerDetach(mixer, control_name.c_str());
157     if (error < 0) {
158       LOG(WARNING) << "MixerDetach: " << control_name << ", "
159                    << wrapper->StrError(error);
160     }
161   }
162 
163   error = wrapper->MixerClose(mixer);
164   if (error < 0) {
165     LOG(WARNING) << "MixerClose: " << wrapper->StrError(error);
166   }
167 }
168 
LoadCaptureMixerElement(media::AlsaWrapper * wrapper,snd_mixer_t * mixer)169 snd_mixer_elem_t* LoadCaptureMixerElement(media::AlsaWrapper* wrapper,
170                                           snd_mixer_t* mixer) {
171   if (!mixer)
172     return NULL;
173 
174   int error = wrapper->MixerLoad(mixer);
175   if (error < 0) {
176     LOG(ERROR) << "MixerLoad: " << wrapper->StrError(error);
177     return NULL;
178   }
179 
180   snd_mixer_elem_t* elem = NULL;
181   snd_mixer_elem_t* mic_elem = NULL;
182   const char kCaptureElemName[] = "Capture";
183   const char kMicElemName[] = "Mic";
184   for (elem = wrapper->MixerFirstElem(mixer);
185        elem;
186        elem = wrapper->MixerNextElem(elem)) {
187     if (wrapper->MixerSelemIsActive(elem)) {
188       const char* elem_name = wrapper->MixerSelemName(elem);
189       if (strcmp(elem_name, kCaptureElemName) == 0)
190         return elem;
191       else if (strcmp(elem_name, kMicElemName) == 0)
192         mic_elem = elem;
193     }
194   }
195 
196   // Did not find any Capture handle, use the Mic handle.
197   return mic_elem;
198 }
199 
200 }  // namespace alsa_util
201