| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- // 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
|