|
41 | 41 | #import "components/audio/RTCNativeAudioSessionDelegateAdapter.h" |
42 | 42 | #endif |
43 | 43 |
|
| 44 | +#if TARGET_OS_OSX |
| 45 | +#import "./mac/audio_device_utils_mac.h" |
| 46 | +#endif |
| 47 | + |
44 | 48 | namespace webrtc { |
45 | 49 |
|
46 | 50 | #define LOGI() RTC_LOG(LS_INFO) << "AudioEngineDevice::" |
|
122 | 126 | record_parameters_.reset(config.sampleRate, config.inputNumberOfChannels); |
123 | 127 | #endif |
124 | 128 |
|
| 129 | +#if TARGET_OS_OSX |
| 130 | + UpdateDeviceInformation(); |
| 131 | + engine_state_.output_device_id = |
| 132 | + mac_audio_utils::GetDefaultOutputDeviceID().value_or(kAudioObjectUnknown); |
| 133 | + engine_state_.input_device_id = |
| 134 | + mac_audio_utils::GetDefaultInputDeviceID().value_or(kAudioObjectUnknown); |
| 135 | +#endif |
| 136 | + |
125 | 137 | initialized_ = true; |
126 | 138 | return 0; |
127 | 139 | } |
|
557 | 569 | } |
558 | 570 |
|
559 | 571 | int32_t AudioEngineDevice::SetPlayoutDevice(uint16_t index) { |
560 | | - LOGW() << "SetPlayoutDevice: Not implemented, value: " << index; |
| 572 | + LOGW() << "SetPlayoutDevice value: " << index; |
| 573 | + RTC_DCHECK_RUN_ON(thread_); |
| 574 | + |
| 575 | +#if TARGET_OS_OSX |
| 576 | + if (index > (output_device_ids_.size() - 1)) { |
| 577 | + LOGE() << "Device index is out of range: " << index; |
| 578 | + return -1; |
| 579 | + } |
| 580 | + |
| 581 | + AudioDeviceID output_device_id = output_device_ids_[index]; |
| 582 | + |
| 583 | + SetEngineState([output_device_id](EngineState state) -> EngineState { |
| 584 | + state.output_device_id = output_device_id; |
| 585 | + return state; |
| 586 | + }); |
| 587 | +#endif |
561 | 588 |
|
562 | 589 | return 0; |
563 | 590 | } |
|
567 | 594 |
|
568 | 595 | return -1; |
569 | 596 | } |
570 | | - |
571 | 597 | int32_t AudioEngineDevice::PlayoutDeviceName(uint16_t index, char name[kAdmMaxDeviceNameSize], |
572 | 598 | char guid[kAdmMaxGuidSize]) { |
573 | | - // LOGW() << "PlayoutDeviceName: Not implemented"; |
| 599 | + RTC_DCHECK_RUN_ON(thread_); |
| 600 | + |
| 601 | +#if TARGET_OS_OSX |
| 602 | + RTC_DCHECK(output_device_ids_.size() == output_device_labels_.size()); |
| 603 | + |
| 604 | + if ((index > (output_device_ids_.size() - 1)) || (name == NULL)) { |
| 605 | + LOGE() << "Device index is out of range: " << index; |
| 606 | + return -1; |
| 607 | + } |
| 608 | + |
| 609 | + memset(name, 0, kAdmMaxDeviceNameSize); |
| 610 | + memset(guid, 0, kAdmMaxGuidSize); |
574 | 611 |
|
| 612 | + // Get device name |
| 613 | + strncpy(name, output_device_labels_[index].c_str(), kAdmMaxDeviceNameSize - 1); |
| 614 | + |
| 615 | + std::optional<std::string> device_guid = |
| 616 | + mac_audio_utils::GetDeviceUniqueID(output_device_ids_[index]); |
| 617 | + if (device_guid) { |
| 618 | + strncpy(guid, device_guid->c_str(), kAdmMaxGuidSize - 1); |
| 619 | + } else { |
| 620 | + LOGE() << "Failed to get device unique ID for device: " << output_device_ids_[index]; |
| 621 | + return -1; |
| 622 | + } |
| 623 | + |
| 624 | + return 0; |
| 625 | +#else |
575 | 626 | return -1; |
| 627 | +#endif |
576 | 628 | } |
577 | 629 |
|
578 | 630 | int16_t AudioEngineDevice::PlayoutDevices() { |
579 | | - // LOGI() << "PlayoutDevices"; |
| 631 | + RTC_DCHECK_RUN_ON(thread_); |
580 | 632 |
|
| 633 | +#if TARGET_OS_OSX |
| 634 | + return output_device_ids_.size(); |
| 635 | +#else |
581 | 636 | return (int16_t)1; |
| 637 | +#endif |
582 | 638 | } |
583 | 639 |
|
584 | 640 | // ---------------------------------------------------------------------------------------------------- |
585 | 641 | // Recording Device |
586 | 642 |
|
587 | 643 | int32_t AudioEngineDevice::RecordingDeviceName(uint16_t index, char name[kAdmMaxDeviceNameSize], |
588 | 644 | char guid[kAdmMaxGuidSize]) { |
589 | | - // LOGW() << "RecordingDeviceName"; |
| 645 | +#if TARGET_OS_OSX |
| 646 | + RTC_DCHECK(input_device_ids_.size() == input_device_labels_.size()); |
| 647 | + |
| 648 | + if ((index > (input_device_ids_.size() - 1)) || (name == NULL)) { |
| 649 | + LOGE() << "Device index is out of range: " << index; |
| 650 | + return -1; |
| 651 | + } |
| 652 | + |
| 653 | + memset(name, 0, kAdmMaxDeviceNameSize); |
| 654 | + memset(guid, 0, kAdmMaxGuidSize); |
| 655 | + |
| 656 | + // Get device name |
| 657 | + strncpy(name, input_device_labels_[index].c_str(), kAdmMaxDeviceNameSize - 1); |
| 658 | + |
| 659 | + std::optional<std::string> device_guid = |
| 660 | + mac_audio_utils::GetDeviceUniqueID(input_device_ids_[index]); |
| 661 | + if (device_guid) { |
| 662 | + strncpy(guid, device_guid->c_str(), kAdmMaxGuidSize - 1); |
| 663 | + } else { |
| 664 | + LOGE() << "Failed to get device unique ID for device: " << input_device_ids_[index]; |
| 665 | + return -1; |
| 666 | + } |
590 | 667 |
|
| 668 | + return 0; |
| 669 | +#else |
591 | 670 | return -1; |
| 671 | +#endif |
592 | 672 | } |
593 | 673 |
|
594 | 674 | int32_t AudioEngineDevice::SetRecordingDevice(uint16_t index) { |
595 | 675 | LOGI() << "SetRecordingDevice, index: " << index; |
| 676 | + RTC_DCHECK_RUN_ON(thread_); |
| 677 | + |
| 678 | +#if TARGET_OS_OSX |
| 679 | + if (index > (input_device_ids_.size() - 1)) { |
| 680 | + RTC_LOG(LS_ERROR) << "Device index is out of range"; |
| 681 | + return -1; |
| 682 | + } |
| 683 | + |
| 684 | + AudioDeviceID input_device_id = input_device_ids_[index]; |
| 685 | + |
| 686 | + SetEngineState([input_device_id](EngineState state) -> EngineState { |
| 687 | + state.input_device_id = input_device_id; |
| 688 | + return state; |
| 689 | + }); |
| 690 | +#endif |
596 | 691 |
|
597 | 692 | return 0; |
598 | 693 | } |
|
615 | 710 | } |
616 | 711 |
|
617 | 712 | int16_t AudioEngineDevice::RecordingDevices() { |
618 | | - // LOGI() << "RecordingDevices"; |
| 713 | + RTC_DCHECK_RUN_ON(thread_); |
619 | 714 |
|
| 715 | +#if TARGET_OS_OSX |
| 716 | + return input_device_ids_.size(); |
| 717 | +#else |
620 | 718 | return (int16_t)1; |
| 719 | +#endif |
621 | 720 | } |
622 | 721 |
|
623 | 722 | // |
|
1143 | 1242 | RTC_DCHECK(engine_manual_input_ == nullptr); |
1144 | 1243 |
|
1145 | 1244 | if (state.prev.IsAnyRunning() && |
1146 | | - (!state.next.IsAnyRunning() || state.DidUpdateAudioGraph() || state.DidBeginInterruption() || |
1147 | | - state.IsEngineRecreateRequired())) { |
| 1245 | + (!state.next.IsAnyRunning() || state.IsEngineRestartRequired() || |
| 1246 | + state.DidBeginInterruption() || state.IsEngineRecreateRequired())) { |
1148 | 1247 | LOGI() << "Stopping AVAudioEngine..."; |
1149 | 1248 | RTC_DCHECK(engine_device_ != nil); |
1150 | 1249 |
|
|
1454 | 1553 | this->InputNode().voiceProcessingAGCEnabled = state.next.voice_processing_agc_enabled; |
1455 | 1554 | } |
1456 | 1555 |
|
| 1556 | +#if TARGET_OS_OSX |
| 1557 | + |
| 1558 | + if (state.next.IsAnyEnabled() && |
| 1559 | + (!state.prev.IsAnyEnabled() || state.IsEngineRecreateRequired() || |
| 1560 | + state.DidUpdateInputDevice() || state.DidUpdateOutputDevice())) { |
| 1561 | + if (state.next.IsInputEnabled() && state.next.input_device_id != 0) { |
| 1562 | + // Input device selection |
| 1563 | + OSStatus err = noErr; |
| 1564 | + uint32_t input_deviceId = state.next.input_device_id; |
| 1565 | + LOGI() << "Setting input device: " << input_deviceId; |
| 1566 | + AudioUnit inputUnit = this->InputNode().audioUnit; |
| 1567 | + err = |
| 1568 | + AudioUnitSetProperty(inputUnit, kAudioOutputUnitProperty_CurrentDevice, |
| 1569 | + kAudioUnitScope_Global, 1, &input_deviceId, sizeof(input_deviceId)); |
| 1570 | + if (err != noErr) { |
| 1571 | + LOGE() << "Failed to set input device: " << input_deviceId; |
| 1572 | + } |
| 1573 | + } |
| 1574 | + |
| 1575 | + if (state.next.IsOutputEnabled() && state.next.output_device_id != 0) { |
| 1576 | + // Output device selection |
| 1577 | + OSStatus err = noErr; |
| 1578 | + uint32_t output_deviceId = state.next.output_device_id; |
| 1579 | + LOGI() << "Setting output device: " << output_deviceId; |
| 1580 | + AudioUnit outputUnit = this->OutputNode().audioUnit; |
| 1581 | + err = AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_CurrentDevice, |
| 1582 | + kAudioUnitScope_Global, 0, &output_deviceId, |
| 1583 | + sizeof(output_deviceId)); |
| 1584 | + if (err != noErr) { |
| 1585 | + LOGE() << "Failed to set output device: " << output_deviceId; |
| 1586 | + } |
| 1587 | + } |
| 1588 | + } |
| 1589 | + |
| 1590 | +#endif |
| 1591 | + |
1457 | 1592 | // Start playout buffer if output is running |
1458 | 1593 | if (state.next.IsOutputEnabled() && !audio_device_buffer_->IsPlaying()) { |
1459 | 1594 | if (engine_device_ != nullptr) { |
|
1477 | 1612 | } |
1478 | 1613 |
|
1479 | 1614 | if (state.next.IsAnyRunning()) { |
1480 | | - if (!state.prev.IsAnyRunning() || state.DidEndInterruption() || state.DidUpdateAudioGraph() || |
1481 | | - state.IsEngineRecreateRequired()) { |
| 1615 | + if (!state.prev.IsAnyRunning() || state.DidEndInterruption() || |
| 1616 | + state.IsEngineRestartRequired() || state.IsEngineRecreateRequired()) { |
1482 | 1617 | if (observer_ != nullptr) { |
1483 | 1618 | observer_->OnEngineWillStart(engine_device_, state.next.IsOutputEnabled(), |
1484 | 1619 | state.next.IsInputEnabled()); |
|
1601 | 1736 | } |
1602 | 1737 | } |
1603 | 1738 |
|
| 1739 | +// ---------------------------------------------------------------------------------------------------- |
| 1740 | +// Private - Device access |
| 1741 | + |
| 1742 | +#if TARGET_OS_OSX |
| 1743 | + |
| 1744 | +void AudioEngineDevice::UpdateDeviceInformation() { |
| 1745 | + using namespace webrtc::mac_audio_utils; |
| 1746 | + |
| 1747 | + input_device_ids_.clear(); |
| 1748 | + output_device_ids_.clear(); |
| 1749 | + input_device_labels_.clear(); |
| 1750 | + output_device_labels_.clear(); |
| 1751 | + |
| 1752 | + std::vector<AudioObjectID> all_device_ids = GetAllAudioDeviceIDs(); |
| 1753 | + |
| 1754 | + for (AudioObjectID device_id : all_device_ids) { |
| 1755 | + if (IsInputDevice(device_id)) { |
| 1756 | + input_device_ids_.push_back(device_id); |
| 1757 | + auto label = GetDeviceLabel(device_id, true); |
| 1758 | + if (label) { |
| 1759 | + input_device_labels_.push_back(*label); |
| 1760 | + } else { |
| 1761 | + input_device_labels_.push_back("Unknown Input Device"); |
| 1762 | + } |
| 1763 | + } |
| 1764 | + |
| 1765 | + if (IsOutputDevice(device_id)) { |
| 1766 | + output_device_ids_.push_back(device_id); |
| 1767 | + auto label = GetDeviceLabel(device_id, false); |
| 1768 | + if (label) { |
| 1769 | + output_device_labels_.push_back(*label); |
| 1770 | + } else { |
| 1771 | + output_device_labels_.push_back("Unknown Output Device"); |
| 1772 | + } |
| 1773 | + } |
| 1774 | + } |
| 1775 | +} |
| 1776 | + |
| 1777 | +#endif |
| 1778 | + |
1604 | 1779 | // ---------------------------------------------------------------------------------------------------- |
1605 | 1780 | // Private - Debug |
1606 | 1781 |
|
|
0 commit comments