Improves device enumeration in ADM2 for Windows.
Summary of changes/improvements and fixes: Changes container for list of devices from std::vector to std:deque to allow fast insertion and deletion at both its beginning and its end. This approach makes it easier to first build a list of all available devices and then check the size of the list. If size > 0 => two more devices are added at the front (Default and Default Communication). The old solution contained a risk of adding invalid Default and Default Communication devices in cases where not physical device could be found. Adds usage of |device_index_| in CoreAudioBase to ensure that the selected device is unique. The previous version used only an ID but that ID is not unique when e.g. only one device exists since it can have up to three different roles. Improves logging and comments. No-Try: True Tbr: thaloun@chromium.org Bug: webrtc:11107 Change-Id: I9a09f7716ed8d8858dcc6a5354b038fc06496166 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/160050 Commit-Queue: Henrik Andreassson <henrika@webrtc.org> Reviewed-by: Henrik Andreassson <henrika@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29874}
This commit is contained in:
@ -11,8 +11,8 @@
|
|||||||
#ifndef MODULES_AUDIO_DEVICE_AUDIO_DEVICE_NAME_H_
|
#ifndef MODULES_AUDIO_DEVICE_AUDIO_DEVICE_NAME_H_
|
||||||
#define MODULES_AUDIO_DEVICE_AUDIO_DEVICE_NAME_H_
|
#define MODULES_AUDIO_DEVICE_AUDIO_DEVICE_NAME_H_
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ struct AudioDeviceName {
|
|||||||
std::string unique_id; // Unique identifier for the device.
|
std::string unique_id; // Unique identifier for the device.
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<AudioDeviceName> AudioDeviceNames;
|
typedef std::deque<AudioDeviceName> AudioDeviceNames;
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
||||||
|
@ -56,6 +56,34 @@ const char* DirectionToString(CoreAudioBase::Direction direction) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* RoleToString(const ERole role) {
|
||||||
|
switch (role) {
|
||||||
|
case eConsole:
|
||||||
|
return "Console";
|
||||||
|
case eMultimedia:
|
||||||
|
return "Multimedia";
|
||||||
|
case eCommunications:
|
||||||
|
return "Communications";
|
||||||
|
default:
|
||||||
|
return "Unsupported";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string IndexToString(int index) {
|
||||||
|
std::string ss = std::to_string(index);
|
||||||
|
switch (index) {
|
||||||
|
case kDefault:
|
||||||
|
ss += " (Default)";
|
||||||
|
break;
|
||||||
|
case kDefaultCommunications:
|
||||||
|
ss += " (Communications)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ss;
|
||||||
|
}
|
||||||
|
|
||||||
const char* SessionStateToString(AudioSessionState state) {
|
const char* SessionStateToString(AudioSessionState state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case AudioSessionStateActive:
|
case AudioSessionStateActive:
|
||||||
@ -204,15 +232,23 @@ bool CoreAudioBase::IsDefaultCommunicationsDevice(int index) const {
|
|||||||
return index == kDefaultCommunications;
|
return index == kDefaultCommunications;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CoreAudioBase::IsDefaultDevice(const std::string& device_id) const {
|
bool CoreAudioBase::IsDefaultDeviceId(const std::string& device_id) const {
|
||||||
|
// Returns true if |device_id| corresponds to the id of the default
|
||||||
|
// device. Note that, if only one device is available (or if the user has not
|
||||||
|
// explicitly set a default device), |device_id| will also math
|
||||||
|
// IsDefaultCommunicationsDeviceId().
|
||||||
return (IsInput() &&
|
return (IsInput() &&
|
||||||
(device_id == core_audio_utility::GetDefaultInputDeviceID())) ||
|
(device_id == core_audio_utility::GetDefaultInputDeviceID())) ||
|
||||||
(IsOutput() &&
|
(IsOutput() &&
|
||||||
(device_id == core_audio_utility::GetDefaultOutputDeviceID()));
|
(device_id == core_audio_utility::GetDefaultOutputDeviceID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CoreAudioBase::IsDefaultCommunicationsDevice(
|
bool CoreAudioBase::IsDefaultCommunicationsDeviceId(
|
||||||
const std::string& device_id) const {
|
const std::string& device_id) const {
|
||||||
|
// Returns true if |device_id| corresponds to the id of the default
|
||||||
|
// communication device. Note that, if only one device is available (or if
|
||||||
|
// the user has not explicitly set a communication device), |device_id| will
|
||||||
|
// also math IsDefaultDeviceId().
|
||||||
return (IsInput() &&
|
return (IsInput() &&
|
||||||
(device_id ==
|
(device_id ==
|
||||||
core_audio_utility::GetCommunicationsInputDeviceID())) ||
|
core_audio_utility::GetCommunicationsInputDeviceID())) ||
|
||||||
@ -256,13 +292,14 @@ std::string CoreAudioBase::GetDeviceID(int index) const {
|
|||||||
|
|
||||||
int CoreAudioBase::SetDevice(int index) {
|
int CoreAudioBase::SetDevice(int index) {
|
||||||
RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
|
RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
|
||||||
<< "]";
|
<< "]: index=" << IndexToString(index);
|
||||||
if (initialized_) {
|
if (initialized_) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string device_id = GetDeviceID(index);
|
std::string device_id = GetDeviceID(index);
|
||||||
RTC_DLOG(INFO) << "index=" << index << " => device_id: " << device_id;
|
RTC_DLOG(INFO) << "index=" << IndexToString(index)
|
||||||
|
<< " => device_id: " << device_id;
|
||||||
device_index_ = index;
|
device_index_ = index;
|
||||||
device_id_ = device_id;
|
device_id_ = device_id;
|
||||||
|
|
||||||
@ -273,7 +310,7 @@ int CoreAudioBase::DeviceName(int index,
|
|||||||
std::string* name,
|
std::string* name,
|
||||||
std::string* guid) const {
|
std::string* guid) const {
|
||||||
RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
|
RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
|
||||||
<< "]";
|
<< "]: index=" << IndexToString(index);
|
||||||
if (index > NumberOfEnumeratedDevices() - 1) {
|
if (index > NumberOfEnumeratedDevices() - 1) {
|
||||||
RTC_LOG(LS_ERROR) << "Invalid device index";
|
RTC_LOG(LS_ERROR) << "Invalid device index";
|
||||||
return -1;
|
return -1;
|
||||||
@ -282,6 +319,8 @@ int CoreAudioBase::DeviceName(int index,
|
|||||||
AudioDeviceNames device_names;
|
AudioDeviceNames device_names;
|
||||||
bool ok = IsInput() ? core_audio_utility::GetInputDeviceNames(&device_names)
|
bool ok = IsInput() ? core_audio_utility::GetInputDeviceNames(&device_names)
|
||||||
: core_audio_utility::GetOutputDeviceNames(&device_names);
|
: core_audio_utility::GetOutputDeviceNames(&device_names);
|
||||||
|
// Validate the index one extra time in-case the size of the generated list
|
||||||
|
// did not match NumberOfEnumeratedDevices().
|
||||||
if (!ok || static_cast<int>(device_names.size()) <= index) {
|
if (!ok || static_cast<int>(device_names.size()) <= index) {
|
||||||
RTC_LOG(LS_ERROR) << "Failed to get the device name";
|
RTC_LOG(LS_ERROR) << "Failed to get the device name";
|
||||||
return -1;
|
return -1;
|
||||||
@ -299,27 +338,26 @@ int CoreAudioBase::DeviceName(int index,
|
|||||||
bool CoreAudioBase::Init() {
|
bool CoreAudioBase::Init() {
|
||||||
RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
|
RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
|
||||||
<< "]";
|
<< "]";
|
||||||
|
RTC_DCHECK_GE(device_index_, 0);
|
||||||
RTC_DCHECK(!device_id_.empty());
|
RTC_DCHECK(!device_id_.empty());
|
||||||
RTC_DCHECK(audio_device_buffer_);
|
RTC_DCHECK(audio_device_buffer_);
|
||||||
RTC_DCHECK(!audio_client_);
|
RTC_DCHECK(!audio_client_);
|
||||||
RTC_DCHECK(!audio_session_control_.Get());
|
RTC_DCHECK(!audio_session_control_.Get());
|
||||||
|
|
||||||
// Use an existing |device_id_| and set parameters which are required to
|
// Use an existing combination of |device_index_| and |device_id_| to set
|
||||||
// create an audio client. It is up to the parent class to set |device_id_|.
|
// parameters which are required to create an audio client. It is up to the
|
||||||
// TODO(henrika): add unique information about device role since |device_id_|
|
// parent class to set |device_index_| and |device_id_|.
|
||||||
// does not uniquely identify the device and role if there is only one
|
std::string device_id = AudioDeviceName::kDefaultDeviceId;
|
||||||
// physical device.
|
ERole role = ERole();
|
||||||
std::string device_id = device_id_;
|
if (IsDefaultDevice(device_index_)) {
|
||||||
ERole role = eConsole;
|
|
||||||
if (IsDefaultDevice(device_id)) {
|
|
||||||
device_id = AudioDeviceName::kDefaultDeviceId;
|
|
||||||
role = eConsole;
|
role = eConsole;
|
||||||
} else if (IsDefaultCommunicationsDevice(device_id)) {
|
} else if (IsDefaultCommunicationsDevice(device_index_)) {
|
||||||
device_id = AudioDeviceName::kDefaultDeviceId;
|
|
||||||
role = eCommunications;
|
role = eCommunications;
|
||||||
} else {
|
} else {
|
||||||
RTC_DLOG(LS_WARNING) << "Not using a default device";
|
device_id = device_id_;
|
||||||
}
|
}
|
||||||
|
RTC_LOG(LS_INFO) << "Unique device identifier: device_id=" << device_id
|
||||||
|
<< ", role=" << RoleToString(role);
|
||||||
|
|
||||||
// Create an IAudioClient interface which enables us to create and initialize
|
// Create an IAudioClient interface which enables us to create and initialize
|
||||||
// an audio stream between an audio application and the audio engine.
|
// an audio stream between an audio application and the audio engine.
|
||||||
|
@ -117,8 +117,8 @@ class CoreAudioBase : public IAudioSessionEvents {
|
|||||||
bool IsOutput() const;
|
bool IsOutput() const;
|
||||||
bool IsDefaultDevice(int index) const;
|
bool IsDefaultDevice(int index) const;
|
||||||
bool IsDefaultCommunicationsDevice(int index) const;
|
bool IsDefaultCommunicationsDevice(int index) const;
|
||||||
bool IsDefaultDevice(const std::string& device_id) const;
|
bool IsDefaultDeviceId(const std::string& device_id) const;
|
||||||
bool IsDefaultCommunicationsDevice(const std::string& device_id) const;
|
bool IsDefaultCommunicationsDeviceId(const std::string& device_id) const;
|
||||||
EDataFlow GetDataFlow() const;
|
EDataFlow GetDataFlow() const;
|
||||||
bool IsRestarting() const;
|
bool IsRestarting() const;
|
||||||
int64_t TimeSinceStart() const;
|
int64_t TimeSinceStart() const;
|
||||||
@ -151,7 +151,7 @@ class CoreAudioBase : public IAudioSessionEvents {
|
|||||||
ScopedHandle restart_event_;
|
ScopedHandle restart_event_;
|
||||||
int64_t start_time_ = 0;
|
int64_t start_time_ = 0;
|
||||||
std::string device_id_;
|
std::string device_id_;
|
||||||
int device_index_;
|
int device_index_ = -1;
|
||||||
// Used by the IAudioSessionEvents implementations. Currently only utilized
|
// Used by the IAudioSessionEvents implementations. Currently only utilized
|
||||||
// for debugging purposes.
|
// for debugging purposes.
|
||||||
LONG ref_count_ = 1;
|
LONG ref_count_ = 1;
|
||||||
|
@ -46,13 +46,13 @@ CoreAudioInput::~CoreAudioInput() {
|
|||||||
int CoreAudioInput::Init() {
|
int CoreAudioInput::Init() {
|
||||||
RTC_DLOG(INFO) << __FUNCTION__;
|
RTC_DLOG(INFO) << __FUNCTION__;
|
||||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||||
StopRecording();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CoreAudioInput::Terminate() {
|
int CoreAudioInput::Terminate() {
|
||||||
RTC_DLOG(INFO) << __FUNCTION__;
|
RTC_DLOG(INFO) << __FUNCTION__;
|
||||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||||
|
StopRecording();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,11 +63,16 @@ int CoreAudioInput::NumDevices() const {
|
|||||||
|
|
||||||
int CoreAudioInput::SetDevice(int index) {
|
int CoreAudioInput::SetDevice(int index) {
|
||||||
RTC_DLOG(INFO) << __FUNCTION__ << ": " << index;
|
RTC_DLOG(INFO) << __FUNCTION__ << ": " << index;
|
||||||
|
RTC_DCHECK_GE(index, 0);
|
||||||
|
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||||
return CoreAudioBase::SetDevice(index);
|
return CoreAudioBase::SetDevice(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CoreAudioInput::SetDevice(AudioDeviceModule::WindowsDeviceType device) {
|
int CoreAudioInput::SetDevice(AudioDeviceModule::WindowsDeviceType device) {
|
||||||
RTC_DLOG(INFO) << __FUNCTION__ << ": " << device;
|
RTC_DLOG(INFO) << __FUNCTION__ << ": "
|
||||||
|
<< ((device == AudioDeviceModule::kDefaultDevice)
|
||||||
|
? "Default"
|
||||||
|
: "DefaultCommunication");
|
||||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||||
return SetDevice((device == AudioDeviceModule::kDefaultDevice) ? 0 : 1);
|
return SetDevice((device == AudioDeviceModule::kDefaultDevice) ? 0 : 1);
|
||||||
}
|
}
|
||||||
@ -239,7 +244,6 @@ int CoreAudioInput::RestartRecording() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CoreAudioInput::Restarting() const {
|
bool CoreAudioInput::Restarting() const {
|
||||||
RTC_DLOG(INFO) << __FUNCTION__;
|
|
||||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||||
return IsRestarting();
|
return IsRestarting();
|
||||||
}
|
}
|
||||||
|
@ -61,12 +61,16 @@ int CoreAudioOutput::NumDevices() const {
|
|||||||
|
|
||||||
int CoreAudioOutput::SetDevice(int index) {
|
int CoreAudioOutput::SetDevice(int index) {
|
||||||
RTC_DLOG(INFO) << __FUNCTION__ << ": " << index;
|
RTC_DLOG(INFO) << __FUNCTION__ << ": " << index;
|
||||||
|
RTC_DCHECK_GE(index, 0);
|
||||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||||
return CoreAudioBase::SetDevice(index);
|
return CoreAudioBase::SetDevice(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CoreAudioOutput::SetDevice(AudioDeviceModule::WindowsDeviceType device) {
|
int CoreAudioOutput::SetDevice(AudioDeviceModule::WindowsDeviceType device) {
|
||||||
RTC_DLOG(INFO) << __FUNCTION__ << ": " << device;
|
RTC_DLOG(INFO) << __FUNCTION__ << ": "
|
||||||
|
<< ((device == AudioDeviceModule::kDefaultDevice)
|
||||||
|
? "Default"
|
||||||
|
: "DefaultCommunication");
|
||||||
RTC_DCHECK_RUN_ON(&thread_checker_);
|
RTC_DCHECK_RUN_ON(&thread_checker_);
|
||||||
return SetDevice((device == AudioDeviceModule::kDefaultDevice) ? 0 : 1);
|
return SetDevice((device == AudioDeviceModule::kDefaultDevice) ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
@ -295,6 +295,13 @@ ComPtr<IMMDevice> CreateDeviceInternal(const std::string& device_id,
|
|||||||
|
|
||||||
_com_error error(S_FALSE);
|
_com_error error(S_FALSE);
|
||||||
if (device_id == AudioDeviceName::kDefaultDeviceId) {
|
if (device_id == AudioDeviceName::kDefaultDeviceId) {
|
||||||
|
// Get the default audio endpoint for the specified data-flow direction and
|
||||||
|
// role. Note that, if only a single rendering or capture device is
|
||||||
|
// available, the system always assigns all three rendering or capture roles
|
||||||
|
// to that device. If the method fails to find a rendering or capture device
|
||||||
|
// for the specified role, this means that no rendering or capture device is
|
||||||
|
// available at all. If no device is available, the method sets the output
|
||||||
|
// pointer to NULL and returns ERROR_NOT_FOUND.
|
||||||
error = device_enum->GetDefaultAudioEndpoint(
|
error = device_enum->GetDefaultAudioEndpoint(
|
||||||
data_flow, role, audio_endpoint_device.GetAddressOf());
|
data_flow, role, audio_endpoint_device.GetAddressOf());
|
||||||
if (FAILED(error.Error())) {
|
if (FAILED(error.Error())) {
|
||||||
@ -303,6 +310,8 @@ ComPtr<IMMDevice> CreateDeviceInternal(const std::string& device_id,
|
|||||||
<< ErrorToString(error);
|
<< ErrorToString(error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Ask for an audio endpoint device that is identified by an endpoint ID
|
||||||
|
// string.
|
||||||
error = device_enum->GetDevice(rtc::ToUtf16(device_id).c_str(),
|
error = device_enum->GetDevice(rtc::ToUtf16(device_id).c_str(),
|
||||||
audio_endpoint_device.GetAddressOf());
|
audio_endpoint_device.GetAddressOf());
|
||||||
if (FAILED(error.Error())) {
|
if (FAILED(error.Error())) {
|
||||||
@ -313,7 +322,7 @@ ComPtr<IMMDevice> CreateDeviceInternal(const std::string& device_id,
|
|||||||
|
|
||||||
// Verify that the audio endpoint device is active, i.e., that the audio
|
// Verify that the audio endpoint device is active, i.e., that the audio
|
||||||
// adapter that connects to the endpoint device is present and enabled.
|
// adapter that connects to the endpoint device is present and enabled.
|
||||||
if (SUCCEEDED(error.Error()) &&
|
if (SUCCEEDED(error.Error()) && !audio_endpoint_device.Get() &&
|
||||||
!IsDeviceActive(audio_endpoint_device.Get())) {
|
!IsDeviceActive(audio_endpoint_device.Get())) {
|
||||||
RTC_LOG(LS_WARNING) << "Selected endpoint device is not active";
|
RTC_LOG(LS_WARNING) << "Selected endpoint device is not active";
|
||||||
audio_endpoint_device.Reset();
|
audio_endpoint_device.Reset();
|
||||||
@ -463,77 +472,124 @@ ComPtr<IMMDeviceCollection> CreateCollectionInternal(EDataFlow data_flow) {
|
|||||||
|
|
||||||
bool GetDeviceNamesInternal(EDataFlow data_flow,
|
bool GetDeviceNamesInternal(EDataFlow data_flow,
|
||||||
webrtc::AudioDeviceNames* device_names) {
|
webrtc::AudioDeviceNames* device_names) {
|
||||||
// Always add the default device in index 0 and the default communication
|
RTC_DLOG(LS_INFO) << "GetDeviceNamesInternal: flow="
|
||||||
// device as index 1 in the vector. The name of the default device starts
|
<< FlowToString(data_flow);
|
||||||
// with "Default - " and the default communication device starts with
|
|
||||||
// "Communication - ".
|
// Generate a collection of active audio endpoint devices for the specified
|
||||||
// Example of friendly name: "Default - Headset (SB Arena Headset)"
|
// direction.
|
||||||
ERole role[] = {eConsole, eCommunications};
|
ComPtr<IMMDeviceCollection> collection = CreateCollectionInternal(data_flow);
|
||||||
|
if (!collection.Get()) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Failed to create a collection of active devices";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the number of active (present, not disabled and plugged in) audio
|
||||||
|
// devices for the specified direction.
|
||||||
|
UINT number_of_active_devices = 0;
|
||||||
|
_com_error error = collection->GetCount(&number_of_active_devices);
|
||||||
|
if (FAILED(error.Error())) {
|
||||||
|
RTC_LOG(LS_ERROR) << "IMMDeviceCollection::GetCount failed: "
|
||||||
|
<< ErrorToString(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (number_of_active_devices == 0) {
|
||||||
|
RTC_DLOG(LS_WARNING) << "Found no active devices";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop over all active devices and add friendly name and unique id to the
|
||||||
|
// |device_names| queue. For now, devices are added at indexes 0, 1, ..., N-1
|
||||||
|
// but they will be moved to 2,3,..., N+1 at the next stage when default and
|
||||||
|
// default communication devices are added at index 0 and 1.
|
||||||
|
ComPtr<IMMDevice> audio_device;
|
||||||
|
for (UINT i = 0; i < number_of_active_devices; ++i) {
|
||||||
|
// Retrieve a pointer to the specified item in the device collection.
|
||||||
|
error = collection->Item(i, audio_device.GetAddressOf());
|
||||||
|
if (FAILED(error.Error())) {
|
||||||
|
// Skip this item and try to get the next item instead; will result in an
|
||||||
|
// incomplete list of devices.
|
||||||
|
RTC_LOG(LS_WARNING) << "IMMDeviceCollection::Item failed: "
|
||||||
|
<< ErrorToString(error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!audio_device.Get()) {
|
||||||
|
RTC_LOG(LS_WARNING) << "Invalid audio device";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the complete device name for the given audio device endpoint.
|
||||||
|
AudioDeviceName device_name(
|
||||||
|
GetDeviceFriendlyNameInternal(audio_device.Get()),
|
||||||
|
GetDeviceIdInternal(audio_device.Get()));
|
||||||
|
// Add combination of user-friendly and unique name to the output list.
|
||||||
|
device_names->push_back(device_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log a warning of the list of device is not complete but let's keep on
|
||||||
|
// trying to add default and default communications device at the front.
|
||||||
|
if (device_names->size() != number_of_active_devices) {
|
||||||
|
RTC_DLOG(LS_WARNING)
|
||||||
|
<< "List of device names does not contain all active devices";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid adding default and default communication devices if no active device
|
||||||
|
// could be added to the queue. We might as well break here and return false
|
||||||
|
// since no active devices were identified.
|
||||||
|
if (device_names->empty()) {
|
||||||
|
RTC_DLOG(LS_ERROR) << "List of active devices is empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend the queue with two more elements: one for the default device and
|
||||||
|
// one for the default communication device (can correspond to the same unique
|
||||||
|
// id if only one active device exists). The first element (index 0) is the
|
||||||
|
// default device and the second element (index 1) is the default
|
||||||
|
// communication device.
|
||||||
|
ERole role[] = {eCommunications, eConsole};
|
||||||
ComPtr<IMMDevice> default_device;
|
ComPtr<IMMDevice> default_device;
|
||||||
AudioDeviceName default_device_name;
|
AudioDeviceName default_device_name;
|
||||||
for (size_t i = 0; i < arraysize(role); ++i) {
|
for (size_t i = 0; i < arraysize(role); ++i) {
|
||||||
default_device = CreateDeviceInternal(AudioDeviceName::kDefaultDeviceId,
|
default_device = CreateDeviceInternal(AudioDeviceName::kDefaultDeviceId,
|
||||||
data_flow, role[i]);
|
data_flow, role[i]);
|
||||||
if (!default_device.Get()) {
|
if (!default_device.Get()) {
|
||||||
return false;
|
// Add empty strings to device name if the device could not be created.
|
||||||
|
RTC_DLOG(LS_WARNING) << "Failed to add device with role: "
|
||||||
|
<< RoleToString(role[i]);
|
||||||
|
default_device_name.device_name = std::string();
|
||||||
|
default_device_name.unique_id = std::string();
|
||||||
|
} else {
|
||||||
|
// Populate the device name with friendly name and unique id.
|
||||||
|
std::string device_name;
|
||||||
|
device_name += (role[i] == eConsole ? "Default - " : "Communication - ");
|
||||||
|
device_name += GetDeviceFriendlyNameInternal(default_device.Get());
|
||||||
|
std::string unique_id = GetDeviceIdInternal(default_device.Get());
|
||||||
|
default_device_name.device_name = std::move(device_name);
|
||||||
|
default_device_name.unique_id = std::move(unique_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string device_name;
|
// Add combination of user-friendly and unique name to the output queue.
|
||||||
device_name += (role[i] == eConsole ? "Default - " : "Communication - ");
|
// The last element (<=> eConsole) will be at the front of the queue, hence
|
||||||
device_name += GetDeviceFriendlyNameInternal(default_device.Get());
|
// at index 0. Empty strings will be added for cases where no default
|
||||||
std::string unique_id = GetDeviceIdInternal(default_device.Get());
|
// devices were found.
|
||||||
|
device_names->push_front(default_device_name);
|
||||||
default_device_name.device_name = std::move(device_name);
|
|
||||||
default_device_name.unique_id = std::move(unique_id);
|
|
||||||
RTC_DLOG(INFO) << "friendly name: " << default_device_name.device_name;
|
|
||||||
RTC_DLOG(INFO) << "unique id : " << default_device_name.unique_id;
|
|
||||||
// Add combination of user-friendly and unique name to the output list.
|
|
||||||
device_names->emplace_back(default_device_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next, add all active input devices on index 2 and above. Note that,
|
// Example of log output when only one device is active. Note that the queue
|
||||||
// one device can have more than one role. Hence, if only one input device
|
// contains two extra elements at index 0 (Default) and 1 (Communication) to
|
||||||
// is present, the output vector will contain three elements all with the
|
// allow selection of device by role instead of id. All elements corresponds
|
||||||
// same unique ID but with different names.
|
// the same unique id.
|
||||||
// Example (one capture device but three elements in device_names):
|
// [0] friendly name: Default - Headset Microphone (2- Arctis 7 Chat)
|
||||||
// 0: friendly name: Default - Headset (SB Arena Headset)
|
// [0] unique id : {0.0.1.00000000}.{ff9eed76-196e-467a-b295-26986e69451c}
|
||||||
// 0: unique id : {0.0.1.00000000}.{822d99bb-d9b0-4f6f-b2a5-cd1be220d338}
|
// [1] friendly name: Communication - Headset Microphone (2- Arctis 7 Chat)
|
||||||
// 1: friendly name: Communication - Headset (SB Arena Headset)
|
// [1] unique id : {0.0.1.00000000}.{ff9eed76-196e-467a-b295-26986e69451c}
|
||||||
// 1: unique id : {0.0.1.00000000}.{822d99bb-d9b0-4f6f-b2a5-cd1be220d338}
|
// [2] friendly name: Headset Microphone (2- Arctis 7 Chat)
|
||||||
// 2: friendly name: Headset (SB Arena Headset)
|
// [2] unique id : {0.0.1.00000000}.{ff9eed76-196e-467a-b295-26986e69451c}
|
||||||
// 2: unique id : {0.0.1.00000000}.{822d99bb-d9b0-4f6f-b2a5-cd1be220d338}
|
for (size_t i = 0; i < device_names->size(); ++i) {
|
||||||
|
RTC_DLOG(INFO) << "[" << i
|
||||||
// Generate a collection of active audio endpoint devices for the specified
|
<< "] friendly name: " << (*device_names)[i].device_name;
|
||||||
// direction.
|
RTC_DLOG(INFO) << "[" << i
|
||||||
ComPtr<IMMDeviceCollection> collection = CreateCollectionInternal(data_flow);
|
<< "] unique id : " << (*device_names)[i].unique_id;
|
||||||
if (!collection.Get()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the number of active audio devices for the specified direction.
|
|
||||||
UINT number_of_active_devices = 0;
|
|
||||||
collection->GetCount(&number_of_active_devices);
|
|
||||||
if (number_of_active_devices == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop over all active devices and add friendly name and unique ID to the
|
|
||||||
// |device_names| list which already contains two elements
|
|
||||||
RTC_DCHECK_EQ(device_names->size(), 2);
|
|
||||||
for (UINT i = 0; i < number_of_active_devices; ++i) {
|
|
||||||
// Retrieve a pointer to the specified item in the device collection.
|
|
||||||
ComPtr<IMMDevice> audio_device;
|
|
||||||
_com_error error = collection->Item(i, audio_device.GetAddressOf());
|
|
||||||
if (FAILED(error.Error()))
|
|
||||||
continue;
|
|
||||||
// Retrieve the complete device name for the given audio device endpoint.
|
|
||||||
AudioDeviceName device_name(
|
|
||||||
GetDeviceFriendlyNameInternal(audio_device.Get()),
|
|
||||||
GetDeviceIdInternal(audio_device.Get()));
|
|
||||||
RTC_DLOG(INFO) << "friendly name: " << device_name.device_name;
|
|
||||||
RTC_DLOG(INFO) << "unique id : " << device_name.unique_id;
|
|
||||||
// Add combination of user-friendly and unique name to the output list.
|
|
||||||
device_names->emplace_back(device_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -741,12 +797,14 @@ EDataFlow GetDataFlow(IMMDevice* device) {
|
|||||||
bool GetInputDeviceNames(webrtc::AudioDeviceNames* device_names) {
|
bool GetInputDeviceNames(webrtc::AudioDeviceNames* device_names) {
|
||||||
RTC_DLOG(INFO) << "GetInputDeviceNames";
|
RTC_DLOG(INFO) << "GetInputDeviceNames";
|
||||||
RTC_DCHECK(device_names);
|
RTC_DCHECK(device_names);
|
||||||
|
RTC_DCHECK(device_names->empty());
|
||||||
return GetDeviceNamesInternal(eCapture, device_names);
|
return GetDeviceNamesInternal(eCapture, device_names);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetOutputDeviceNames(webrtc::AudioDeviceNames* device_names) {
|
bool GetOutputDeviceNames(webrtc::AudioDeviceNames* device_names) {
|
||||||
RTC_DLOG(INFO) << "GetOutputDeviceNames";
|
RTC_DLOG(INFO) << "GetOutputDeviceNames";
|
||||||
RTC_DCHECK(device_names);
|
RTC_DCHECK(device_names);
|
||||||
|
RTC_DCHECK(device_names->empty());
|
||||||
return GetDeviceNamesInternal(eRender, device_names);
|
return GetDeviceNamesInternal(eRender, device_names);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user