cras_unified.cc 12 KB

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