Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / media / audio / alsa / audio_manager_alsa.cc
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/audio_manager_alsa.h"
6
7 #include "base/command_line.h"
8 #include "base/environment.h"
9 #include "base/files/file_path.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/nix/xdg_util.h"
13 #include "base/process/launch.h"
14 #include "base/stl_util.h"
15 #include "media/audio/audio_output_dispatcher.h"
16 #include "media/audio/audio_parameters.h"
17 #if defined(USE_CRAS)
18 #include "media/audio/cras/audio_manager_cras.h"
19 #endif
20 #include "media/audio/alsa/alsa_input.h"
21 #include "media/audio/alsa/alsa_output.h"
22 #include "media/audio/alsa/alsa_wrapper.h"
23 #if defined(USE_PULSEAUDIO)
24 #include "media/audio/pulse/audio_manager_pulse.h"
25 #endif
26 #include "media/base/channel_layout.h"
27 #include "media/base/limits.h"
28 #include "media/base/media_switches.h"
29
30 namespace media {
31
32 // Maximum number of output streams that can be open simultaneously.
33 static const int kMaxOutputStreams = 50;
34
35 // Default sample rate for input and output streams.
36 static const int kDefaultSampleRate = 48000;
37
38 // Since "default", "pulse" and "dmix" devices are virtual devices mapped to
39 // real devices, we remove them from the list to avoiding duplicate counting.
40 // In addition, note that we support no more than 2 channels for recording,
41 // hence surround devices are not stored in the list.
42 static const char* kInvalidAudioInputDevices[] = {
43   "default",
44   "dmix",
45   "null",
46   "pulse",
47   "surround",
48 };
49
50 // static
51 void AudioManagerAlsa::ShowLinuxAudioInputSettings() {
52   scoped_ptr<base::Environment> env(base::Environment::Create());
53   CommandLine command_line(CommandLine::NO_PROGRAM);
54   switch (base::nix::GetDesktopEnvironment(env.get())) {
55     case base::nix::DESKTOP_ENVIRONMENT_GNOME:
56       command_line.SetProgram(base::FilePath("gnome-volume-control"));
57       break;
58     case base::nix::DESKTOP_ENVIRONMENT_KDE3:
59     case base::nix::DESKTOP_ENVIRONMENT_KDE4:
60       command_line.SetProgram(base::FilePath("kmix"));
61       break;
62     case base::nix::DESKTOP_ENVIRONMENT_UNITY:
63       command_line.SetProgram(base::FilePath("gnome-control-center"));
64       command_line.AppendArg("sound");
65       command_line.AppendArg("input");
66       break;
67     default:
68       LOG(ERROR) << "Failed to show audio input settings: we don't know "
69                  << "what command to use for your desktop environment.";
70       return;
71   }
72   base::LaunchProcess(command_line, base::LaunchOptions(), NULL);
73 }
74
75 // Implementation of AudioManager.
76 bool AudioManagerAlsa::HasAudioOutputDevices() {
77   return HasAnyAlsaAudioDevice(kStreamPlayback);
78 }
79
80 bool AudioManagerAlsa::HasAudioInputDevices() {
81   return HasAnyAlsaAudioDevice(kStreamCapture);
82 }
83
84 AudioManagerAlsa::AudioManagerAlsa(AudioLogFactory* audio_log_factory)
85     : AudioManagerBase(audio_log_factory),
86       wrapper_(new AlsaWrapper()) {
87   SetMaxOutputStreamsAllowed(kMaxOutputStreams);
88 }
89
90 AudioManagerAlsa::~AudioManagerAlsa() {
91   Shutdown();
92 }
93
94 void AudioManagerAlsa::ShowAudioInputSettings() {
95   ShowLinuxAudioInputSettings();
96 }
97
98 void AudioManagerAlsa::GetAudioInputDeviceNames(
99     AudioDeviceNames* device_names) {
100   DCHECK(device_names->empty());
101   GetAlsaAudioDevices(kStreamCapture, device_names);
102 }
103
104 void AudioManagerAlsa::GetAudioOutputDeviceNames(
105     AudioDeviceNames* device_names) {
106   DCHECK(device_names->empty());
107   GetAlsaAudioDevices(kStreamPlayback, device_names);
108 }
109
110 AudioParameters AudioManagerAlsa::GetInputStreamParameters(
111     const std::string& device_id) {
112   static const int kDefaultInputBufferSize = 1024;
113
114   return AudioParameters(
115       AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO,
116       kDefaultSampleRate, 16, kDefaultInputBufferSize);
117 }
118
119 void AudioManagerAlsa::GetAlsaAudioDevices(
120     StreamType type,
121     media::AudioDeviceNames* device_names) {
122   // Constants specified by the ALSA API for device hints.
123   static const char kPcmInterfaceName[] = "pcm";
124   int card = -1;
125
126   // Loop through the sound cards to get ALSA device hints.
127   while (!wrapper_->CardNext(&card) && card >= 0) {
128     void** hints = NULL;
129     int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
130     if (!error) {
131       GetAlsaDevicesInfo(type, hints, device_names);
132
133       // Destroy the hints now that we're done with it.
134       wrapper_->DeviceNameFreeHint(hints);
135     } else {
136       DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: "
137                     << wrapper_->StrError(error);
138     }
139   }
140 }
141
142 void AudioManagerAlsa::GetAlsaDevicesInfo(
143     AudioManagerAlsa::StreamType type,
144     void** hints,
145     media::AudioDeviceNames* device_names) {
146   static const char kIoHintName[] = "IOID";
147   static const char kNameHintName[] = "NAME";
148   static const char kDescriptionHintName[] = "DESC";
149
150   const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type);
151
152   for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
153     // Only examine devices of the right type.  Valid values are
154     // "Input", "Output", and NULL which means both input and output.
155     scoped_ptr<char, base::FreeDeleter> io(wrapper_->DeviceNameGetHint(
156         *hint_iter, kIoHintName));
157     if (io != NULL && strcmp(unwanted_device_type, io.get()) == 0)
158       continue;
159
160     // Found a device, prepend the default device since we always want
161     // it to be on the top of the list for all platforms. And there is
162     // no duplicate counting here since it is only done if the list is
163     // still empty.  Note, pulse has exclusively opened the default
164     // device, so we must open the device via the "default" moniker.
165     if (device_names->empty()) {
166       device_names->push_front(media::AudioDeviceName(
167           AudioManagerBase::kDefaultDeviceName,
168           AudioManagerBase::kDefaultDeviceId));
169     }
170
171     // Get the unique device name for the device.
172     scoped_ptr<char, base::FreeDeleter> unique_device_name(
173         wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
174
175     // Find out if the device is available.
176     if (IsAlsaDeviceAvailable(type, unique_device_name.get())) {
177       // Get the description for the device.
178       scoped_ptr<char, base::FreeDeleter> desc(wrapper_->DeviceNameGetHint(
179           *hint_iter, kDescriptionHintName));
180
181       media::AudioDeviceName name;
182       name.unique_id = unique_device_name.get();
183       if (desc) {
184         // Use the more user friendly description as name.
185         // Replace '\n' with '-'.
186         char* pret = strchr(desc.get(), '\n');
187         if (pret)
188           *pret = '-';
189         name.device_name = desc.get();
190       } else {
191         // Virtual devices don't necessarily have descriptions.
192         // Use their names instead.
193         name.device_name = unique_device_name.get();
194       }
195
196       // Store the device information.
197       device_names->push_back(name);
198     }
199   }
200 }
201
202 // static
203 bool AudioManagerAlsa::IsAlsaDeviceAvailable(
204     AudioManagerAlsa::StreamType type,
205     const char* device_name) {
206   if (!device_name)
207     return false;
208
209   // We do prefix matches on the device name to see whether to include
210   // it or not.
211   if (type == kStreamCapture) {
212     // Check if the device is in the list of invalid devices.
213     for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) {
214       if (strncmp(kInvalidAudioInputDevices[i], device_name,
215                   strlen(kInvalidAudioInputDevices[i])) == 0)
216         return false;
217     }
218     return true;
219   } else {
220     DCHECK_EQ(kStreamPlayback, type);
221     // We prefer the device type that maps straight to hardware but
222     // goes through software conversion if needed (e.g. incompatible
223     // sample rate).
224     // TODO(joi): Should we prefer "hw" instead?
225     static const char kDeviceTypeDesired[] = "plughw";
226     return strncmp(kDeviceTypeDesired,
227                    device_name,
228                    arraysize(kDeviceTypeDesired) - 1) == 0;
229   }
230 }
231
232 // static
233 const char* AudioManagerAlsa::UnwantedDeviceTypeWhenEnumerating(
234     AudioManagerAlsa::StreamType wanted_type) {
235   return wanted_type == kStreamPlayback ? "Input" : "Output";
236 }
237
238 bool AudioManagerAlsa::HasAnyAlsaAudioDevice(
239     AudioManagerAlsa::StreamType stream) {
240   static const char kPcmInterfaceName[] = "pcm";
241   static const char kIoHintName[] = "IOID";
242   void** hints = NULL;
243   bool has_device = false;
244   int card = -1;
245
246   // Loop through the sound cards.
247   // Don't use snd_device_name_hint(-1,..) since there is a access violation
248   // inside this ALSA API with libasound.so.2.0.0.
249   while (!wrapper_->CardNext(&card) && (card >= 0) && !has_device) {
250     int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
251     if (!error) {
252       for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
253         // Only examine devices that are |stream| capable.  Valid values are
254         // "Input", "Output", and NULL which means both input and output.
255         scoped_ptr<char, base::FreeDeleter> io(wrapper_->DeviceNameGetHint(
256             *hint_iter, kIoHintName));
257         const char* unwanted_type = UnwantedDeviceTypeWhenEnumerating(stream);
258         if (io != NULL && strcmp(unwanted_type, io.get()) == 0)
259           continue;  // Wrong type, skip the device.
260
261         // Found an input device.
262         has_device = true;
263         break;
264       }
265
266       // Destroy the hints now that we're done with it.
267       wrapper_->DeviceNameFreeHint(hints);
268       hints = NULL;
269     } else {
270       DLOG(WARNING) << "HasAnyAudioDevice: unable to get device hints: "
271                     << wrapper_->StrError(error);
272     }
273   }
274
275   return has_device;
276 }
277
278 AudioOutputStream* AudioManagerAlsa::MakeLinearOutputStream(
279     const AudioParameters& params) {
280   DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
281   return MakeOutputStream(params);
282 }
283
284 AudioOutputStream* AudioManagerAlsa::MakeLowLatencyOutputStream(
285     const AudioParameters& params,
286     const std::string& device_id) {
287   DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!";
288   DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
289   return MakeOutputStream(params);
290 }
291
292 AudioInputStream* AudioManagerAlsa::MakeLinearInputStream(
293     const AudioParameters& params, const std::string& device_id) {
294   DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
295   return MakeInputStream(params, device_id);
296 }
297
298 AudioInputStream* AudioManagerAlsa::MakeLowLatencyInputStream(
299     const AudioParameters& params, const std::string& device_id) {
300   DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
301   return MakeInputStream(params, device_id);
302 }
303
304 AudioParameters AudioManagerAlsa::GetPreferredOutputStreamParameters(
305     const std::string& output_device_id,
306     const AudioParameters& input_params) {
307   // TODO(tommi): Support |output_device_id|.
308   DLOG_IF(ERROR, !output_device_id.empty()) << "Not implemented!";
309   static const int kDefaultOutputBufferSize = 2048;
310   ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO;
311   int sample_rate = kDefaultSampleRate;
312   int buffer_size = kDefaultOutputBufferSize;
313   int bits_per_sample = 16;
314   if (input_params.IsValid()) {
315     // Some clients, such as WebRTC, have a more limited use case and work
316     // acceptably with a smaller buffer size.  The check below allows clients
317     // which want to try a smaller buffer size on Linux to do so.
318     // TODO(dalecurtis): This should include bits per channel and channel layout
319     // eventually.
320     sample_rate = input_params.sample_rate();
321     bits_per_sample = input_params.bits_per_sample();
322     channel_layout = input_params.channel_layout();
323     buffer_size = std::min(input_params.frames_per_buffer(), buffer_size);
324   }
325
326   int user_buffer_size = GetUserBufferSize();
327   if (user_buffer_size)
328     buffer_size = user_buffer_size;
329
330   return AudioParameters(
331       AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout,
332       sample_rate, bits_per_sample, buffer_size, AudioParameters::NO_EFFECTS);
333 }
334
335 AudioOutputStream* AudioManagerAlsa::MakeOutputStream(
336     const AudioParameters& params) {
337   std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice;
338   if (CommandLine::ForCurrentProcess()->HasSwitch(
339           switches::kAlsaOutputDevice)) {
340     device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
341         switches::kAlsaOutputDevice);
342   }
343   return new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this);
344 }
345
346 AudioInputStream* AudioManagerAlsa::MakeInputStream(
347     const AudioParameters& params, const std::string& device_id) {
348   std::string device_name = (device_id == AudioManagerBase::kDefaultDeviceId) ?
349       AlsaPcmInputStream::kAutoSelectDevice : device_id;
350   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaInputDevice)) {
351     device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
352         switches::kAlsaInputDevice);
353   }
354
355   return new AlsaPcmInputStream(this, device_name, params, wrapper_.get());
356 }
357
358 }  // namespace media