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