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
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
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
Close stale issues and PRs / stale (push) Successful in 13s
Needs user action. / needs-user-action (push) Failing after 8s
Can't reproduce. / cant-reproduce (push) Failing after 8s
This commit is contained in:
396
Telegram/lib_webrtc/webrtc/webrtc_environment.cpp
Normal file
396
Telegram/lib_webrtc/webrtc/webrtc_environment.cpp
Normal file
@@ -0,0 +1,396 @@
|
||||
// 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/webrtc_environment.h"
|
||||
|
||||
#include "base/debug_log.h"
|
||||
#include "webrtc/platform/webrtc_platform_environment.h"
|
||||
#include "webrtc/details/webrtc_environment_openal.h"
|
||||
#include "webrtc/details/webrtc_environment_video_capture.h"
|
||||
|
||||
#ifdef WEBRTC_MAC
|
||||
#include "webrtc/platform/mac/webrtc_environment_mac.h"
|
||||
#endif // WEBRTC_MAC
|
||||
|
||||
#include <crl/crl_async.h>
|
||||
|
||||
namespace Webrtc {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QString SerializeDevices(const std::vector<DeviceInfo> &list) {
|
||||
auto result = QStringList();
|
||||
for (const auto &device : list) {
|
||||
result.push_back('"' + device.name + "\" <" + device.id + ">");
|
||||
}
|
||||
return "{ " + result.join(", ") + " }";
|
||||
}
|
||||
|
||||
[[nodiscard]] QString TypeToString(DeviceType type) {
|
||||
switch (type) {
|
||||
case DeviceType::Playback: return "Playback";
|
||||
case DeviceType::Capture: return "Capture";
|
||||
case DeviceType::Camera: return "Camera";
|
||||
}
|
||||
Unexpected("Type in TypeToString.");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Environment::Environment()
|
||||
: _platform(
|
||||
Platform::CreateEnvironment((Platform::EnvironmentDelegate*)this))
|
||||
, _devices{
|
||||
resolveDevices(DeviceType::Playback),
|
||||
resolveDevices(DeviceType::Capture),
|
||||
resolveDevices(DeviceType::Camera),
|
||||
} {
|
||||
using Type = DeviceType;
|
||||
for (const auto type : { Type::Playback, Type::Capture, Type::Camera }) {
|
||||
if (synced(type)) {
|
||||
logState(type, LogType::Initial);
|
||||
} else {
|
||||
logSyncError(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Environment::~Environment() = default;
|
||||
|
||||
int Environment::TypeToIndex(DeviceType type) {
|
||||
const auto result = int(type);
|
||||
|
||||
Ensures(result >= 0 && result < kTypeCount);
|
||||
return result;
|
||||
}
|
||||
|
||||
Environment::Devices Environment::resolveDevices(DeviceType type) const {
|
||||
return {
|
||||
.defaultId = _platform->defaultId(type),
|
||||
.list = _platform->devices(type),
|
||||
.refreshFullListOnChange = _platform->refreshFullListOnChange(type),
|
||||
};
|
||||
}
|
||||
|
||||
QString Environment::defaultId(DeviceType type) const {
|
||||
validateDefaultId(type);
|
||||
return _devices[TypeToIndex(type)].defaultId;
|
||||
}
|
||||
|
||||
std::vector<DeviceInfo> Environment::devices(DeviceType type) const {
|
||||
validateDevices(type);
|
||||
return _devices[TypeToIndex(type)].list;
|
||||
}
|
||||
|
||||
rpl::producer<DevicesChange> Environment::changes(
|
||||
DeviceType type) const {
|
||||
const auto devices = &_devices[TypeToIndex(type)];
|
||||
return devices->changes.events(
|
||||
) | rpl::map([=](DevicesChangeEvent &&event) -> DevicesChange {
|
||||
return { std::move(event.defaultChange), devices->list };
|
||||
});
|
||||
}
|
||||
|
||||
rpl::producer<DeviceChange> Environment::defaultChanges(
|
||||
DeviceType type) const {
|
||||
return _devices[TypeToIndex(type)].changes.events(
|
||||
) | rpl::filter([](const DevicesChangeEvent &event) {
|
||||
return !!event.defaultChange;
|
||||
}) | rpl::map([](DevicesChangeEvent &&event) {
|
||||
return std::move(event.defaultChange);
|
||||
});
|
||||
}
|
||||
|
||||
rpl::producer<std::vector<DeviceInfo>> Environment::devicesValue(
|
||||
DeviceType type) const {
|
||||
validateDevices(type);
|
||||
|
||||
const auto devices = &_devices[TypeToIndex(type)];
|
||||
return devices->changes.events_starting_with(
|
||||
DevicesChangeEvent{ .listChanged = true }
|
||||
) | rpl::filter([](const DevicesChangeEvent &event) {
|
||||
return event.listChanged;
|
||||
}) | rpl::map([=] {
|
||||
return devices->list;
|
||||
});
|
||||
}
|
||||
|
||||
void Environment::forceRefresh(DeviceType type) {
|
||||
auto &devices = _devices[TypeToIndex(type)];
|
||||
devices.defaultChangeFrom = std::exchange(
|
||||
devices.defaultId,
|
||||
_platform->defaultId(type));
|
||||
const auto &old = *devices.defaultChangeFrom;
|
||||
const auto newIsInOldList = ranges::contains(
|
||||
devices.list,
|
||||
devices.defaultId,
|
||||
&DeviceInfo::id);
|
||||
refreshDevices(type);
|
||||
const auto oldIsInNewList = ranges::contains(
|
||||
devices.list,
|
||||
old,
|
||||
&DeviceInfo::id);
|
||||
if (devices.defaultId != old) {
|
||||
devices.defaultChangeReason = !oldIsInNewList
|
||||
? DeviceChangeReason::Disconnected
|
||||
: !newIsInOldList
|
||||
? DeviceChangeReason::Connected
|
||||
: DeviceChangeReason::Manual;
|
||||
}
|
||||
maybeNotify(type);
|
||||
}
|
||||
|
||||
bool Environment::desktopCaptureAllowed() const {
|
||||
return _platform->desktopCaptureAllowed();
|
||||
}
|
||||
|
||||
std::optional<QString> Environment::uniqueDesktopCaptureSource() const {
|
||||
return _platform->uniqueDesktopCaptureSource();
|
||||
}
|
||||
|
||||
DeviceResolvedId Environment::threadSafeResolveId(
|
||||
const DeviceResolvedId &lastResolvedId,
|
||||
const QString &savedId) const {
|
||||
return _platform->threadSafeResolveId(lastResolvedId, savedId);
|
||||
}
|
||||
|
||||
void Environment::setCaptureMuted(bool muted) {
|
||||
_platform->setCaptureMuted(muted);
|
||||
}
|
||||
|
||||
void Environment::setCaptureMuteTracker(
|
||||
not_null<CaptureMuteTracker*> tracker,
|
||||
bool track) {
|
||||
_platform->setCaptureMuteTracker(tracker, track);
|
||||
}
|
||||
|
||||
RecordAvailability Environment::recordAvailability() const {
|
||||
const_cast<Environment*>(this)->refreshRecordAvailability();
|
||||
return _recordAvailability.current();
|
||||
}
|
||||
|
||||
auto Environment::recordAvailabilityValue() const
|
||||
->rpl::producer<RecordAvailability> {
|
||||
const_cast<Environment*>(this)->refreshRecordAvailability();
|
||||
return _recordAvailability.value();
|
||||
}
|
||||
|
||||
void Environment::refreshRecordAvailability() {
|
||||
if (_recordAvailabilityRefreshing) {
|
||||
_recordAvailabilityRefreshPending = true;
|
||||
return;
|
||||
}
|
||||
_recordAvailabilityRefreshing = true;
|
||||
const auto strong = static_cast<base::has_weak_ptr*>(this);
|
||||
const auto weak = base::make_weak(strong);
|
||||
crl::async([weak] {
|
||||
const auto type = DeviceType::Capture;
|
||||
const auto audio = details::EnvironmentOpenAL::DefaultId(type);
|
||||
#ifdef WEBRTC_MAC
|
||||
const auto vtype = DeviceType::Camera;
|
||||
const auto video = Platform::EnvironmentMac::DefaultId(vtype);
|
||||
#else // WEBRTC_MAC
|
||||
const auto video = details::EnvironmentVideoCapture::DefaultId();
|
||||
#endif // WEBRTC_MAC
|
||||
const auto availability = audio.isEmpty()
|
||||
? RecordAvailability::None
|
||||
: video.isEmpty()
|
||||
? RecordAvailability::Audio
|
||||
: RecordAvailability::VideoAndAudio;
|
||||
crl::on_main([weak, availability] {
|
||||
if (const auto strong = weak.get()) {
|
||||
const auto that = static_cast<Environment*>(strong);
|
||||
that->applyRecordAvailability(availability);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void Environment::applyRecordAvailability(RecordAvailability value) {
|
||||
_recordAvailability = value;
|
||||
_recordAvailabilityRefreshing = false;
|
||||
if (base::take(_recordAvailabilityRefreshPending)) {
|
||||
refreshRecordAvailability();
|
||||
}
|
||||
}
|
||||
|
||||
void Environment::defaultChanged(
|
||||
DeviceType type,
|
||||
DeviceChangeReason reason,
|
||||
QString nowId) {
|
||||
const auto guard = gsl::finally([&] {
|
||||
validateAfterDefaultChange(type);
|
||||
maybeNotify(type);
|
||||
});
|
||||
auto &devices = _devices[TypeToIndex(type)];
|
||||
devices.defaultChangeFrom = std::exchange(
|
||||
devices.defaultId,
|
||||
std::move(nowId));
|
||||
devices.defaultChangeReason = reason;
|
||||
}
|
||||
|
||||
void Environment::deviceStateChanged(
|
||||
DeviceType type,
|
||||
QString id,
|
||||
DeviceStateChange state) {
|
||||
const auto guard = gsl::finally([&] {
|
||||
validateAfterListChange(type);
|
||||
maybeNotify(type);
|
||||
});
|
||||
|
||||
auto &devices = _devices[TypeToIndex(type)];
|
||||
if (devices.refreshFullListOnChange) {
|
||||
refreshDevices(type);
|
||||
}
|
||||
const auto i = ranges::find(
|
||||
devices.list,
|
||||
id,
|
||||
&DeviceInfo::id);
|
||||
if (i == end(devices.list)) {
|
||||
if (state == DeviceStateChange::Disconnected) {
|
||||
return;
|
||||
} else if (auto info = _platform->device(type, id)) {
|
||||
devices.list.push_back(std::move(info));
|
||||
devices.listChanged = true;
|
||||
}
|
||||
} else if (state == DeviceStateChange::Disconnected) {
|
||||
const auto from = ranges::remove(
|
||||
i,
|
||||
end(devices.list),
|
||||
id,
|
||||
&DeviceInfo::id);
|
||||
if (from != end(devices.list)) {
|
||||
devices.list.erase(from, end(devices.list));
|
||||
devices.listChanged = true;
|
||||
}
|
||||
} else if (i != end(devices.list)) {
|
||||
const auto inactive = (state != DeviceStateChange::Active);
|
||||
if (i->inactive != inactive) {
|
||||
i->inactive = inactive;
|
||||
devices.listChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Environment::devicesForceRefresh(DeviceType type) {
|
||||
forceRefresh(type);
|
||||
}
|
||||
|
||||
void Environment::validateDefaultId(DeviceType type) const {
|
||||
_platform->defaultIdRequested(type);
|
||||
}
|
||||
|
||||
void Environment::validateDevices(DeviceType type) const {
|
||||
_platform->devicesRequested(type);
|
||||
}
|
||||
|
||||
bool Environment::synced(DeviceType type) const {
|
||||
const auto &devices = _devices[TypeToIndex(type)];
|
||||
return ranges::contains(
|
||||
devices.list,
|
||||
devices.defaultId,
|
||||
&DeviceInfo::id);
|
||||
}
|
||||
|
||||
void Environment::validateAfterListChange(DeviceType type) {
|
||||
auto &devices = _devices[TypeToIndex(type)];
|
||||
if (!devices.listChanged || synced(type)) {
|
||||
return;
|
||||
}
|
||||
devices.defaultChangeFrom = std::exchange(
|
||||
devices.defaultId,
|
||||
_platform->defaultId(type));
|
||||
devices.defaultChangeReason = DeviceChangeReason::Disconnected;
|
||||
if (devices.defaultChangeFrom != devices.defaultId
|
||||
&& synced(type)) {
|
||||
return;
|
||||
}
|
||||
refreshDevices(type);
|
||||
if (!devices.listChanged || !synced(type)) {
|
||||
logSyncError(type);
|
||||
}
|
||||
}
|
||||
|
||||
void Environment::validateAfterDefaultChange(DeviceType type) {
|
||||
auto &devices = _devices[TypeToIndex(type)];
|
||||
if (!devices.defaultChangeFrom
|
||||
|| devices.defaultChangeFrom == devices.defaultId
|
||||
|| synced(type)) {
|
||||
return;
|
||||
}
|
||||
refreshDevices(type);
|
||||
if (devices.listChanged && synced(type)) {
|
||||
return;
|
||||
}
|
||||
auto changedOneMoreFromId = std::exchange(
|
||||
devices.defaultId,
|
||||
_platform->defaultId(type));
|
||||
devices.defaultChangeReason = DeviceChangeReason::Disconnected;
|
||||
if (devices.defaultId == changedOneMoreFromId || !synced(type)) {
|
||||
logSyncError(type);
|
||||
}
|
||||
}
|
||||
|
||||
void Environment::maybeNotify(DeviceType type) {
|
||||
auto &devices = _devices[TypeToIndex(type)];
|
||||
if (devices.defaultChangeFrom == devices.defaultId) {
|
||||
devices.defaultChangeFrom = std::nullopt;
|
||||
}
|
||||
if (!devices.listChanged && !devices.defaultChangeFrom) {
|
||||
return;
|
||||
}
|
||||
const auto listChanged = base::take(devices.listChanged);
|
||||
const auto from = base::take(devices.defaultChangeFrom);
|
||||
const auto reason = base::take(devices.defaultChangeReason);
|
||||
devices.changes.fire({
|
||||
.defaultChange = {
|
||||
.wasId = from.value_or(QString()),
|
||||
.nowId = from ? devices.defaultId : QString(),
|
||||
.reason = (from ? reason : DeviceChangeReason::Manual),
|
||||
},
|
||||
.listChanged = listChanged,
|
||||
});
|
||||
}
|
||||
|
||||
void Environment::logSyncError(DeviceType type) {
|
||||
auto &devices = _devices[TypeToIndex(type)];
|
||||
LOG(("Media Error: "
|
||||
"Can't sync default device for type %1, default: %2, list: %3"
|
||||
).arg(TypeToString(type)
|
||||
).arg(devices.defaultId
|
||||
).arg(SerializeDevices(devices.list)));
|
||||
}
|
||||
|
||||
void Environment::logState(DeviceType type, LogType log) {
|
||||
auto &devices = _devices[TypeToIndex(type)];
|
||||
auto phrase = u"Media Info: Type %1, default: %2, list: %3"_q
|
||||
.arg(TypeToString(type))
|
||||
.arg(devices.defaultId)
|
||||
.arg(SerializeDevices(devices.list));
|
||||
if (log == LogType::Initial) {
|
||||
phrase += u", full list refresh: %1"_q.arg(
|
||||
devices.refreshFullListOnChange ? "true" : "false");
|
||||
}
|
||||
switch (log) {
|
||||
case LogType::Initial:
|
||||
case LogType::Always:
|
||||
LOG((phrase));
|
||||
break;
|
||||
case LogType::Debug:
|
||||
DEBUG_LOG((phrase));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Environment::refreshDevices(DeviceType type) {
|
||||
auto &devices = _devices[TypeToIndex(type)];
|
||||
auto list = _platform->devices(type);
|
||||
if (devices.list != list) {
|
||||
devices.list = std::move(list);
|
||||
devices.listChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Webrtc
|
||||
Reference in New Issue
Block a user