tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

desktop_capture_impl.cc (21226B)


      1 /*
      2 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include "desktop_capture_impl.h"
     12 
     13 #include <charconv>
     14 #include <cstdlib>
     15 #include <memory>
     16 #include <string>
     17 
     18 #include "CamerasTypes.h"
     19 #include "VideoEngine.h"
     20 #include "api/video/i420_buffer.h"
     21 #include "common_video/libyuv/include/webrtc_libyuv.h"
     22 #include "desktop_device_info.h"
     23 #include "libyuv/convert.h"
     24 #include "modules/desktop_capture/desktop_and_cursor_composer.h"
     25 #include "modules/desktop_capture/desktop_capture_options.h"
     26 #include "modules/desktop_capture/desktop_capturer_differ_wrapper.h"
     27 #include "modules/desktop_capture/desktop_frame.h"
     28 #include "modules/video_capture/video_capture.h"
     29 #include "mozilla/StaticPrefs_media.h"
     30 #include "mozilla/SyncRunnable.h"
     31 #include "mozilla/TimeStamp.h"
     32 #include "nsThreadUtils.h"
     33 #include "rtc_base/logging.h"
     34 #include "rtc_base/time_utils.h"
     35 #include "rtc_base/trace_event.h"
     36 #include "tab_capturer.h"
     37 
     38 #ifdef XP_MACOSX
     39 #  include "modules/desktop_capture/mac/screen_capturer_sck.h"
     40 #endif
     41 
     42 using mozilla::NewRunnableMethod;
     43 using mozilla::TabCapturerWebrtc;
     44 using mozilla::TimeDuration;
     45 using mozilla::camera::CaptureDeviceType;
     46 using mozilla::camera::CaptureEngine;
     47 
     48 static void CaptureFrameOnThread(nsITimer* aTimer, void* aClosure) {
     49  static_cast<webrtc::DesktopCaptureImpl*>(aClosure)->CaptureFrameOnThread();
     50 }
     51 
     52 namespace webrtc {
     53 
     54 DesktopCaptureImpl* DesktopCaptureImpl::Create(const int32_t aModuleId,
     55                                               const char* aUniqueId,
     56                                               const CaptureDeviceType aType) {
     57  return new webrtc::RefCountedObject<DesktopCaptureImpl>(aModuleId, aUniqueId,
     58                                                          aType);
     59 }
     60 
     61 static DesktopCaptureOptions CreateDesktopCaptureOptions() {
     62  DesktopCaptureOptions options;
     63 // Help avoid an X11 deadlock, see bug 1456101.
     64 #ifdef MOZ_X11
     65  MOZ_ALWAYS_SUCCEEDS(mozilla::SyncRunnable::DispatchToThread(
     66      mozilla::GetMainThreadSerialEventTarget(),
     67      NS_NewRunnableFunction(__func__, [&] {
     68        options = DesktopCaptureOptions::CreateDefault();
     69      })));
     70 #else
     71  options = DesktopCaptureOptions::CreateDefault();
     72 #endif
     73 
     74  // Leave desktop effects enabled during WebRTC captures.
     75  options.set_disable_effects(false);
     76 
     77 #if defined(WEBRTC_WIN)
     78  options.set_allow_directx_capturer(
     79      mozilla::StaticPrefs::media_webrtc_capture_allow_directx());
     80  options.set_allow_cropping_window_capturer(true);
     81 #  if defined(RTC_ENABLE_WIN_WGC)
     82  if (mozilla::StaticPrefs::media_webrtc_capture_screen_allow_wgc()) {
     83    options.set_allow_wgc_screen_capturer(true);
     84    options.set_allow_wgc_zero_hertz(
     85        mozilla::StaticPrefs::media_webrtc_capture_wgc_allow_zero_hertz());
     86  }
     87  if (mozilla::StaticPrefs::media_webrtc_capture_window_allow_wgc()) {
     88    options.set_allow_wgc_window_capturer(true);
     89    options.set_allow_wgc_zero_hertz(
     90        mozilla::StaticPrefs::media_webrtc_capture_wgc_allow_zero_hertz());
     91  }
     92 #  endif
     93 #endif
     94 
     95 #if defined(WEBRTC_MAC)
     96  options.set_prefer_cursor_embedded(true);
     97  options.set_allow_sck_capturer(
     98      mozilla::StaticPrefs::
     99          media_getdisplaymedia_screencapturekit_enabled_AtStartup());
    100  options.set_allow_sck_system_picker(
    101      GenericCapturerSckWithPickerAvailable() &&
    102      mozilla::StaticPrefs::
    103          media_getdisplaymedia_screencapturekit_picker_enabled_AtStartup());
    104  options.set_allow_iosurface(
    105      mozilla::StaticPrefs::media_webrtc_capture_allow_iosurface());
    106 #endif
    107 
    108 #if defined(WEBRTC_USE_PIPEWIRE)
    109  options.set_allow_pipewire(
    110      mozilla::StaticPrefs::media_webrtc_capture_allow_pipewire() &&
    111      webrtc::DesktopCapturer::IsRunningUnderWayland());
    112 #endif
    113 
    114  return options;
    115 }
    116 
    117 std::shared_ptr<VideoCaptureModule::DeviceInfo>
    118 DesktopCaptureImpl::CreateDeviceInfo(const int32_t aId,
    119                                     const CaptureDeviceType aType) {
    120  if (aType == CaptureDeviceType::Screen) {
    121    auto options = CreateDesktopCaptureOptions();
    122 #ifdef XP_MACOSX
    123    if (!options.allow_sck_system_picker() &&
    124        !mozilla::StaticPrefs::
    125            media_getdisplaymedia_screencapturekit_enumeration_enabled_AtStartup()) {
    126      options.set_allow_sck_capturer(false);
    127    }
    128 #endif
    129    return CreateDesktopDeviceInfo(aId, CreateScreenCaptureInfo(options));
    130  }
    131  if (aType == CaptureDeviceType::Window) {
    132    return CreateDesktopDeviceInfo(
    133        aId, CreateWindowCaptureInfo(CreateDesktopCaptureOptions()));
    134  }
    135  if (aType == CaptureDeviceType::Browser) {
    136    return CreateTabDeviceInfo(aId, CreateTabCaptureInfo());
    137  }
    138  return nullptr;
    139 }
    140 
    141 const char* DesktopCaptureImpl::CurrentDeviceName() const {
    142  return mDeviceUniqueId.c_str();
    143 }
    144 
    145 static std::unique_ptr<DesktopCapturer> CreateTabCapturer(
    146    const DesktopCaptureOptions& options, DesktopCapturer::SourceId aSourceId,
    147    nsCOMPtr<nsISerialEventTarget> aCaptureThread) {
    148  std::unique_ptr<DesktopCapturer> capturer =
    149      TabCapturerWebrtc::Create(aSourceId, std::move(aCaptureThread));
    150  if (capturer && options.detect_updated_region()) {
    151    capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
    152  }
    153 
    154  return capturer;
    155 }
    156 
    157 static std::unique_ptr<DesktopCapturer> CreateDesktopCapturerAndThread(
    158    CaptureDeviceType aDeviceType, DesktopCapturer::SourceId aSourceId,
    159    nsIThread** aOutThread) {
    160  DesktopCaptureOptions options = CreateDesktopCaptureOptions();
    161  auto ensureThread = [&]() {
    162    if (*aOutThread) {
    163      return *aOutThread;
    164    }
    165 
    166    nsIThreadManager::ThreadCreationOptions threadOptions;
    167 #if defined(XP_WIN) || defined(XP_MACOSX)
    168    // Windows desktop capture needs a UI thread.
    169    // Mac screen capture needs a thread with a CFRunLoop.
    170    threadOptions.isUiThread = true;
    171 #endif
    172    NS_NewNamedThread("DesktopCapture", aOutThread, nullptr, threadOptions);
    173    return *aOutThread;
    174  };
    175 
    176  auto createCapturer = [&]() -> std::unique_ptr<DesktopCapturer> {
    177    if (aDeviceType == CaptureDeviceType::Screen ||
    178        aDeviceType == CaptureDeviceType::Window) {
    179      auto capturer = DesktopCapturer::CreateGenericCapturer(options);
    180      if (capturer) {
    181 #if defined(XP_MACOSX)
    182        // See comment for same conditional below.
    183        if (options.prefer_cursor_embedded() && options.allow_sck_capturer() &&
    184            ScreenCapturerSckAvailable()) {
    185          return capturer;
    186        }
    187 #endif
    188        return std::make_unique<DesktopAndCursorComposer>(std::move(capturer),
    189                                                          options);
    190      }
    191    }
    192 
    193    if (aDeviceType == CaptureDeviceType::Screen) {
    194      auto capturer = DesktopCapturer::CreateScreenCapturer(options);
    195      if (!capturer) {
    196        return capturer;
    197      }
    198 
    199      capturer->SelectSource(aSourceId);
    200 
    201 #if defined(XP_MACOSX)
    202      // The MouseCursorMonitor on macOS is rather expensive, as for every
    203      // pulled frame it compares all pixels of the cursors used for the current
    204      // and last frames. Getting to the pixels may also incur a conversion.
    205      //
    206      // Note that this comparison happens even if the backend reports it had
    207      // embedded the cursor already, as the embedding only affects composing
    208      // the monitored cursor into a captured frame.
    209      //
    210      // Avoid the composer (and monitor) if we can.
    211      if (options.prefer_cursor_embedded() && options.allow_sck_capturer() &&
    212          ScreenCapturerSckAvailable()) {
    213        return capturer;
    214      }
    215 #endif
    216 
    217      return std::make_unique<DesktopAndCursorComposer>(std::move(capturer),
    218                                                        options);
    219    }
    220 
    221    if (aDeviceType == CaptureDeviceType::Window) {
    222 #if defined(RTC_ENABLE_WIN_WGC)
    223      options.set_allow_wgc_capturer_fallback(true);
    224 #endif
    225      auto capturer = DesktopCapturer::CreateWindowCapturer(options);
    226      if (!capturer) {
    227        return capturer;
    228      }
    229 
    230      capturer->SelectSource(aSourceId);
    231 
    232      return std::make_unique<DesktopAndCursorComposer>(std::move(capturer),
    233                                                        options);
    234    }
    235 
    236    if (aDeviceType == CaptureDeviceType::Browser) {
    237      // XXX We don't capture cursors, so avoid the extra indirection layer. We
    238      // could also pass null for the pMouseCursorMonitor.
    239      return CreateTabCapturer(options, aSourceId, ensureThread());
    240    }
    241 
    242    return nullptr;
    243  };
    244 
    245  std::unique_ptr<DesktopCapturer> capturer = createCapturer();
    246  if (!capturer) {
    247    return capturer;
    248  }
    249 
    250  MOZ_ASSERT(capturer);
    251  ensureThread();
    252 
    253  return capturer;
    254 }
    255 
    256 DesktopCaptureImpl::DesktopCaptureImpl(const int32_t aId, const char* aUniqueId,
    257                                       const CaptureDeviceType aType)
    258    : mModuleId(aId),
    259      mTrackingId(mozilla::TrackingId(CaptureEngineToTrackingSourceStr([&] {
    260                                        switch (aType) {
    261                                          case CaptureDeviceType::Screen:
    262                                            return CaptureEngine::ScreenEngine;
    263                                          case CaptureDeviceType::Window:
    264                                            return CaptureEngine::WinEngine;
    265                                          case CaptureDeviceType::Browser:
    266                                            return CaptureEngine::BrowserEngine;
    267                                          default:
    268                                            return CaptureEngine::InvalidEngine;
    269                                        }
    270                                      }()),
    271                                      aId)),
    272      mDeviceUniqueId(aUniqueId),
    273      mDeviceType(aType),
    274      mControlThread(mozilla::GetCurrentSerialEventTarget()),
    275      mNextFrameMinimumTime(Timestamp::Zero()),
    276      mCallbacks("DesktopCaptureImpl::mCallbacks"),
    277      mBufferPool(false, 2) {}
    278 
    279 DesktopCaptureImpl::~DesktopCaptureImpl() {
    280  MOZ_ASSERT(!mCaptureThread);
    281  MOZ_ASSERT(!mRequestedCapability);
    282 }
    283 
    284 void DesktopCaptureImpl::RegisterCaptureDataCallback(
    285    webrtc::VideoSinkInterface<VideoFrame>* aDataCallback) {
    286  auto callbacks = mCallbacks.Lock();
    287  callbacks->insert(aDataCallback);
    288 }
    289 
    290 void DesktopCaptureImpl::DeRegisterCaptureDataCallback(
    291    webrtc::VideoSinkInterface<VideoFrame>* aDataCallback) {
    292  auto callbacks = mCallbacks.Lock();
    293  auto it = callbacks->find(aDataCallback);
    294  if (it != callbacks->end()) {
    295    callbacks->erase(it);
    296  }
    297 }
    298 
    299 int32_t DesktopCaptureImpl::StopCaptureIfAllClientsClose() {
    300  {
    301    auto callbacks = mCallbacks.Lock();
    302    if (!callbacks->empty()) {
    303      return 0;
    304    }
    305  }
    306  return StopCapture();
    307 }
    308 
    309 int32_t DesktopCaptureImpl::SetCaptureRotation(VideoRotation aRotation) {
    310  MOZ_ASSERT_UNREACHABLE("Unused");
    311  return -1;
    312 }
    313 
    314 bool DesktopCaptureImpl::SetApplyRotation(bool aEnable) { return true; }
    315 
    316 int32_t DesktopCaptureImpl::StartCapture(
    317    const VideoCaptureCapability& aCapability) {
    318  RTC_DCHECK_RUN_ON(&mControlThreadChecker);
    319 
    320  const int maxFps = std::max(aCapability.maxFPS, 1);
    321  if (mRequestedCapability) {
    322    MOZ_DIAGNOSTIC_ASSERT(mCaptureThread);
    323    if (std::max(mRequestedCapability->maxFPS, 1) == maxFps) {
    324      // No change in effective requested capability (only knob is fps).
    325      return 0;
    326    }
    327    mRequestedCapability = mozilla::Some(aCapability);
    328    MOZ_ALWAYS_SUCCEEDS(mCaptureThread->Dispatch(
    329        NS_NewRunnableFunction("DesktopCaptureImpl::UpdateOnThread",
    330                               [this, self = RefPtr(this), maxFps]() mutable {
    331                                 UpdateOnThread(maxFps);
    332                               })));
    333    return 0;
    334  }
    335  DesktopCapturer::SourceId sourceId{};
    336  auto [firstNoMatch, error] = std::from_chars(
    337      mDeviceUniqueId.data(), mDeviceUniqueId.data() + mDeviceUniqueId.size(),
    338      sourceId);
    339  if (error != std::errc() ||
    340      firstNoMatch != (mDeviceUniqueId.data() + mDeviceUniqueId.size())) {
    341    std::string errorMsg =
    342        error == std::errc::invalid_argument
    343            ? "Invalid value of mDeviceUniqueId."
    344        : error == std::errc::result_out_of_range
    345            ? "mDeviceUniqueIds value is out of range to cast."
    346            : "An unknown error has occurred.";
    347    MOZ_ASSERT_UNREACHABLE("Error casting mDeviceUniqueId to SourceId.");
    348    RTC_LOG(LS_ERROR)
    349        << "Attempting to cast mDeviceUniqueId to SourceId returned an error: "
    350        << errorMsg;
    351    return -1;
    352  }
    353  std::unique_ptr capturer = CreateDesktopCapturerAndThread(
    354      mDeviceType, sourceId, getter_AddRefs(mCaptureThread));
    355 
    356  MOZ_ASSERT(!capturer == !mCaptureThread);
    357  if (!capturer) {
    358    return -1;
    359  }
    360 
    361  mRequestedCapability = mozilla::Some(aCapability);
    362  mCaptureThreadChecker.Detach();
    363 
    364  MOZ_ALWAYS_SUCCEEDS(mCaptureThread->Dispatch(NS_NewRunnableFunction(
    365      "DesktopCaptureImpl::InitOnThread",
    366      [this, self = RefPtr(this), capturer = std::move(capturer),
    367       maxFps]() mutable { InitOnThread(std::move(capturer), maxFps); })));
    368 
    369  return 0;
    370 }
    371 
    372 bool DesktopCaptureImpl::FocusOnSelectedSource() {
    373  RTC_DCHECK_RUN_ON(&mControlThreadChecker);
    374  if (!mCaptureThread) {
    375    MOZ_ASSERT_UNREACHABLE(
    376        "FocusOnSelectedSource must be called after StartCapture");
    377    return false;
    378  }
    379 
    380  bool success = false;
    381  MOZ_ALWAYS_SUCCEEDS(mozilla::SyncRunnable::DispatchToThread(
    382      mCaptureThread, NS_NewRunnableFunction(__func__, [&] {
    383        RTC_DCHECK_RUN_ON(&mCaptureThreadChecker);
    384        MOZ_ASSERT(mCapturer);
    385        success = mCapturer && mCapturer->FocusOnSelectedSource();
    386      })));
    387  return success;
    388 }
    389 
    390 int32_t DesktopCaptureImpl::StopCapture() {
    391  RTC_DCHECK_RUN_ON(&mControlThreadChecker);
    392  if (mRequestedCapability) {
    393    // Sync-cancel the capture timer so no CaptureFrame calls will come in after
    394    // we return.
    395    MOZ_ALWAYS_SUCCEEDS(mozilla::SyncRunnable::DispatchToThread(
    396        mCaptureThread,
    397        NewRunnableMethod(__func__, this,
    398                          &DesktopCaptureImpl::ShutdownOnThread)));
    399 
    400    mRequestedCapability = mozilla::Nothing();
    401  }
    402 
    403  mBufferPool.Release();
    404 
    405  if (mCaptureThread) {
    406    // CaptureThread shutdown.
    407    mCaptureThread->AsyncShutdown();
    408    mCaptureThread = nullptr;
    409  }
    410 
    411  return 0;
    412 }
    413 
    414 bool DesktopCaptureImpl::CaptureStarted() {
    415  MOZ_ASSERT_UNREACHABLE("Unused");
    416  return true;
    417 }
    418 
    419 int32_t DesktopCaptureImpl::CaptureSettings(VideoCaptureCapability& aSettings) {
    420  MOZ_ASSERT_UNREACHABLE("Unused");
    421  return -1;
    422 }
    423 
    424 void DesktopCaptureImpl::OnCaptureResult(DesktopCapturer::Result aResult,
    425                                         std::unique_ptr<DesktopFrame> aFrame) {
    426  RTC_DCHECK_RUN_ON(&mCaptureThreadChecker);
    427 
    428  if (aResult == DesktopCapturer::Result::ERROR_PERMANENT) {
    429    // This is non-recoverable error, therefore stop asking for frames
    430    mCaptureTimer->Cancel();
    431    mCaptureTimer = nullptr;
    432    mCaptureEndedEvent.Notify();
    433    return;
    434  }
    435 
    436  if (!aFrame) {
    437    return;
    438  }
    439 
    440  const auto startProcessTime = Timestamp::Micros(webrtc::TimeMicros());
    441  auto frameTime = startProcessTime;
    442  if (auto diff = startProcessTime - mNextFrameMinimumTime;
    443      diff < TimeDelta::Zero()) {
    444    if (diff > TimeDelta::Millis(-1)) {
    445      // Two consecutive frames within a millisecond is OK. It could happen due
    446      // to timing.
    447      frameTime = mNextFrameMinimumTime;
    448    } else {
    449      // Three consecutive frames within two milliseconds seems too much, drop
    450      // one.
    451      MOZ_ASSERT(diff >= TimeDelta::Millis(-2));
    452      RTC_LOG(LS_WARNING) << "DesktopCapture render time is getting too far "
    453                             "ahead. Framerate is unexpectedly high.";
    454      return;
    455    }
    456  }
    457 
    458  uint8_t* videoFrame = aFrame->data();
    459  VideoCaptureCapability frameInfo;
    460  frameInfo.width = aFrame->size().width();
    461  frameInfo.height = aFrame->size().height();
    462  frameInfo.videoType = VideoType::kARGB;
    463 
    464  size_t videoFrameLength =
    465      frameInfo.width * frameInfo.height * DesktopFrame::kBytesPerPixel;
    466 
    467  const int32_t width = frameInfo.width;
    468  const int32_t height = frameInfo.height;
    469 
    470  // Not encoded, convert to I420.
    471  if (frameInfo.videoType != VideoType::kMJPEG &&
    472      CalcBufferSize(frameInfo.videoType, width, abs(height)) !=
    473          videoFrameLength) {
    474    RTC_LOG(LS_ERROR) << "Wrong incoming frame length.";
    475    return;
    476  }
    477 
    478  // Setting absolute height (in case it was negative).
    479  // In Windows, the image starts bottom left, instead of top left.
    480  // Setting a negative source height, inverts the image (within LibYuv).
    481 
    482  mozilla::PerformanceRecorder<mozilla::CopyVideoStage> rec(
    483      "DesktopCaptureImpl::ConvertToI420"_ns, mTrackingId, width, abs(height));
    484 
    485  webrtc::scoped_refptr<webrtc::I420Buffer> buffer =
    486      mBufferPool.CreateI420Buffer(width, abs(height));
    487  if (!buffer) {
    488    RTC_LOG(LS_ERROR) << "Failed to allocate I420Buffer from pool.";
    489    MOZ_ASSERT_UNREACHABLE(
    490        "We might fail to allocate a buffer, but with this "
    491        "being a recycling pool that shouldn't happen");
    492    return;
    493  }
    494 
    495  const int conversionResult = libyuv::ConvertToI420(
    496      videoFrame, videoFrameLength, buffer->MutableDataY(), buffer->StrideY(),
    497      buffer->MutableDataU(), buffer->StrideU(), buffer->MutableDataV(),
    498      buffer->StrideV(), 0, 0,  // No Cropping
    499      aFrame->stride() / DesktopFrame::kBytesPerPixel, height, width, height,
    500      libyuv::kRotate0, ConvertVideoType(frameInfo.videoType));
    501  if (conversionResult != 0) {
    502    RTC_LOG(LS_ERROR) << "Failed to convert capture frame from type "
    503                      << static_cast<int>(frameInfo.videoType) << "to I420.";
    504    return;
    505  }
    506  rec.Record();
    507 
    508  NotifyOnFrame(VideoFrame::Builder()
    509                    .set_video_frame_buffer(buffer)
    510                    .set_timestamp_us(frameTime.us())
    511                    .build());
    512 
    513  const TimeDelta processTime =
    514      Timestamp::Micros(webrtc::TimeMicros()) - startProcessTime;
    515 
    516  if (processTime > TimeDelta::Millis(10)) {
    517    RTC_LOG(LS_WARNING)
    518        << "Too long processing time of incoming frame with dimensions "
    519        << width << "x" << height << ": " << processTime.ms() << " ms";
    520  }
    521 }
    522 
    523 void DesktopCaptureImpl::NotifyOnFrame(const VideoFrame& aFrame) {
    524  RTC_DCHECK_RUN_ON(&mCaptureThreadChecker);
    525  // Set the next frame's minimum time to ensure two consecutive frames don't
    526  // have an identical render time (which is in milliseconds).
    527  Timestamp nextFrameMinimumTime =
    528      Timestamp::Millis(aFrame.render_time_ms()) + TimeDelta::Millis(1);
    529 
    530  MOZ_ASSERT(nextFrameMinimumTime >= mNextFrameMinimumTime);
    531 
    532  mNextFrameMinimumTime = nextFrameMinimumTime;
    533  auto callbacks = mCallbacks.Lock();
    534  for (auto* cb : *callbacks) {
    535    cb->OnFrame(aFrame);
    536  }
    537 }
    538 
    539 void DesktopCaptureImpl::InitOnThread(
    540    std::unique_ptr<DesktopCapturer> aCapturer, int aFramerate) {
    541  RTC_DCHECK_RUN_ON(&mCaptureThreadChecker);
    542 
    543  mCapturer = std::move(aCapturer);
    544 
    545  // We need to call Start on the same thread we call CaptureFrame on.
    546  mCapturer->Start(this);
    547 
    548  mCaptureTimer = NS_NewTimer();
    549  mRequestedCaptureInterval = mozilla::Some(
    550      TimeDuration::FromSeconds(1. / static_cast<double>(aFramerate)));
    551 
    552  CaptureFrameOnThread();
    553 }
    554 
    555 void DesktopCaptureImpl::UpdateOnThread(int aFramerate) {
    556  RTC_DCHECK_RUN_ON(&mCaptureThreadChecker);
    557  MOZ_DIAGNOSTIC_ASSERT(mCapturer);
    558  MOZ_DIAGNOSTIC_ASSERT(mCaptureTimer);
    559 
    560  mRequestedCaptureInterval = mozilla::Some(
    561      TimeDuration::FromSeconds(1. / static_cast<double>(aFramerate)));
    562 
    563  CaptureFrameOnThread();
    564 }
    565 
    566 void DesktopCaptureImpl::ShutdownOnThread() {
    567  RTC_DCHECK_RUN_ON(&mCaptureThreadChecker);
    568  if (mCaptureTimer) {
    569    mCaptureTimer->Cancel();
    570    mCaptureTimer = nullptr;
    571  }
    572 
    573  // DesktopCapturer dtor blocks until fully shut down. TabCapturerWebrtc needs
    574  // the capture thread to be alive.
    575  mCapturer = nullptr;
    576 
    577  mRequestedCaptureInterval = mozilla::Nothing();
    578 }
    579 
    580 void DesktopCaptureImpl::CaptureFrameOnThread() {
    581  RTC_DCHECK_RUN_ON(&mCaptureThreadChecker);
    582 
    583  auto start = mozilla::TimeStamp::Now();
    584  mCapturer->CaptureFrame();
    585 
    586  // Sync result callback may have canceled the timer in CaptureFrame because of
    587  // a permanent error and there is no point to continue.
    588  if (!mCaptureTimer) {
    589    return;
    590  }
    591 
    592  auto end = mozilla::TimeStamp::Now();
    593 
    594  // Calculate next capture time.
    595  const auto duration = end - start;
    596  const auto timeUntilRequestedCapture = *mRequestedCaptureInterval - duration;
    597 
    598  // Use at most x% CPU or limit framerate
    599  constexpr float sleepTimeFactor =
    600      (100.0f / kMaxDesktopCaptureCpuUsage) - 1.0f;
    601  static_assert(sleepTimeFactor >= 0.0);
    602  static_assert(sleepTimeFactor < 100.0);
    603  const auto sleepTime = duration.MultDouble(sleepTimeFactor);
    604 
    605  mCaptureTimer->InitHighResolutionWithNamedFuncCallback(
    606      &::CaptureFrameOnThread, this,
    607      std::max(timeUntilRequestedCapture, sleepTime), nsITimer::TYPE_ONE_SHOT,
    608      "DesktopCaptureImpl::mCaptureTimer"_ns);
    609 }
    610 
    611 mozilla::MediaEventSource<void>* DesktopCaptureImpl::CaptureEndedEvent() {
    612  return &mCaptureEndedEvent;
    613 }
    614 
    615 }  // namespace webrtc