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/cras/cras_unified.h"
6 
7 #include <algorithm>
8 
9 #include "base/logging.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "media/audio/cras/audio_manager_cras_base.h"
13 
14 namespace media {
15 
16 namespace {
17 
GetDevicePin(AudioManagerCrasBase * manager,const std::string & device_id)18 int GetDevicePin(AudioManagerCrasBase* manager, const std::string& device_id) {
19   if (!manager->IsDefault(device_id, false)) {
20     uint64_t cras_node_id;
21     base::StringToUint64(device_id, &cras_node_id);
22     return dev_index_of(cras_node_id);
23   }
24   return NO_DEVICE;
25 }
26 
27 }  // namespace
28 
29 // Overview of operation:
30 // 1) An object of CrasUnifiedStream is created by the AudioManager
31 // factory: audio_man->MakeAudioStream().
32 // 2) Next some thread will call Open(), at that point a client is created and
33 // configured for the correct format and sample rate.
34 // 3) Then Start(source) is called and a stream is added to the CRAS client
35 // which will create its own thread that periodically calls the source for more
36 // data as buffers are being consumed.
37 // 4) When finished Stop() is called, which is handled by stopping the stream.
38 // 5) Finally Close() is called. It cleans up and notifies the audio manager,
39 // which likely will destroy this object.
40 //
41 // Simplified data flow for output only streams:
42 //
43 //   +-------------+                  +------------------+
44 //   | CRAS Server |                  | Chrome Client    |
45 //   +------+------+    Add Stream    +---------+--------+
46 //          |<----------------------------------|
47 //          |                                   |
48 //          | Near out of samples, request more |
49 //          |---------------------------------->|
50 //          |                                   |  UnifiedCallback()
51 //          |                                   |  WriteAudio()
52 //          |                                   |
53 //          |  buffer_frames written to shm     |
54 //          |<----------------------------------|
55 //          |                                   |
56 //         ...  Repeats for each block.        ...
57 //          |                                   |
58 //          |                                   |
59 //          |  Remove stream                    |
60 //          |<----------------------------------|
61 //          |                                   |
62 //
63 // For Unified streams the Chrome client is notified whenever buffer_frames have
64 // been captured.  For Output streams the client is notified a few milliseconds
65 // before the hardware buffer underruns and fills the buffer with another block
66 // of audio.
67 
CrasUnifiedStream(const AudioParameters & params,AudioManagerCrasBase * manager,const std::string & device_id)68 CrasUnifiedStream::CrasUnifiedStream(const AudioParameters& params,
69                                      AudioManagerCrasBase* manager,
70                                      const std::string& device_id)
71     : client_(NULL),
72       stream_id_(0),
73       params_(params),
74       bytes_per_frame_(0),
75       is_playing_(false),
76       volume_(1.0),
77       manager_(manager),
78       source_callback_(NULL),
79       output_bus_(AudioBus::Create(params)),
80       stream_direction_(CRAS_STREAM_OUTPUT),
81       pin_device_(GetDevicePin(manager, device_id)) {
82   DCHECK(manager_);
83   DCHECK_GT(params_.channels(), 0);
84 }
85 
~CrasUnifiedStream()86 CrasUnifiedStream::~CrasUnifiedStream() {
87   DCHECK(!is_playing_);
88 }
89 
Open()90 bool CrasUnifiedStream::Open() {
91   // Sanity check input values.
92   if (params_.sample_rate() <= 0) {
93     LOG(WARNING) << "Unsupported audio frequency.";
94     return false;
95   }
96 
97   // Create the client and connect to the CRAS server.
98   if (cras_client_create(&client_)) {
99     LOG(WARNING) << "Couldn't create CRAS client.\n";
100     client_ = NULL;
101     return false;
102   }
103 
104   if (cras_client_connect(client_)) {
105     LOG(WARNING) << "Couldn't connect CRAS client.\n";
106     cras_client_destroy(client_);
107     client_ = NULL;
108     return false;
109   }
110 
111   // Then start running the client.
112   if (cras_client_run_thread(client_)) {
113     LOG(WARNING) << "Couldn't run CRAS client.\n";
114     cras_client_destroy(client_);
115     client_ = NULL;
116     return false;
117   }
118 
119   return true;
120 }
121 
Close()122 void CrasUnifiedStream::Close() {
123   if (client_) {
124     cras_client_stop(client_);
125     cras_client_destroy(client_);
126     client_ = NULL;
127   }
128 
129   // Signal to the manager that we're closed and can be removed.
130   // Should be last call in the method as it deletes "this".
131   manager_->ReleaseOutputStream(this);
132 }
133 
134 // This stream is always used with sub second buffer sizes, where it's
135 // sufficient to simply always flush upon Start().
Flush()136 void CrasUnifiedStream::Flush() {}
137 
Start(AudioSourceCallback * callback)138 void CrasUnifiedStream::Start(AudioSourceCallback* callback) {
139   CHECK(callback);
140 
141   // Channel map to CRAS_CHANNEL, values in the same order of
142   // corresponding source in Chromium defined Channels.
143   static const int kChannelMap[] = {
144     CRAS_CH_FL,
145     CRAS_CH_FR,
146     CRAS_CH_FC,
147     CRAS_CH_LFE,
148     CRAS_CH_RL,
149     CRAS_CH_RR,
150     CRAS_CH_FLC,
151     CRAS_CH_FRC,
152     CRAS_CH_RC,
153     CRAS_CH_SL,
154     CRAS_CH_SR
155   };
156 
157   source_callback_ = callback;
158 
159   // Only start if we can enter the playing state.
160   if (is_playing_)
161     return;
162 
163   // Prepare |audio_format| and |stream_params| for the stream we
164   // will create.
165   cras_audio_format* audio_format = cras_audio_format_create(
166       SND_PCM_FORMAT_S16, params_.sample_rate(), params_.channels());
167   if (!audio_format) {
168     LOG(WARNING) << "Error setting up audio parameters.";
169     callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
170     return;
171   }
172 
173   // Initialize channel layout to all -1 to indicate that none of
174   // the channels is set in the layout.
175   int8_t layout[CRAS_CH_MAX] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
176 
177   // Converts to CRAS defined channels. ChannelOrder will return -1
178   // for channels that does not present in params_.channel_layout().
179   for (size_t i = 0; i < base::size(kChannelMap); ++i)
180     layout[kChannelMap[i]] = ChannelOrder(params_.channel_layout(),
181                                           static_cast<Channels>(i));
182 
183   if (cras_audio_format_set_channel_layout(audio_format, layout)) {
184     LOG(WARNING) << "Error setting channel layout.";
185     callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
186     return;
187   }
188 
189   cras_stream_params* stream_params = cras_client_unified_params_create(
190       stream_direction_,
191       params_.frames_per_buffer(),
192       CRAS_STREAM_TYPE_DEFAULT,
193       0,
194       this,
195       CrasUnifiedStream::UnifiedCallback,
196       CrasUnifiedStream::StreamError,
197       audio_format);
198   if (!stream_params) {
199     LOG(WARNING) << "Error setting up stream parameters.";
200     callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
201     cras_audio_format_destroy(audio_format);
202     return;
203   }
204 
205   cras_client_stream_params_set_client_type(stream_params,
206                                             manager_->GetClientType());
207 
208   // Before starting the stream, save the number of bytes in a frame for use in
209   // the callback.
210   bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
211 
212   // Adding the stream will start the audio callbacks requesting data.
213   if (cras_client_add_pinned_stream(client_, pin_device_, &stream_id_,
214                                     stream_params)) {
215     LOG(WARNING) << "Failed to add the stream.";
216     callback->OnError(AudioSourceCallback::ErrorType::kUnknown);
217     cras_audio_format_destroy(audio_format);
218     cras_client_stream_params_destroy(stream_params);
219     return;
220   }
221 
222   // Set initial volume.
223   cras_client_set_stream_volume(client_, stream_id_, volume_);
224 
225   // Done with config params.
226   cras_audio_format_destroy(audio_format);
227   cras_client_stream_params_destroy(stream_params);
228 
229   is_playing_ = true;
230 }
231 
Stop()232 void CrasUnifiedStream::Stop() {
233   if (!client_)
234     return;
235 
236   // Removing the stream from the client stops audio.
237   cras_client_rm_stream(client_, stream_id_);
238 
239   is_playing_ = false;
240 }
241 
SetVolume(double volume)242 void CrasUnifiedStream::SetVolume(double volume) {
243   if (!client_)
244     return;
245   volume_ = static_cast<float>(volume);
246   cras_client_set_stream_volume(client_, stream_id_, volume_);
247 }
248 
GetVolume(double * volume)249 void CrasUnifiedStream::GetVolume(double* volume) {
250   *volume = volume_;
251 }
252 
253 // Static callback asking for samples.
UnifiedCallback(cras_client * client,cras_stream_id_t stream_id,uint8_t * input_samples,uint8_t * output_samples,unsigned int frames,const timespec * input_ts,const timespec * output_ts,void * arg)254 int CrasUnifiedStream::UnifiedCallback(cras_client* client,
255                                        cras_stream_id_t stream_id,
256                                        uint8_t* input_samples,
257                                        uint8_t* output_samples,
258                                        unsigned int frames,
259                                        const timespec* input_ts,
260                                        const timespec* output_ts,
261                                        void* arg) {
262   CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
263   return me->DispatchCallback(frames,
264                               input_samples,
265                               output_samples,
266                               input_ts,
267                               output_ts);
268 }
269 
270 // Static callback for stream errors.
StreamError(cras_client * client,cras_stream_id_t stream_id,int err,void * arg)271 int CrasUnifiedStream::StreamError(cras_client* client,
272                                    cras_stream_id_t stream_id,
273                                    int err,
274                                    void* arg) {
275   CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
276   me->NotifyStreamError(err);
277   return 0;
278 }
279 
280 // Calls the appropriate rendering function for this type of stream.
DispatchCallback(size_t frames,uint8_t * input_samples,uint8_t * output_samples,const timespec * input_ts,const timespec * output_ts)281 uint32_t CrasUnifiedStream::DispatchCallback(size_t frames,
282                                              uint8_t* input_samples,
283                                              uint8_t* output_samples,
284                                              const timespec* input_ts,
285                                              const timespec* output_ts) {
286   switch (stream_direction_) {
287     case CRAS_STREAM_OUTPUT:
288       return WriteAudio(frames, output_samples, output_ts);
289     case CRAS_STREAM_INPUT:
290       NOTREACHED() << "CrasUnifiedStream doesn't support input streams.";
291       return 0;
292     default:
293       break;
294   }
295 
296   return 0;
297 }
298 
WriteAudio(size_t frames,uint8_t * buffer,const timespec * sample_ts)299 uint32_t CrasUnifiedStream::WriteAudio(size_t frames,
300                                        uint8_t* buffer,
301                                        const timespec* sample_ts) {
302   DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
303 
304   // Determine latency and pass that on to the source.
305   timespec latency_ts  = {0, 0};
306   cras_client_calc_playback_latency(sample_ts, &latency_ts);
307 
308   // Treat negative latency (if we are too slow to render) as 0.
309   const base::TimeDelta delay =
310       std::max(base::TimeDelta::FromTimeSpec(latency_ts), base::TimeDelta());
311 
312   int frames_filled = source_callback_->OnMoreData(
313       delay, base::TimeTicks::Now(), 0, output_bus_.get());
314 
315   // Note: If this ever changes to output raw float the data must be clipped and
316   // sanitized since it may come from an untrusted source such as NaCl.
317   output_bus_->ToInterleaved<SignedInt16SampleTypeTraits>(
318       frames_filled, reinterpret_cast<int16_t*>(buffer));
319 
320   return frames_filled;
321 }
322 
NotifyStreamError(int err)323 void CrasUnifiedStream::NotifyStreamError(int err) {
324   // This will remove the stream from the client.
325   // TODO(dalecurtis): Consider sending a translated |err| code.
326   if (source_callback_)
327     source_callback_->OnError(AudioSourceCallback::ErrorType::kUnknown);
328 }
329 
330 }  // namespace media
331