chromium/chrome/browser/media/webrtc/native_desktop_media_list.cc

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#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 {

// The enable/disable property of this feature has no impact. The feature is
// used solely to pass on the parameter below.
BASE_FEATURE();

// The maximum number of window thumbnails that are concurrently captured when
// the frame delivery mode is set to kMultipleSourcesRecurrent.
// ThumbnailCapturerMac is the only capturer at the moment that implements this.
const base::FeatureParam<int> kNativeDesktopMediaListMaxConcurrentStreams{};

#if defined(USE_AURA)
// Controls whether we take VideoCaptureLocks for aura windows to force them
// to be visible. This is required for their thumbnails to be taken correctly
// if native occlusion applying to the compositor
// (`kApplyNativeOcclusionToCompositor`) is enabled.
BASE_FEATURE();
#endif

// Update the list every second.
const int kDefaultNativeDesktopMediaListUpdatePeriod =;

// Returns a hash of a DesktopFrame content to detect when image for a desktop
// media source has changed, if the frame is valid, or absl::null_opt if not.
std::optional<size_t> GetFrameHash(webrtc::DesktopFrame* frame) {}

gfx::ImageSkia ScaleDesktopFrame(std::unique_ptr<webrtc::DesktopFrame> frame,
                                 gfx::Size size) {}

#if BUILDFLAG(IS_WIN)
// These Collector functions are repeatedly invoked by `::EnumWindows` and they
// add HWNDs to the vector contained in `param`. Return TRUE to continue the
// enumeration or FALSE to end early.
//
// Collects all capturable HWNDs which are owned by the current process.
BOOL CALLBACK CapturableCurrentProcessHwndCollector(HWND hwnd, LPARAM param) {
  DWORD process_id;
  ::GetWindowThreadProcessId(hwnd, &process_id);
  if (process_id != ::GetCurrentProcessId())
    return TRUE;

  // Skip windows that aren't visible or are minimized.
  if (!::IsWindowVisible(hwnd) || ::IsIconic(hwnd))
    return TRUE;

  // Skip windows which are not presented in the taskbar, e.g. the "Restore
  // pages?" window.
  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;
}

// Collects all HWNDs, which are enumerated in z-order, to create a reference
// for sorting.
BOOL CALLBACK AllHwndCollector(HWND hwnd, LPARAM param) {
  auto* hwnds = reinterpret_cast<std::vector<HWND>*>(param);
  hwnds->push_back(hwnd);
  return TRUE;
}
#endif  // BUILDFLAG(IS_WIN)

#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) {}

}  // namespace

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) {}

// static
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)
// static
std::vector<DesktopMediaListBase::SourceDescription>
NativeDesktopMediaList::Worker::GetCurrentProcessWindows() {
  std::vector<HWND> current_process_windows;
  if (!::EnumWindows(CapturableCurrentProcessHwndCollector,
                     reinterpret_cast<LPARAM>(&current_process_windows))) {
    return std::vector<SourceDescription>();
  }

  std::vector<SourceDescription> current_process_sources;
  for (HWND hwnd : current_process_windows) {
    // Leave these sources untitled, we must get their title from the UI thread.
    current_process_sources.emplace_back(
        DesktopMediaID(
            DesktopMediaID::Type::TYPE_WINDOW,
            reinterpret_cast<webrtc::DesktopCapturer::SourceId>(hwnd)),
        u"");
  }

  return current_process_sources;
}

// static
std::vector<DesktopMediaListBase::SourceDescription>
NativeDesktopMediaList::Worker::MergeAndSortWindowSources(
    std::vector<SourceDescription> sources_a,
    std::vector<SourceDescription> sources_b) {
  // |EnumWindows| enumerates top level windows in z-order, we use this as a
  // reference for sorting.
  std::vector<HWND> z_ordered_windows;
  if (!::EnumWindows(AllHwndCollector,
                     reinterpret_cast<LPARAM>(&z_ordered_windows))) {
    // Since we can't get the z-order for the windows, we can't sort them. So,
    // let's just concatenate.
    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  // BUILDFLAG(IS_WIN)

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  // defined(USE_AURA)

scoped_refptr<base::SingleThreadTaskRunner>
NativeDesktopMediaList::GetCapturerTaskRunnerForTesting() const {}