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,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