Commit 2ce4eb86 authored by Andreas Pehrson's avatar Andreas Pehrson
Browse files

Bug 1918746 - Don't stop video capturers before reconfiguring them. r=grulja,webrtc-reviewers,jib

Already today we reconfigure capturers without stopping them in cases where they
are shared between multiple gUM/gDM requests: We find the device capability
(for cameras) that satisfies all the requested capabilities (downscaling, frame
dropping allowed) and call StartCapture again with that.

Thus, there is no concern about camera backends not supporting this call
sequence.

Desktop capture backends have a simpler API (only Start, for Stop they have to
be destroyed) and are not actually re-started. Resolution is always captured in
full and frame rate is controlled by the timer that triggers CaptureFrame().

This patch makes content processes not request capture to be stopped when
updating their requested capability. This means the path described above will be
exercised more. This also brings with it some invariants that no longer hold,
but are handled explicitly instead: capabilities for a captureId may now be
updated on the fly, without prior removal.

Differential Revision: https://phabricator.services.mozilla.com/D222242
parent 0899941f
Loading
Loading
Loading
Loading
+42 −28
Original line number Diff line number Diff line
@@ -902,7 +902,6 @@ ipc::IPCResult CamerasParent::RecvStartCapture(
      mVideoCaptureThread, __func__,
      [this, self = RefPtr(this), aCapEngine, aCaptureId, aIpcCaps] {
        LOG_FUNCTION();
        CallbackHelper** cbh;
        int error = -1;

        if (!EnsureInitialized(aCapEngine)) {
@@ -910,9 +909,6 @@ ipc::IPCResult CamerasParent::RecvStartCapture(
                                           "CamerasParent::RecvStartCapture");
        }

        cbh = mCallbacks.AppendElement(new CallbackHelper(
            static_cast<CaptureEngine>(aCapEngine), aCaptureId, this));

        mEngines->ElementAt(aCapEngine)
            ->WithEntry(aCaptureId, [&](VideoEngine::CaptureEntry& cap) {
              webrtc::VideoCaptureCapability capability;
@@ -923,19 +919,21 @@ ipc::IPCResult CamerasParent::RecvStartCapture(
                  static_cast<webrtc::VideoType>(aIpcCaps.videoType());
              capability.interlaced = aIpcCaps.interlaced();

#ifndef FUZZING_SNAPSHOT
              MOZ_DIAGNOSTIC_ASSERT(sDeviceUniqueIDs.find(aCaptureId) ==
                                    sDeviceUniqueIDs.end());
#endif
              sDeviceUniqueIDs.emplace(aCaptureId,
                                       cap.VideoCapture()->CurrentDeviceName());

#ifndef FUZZING_SNAPSHOT
              if (sDeviceUniqueIDs.find(aCaptureId) == sDeviceUniqueIDs.end()) {
                sDeviceUniqueIDs.emplace(
                    aCaptureId, cap.VideoCapture()->CurrentDeviceName());
                sAllRequestedCapabilities.emplace(aCaptureId, capability);
              } else {
                // Starting capture for an id that already exists. Update its
                // requested capability.
                MOZ_DIAGNOSTIC_ASSERT(
                    strcmp(sDeviceUniqueIDs[aCaptureId],
                           cap.VideoCapture()->CurrentDeviceName()) == 0);
                MOZ_DIAGNOSTIC_ASSERT(
                  sAllRequestedCapabilities.find(aCaptureId) ==
                    sAllRequestedCapabilities.find(aCaptureId) !=
                    sAllRequestedCapabilities.end());
#endif
              sAllRequestedCapabilities.emplace(aCaptureId, capability);
                sAllRequestedCapabilities[aCaptureId] = capability;
              }

              if (aCapEngine == CameraEngine) {
                for (const auto& it : sDeviceUniqueIDs) {
@@ -999,11 +997,26 @@ ipc::IPCResult CamerasParent::RecvStartCapture(
                }
              }

              bool cbhExists = false;
              CallbackHelper** cbh = nullptr;
              for (auto* cb : mCallbacks) {
                if (cb->mCapEngine == aCapEngine &&
                    cb->mStreamId == (uint32_t)aCaptureId) {
                  cbhExists = true;
                  break;
                }
              }
              if (!cbhExists) {
                cbh = mCallbacks.AppendElement(new CallbackHelper(
                    static_cast<CaptureEngine>(aCapEngine), aCaptureId, this));
                cap.VideoCapture()->SetTrackingId(
                    (*cbh)->mTrackingId.mUniqueInProcId);
              }

              error = cap.VideoCapture()->StartCapture(capability);

              if (!error) {
                if (cbh) {
                  cap.VideoCapture()->RegisterCaptureDataCallback(
                      static_cast<rtc::VideoSinkInterface<webrtc::VideoFrame>*>(
                          *cbh));
@@ -1014,6 +1027,7 @@ ipc::IPCResult CamerasParent::RecvStartCapture(
                                       &CallbackHelper::OnCaptureEnded);
                    (*cbh)->mConnectedToCaptureEnded = true;
                  }
                }
              } else {
                sDeviceUniqueIDs.erase(aCaptureId);
                sAllRequestedCapabilities.erase(aCaptureId);
+24 −8
Original line number Diff line number Diff line
@@ -315,14 +315,21 @@ int32_t DesktopCaptureImpl::StartCapture(
    const VideoCaptureCapability& aCapability) {
  RTC_DCHECK_RUN_ON(&mControlThreadChecker);

  const int maxFps = std::max(aCapability.maxFPS, 1);
  if (mRequestedCapability) {
    // Already initialized
    MOZ_ASSERT(*mRequestedCapability == aCapability);

    MOZ_DIAGNOSTIC_ASSERT(mCaptureThread);
    if (std::max(mRequestedCapability->maxFPS, 1) == maxFps) {
      // No change in effective requested capability (only knob is fps).
      return 0;
    }
    mRequestedCapability = mozilla::Some(aCapability);
    MOZ_ALWAYS_SUCCEEDS(mCaptureThread->Dispatch(
        NS_NewRunnableFunction("DesktopCaptureImpl::UpdateOnThread",
                               [this, self = RefPtr(this), maxFps]() mutable {
                                 UpdateOnThread(maxFps);
                               })));
    return 0;
  }

  MOZ_ASSERT(!mCaptureThread);

  DesktopCapturer::SourceId sourceId = std::stoi(mDeviceUniqueId);
  std::unique_ptr capturer = CreateDesktopCapturerAndThread(
@@ -339,9 +346,7 @@ int32_t DesktopCaptureImpl::StartCapture(
  MOZ_ALWAYS_SUCCEEDS(mCaptureThread->Dispatch(NS_NewRunnableFunction(
      "DesktopCaptureImpl::InitOnThread",
      [this, self = RefPtr(this), capturer = std::move(capturer),
       maxFps = std::max(aCapability.maxFPS, 1)]() mutable {
        InitOnThread(std::move(capturer), maxFps);
      })));
       maxFps]() mutable { InitOnThread(std::move(capturer), maxFps); })));

  return 0;
}
@@ -523,6 +528,17 @@ void DesktopCaptureImpl::InitOnThread(
  CaptureFrameOnThread();
}

void DesktopCaptureImpl::UpdateOnThread(int aFramerate) {
  RTC_DCHECK_RUN_ON(&mCaptureThreadChecker);
  MOZ_DIAGNOSTIC_ASSERT(mCapturer);
  MOZ_DIAGNOSTIC_ASSERT(mCaptureTimer);

  mRequestedCaptureInterval = mozilla::Some(
      TimeDuration::FromSeconds(1. / static_cast<double>(aFramerate)));

  CaptureFrameOnThread();
}

void DesktopCaptureImpl::ShutdownOnThread() {
  RTC_DCHECK_RUN_ON(&mCaptureThreadChecker);
  if (mCaptureTimer) {
+1 −0
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ class DesktopCaptureImpl : public DesktopCapturer::Callback,
  // Maximum CPU usage in %.
  static constexpr uint32_t kMaxDesktopCaptureCpuUsage = 50;
  void InitOnThread(std::unique_ptr<DesktopCapturer> aCapturer, int aFramerate);
  void UpdateOnThread(int aFramerate);
  void ShutdownOnThread();
  // DesktopCapturer::Callback interface.
  void OnCaptureResult(DesktopCapturer::Result aResult,
+2 −15
Original line number Diff line number Diff line
@@ -216,7 +216,7 @@ nsresult MediaEngineRemoteVideoSource::Start() {
  LOG("%s", __PRETTY_FUNCTION__);
  AssertIsOnOwningThread();

  MOZ_ASSERT(mState == kAllocated || mState == kStopped);
  MOZ_ASSERT(mState == kAllocated || mState == kStarted || mState == kStopped);
  MOZ_ASSERT(mTrack);

  {
@@ -317,26 +317,13 @@ nsresult MediaEngineRemoteVideoSource::Reconfigure(
    return NS_OK;
  }

  bool started = mState == kStarted;
  if (started) {
    nsresult rv = Stop();
    if (NS_WARN_IF(NS_FAILED(rv))) {
      nsAutoCString name;
      GetErrorName(rv, name);
      LOG("Video source %p for video device %d Reconfigure() failed "
          "unexpectedly in Stop(). rv=%s",
          this, mCaptureId, name.Data());
      return NS_ERROR_UNEXPECTED;
    }
  }

  {
    MutexAutoLock lock(mMutex);
    // Start() applies mCapability on the device.
    mCapability = newCapability;
  }

  if (started) {
  if (mState == kStarted) {
    nsresult rv = Start();
    if (NS_WARN_IF(NS_FAILED(rv))) {
      nsAutoCString name;