| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "media_perception/chrome_audio_service_client_impl.h" |
| |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "media_perception/device_management.pb.h" |
| #include "media_perception/proto_mojom_conversion.h" |
| #include "media_perception/serialized_proto.h" |
| |
| namespace mri { |
| |
| namespace { |
| |
| // Max number of devices to probe. |
| const size_t MAX_IODEVS = 10; |
| |
| // Max number of nodes to probe. |
| const size_t MAX_IONODES = 20; |
| |
| } // namespace |
| |
| bool ChromeAudioServiceClientImpl::Connect() { |
| if (IsConnected()) { |
| LOG(INFO) << "Client is already connected."; |
| return true; |
| } |
| |
| base::AutoLock auto_lock(client_lock_); |
| // cras_helper_create_connect returns 0 on success, or a negative error code |
| // on failure. |
| int rc = cras_helper_create_connect(&client_); |
| if (rc < 0) { |
| LOG(ERROR) << "Failed to connect to CrAS server with error code: " << rc; |
| DestroyClient(); |
| return false; |
| } |
| return true; |
| } |
| |
| void ChromeAudioServiceClientImpl::DestroyClient() { |
| base::AutoLock auto_lock(client_lock_); |
| if (client_) { |
| cras_client_destroy(client_); |
| client_ = nullptr; |
| } |
| } |
| |
| ChromeAudioServiceClientImpl::~ChromeAudioServiceClientImpl() { |
| DestroyClient(); |
| } |
| |
| bool ChromeAudioServiceClientImpl::IsConnected() { |
| base::AutoLock auto_lock(client_lock_); |
| return client_ != nullptr; |
| } |
| |
| std::vector<SerializedAudioDevice> |
| ChromeAudioServiceClientImpl::GetInputDevices() { |
| std::vector<SerializedAudioDevice> serialized_audio_devices; |
| if (!IsConnected()) { |
| LOG(WARNING) << "CrAS client is not connected. " |
| << "Must Connect() to query devices."; |
| return serialized_audio_devices; |
| } |
| |
| struct cras_iodev_info devs[MAX_IODEVS]; |
| struct cras_ionode_info nodes[MAX_IONODES]; |
| size_t num_devs = MAX_IODEVS; |
| size_t num_nodes = MAX_IONODES; |
| |
| base::AutoLock auto_lock(client_lock_); |
| // Get the current list of input devices. Return 0 on success, -EINVAL if the |
| // client isn't valid or isn't running. |
| int rc = cras_client_get_input_devices(client_, devs, nodes, &num_devs, |
| &num_nodes); |
| if (rc != 0) { |
| LOG(ERROR) << "Failed to query audio input devices with error code: " << rc; |
| return serialized_audio_devices; |
| } |
| |
| LOG(INFO) << "Got devices from CrAS."; |
| |
| // Loop through the result and create the necessary AudioDevice protos. |
| for (int i = 0; i < num_devs; i++) { |
| if (std::string(devs[i].name).empty()) { |
| continue; |
| } |
| AudioDevice audio_device; |
| audio_device.set_id(std::to_string(devs[i].stable_id)); |
| audio_device.set_display_name(std::string(devs[i].name)); |
| serialized_audio_devices.push_back( |
| Serialized<AudioDevice>(audio_device).GetBytes()); |
| |
| // Add the stable device id to the audio receivers map if it does not exist. |
| if (device_id_to_audio_receiver_map_.find(audio_device.id()) == |
| device_id_to_audio_receiver_map_.end()) { |
| device_id_to_audio_receiver_map_.emplace(audio_device.id(), |
| AudioReceiver(audio_device.id())); |
| } |
| } |
| return serialized_audio_devices; |
| } |
| |
| bool ChromeAudioServiceClientImpl::IsAudioCaptureStartedForDevice( |
| const std::string& device_id, |
| SerializedAudioStreamParams* capture_format) { |
| base::AutoLock auto_lock(client_lock_); |
| std::map<std::string, AudioReceiver>::iterator it = |
| device_id_to_audio_receiver_map_.find(device_id); |
| if (it == device_id_to_audio_receiver_map_.end()) { |
| LOG(WARNING) << "Device id not found in map."; |
| return false; |
| } |
| |
| if (it->second.GetFrameHandlerCount() == 0) { |
| return false; |
| } |
| |
| *capture_format = Serialized<AudioStreamParams>( |
| it->second.GetAudioStreamParams()).GetBytes(); |
| return true; |
| } |
| |
| int ChromeAudioServiceClientImpl::AddFrameHandler( |
| const std::string& device_id, |
| const SerializedAudioStreamParams& capture_format, |
| AudioFrameHandler handler) { |
| AudioStreamParams params = Serialized<AudioStreamParams>( |
| capture_format).Deserialize(); |
| |
| base::AutoLock auto_lock(client_lock_); |
| std::map<std::string, AudioReceiver>::iterator |
| receiver_it = device_id_to_audio_receiver_map_.find(device_id); |
| if (receiver_it == device_id_to_audio_receiver_map_.end()) { |
| LOG(WARNING) << "Device id not found in map."; |
| return 0; |
| } |
| |
| // If the internal handler map already has a frame handler, then we need to |
| // compare the requested params with the params saved for the device. |
| if (receiver_it->second.GetFrameHandlerCount() > 0 && |
| !receiver_it->second.CaptureFormatMatches(params)) { |
| LOG(WARNING) |
| << "Capture formats do not match for device already streaming."; |
| return 0; |
| } else if (receiver_it->second.GetFrameHandlerCount() == 0) { |
| // Start streaming audio for this device since it not started. |
| struct cras_iodev_info devs[MAX_IODEVS]; |
| struct cras_ionode_info nodes[MAX_IONODES]; |
| size_t num_devs = MAX_IODEVS; |
| size_t num_nodes = MAX_IONODES; |
| |
| // Get the current list of input devices. Return 0 on success, -EINVAL if |
| // the client isn't valid or isn't running. |
| int rc = cras_client_get_input_devices(client_, devs, nodes, &num_devs, |
| &num_nodes); |
| if (rc != 0) { |
| LOG(ERROR) << "Failed to query audio input devices with error code: " |
| << rc; |
| return 0; |
| } |
| |
| int dev_idx = NO_DEVICE; |
| bool found_matched_device = false; |
| for (int i = 0; i < num_devs; i++) { |
| if (std::to_string(devs[i].stable_id) == device_id) { |
| LOG(INFO) << "Found audio device with stable id: " << device_id; |
| found_matched_device = true; |
| dev_idx = devs[i].idx; |
| } |
| } |
| |
| if (!found_matched_device) { |
| LOG(WARNING) << "Failed to find a matched audio device for id: " |
| << device_id; |
| return 0; |
| } |
| |
| if (!receiver_it->second.SetAudioStreamParams(params)) { |
| LOG(ERROR) << "Failed to set the audio stream params."; |
| return 0; |
| } |
| |
| // Use the dev_idx to start audio capture. |
| if (!receiver_it->second.StartAudioCaptureForDeviceIdx(client_, dev_idx)) { |
| LOG(ERROR) << "Failed to start audio capture."; |
| return 0; |
| } |
| } |
| |
| // Store the audio stream parameters for this device. |
| return receiver_it->second.AddAudioFrameHandler(std::move(handler)); |
| } |
| |
| bool ChromeAudioServiceClientImpl::RemoveFrameHandler( |
| const std::string& device_id, int frame_handler_id) { |
| base::AutoLock auto_lock(client_lock_); |
| std::map<std::string, AudioReceiver>::iterator it = |
| device_id_to_audio_receiver_map_.find(device_id); |
| if (it == device_id_to_audio_receiver_map_.end()) { |
| LOG(WARNING) << "Device id not found in map."; |
| return false; |
| } |
| |
| bool success = it->second.RemoveFrameHandler(frame_handler_id); |
| if (success && it->second.GetFrameHandlerCount() == 0) { |
| it->second.StopAudioCapture(client_); |
| } |
| return success; |
| } |
| |
| } // namespace mri |