#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "chrome/browser/media/webrtc/native_desktop_media_list.h"
#include <memory>
#include <optional>
#include <utility>
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/hash/hash.h"
#include "base/message_loop/message_pump_type.h"
#include "base/metrics/field_trial_params.h"
#include "base/numerics/checked_math.h"
#include "base/ranges/algorithm.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/media/webrtc/desktop_media_list.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/desktop_capture.h"
#include "content/public/common/content_features.h"
#include "media/base/video_util.h"
#include "third_party/libyuv/include/libyuv/scale_argb.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/snapshot/snapshot.h"
#if defined(USE_AURA)
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/snapshot/snapshot_aura.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "components/remote_cocoa/browser/scoped_cg_window_id.h"
#endif
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include "base/strings/string_util_win.h"
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h"
#endif
DesktopMediaID;
namespace {
BASE_FEATURE(…);
const base::FeatureParam<int> kNativeDesktopMediaListMaxConcurrentStreams{ … };
#if defined(USE_AURA)
BASE_FEATURE(…);
#endif
const int kDefaultNativeDesktopMediaListUpdatePeriod = …;
std::optional<size_t> GetFrameHash(webrtc::DesktopFrame* frame) { … }
gfx::ImageSkia ScaleDesktopFrame(std::unique_ptr<webrtc::DesktopFrame> frame,
gfx::Size size) { … }
#if BUILDFLAG(IS_WIN)
BOOL CALLBACK CapturableCurrentProcessHwndCollector(HWND hwnd, LPARAM param) {
DWORD process_id;
::GetWindowThreadProcessId(hwnd, &process_id);
if (process_id != ::GetCurrentProcessId())
return TRUE;
if (!::IsWindowVisible(hwnd) || ::IsIconic(hwnd))
return TRUE;
HWND owner = ::GetWindow(hwnd, GW_OWNER);
LONG exstyle = ::GetWindowLong(hwnd, GWL_EXSTYLE);
if (owner && !(exstyle & WS_EX_APPWINDOW))
return TRUE;
auto* current_process_windows = reinterpret_cast<std::vector<HWND>*>(param);
current_process_windows->push_back(hwnd);
return TRUE;
}
BOOL CALLBACK AllHwndCollector(HWND hwnd, LPARAM param) {
auto* hwnds = reinterpret_cast<std::vector<HWND>*>(param);
hwnds->push_back(hwnd);
return TRUE;
}
#endif
#if BUILDFLAG(IS_MAC)
BASE_FEATURE(kWindowCaptureMacV2,
"WindowCaptureMacV2",
base::FEATURE_ENABLED_BY_DEFAULT);
#endif
content::DesktopMediaID::Type ConvertToDesktopMediaIDType(
DesktopMediaList::Type type) { … }
content::DesktopMediaID::Id GetUpdatedWindowId(
const content::DesktopMediaID& desktop_media_id,
bool is_source_list_delegated) { … }
ThumbnailCallback;
void AssignWindowIdAndUpdateThumbnail(
content::DesktopMediaID desktop_media_id,
bool is_source_list_delegated,
const gfx::ImageSkia& thumbnail,
ThumbnailCallback update_thumbnail_callback) { … }
}
class NativeDesktopMediaList::Worker
: public ThumbnailCapturer::Consumer,
public webrtc::DelegatedSourceListController::Observer { … };
NativeDesktopMediaList::Worker::Worker(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
base::WeakPtr<NativeDesktopMediaList> media_list,
DesktopMediaList::Type type,
std::unique_ptr<ThumbnailCapturer> capturer,
bool add_current_process_windows,
bool auto_show_delegated_source_list)
: … { … }
NativeDesktopMediaList::Worker::~Worker() { … }
void NativeDesktopMediaList::Worker::Start() { … }
void NativeDesktopMediaList::Worker::Refresh(bool update_thumbnails) { … }
void NativeDesktopMediaList::Worker::RefreshThumbnails(
std::vector<DesktopMediaID> native_ids,
const gfx::Size& thumbnail_size) { … }
std::vector<DesktopMediaListBase::SourceDescription>
NativeDesktopMediaList::Worker::FormatSources(
const webrtc::DesktopCapturer::SourceList& sources,
const DesktopMediaID::Type source_type,
DesktopMediaID::Id excluded_window_id) { … }
#if BUILDFLAG(IS_WIN)
std::vector<DesktopMediaListBase::SourceDescription>
NativeDesktopMediaList::Worker::GetCurrentProcessWindows() {
std::vector<HWND> current_process_windows;
if (!::EnumWindows(CapturableCurrentProcessHwndCollector,
reinterpret_cast<LPARAM>(¤t_process_windows))) {
return std::vector<SourceDescription>();
}
std::vector<SourceDescription> current_process_sources;
for (HWND hwnd : current_process_windows) {
current_process_sources.emplace_back(
DesktopMediaID(
DesktopMediaID::Type::TYPE_WINDOW,
reinterpret_cast<webrtc::DesktopCapturer::SourceId>(hwnd)),
u"");
}
return current_process_sources;
}
std::vector<DesktopMediaListBase::SourceDescription>
NativeDesktopMediaList::Worker::MergeAndSortWindowSources(
std::vector<SourceDescription> sources_a,
std::vector<SourceDescription> sources_b) {
std::vector<HWND> z_ordered_windows;
if (!::EnumWindows(AllHwndCollector,
reinterpret_cast<LPARAM>(&z_ordered_windows))) {
sources_a.insert(sources_a.end(),
std::make_move_iterator(sources_b.begin()),
std::make_move_iterator(sources_b.end()));
return sources_a;
}
std::vector<const std::vector<SourceDescription>*> source_containers = {
&sources_a, &sources_b};
std::vector<SourceDescription> sorted_sources;
auto id_hwnd_projection = [](const SourceDescription& source) {
return reinterpret_cast<const HWND>(source.id.id);
};
for (HWND window : z_ordered_windows) {
for (const auto* source_container : source_containers) {
auto source_it =
base::ranges::find(*source_container, window, id_hwnd_projection);
if (source_it != source_container->end()) {
sorted_sources.push_back(*source_it);
break;
}
}
}
return sorted_sources;
}
#endif
void NativeDesktopMediaList::Worker::RefreshNextThumbnail() { … }
void NativeDesktopMediaList::Worker::OnCaptureResult(
webrtc::DesktopCapturer::Result result,
std::unique_ptr<webrtc::DesktopFrame> frame) { … }
void NativeDesktopMediaList::Worker::OnRecurrentCaptureResult(
ThumbnailCapturer::Result result,
std::unique_ptr<webrtc::DesktopFrame> frame,
ThumbnailCapturer::SourceId source_id) { … }
void NativeDesktopMediaList::Worker::OnSourceListUpdated() { … }
void NativeDesktopMediaList::Worker::ClearDelegatedSourceListSelection() { … }
void NativeDesktopMediaList::Worker::SetExcludedWindow(
DesktopMediaID::Id excluded_window_id) { … }
void NativeDesktopMediaList::Worker::SetThumbnailSize(
const gfx::Size& thumbnail_size) { … }
void NativeDesktopMediaList::Worker::FocusList() { … }
void NativeDesktopMediaList::Worker::HideList() { … }
void NativeDesktopMediaList::Worker::ShowDelegatedList() { … }
void NativeDesktopMediaList::Worker::OnSelection() { … }
void NativeDesktopMediaList::Worker::OnCancelled() { … }
void NativeDesktopMediaList::Worker::OnError() { … }
NativeDesktopMediaList::NativeDesktopMediaList(
DesktopMediaList::Type type,
std::unique_ptr<ThumbnailCapturer> capturer)
: … { … }
NativeDesktopMediaList::NativeDesktopMediaList(
DesktopMediaList::Type type,
std::unique_ptr<ThumbnailCapturer> capturer,
bool add_current_process_windows,
bool auto_show_delegated_source_list)
: … { … }
NativeDesktopMediaList::~NativeDesktopMediaList() { … }
void NativeDesktopMediaList::SetViewDialogWindowId(DesktopMediaID dialog_id) { … }
void NativeDesktopMediaList::SetThumbnailSize(const gfx::Size& thumbnail_size) { … }
bool NativeDesktopMediaList::IsSourceListDelegated() const { … }
void NativeDesktopMediaList::StartDelegatedCapturer() { … }
void NativeDesktopMediaList::StartCapturer() { … }
void NativeDesktopMediaList::ClearDelegatedSourceListSelection() { … }
void NativeDesktopMediaList::FocusList() { … }
void NativeDesktopMediaList::HideList() { … }
void NativeDesktopMediaList::ShowDelegatedList() { … }
void NativeDesktopMediaList::Refresh(bool update_thumbnails) { … }
void NativeDesktopMediaList::RefreshForVizFrameSinkWindows(
std::vector<SourceDescription> sources,
bool update_thumbnails) { … }
void NativeDesktopMediaList::UpdateNativeThumbnailsFinished() { … }
#if defined(USE_AURA)
void NativeDesktopMediaList::CaptureAuraWindowThumbnail(
const DesktopMediaID& id) { … }
void NativeDesktopMediaList::OnAuraThumbnailCaptured(const DesktopMediaID& id,
gfx::Image image) { … }
#endif
scoped_refptr<base::SingleThreadTaskRunner>
NativeDesktopMediaList::GetCapturerTaskRunnerForTesting() const { … }