init
Some checks failed
Docker. / Ubuntu (push) Has been cancelled
User-agent updater. / User-agent (push) Failing after 15s
Lock Threads / lock (push) Failing after 10s
Waiting for answer. / waiting-for-answer (push) Failing after 22s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
Close stale issues and PRs / stale (push) Has been cancelled

This commit is contained in:
allhaileris
2026-02-16 15:50:16 +03:00
commit afb81b8278
13816 changed files with 3689732 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "webrtc/platform/linux/webrtc_environment_linux.h"
#include "base/debug_log.h"
#ifdef WEBRTC_USE_PIPEWIRE
#include <modules/desktop_capture/linux/wayland/base_capturer_pipewire.h>
#include <modules/portal/pipewire_utils.h>
#endif // WEBRTC_USE_PIPEWIRE
namespace Webrtc::Platform {
EnvironmentLinux::EnvironmentLinux(not_null<EnvironmentDelegate*> delegate)
: _audioFallback(delegate)
, _cameraFallback(delegate) {
#ifdef WEBRTC_USE_PIPEWIRE
if (!webrtc::InitializePipeWire()) {
LOG(("Audio Info: Failed to load pipewire 0.3 stubs."));
}
#endif // WEBRTC_USE_PIPEWIRE
}
EnvironmentLinux::~EnvironmentLinux() {
}
QString EnvironmentLinux::defaultId(DeviceType type) {
return (type == DeviceType::Camera)
? _cameraFallback.defaultId(type)
: _audioFallback.defaultId(type);
}
DeviceInfo EnvironmentLinux::device(DeviceType type, const QString &id) {
return (type == DeviceType::Camera)
? _cameraFallback.device(type, id)
: _audioFallback.device(type, id);
}
std::vector<DeviceInfo> EnvironmentLinux::devices(DeviceType type) {
return (type == DeviceType::Camera)
? _cameraFallback.devices(type)
: _audioFallback.devices(type);
}
bool EnvironmentLinux::refreshFullListOnChange(DeviceType type) {
return (type == DeviceType::Camera)
? _cameraFallback.refreshFullListOnChange(type)
: _audioFallback.refreshFullListOnChange(type);
}
bool EnvironmentLinux::desktopCaptureAllowed() const {
return true;
}
std::optional<QString> EnvironmentLinux::uniqueDesktopCaptureSource() const {
#ifdef WEBRTC_USE_PIPEWIRE
if (webrtc::DesktopCapturer::IsRunningUnderWayland()) {
return u"desktop_capturer_pipewire"_q;
}
#endif // WEBRTC_USE_PIPEWIRE
return std::nullopt;
}
void EnvironmentLinux::defaultIdRequested(DeviceType type) {
if (type == DeviceType::Camera) {
_cameraFallback.defaultIdRequested(type);
} else {
_audioFallback.defaultIdRequested(type);
}
}
void EnvironmentLinux::devicesRequested(DeviceType type) {
if (type == DeviceType::Camera) {
_cameraFallback.devicesRequested(type);
} else {
_audioFallback.devicesRequested(type);
}
}
DeviceResolvedId EnvironmentLinux::threadSafeResolveId(
const DeviceResolvedId &lastResolvedId,
const QString &savedId) {
return (lastResolvedId.type == DeviceType::Camera)
? _cameraFallback.threadSafeResolveId(lastResolvedId, savedId)
: _audioFallback.threadSafeResolveId(lastResolvedId, savedId);
}
std::unique_ptr<Environment> CreateEnvironment(
not_null<EnvironmentDelegate*> delegate) {
return std::make_unique<EnvironmentLinux>(delegate);
}
} // namespace Webrtc::Platform

View File

@@ -0,0 +1,43 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "webrtc/platform/webrtc_platform_environment.h"
#include "webrtc/details/webrtc_environment_openal.h"
#include "webrtc/details/webrtc_environment_video_capture.h"
namespace Webrtc::Platform {
class EnvironmentLinux final : public Environment {
public:
explicit EnvironmentLinux(not_null<EnvironmentDelegate*> delegate);
~EnvironmentLinux();
QString defaultId(DeviceType type) override;
DeviceInfo device(DeviceType type, const QString &id) override;
std::vector<DeviceInfo> devices(DeviceType type) override;
bool refreshFullListOnChange(DeviceType type) override;
bool desktopCaptureAllowed() const override;
std::optional<QString> uniqueDesktopCaptureSource() const override;
void defaultIdRequested(DeviceType type) override;
void devicesRequested(DeviceType type) override;
DeviceResolvedId threadSafeResolveId(
const DeviceResolvedId &lastResolvedId,
const QString &savedId) override;
private:
details::EnvironmentOpenAL _audioFallback;
details::EnvironmentVideoCapture _cameraFallback;
};
} // namespace Webrtc::Platform

View File

@@ -0,0 +1,85 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "base/timer.h"
#include "base/weak_ptr.h"
#include "webrtc/platform/webrtc_platform_environment.h"
#include <media/engine/webrtc_media_engine.h>
namespace webrtc {
template <class T>
class scoped_refptr;
} // namespace webrtc
namespace rtc {
template <typename T>
using scoped_refptr = webrtc::scoped_refptr<T>;
} // namespace rtc
namespace webrtc {
class TaskQueueFactory;
class AudioDeviceModule;
} // namespace webrtc
namespace Webrtc::Platform {
class EnvironmentMac final : public Environment, public base::has_weak_ptr {
public:
explicit EnvironmentMac(not_null<EnvironmentDelegate*> delegate);
~EnvironmentMac();
QString defaultId(DeviceType type) override;
DeviceInfo device(DeviceType type, const QString &id) override;
std::vector<DeviceInfo> devices(DeviceType type) override;
bool refreshFullListOnChange(DeviceType type) override;
bool desktopCaptureAllowed() const override;
std::optional<QString> uniqueDesktopCaptureSource() const override;
void defaultIdRequested(DeviceType type) override;
void devicesRequested(DeviceType type) override;
void setCaptureMuted(bool muted) override;
void setCaptureMuteTracker(
not_null<CaptureMuteTracker*> tracker,
bool track) override;
void defaultPlaybackDeviceChanged();
void defaultCaptureDeviceChanged();
void audioDeviceListChanged();
[[nodiscard]] static QString DefaultId(DeviceType type);
private:
void captureMuteSubscribe();
void captureMuteUnsubscribe();
void captureMuteRestartAdm();
void sendCaptureMutedValue();
const not_null<EnvironmentDelegate*> _delegate;
base::Timer _captureMuteDebounceTimer;
CaptureMuteTracker *_captureMuteTracker = nullptr;
bool _captureMuteNotification = false;
bool _captureMuted = false;
std::unique_ptr<webrtc::TaskQueueFactory> _admTaskQueueFactory;
rtc::scoped_refptr<webrtc::AudioDeviceModule> _adm;
Fn<void(DeviceResolvedId)> _admSetDeviceIdCallback;
DeviceResolvedId _admCaptureDeviceId;
rpl::lifetime _captureMuteTrackerLifetime;
rpl::lifetime _captureMuteSubscriptionLifetime;
rpl::lifetime _lifetime;
};
} // namespace Webrtc::Platform

View File

@@ -0,0 +1,691 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "webrtc/platform/mac/webrtc_environment_mac.h"
#include "base/weak_ptr.h"
#include "webrtc/webrtc_environment.h"
#include "webrtc/webrtc_create_adm.h"
#include <modules/audio_device/include/audio_device_defines.h>
#include <modules/audio_device/include/audio_device.h>
#include <api/task_queue/default_task_queue_factory.h>
#import <AVFoundation/AVFoundation.h>
#import <IOKit/hidsystem/IOHIDLib.h>
#import <CoreAudio/CoreAudio.h>
#import <Cocoa/Cocoa.h>
@interface InputMuteObserver : NSObject {
}
- (id) init;
- (void) inputMuteStateChange:(NSNotification *)aNotification;
@end // @interface InputMuteObserver
@implementation InputMuteObserver {
}
- (id) init {
if (self = [super init]) {
}
return self;
}
- (void) inputMuteStateChange:(NSNotification *)aNotification {
}
@end // @implementation InputMuteObserver
namespace Webrtc::Platform {
namespace {
constexpr auto kMaxNameLength = 256;
constexpr auto kCaptureMuteDebounceTimeout = crl::time(300);
class EmptyCallback final : public webrtc::AudioTransport {
public:
int32_t RecordedDataIsAvailable(
const void* audioSamples,
const size_t nSamples,
const size_t nBytesPerSample,
const size_t nChannels,
const uint32_t samplesPerSec,
const uint32_t totalDelayMS,
const int32_t clockDrift,
const uint32_t currentMicLevel,
const bool keyPressed,
uint32_t& newMicLevel) override {
return 0;
}
// Implementation has to setup safe values for all specified out parameters.
int32_t NeedMorePlayData(
const size_t nSamples,
const size_t nBytesPerSample,
const size_t nChannels,
const uint32_t samplesPerSec,
void* audioSamples,
size_t& nSamplesOut, // NOLINT
int64_t* elapsed_time_ms,
int64_t* ntp_time_ms) override {
nSamplesOut = 0;
return 0;
}
void PullRenderData(
int bits_per_sample,
int sample_rate,
size_t number_of_channels,
size_t number_of_frames,
void* audio_data,
int64_t* elapsed_time_ms,
int64_t* ntp_time_ms) override {
}
};
class PropertyMonitor {
public:
using Method = void (EnvironmentMac::*)();
PropertyMonitor(
AudioObjectPropertyAddress address,
Method method);
void registerEnvironment(not_null<EnvironmentMac*> environment);
void unregisterEnvironment(not_null<EnvironmentMac*> environment);
private:
void subscribe();
void unsubscribe();
void process();
static OSStatus Callback(
AudioObjectID inObjectID,
UInt32 inNumberAddresses,
const AudioObjectPropertyAddress *inAddresses,
void *inClientData);
const AudioObjectPropertyAddress _address = {};
Method _method = nullptr;
std::vector<not_null<EnvironmentMac*>> _list;
};
auto DefaultPlaybackDeviceChangedMonitor = PropertyMonitor({
.mSelector = kAudioHardwarePropertyDefaultOutputDevice,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMain,
}, &EnvironmentMac::defaultPlaybackDeviceChanged);
auto DefaultCaptureDeviceChangedMonitor = PropertyMonitor({
.mSelector = kAudioHardwarePropertyDefaultInputDevice,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMain,
}, &EnvironmentMac::defaultCaptureDeviceChanged);
auto AudioDeviceListChangedMonitor = PropertyMonitor({
.mSelector = kAudioHardwarePropertyDevices,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMain,
}, &EnvironmentMac::audioDeviceListChanged);
PropertyMonitor::PropertyMonitor(
AudioObjectPropertyAddress address,
Method method)
: _address(address)
, _method(method) {
}
void PropertyMonitor::registerEnvironment(
not_null<EnvironmentMac*> environment) {
if (empty(_list)) {
subscribe();
}
_list.push_back(environment);
}
void PropertyMonitor::unregisterEnvironment(
not_null<EnvironmentMac*> environment) {
_list.erase(ranges::remove(_list, environment), end(_list));
if (empty(_list)) {
unsubscribe();
}
}
void PropertyMonitor::subscribe() {
AudioObjectAddPropertyListener(
kAudioObjectSystemObject,
&_address,
&PropertyMonitor::Callback,
this);
}
void PropertyMonitor::unsubscribe() {
AudioObjectRemovePropertyListener(
kAudioObjectSystemObject,
&_address,
&PropertyMonitor::Callback,
this);
}
void PropertyMonitor::process() {
for (auto i = 0; i < _list.size(); ++i) {
(_list[i]->*_method)();
}
}
OSStatus PropertyMonitor::Callback(
AudioObjectID inObjectID,
UInt32 inNumberAddresses,
const AudioObjectPropertyAddress *inAddresses,
void *inClientData) {
const auto monitor = static_cast<PropertyMonitor*>(inClientData);
crl::on_main([monitor] {
monitor->process();
});
return 0;
}
[[nodiscard]] QString CFStringToQString(CFStringRef text) {
if (!text) {
return QString();
}
const auto length = CFStringGetLength(text);
const auto bytes = length * sizeof(QChar);
auto result = QString(length, QChar(0));
auto used = CFIndex(0);
const auto converted = CFStringGetBytes(
text,
CFRange{ 0, length },
kCFStringEncodingUTF16LE,
UInt8(),
false,
reinterpret_cast<UInt8*>(result.data()),
bytes,
&used);
if (!converted || !used || (used % sizeof(QChar))) {
return QString();
} else if (used < bytes) {
result.resize(used / sizeof(QChar));
}
return result;
}
[[nodiscard]] QString GetDeviceUID(AudioDeviceID deviceId) {
if (deviceId == kAudioDeviceUnknown) {
return QString();
}
auto uid = CFStringRef(nullptr);
const auto guard = gsl::finally([&] {
if (uid) {
CFRelease(uid);
}
});
auto size = UInt32(sizeof(uid));
const auto address = AudioObjectPropertyAddress{
.mSelector = kAudioDevicePropertyDeviceUID,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMain,
};
const auto result = AudioObjectGetPropertyData(
deviceId,
&address,
0,
nil,
&size,
&uid);
if (result != kAudioHardwareNoError || !uid) {
return QString();
}
return CFStringToQString(uid);
}
[[nodiscard]] AudioDeviceID GetDeviceByUID(const QString &id) {
const auto utf = id.toUtf8();
auto uid = CFStringCreateWithCString(
nullptr,
utf.data(),
kCFStringEncodingUTF8);
if (!uid) {
return kAudioObjectUnknown;
}
const auto guard = gsl::finally([&] {
CFRelease(uid);
});
const auto address = AudioObjectPropertyAddress{
.mSelector = kAudioHardwarePropertyDeviceForUID,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMain,
};
auto deviceId = AudioDeviceID(kAudioObjectUnknown);
auto deviceSize = UInt32(sizeof(deviceId));
AudioValueTranslation value;
value.mInputData = &uid;
value.mInputDataSize = sizeof(CFStringRef);
value.mOutputData = &deviceId;
value.mOutputDataSize = deviceSize;
auto valueSize = UInt32(sizeof(AudioValueTranslation));
const auto result = AudioObjectGetPropertyData(
kAudioObjectSystemObject,
&address,
0,
0,
&valueSize,
&value);
return (result == noErr) ? deviceId : kAudioObjectUnknown;
}
[[nodiscard]] QString GetDeviceName(
DeviceType type,
AudioDeviceID deviceId) {
auto name = (CFStringRef)nullptr;
const auto guard = gsl::finally([&] {
if (name) {
CFRelease(name);
}
});
auto size = UInt32(sizeof(name));
const auto address = AudioObjectPropertyAddress{
.mSelector = kAudioDevicePropertyDeviceNameCFString,
.mScope = (type == DeviceType::Playback
? kAudioObjectPropertyScopeOutput
: kAudioObjectPropertyScopeInput),
.mElement = kAudioObjectPropertyElementMain,
};
const auto result = AudioObjectGetPropertyData(
deviceId,
&address,
0,
nullptr,
&size,
&name);
if (result != kAudioHardwareNoError || !name) {
return QString();
}
return CFStringToQString(name);
}
[[nodiscard]] bool DeviceHasType(
AudioDeviceID deviceId,
DeviceType type) {
auto size = UInt32(0);
const auto address = AudioObjectPropertyAddress{
.mSelector = kAudioDevicePropertyStreamConfiguration,
.mScope = (type == DeviceType::Playback
? kAudioObjectPropertyScopeOutput
: kAudioObjectPropertyScopeInput),
.mElement = kAudioObjectPropertyElementMain,
};
auto result = AudioObjectGetPropertyDataSize(
deviceId,
&address,
0,
0,
&size);
if (result != noErr) {
return false;
}
AudioBufferList *list = (AudioBufferList*)malloc(size);
const auto guard = gsl::finally([&] {
free(list);
});
result = AudioObjectGetPropertyData(
deviceId,
&address,
0,
nil,
&size,
list);
return (result == noErr) && (list->mNumberBuffers > 0);
}
[[nodiscard]] std::vector<AudioDeviceID> GetAllDeviceIds() {
auto size = UInt32();
const auto address = AudioObjectPropertyAddress{
.mSelector = kAudioHardwarePropertyDevices,
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMain,
};
auto result = AudioObjectGetPropertyDataSize(
kAudioObjectSystemObject,
&address,
0,
nil,
&size);
if (result != noErr) {
return {};
}
const auto count = size / sizeof(AudioDeviceID);
auto list = std::vector<AudioDeviceID>(count, kAudioDeviceUnknown);
result = AudioObjectGetPropertyData(
kAudioObjectSystemObject,
&address,
0,
nil,
&size,
list.data());
return (result == noErr) ? list : std::vector<AudioDeviceID>();
}
[[nodiscard]] QString NS2QString(NSString *text) {
return QString::fromUtf8(
[text cStringUsingEncoding:NSUTF8StringEncoding]);
}
[[nodiscard]] QString CaptureDeviceId(AVCaptureDevice *device) {
return device ? NS2QString([device uniqueID]) : QString();
}
[[nodiscard]] DeviceInfo CaptureDeviceInfo(AVCaptureDevice *device) {
const auto id = CaptureDeviceId(device);
if (id.isEmpty()) {
return {};
}
return {
.id = id,
.name = NS2QString([device localizedName]),
.type = DeviceType::Camera,
};
}
} // namespace
EnvironmentMac::EnvironmentMac(not_null<EnvironmentDelegate*> delegate)
: _delegate(delegate)
, _captureMuteDebounceTimer([=] { sendCaptureMutedValue(); }) {
DefaultPlaybackDeviceChangedMonitor.registerEnvironment(this);
DefaultCaptureDeviceChangedMonitor.registerEnvironment(this);
AudioDeviceListChangedMonitor.registerEnvironment(this);
if (@available(macOS 14.0, *)) {
const auto weak = base::make_weak(this);
id block = [^(BOOL shouldBeMuted){
crl::on_main([weak, mute = shouldBeMuted ? true : false] {
if (const auto strong = weak.get()) {
if (strong->_captureMuteTracker) {
strong->_captureMuted = mute;
strong->_captureMuteDebounceTimer.callOnce(
kCaptureMuteDebounceTimeout);
}
}
});
return YES;
} copy];
_lifetime.add([block] { [block release]; });
[[AVAudioApplication sharedInstance]
setInputMuteStateChangeHandler:block
error:nil];
}
}
EnvironmentMac::~EnvironmentMac() {
DefaultPlaybackDeviceChangedMonitor.unregisterEnvironment(this);
DefaultCaptureDeviceChangedMonitor.unregisterEnvironment(this);
AudioDeviceListChangedMonitor.unregisterEnvironment(this);
}
void EnvironmentMac::sendCaptureMutedValue() {
Expects(_captureMuteTracker != nullptr);
const auto weak = base::make_weak(this);
_captureMuteNotification = true;
_captureMuteTracker->captureMuteChanged(_captureMuted);
if (const auto strong = weak.get()) {
strong->_captureMuteNotification = false;
}
}
void EnvironmentMac::defaultPlaybackDeviceChanged() {
const auto type = DeviceType::Playback;
_delegate->defaultChanged(type, DeviceChangeReason::Manual, defaultId(type));
}
void EnvironmentMac::defaultCaptureDeviceChanged() {
const auto type = DeviceType::Capture;
_delegate->defaultChanged(type, DeviceChangeReason::Manual, defaultId(type));
}
void EnvironmentMac::audioDeviceListChanged() {
_delegate->devicesForceRefresh(DeviceType::Playback);
_delegate->devicesForceRefresh(DeviceType::Capture);
}
QString EnvironmentMac::defaultId(DeviceType type) {
return DefaultId(type);
}
QString EnvironmentMac::DefaultId(DeviceType type) {
if (type == DeviceType::Camera) {
return CaptureDeviceId(
[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]);
}
auto deviceId = AudioDeviceID(kAudioDeviceUnknown);
auto size = UInt32(sizeof(deviceId));
const auto address = AudioObjectPropertyAddress{
.mSelector = (type == DeviceType::Playback
? kAudioHardwarePropertyDefaultOutputDevice
: kAudioHardwarePropertyDefaultInputDevice),
.mScope = kAudioObjectPropertyScopeGlobal,
.mElement = kAudioObjectPropertyElementMain,
};
const auto result = AudioObjectGetPropertyData(
kAudioObjectSystemObject,
&address,
0,
0,
&size,
&deviceId);
return (result == kAudioHardwareNoError) ? GetDeviceUID(deviceId) : QString();
}
DeviceInfo EnvironmentMac::device(DeviceType type, const QString &id) {
if (type == DeviceType::Camera) {
NSArray<AVCaptureDevice*> *devices
= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in devices) {
if (CaptureDeviceId(device) == id) {
return CaptureDeviceInfo(device);
}
}
return {};
}
const auto deviceId = GetDeviceByUID(id);
if (deviceId != kAudioDeviceUnknown) {
return {
.id = id,
.name = GetDeviceName(type, deviceId),
.type = type,
.inactive = false,
};
}
const auto list = devices(type);
return list.empty() ? DeviceInfo() : list.front();
}
std::vector<DeviceInfo> EnvironmentMac::devices(DeviceType type) {
if (type == DeviceType::Camera) {
auto result = std::vector<DeviceInfo>();
const auto add = [&](AVCaptureDevice *device) {
if (auto info = CaptureDeviceInfo(device)) {
if (!ranges::contains(result, info.id, &DeviceInfo::id)) {
result.push_back(std::move(info));
}
}
};
add([AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]);
NSArray<AVCaptureDevice*> *devices
= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in devices) {
add(device);
}
return result;
}
auto result = std::vector<DeviceInfo>();
for (const auto deviceId : GetAllDeviceIds()) {
if (DeviceHasType(deviceId, type)) {
if (const auto uid = GetDeviceUID(deviceId); !uid.isEmpty()) {
result.push_back({
.id = uid,
.name = GetDeviceName(type, deviceId),
.type = type,
.inactive = false,
});
}
}
}
return result;
}
bool EnvironmentMac::refreshFullListOnChange(DeviceType type) {
// We have simple change notifications, no exact change information.
return true;
}
bool EnvironmentMac::desktopCaptureAllowed() const {
if (@available(macOS 11.0, *)) {
// Screen Recording is required on macOS 10.15 an later.
// Even if user grants access, restart is required.
static const auto result = CGPreflightScreenCaptureAccess();
return result;
} else if (@available(macOS 10.15, *)) {
const auto stream = CGDisplayStreamCreate(
CGMainDisplayID(),
1,
1,
kCVPixelFormatType_32BGRA,
CFDictionaryRef(),
^(
CGDisplayStreamFrameStatus status,
uint64_t display_time,
IOSurfaceRef frame_surface,
CGDisplayStreamUpdateRef updateRef) {
});
if (!stream) {
return false;
}
CFRelease(stream);
return true;
}
return true;
}
std::optional<QString> EnvironmentMac::uniqueDesktopCaptureSource() const {
return {};
}
void EnvironmentMac::defaultIdRequested(DeviceType type) {
}
void EnvironmentMac::devicesRequested(DeviceType type) {
}
void EnvironmentMac::setCaptureMuted(bool muted) {
if (@available(macOS 14.0, *)) {
if (!_captureMuteNotification) {
const auto value = muted ? YES : NO;
[[AVAudioApplication sharedInstance] setInputMuted:value error:nil];
}
}
}
void EnvironmentMac::captureMuteSubscribe() {
if (@available(macOS 14.0, *)) {
id observer = [[InputMuteObserver alloc] init];
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserver:observer
selector:@selector(inputMuteStateChange:)
name:AVAudioApplicationInputMuteStateChangeNotification
object:nil];
_admTaskQueueFactory = webrtc::CreateDefaultTaskQueueFactory();
const auto saveSetDeviceIdCallback = [=](
Fn<void(DeviceResolvedId)> setDeviceIdCallback) {
_admSetDeviceIdCallback = std::move(setDeviceIdCallback);
if (!_admCaptureDeviceId.isDefault()) {
_admSetDeviceIdCallback(_admCaptureDeviceId);
}
};
_adm = CreateAudioDeviceModule(
_admTaskQueueFactory.get(),
saveSetDeviceIdCallback);
// We don't need captured data, we need simply to have active recording.
static auto kEmptyCallback = EmptyCallback();
_adm->RegisterAudioCallback(&kEmptyCallback);
_captureMuteSubscriptionLifetime.add([=] {
_admSetDeviceIdCallback = nullptr;
_adm = nullptr;
_admTaskQueueFactory = nullptr;
[[[NSWorkspace sharedWorkspace] notificationCenter]
removeObserver:observer
name:AVAudioApplicationInputMuteStateChangeNotification
object:nil];
[observer release];
});
}
}
void EnvironmentMac::captureMuteUnsubscribe() {
_captureMuteSubscriptionLifetime.destroy();
}
void EnvironmentMac::captureMuteRestartAdm() {
_adm->StopRecording();
_adm->SetRecordingDevice(0);
if (_adm->InitRecording() == 0) {
_adm->StartRecording();
}
}
void EnvironmentMac::setCaptureMuteTracker(
not_null<CaptureMuteTracker*> tracker,
bool track) {
if (@available(macOS 14.0, *)) {
if (track) {
if (!_captureMuteTracker) {
captureMuteSubscribe();
} else if (_captureMuteTracker == tracker) {
return;
}
_captureMuteTrackerLifetime.destroy();
_captureMuteTracker = tracker;
_captureMuteTracker->captureMuteDeviceId(
) | rpl::on_next([=](DeviceResolvedId deviceId) {
_admSetDeviceIdCallback(deviceId);
captureMuteRestartAdm();
}, _captureMuteTrackerLifetime);
} else if (_captureMuteTracker == tracker) {
_captureMuteTrackerLifetime.destroy();
_captureMuteDebounceTimer.cancel();
_captureMuteTracker = nullptr;
captureMuteUnsubscribe();
if (!_captureMuted) {
_captureMuted = true;
setCaptureMuted(true);
}
}
}
}
std::unique_ptr<Environment> CreateEnvironment(
not_null<EnvironmentDelegate*> delegate) {
return std::make_unique<EnvironmentMac>(delegate);
}
} // namespace Webrtc::Platform

View File

@@ -0,0 +1,56 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "webrtc/webrtc_device_common.h"
#include <optional>
namespace Webrtc::Platform {
class EnvironmentDelegate;
class Environment {
public:
virtual ~Environment() = default;
[[nodiscard]] virtual QString defaultId(DeviceType type) = 0;
[[nodiscard]] virtual DeviceInfo device(
DeviceType type,
const QString &id) = 0;
[[nodiscard]] virtual std::vector<DeviceInfo> devices(
DeviceType type) = 0;
[[nodiscard]] virtual bool refreshFullListOnChange(
DeviceType type) = 0;
[[nodiscard]] virtual bool desktopCaptureAllowed() const = 0;
[[nodiscard]] virtual auto uniqueDesktopCaptureSource() const
-> std::optional<QString> = 0;
virtual void defaultIdRequested(DeviceType type) = 0;
virtual void devicesRequested(DeviceType type) = 0;
[[nodiscard]] virtual DeviceResolvedId threadSafeResolveId(
const DeviceResolvedId &lastResolvedId,
const QString &savedId) {
return lastResolvedId;
}
virtual void setCaptureMuted(bool muted) {
}
virtual void setCaptureMuteTracker(
not_null<CaptureMuteTracker*> tracker,
bool track) {
}
};
[[nodiscard]] std::unique_ptr<Environment> CreateEnvironment(
not_null<EnvironmentDelegate*> delegate);
} // namespace Webrtc::Platform

View File

@@ -0,0 +1,408 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "webrtc/platform/win/webrtc_environment_win.h"
#include "base/platform/win/base_windows_co_task_mem.h"
#include "base/weak_ptr.h"
#include "webrtc/webrtc_environment.h"
#include <MMDeviceAPI.h>
#include <winrt/base.h>
#include <functiondiscoverykeys_devpkey.h>
#include <propvarutil.h>
#include <propkey.h>
namespace Webrtc::Platform {
namespace {
constexpr auto kMaxNameLength = 256;
[[nodiscard]] auto RoleForType(DeviceType type) {
return (type == DeviceType::Playback) ? eConsole : eCommunications;
}
} // namespace
class EnvironmentWin::Client
: public winrt::implements<Client, IMMNotificationClient>
, public base::has_weak_ptr {
public:
Client(
Fn<void(DeviceType, QString)> defaultChanged,
Fn<void(QString, std::optional<DeviceStateChange>)> deviceToggled);
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
LPCWSTR deviceId,
const PROPERTYKEY key) override;
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR deviceId) override;
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR deviceId) override;
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
LPCWSTR deviceId,
DWORD newState) override;
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
EDataFlow flow,
ERole role,
LPCWSTR newDefaultDeviceId) override;
private:
Fn<void(DeviceType, QString)> _defaultChanged;
Fn<void(QString, std::optional<DeviceStateChange>)> _deviceToggled;
};
EnvironmentWin::Client::Client(
Fn<void(DeviceType, QString)> defaultChanged,
Fn<void(QString, std::optional<DeviceStateChange>)> deviceToggled)
: _defaultChanged(std::move(defaultChanged))
, _deviceToggled(std::move(deviceToggled)) {
}
HRESULT STDMETHODCALLTYPE EnvironmentWin::Client::OnPropertyValueChanged(
LPCWSTR deviceId,
const PROPERTYKEY key) {
return S_OK;
}
HRESULT STDMETHODCALLTYPE EnvironmentWin::Client::OnDeviceAdded(
LPCWSTR deviceId) {
const auto id = QString::fromWCharArray(deviceId);
crl::on_main(this, [=] {
const auto onstack = _deviceToggled;
onstack(id, std::nullopt);
});
return S_OK;
}
HRESULT STDMETHODCALLTYPE EnvironmentWin::Client::OnDeviceRemoved(
LPCWSTR deviceId) {
const auto id = QString::fromWCharArray(deviceId);
const auto change = DeviceStateChange::Disconnected;
crl::on_main(this, [=] {
const auto onstack = _deviceToggled;
onstack(id, change);
});
return S_OK;
}
HRESULT STDMETHODCALLTYPE EnvironmentWin::Client::OnDeviceStateChanged(
LPCWSTR deviceId,
DWORD newState) {
const auto change = (newState == DEVICE_STATE_ACTIVE)
? DeviceStateChange::Active
: DeviceStateChange::Inactive;
const auto id = QString::fromWCharArray(deviceId);
crl::on_main(this, [=] {
const auto onstack = _deviceToggled;
onstack(id, change);
});
return S_OK;
}
HRESULT STDMETHODCALLTYPE EnvironmentWin::Client::OnDefaultDeviceChanged(
EDataFlow flow,
ERole role,
LPCWSTR newDefaultDeviceId) {
const auto type = (flow == eRender)
? DeviceType::Playback
: DeviceType::Capture;
if (role == RoleForType(type)) {
const auto id = QString::fromWCharArray(newDefaultDeviceId);
crl::on_main(this, [=] {
const auto onstack = _defaultChanged;
onstack(type, id);
});
}
return S_OK;
}
EnvironmentWin::EnvironmentWin(not_null<EnvironmentDelegate*> delegate)
: _delegate(delegate)
#ifdef WEBRTC_TESTING_OPENAL
, _audioFallback(delegate)
#endif // WEBRTC_TESTING_OPENAL
, _cameraFallback(delegate) {
#ifndef WEBRTC_TESTING_OPENAL
using namespace base::WinRT;
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
_enumerator = TryCreateInstance<IMMDeviceEnumerator>(
CLSID_MMDeviceEnumerator);
if (!_enumerator) {
const auto hr = CoInitialize(nullptr);
if (SUCCEEDED(hr)) {
_comInitialized = true;
_enumerator = TryCreateInstance<IMMDeviceEnumerator>(
CLSID_MMDeviceEnumerator);
if (!_enumerator) {
LOG(("Media Error: Could not create MMDeviceEnumerator."));
return;
}
}
}
_client = winrt::make<Client>([=](DeviceType type, QString id) {
_delegate->defaultChanged(type, DeviceChangeReason::Manual, id);
}, [=](QString id, std::optional<DeviceStateChange> state) {
processDeviceStateChange(id, state);
});
if (!_client) {
LOG(("Media Error: Could not create IMMNotificationClient."));
return;
}
const auto hr = _enumerator->RegisterEndpointNotificationCallback(
_client.get());
if (FAILED(hr)) {
LOG(("Media Error: RegisterEndpointNotificationCallback failed."));
}
#endif // !WEBRTC_TESTING_OPENAL
}
EnvironmentWin::~EnvironmentWin() {
if (_client) {
Assert(_enumerator != nullptr);
_enumerator->UnregisterEndpointNotificationCallback(_client.get());
_client = nullptr;
}
_enumerator = nullptr;
if (_comInitialized) {
CoUninitialize();
}
}
QString EnvironmentWin::defaultId(DeviceType type) {
if (type == DeviceType::Camera) {
return _cameraFallback.defaultId(type);
} else if (!_enumerator) {
#ifdef WEBRTC_TESTING_OPENAL
return _audioFallback.defaultId(type);
#endif // WEBRTC_TESTING_OPENAL
return {};
}
const auto flow = (type == DeviceType::Playback)
? eRender
: eCapture;
const auto role = RoleForType(type);
auto device = winrt::com_ptr<IMMDevice>();
auto hr = _enumerator->GetDefaultAudioEndpoint(flow, role, device.put());
if (FAILED(hr) || !device) {
return {};
}
auto id = base::CoTaskMemString();
hr = device->GetId(id.put());
if (FAILED(hr) || !id || !*id.data()) {
return {};
}
return QString::fromWCharArray(id.data());
}
void EnvironmentWin::processDeviceStateChange(
const QString &id,
std::optional<DeviceStateChange> change) {
const auto wide = id.toStdWString();
auto device = winrt::com_ptr<IMMDevice>();
auto hr = _enumerator->GetDevice(wide.c_str(), device.put());
if (FAILED(hr)) {
return;
} else if (const auto endpoint = device.try_as<IMMEndpoint>()) {
auto flow = EDataFlow();
hr = endpoint->GetDataFlow(&flow);
if (SUCCEEDED(hr)) {
if (!change) {
auto state = DWORD();
hr = device->GetState(&state);
if (!SUCCEEDED(hr)) {
return;
}
change = (state == DEVICE_STATE_ACTIVE)
? DeviceStateChange::Active
: DeviceStateChange::Inactive;
}
const auto type = (flow == eRender)
? DeviceType::Playback
: DeviceType::Capture;
_delegate->deviceStateChanged(type, id, *change);
}
}
}
DeviceInfo EnvironmentWin::device(DeviceType type, const QString &id) {
if (type == DeviceType::Camera) {
return _cameraFallback.device(type, id);
} else if (!_enumerator) {
#ifdef WEBRTC_TESTING_OPENAL
return _audioFallback.device(type, id);
#endif // WEBRTC_TESTING_OPENAL
return {};
}
const auto wide = id.toStdWString();
auto device = winrt::com_ptr<IMMDevice>();
auto hr = _enumerator->GetDevice(wide.c_str(), device.put());
if (FAILED(hr) || !device) {
return {};
}
auto store = winrt::com_ptr<IPropertyStore>();
hr = device->OpenPropertyStore(STGM_READ, store.put());
if (FAILED(hr) || !store) {
return {};
}
auto name = PROPVARIANT();
hr = store->GetValue(PKEY_Device_FriendlyName, &name);
if (FAILED(hr)) {
return {};
}
const auto guard = gsl::finally([&] { PropVariantClear(&name); });
auto already = std::array<WCHAR, kMaxNameLength>();
hr = PropVariantToString(name, already.data(), MAX_PATH);
if (FAILED(hr) || !already[0]) {
return {};
}
auto state = DWORD();
hr = device->GetState(&state);
if (FAILED(hr)) {
return {};
}
return {
.id = id,
.name = QString::fromWCharArray(already.data()),
.type = type,
.inactive = (state != DEVICE_STATE_ACTIVE),
};
}
std::vector<DeviceInfo> EnvironmentWin::devices(DeviceType type) {
if (type == DeviceType::Camera) {
return _cameraFallback.devices(type);
} else if (!_enumerator) {
#ifdef WEBRTC_TESTING_OPENAL
return _audioFallback.devices(type);
#endif // WEBRTC_TESTING_OPENAL
return {};
}
const auto flow = (type == DeviceType::Playback)
? eRender
: eCapture;
auto collection = winrt::com_ptr<IMMDeviceCollection>();
auto hr = _enumerator->EnumAudioEndpoints(
flow,
DEVICE_STATEMASK_ALL,
collection.put());
if (FAILED(hr) || !collection) {
return {};
}
auto count = UINT();
hr = collection->GetCount(&count);
if (FAILED(hr)) {
return {};
}
auto result = std::vector<DeviceInfo>();
result.reserve(count);
for (auto i = UINT(); i != count; ++i) {
auto device = winrt::com_ptr<IMMDevice>();
hr = collection->Item(i, device.put());
if (FAILED(hr)) {
continue;
}
auto endpoint = device.try_as<IMMEndpoint>();
if (!endpoint) {
continue;
}
auto flow = EDataFlow();
hr = endpoint->GetDataFlow(&flow);
auto id = base::CoTaskMemString();
hr = device->GetId(id.put());
if (FAILED(hr) || !id || !*id.data()) {
continue;
}
auto store = winrt::com_ptr<IPropertyStore>();
hr = device->OpenPropertyStore(STGM_READ, store.put());
if (FAILED(hr) || !store) {
continue;
}
auto name = PROPVARIANT();
hr = store->GetValue(PKEY_Device_FriendlyName, &name);
if (FAILED(hr)) {
continue;
}
const auto guard = gsl::finally([&] { PropVariantClear(&name); });
auto already = std::array<WCHAR, kMaxNameLength>();
hr = PropVariantToString(name, already.data(), MAX_PATH);
if (FAILED(hr) || !already[0]) {
continue;
}
auto state = DWORD();
hr = device->GetState(&state);
if (FAILED(hr)) {
continue;
}
result.push_back({
.id = QString::fromWCharArray(id.data()),
.name = QString::fromWCharArray(already.data()),
.type = type,
.inactive = (state != DEVICE_STATE_ACTIVE),
});
}
return result;
}
bool EnvironmentWin::refreshFullListOnChange(DeviceType type) {
if (type == DeviceType::Camera) {
return _cameraFallback.refreshFullListOnChange(type);
}
#ifdef WEBRTC_TESTING_OPENAL
return true;
#endif // WEBRTC_TESTING_OPENAL
return false;
}
bool EnvironmentWin::desktopCaptureAllowed() const {
return true;
}
std::optional<QString> EnvironmentWin::uniqueDesktopCaptureSource() const {
return {};
}
void EnvironmentWin::defaultIdRequested(DeviceType type) {
if (type == DeviceType::Camera) {
_cameraFallback.defaultIdRequested(type);
#ifdef WEBRTC_TESTING_OPENAL
} else {
_audioFallback.defaultIdRequested(type);
#endif // WEBRTC_TESTING_OPENAL
}
}
void EnvironmentWin::devicesRequested(DeviceType type) {
if (type == DeviceType::Camera) {
_cameraFallback.devicesRequested(type);
#ifdef WEBRTC_TESTING_OPENAL
} else {
_audioFallback.devicesRequested(type);
#endif // WEBRTC_TESTING_OPENAL
}
}
DeviceResolvedId EnvironmentWin::threadSafeResolveId(
const DeviceResolvedId &lastResolvedId,
const QString &savedId) {
return (lastResolvedId.type == DeviceType::Camera)
? _cameraFallback.threadSafeResolveId(lastResolvedId, savedId)
#ifdef WEBRTC_TESTING_OPENAL
: _audioFallback.threadSafeResolveId(lastResolvedId, savedId);
#endif // WEBRTC_TESTING_OPENAL
: lastResolvedId;
}
std::unique_ptr<Environment> CreateEnvironment(
not_null<EnvironmentDelegate*> delegate) {
return std::make_unique<EnvironmentWin>(delegate);
}
} // namespace Webrtc::Platform

View File

@@ -0,0 +1,65 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include "webrtc/platform/webrtc_platform_environment.h"
#include "webrtc/details/webrtc_environment_video_capture.h"
#include "base/platform/win/base_windows_winrt.h"
//#define WEBRTC_TESTING_OPENAL
#ifdef WEBRTC_TESTING_OPENAL
#include "webrtc/details/webrtc_environment_openal.h"
#endif // WEBRTC_TESTING_OPENAL
struct IMMDeviceEnumerator;
struct IMMNotificationClient;
namespace Webrtc::Platform {
class EnvironmentWin final : public Environment {
public:
explicit EnvironmentWin(not_null<EnvironmentDelegate*> delegate);
~EnvironmentWin();
QString defaultId(DeviceType type) override;
DeviceInfo device(DeviceType type, const QString &id) override;
std::vector<DeviceInfo> devices(DeviceType type) override;
bool refreshFullListOnChange(DeviceType type) override;
bool desktopCaptureAllowed() const override;
std::optional<QString> uniqueDesktopCaptureSource() const override;
void defaultIdRequested(DeviceType type) override;
void devicesRequested(DeviceType type) override;
DeviceResolvedId threadSafeResolveId(
const DeviceResolvedId &lastResolvedId,
const QString &savedId) override;
private:
void processDeviceStateChange(
const QString &id,
std::optional<DeviceStateChange> change);
class Client;
const not_null<EnvironmentDelegate*> _delegate;
#ifdef WEBRTC_TESTING_OPENAL
details::EnvironmentOpenAL _audioFallback;
#endif // WEBRTC_TESTING_OPENAL
details::EnvironmentVideoCapture _cameraFallback;
bool _comInitialized = false;
winrt::com_ptr<IMMDeviceEnumerator> _enumerator;
winrt::com_ptr<IMMNotificationClient> _client;
};
} // namespace Webrtc::Platform

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,198 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once
#include <modules/audio_device/include/audio_device.h>
#include <modules/audio_device/audio_device_buffer.h>
#include <api/scoped_refptr.h>
#include <AudioClient.h>
#include <MMDeviceAPI.h>
#include <winrt/base.h>
typedef struct SwrContext SwrContext;
typedef struct AVChannelLayout AVChannelLayout;
namespace rtc {
class Thread;
} // namespace rtc
namespace webrtc {
class AudioProcessing;
class AudioFrame;
} // namespace webrtc
namespace Webrtc::details {
[[nodiscard]] bool IsLoopbackCaptureActive();
void LoopbackCapturePushFarEnd(
crl::time when,
const QByteArray &samples,
int frequency,
int channels);
class AudioDeviceLoopbackWin : public webrtc::AudioDeviceModule {
public:
explicit AudioDeviceLoopbackWin(webrtc::TaskQueueFactory *taskQueueFactory);
~AudioDeviceLoopbackWin();
int32_t ActiveAudioLayer(AudioLayer *audioLayer) const override;
int32_t RegisterAudioCallback(
webrtc::AudioTransport *audioCallback) override;
// Main initializaton and termination
int32_t Init() override;
int32_t Terminate() override;
bool Initialized() const override;
// Device enumeration
int16_t PlayoutDevices() override;
int16_t RecordingDevices() override;
int32_t PlayoutDeviceName(uint16_t index,
char name[webrtc::kAdmMaxDeviceNameSize],
char guid[webrtc::kAdmMaxGuidSize]) override;
int32_t RecordingDeviceName(uint16_t index,
char name[webrtc::kAdmMaxDeviceNameSize],
char guid[webrtc::kAdmMaxGuidSize]) override;
// Device selection
int32_t SetPlayoutDevice(uint16_t index) override;
int32_t SetPlayoutDevice(WindowsDeviceType device) override;
int32_t SetRecordingDevice(uint16_t index) override;
int32_t SetRecordingDevice(WindowsDeviceType device) override;
// Audio transport initialization
int32_t PlayoutIsAvailable(bool *available) override;
int32_t InitPlayout() override;
bool PlayoutIsInitialized() const override;
int32_t RecordingIsAvailable(bool *available) override;
int32_t InitRecording() override;
bool RecordingIsInitialized() const override;
// Audio transport control
int32_t StartPlayout() override;
int32_t StopPlayout() override;
bool Playing() const override;
int32_t StartRecording() override;
int32_t StopRecording() override;
bool Recording() const override;
// Audio mixer initialization
int32_t InitSpeaker() override;
bool SpeakerIsInitialized() const override;
int32_t InitMicrophone() override;
bool MicrophoneIsInitialized() const override;
// Speaker volume controls
int32_t SpeakerVolumeIsAvailable(bool *available) override;
int32_t SetSpeakerVolume(uint32_t volume) override;
int32_t SpeakerVolume(uint32_t *volume) const override;
int32_t MaxSpeakerVolume(uint32_t *maxVolume) const override;
int32_t MinSpeakerVolume(uint32_t *minVolume) const override;
// Microphone volume controls
int32_t MicrophoneVolumeIsAvailable(bool *available) override;
int32_t SetMicrophoneVolume(uint32_t volume) override;
int32_t MicrophoneVolume(uint32_t *volume) const override;
int32_t MaxMicrophoneVolume(uint32_t *maxVolume) const override;
int32_t MinMicrophoneVolume(uint32_t *minVolume) const override;
// Microphone mute control
int32_t MicrophoneMuteIsAvailable(bool *available) override;
int32_t SetMicrophoneMute(bool enable) override;
int32_t MicrophoneMute(bool *enabled) const override;
// Speaker mute control
int32_t SpeakerMuteIsAvailable(bool *available) override;
int32_t SetSpeakerMute(bool enable) override;
int32_t SpeakerMute(bool *enabled) const override;
// Stereo support
int32_t StereoPlayoutIsAvailable(bool *available) const override;
int32_t SetStereoPlayout(bool enable) override;
int32_t StereoPlayout(bool *enabled) const override;
int32_t StereoRecordingIsAvailable(bool *available) const override;
int32_t SetStereoRecording(bool enable) override;
int32_t StereoRecording(bool *enabled) const override;
// Delay information and control
int32_t PlayoutDelay(uint16_t *delayMS) const override;
// Only supported on Android.
bool BuiltInAECIsAvailable() const override;
bool BuiltInAGCIsAvailable() const override;
bool BuiltInNSIsAvailable() const override;
// Enables the built-in audio effects. Only supported on Android.
int32_t EnableBuiltInAEC(bool enable) override;
int32_t EnableBuiltInAGC(bool enable) override;
int32_t EnableBuiltInNS(bool enable) override;
private:
void openPlaybackDeviceForCapture();
void openAudioClient();
void openRecordingDevice();
void closeRecordingDevice();
void captureFailed(const std::string &error);
void startCaptureOnThread();
void stopCaptureOnThread();
void processData();
bool setupResampler(const WAVEFORMATEX &format);
bool setupResampler(
int channels,
AVChannelLayout channelLayout,
int inputFormat, // AVSampleFormat
int sampleRate,
Fn<std::string()> info);
void ensureResampleSpaceAvailable(int samples);
static DWORD WINAPI CaptureThreadMethod(LPVOID context);
DWORD runCaptureThread();
webrtc::AudioDeviceBuffer _audioDeviceBuffer;
winrt::com_ptr<IMMDevice> _endpointDevice;
winrt::com_ptr<IAudioClient> _audioClient;
winrt::com_ptr<IAudioClient> _audioRenderClientForLoopback;
winrt::com_ptr<IAudioCaptureClient> _audioCaptureClient;
rtc::scoped_refptr<webrtc::AudioProcessing> _audioProcessing;
std::unique_ptr<webrtc::AudioFrame> _capturedFrame;
std::unique_ptr<webrtc::AudioFrame> _renderedFrame;
HANDLE _thread = nullptr;
HANDLE _audioSamplesReadyEvent = nullptr;
HANDLE _captureThreadShutdownEvent = nullptr;
UINT32 _bufferSizeFrames = 0;
UINT32 _deviceFrameSize = 0;
QByteArray _deviceBuffer;
QByteArray _resampleBuffer;
UINT32 _bufferOffset = 0;
double _queryPerformanceMultiplier = 0.;
double _deviceFrequencyMultiplier = 0.;
SwrContext *_swrContext = nullptr;
int _swrSrcSampleRate = 0;
bool _microphoneInitialized = false;
bool _initialized = false;
bool _recordingInitialized = false;
bool _recordingFailed = false;
bool _recording = false;
};
} // namespace Webrtc::details