tor-browser

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

VideoEngine.cpp (9265B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set sw=2 ts=8 et ft=cpp : */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "VideoEngine.h"
      8 
      9 #include "libwebrtcglue/SystemTime.h"
     10 #include "video_engine/desktop_capture_impl.h"
     11 
     12 #ifdef MOZ_WIDGET_ANDROID
     13 #  include "mozilla/jni/Utils.h"
     14 #endif
     15 
     16 #if defined(ANDROID)
     17 namespace webrtc {
     18 int32_t SetCaptureAndroidVM(JavaVM* javaVM);
     19 }
     20 #endif
     21 
     22 namespace mozilla::camera {
     23 
     24 mozilla::LazyLogModule gVideoEngineLog("VideoEngine");
     25 #define LOG(args) MOZ_LOG(gVideoEngineLog, mozilla::LogLevel::Debug, args)
     26 #define LOG_ENABLED() MOZ_LOG_TEST(gVideoEngineLog, mozilla::LogLevel::Debug)
     27 
     28 #if defined(ANDROID)
     29 int VideoEngine::SetAndroidObjects() {
     30  LOG(("%s", __PRETTY_FUNCTION__));
     31 
     32  JavaVM* const javaVM = mozilla::jni::GetVM();
     33  if (!javaVM || webrtc::SetCaptureAndroidVM(javaVM) != 0) {
     34    LOG(("Could not set capture Android VM"));
     35    return -1;
     36  }
     37 #  ifdef WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER
     38  if (webrtc::SetRenderAndroidVM(javaVM) != 0) {
     39    LOG(("Could not set render Android VM"));
     40    return -1;
     41  }
     42 #  endif
     43  return 0;
     44 }
     45 #endif
     46 
     47 int32_t VideoEngine::CreateVideoCapture(const char* aDeviceUniqueIdUTF8,
     48                                        uint64_t aWindowID) {
     49  LOG(("%s", __PRETTY_FUNCTION__));
     50  MOZ_ASSERT(aDeviceUniqueIdUTF8);
     51 
     52  for (auto& it : mSharedCapturers) {
     53    if (it.second.VideoCapture() &&
     54        it.second.VideoCapture()->CurrentDeviceName() &&
     55        strcmp(it.second.VideoCapture()->CurrentDeviceName(),
     56               aDeviceUniqueIdUTF8) == 0) {
     57      int32_t id = GenerateId();
     58      LOG(("%sVideoEngine::%s(device=\"%s\", window=%" PRIu64
     59           "): Reusing capturer with id %d. stream id=%d",
     60           EnumValueToString(mCaptureDevType), __func__, aDeviceUniqueIdUTF8,
     61           aWindowID, it.first, id));
     62      mIdToCapturerMap.emplace(id, CaptureHandle{.mCaptureEntryNum = it.first,
     63                                                 .mWindowID = aWindowID});
     64      return id;
     65    }
     66  }
     67 
     68  int32_t id = GenerateId();
     69 
     70  VideoCaptureFactory::CreateVideoCaptureResult capturer =
     71      mVideoCaptureFactory->CreateVideoCapture(id, aDeviceUniqueIdUTF8,
     72                                               mCaptureDevType);
     73 
     74  if (!capturer.mCapturer) {
     75    LOG(("%sVideoEngine::%s(device=\"%s\", window=%" PRIu64
     76         "): Creating video capturer for id %d failed.",
     77         EnumValueToString(mCaptureDevType), __func__, aDeviceUniqueIdUTF8,
     78         aWindowID, id));
     79    return -1;
     80  }
     81 
     82  auto entry =
     83      CaptureEntry(id, std::move(capturer.mCapturer), capturer.mDesktopImpl);
     84 
     85  LOG(("%sVideoEngine::%s(device=\"%s\", window=%" PRIu64
     86       "): Created new video capturer for id %d.",
     87       EnumValueToString(mCaptureDevType), __func__, aDeviceUniqueIdUTF8,
     88       aWindowID, id));
     89 
     90  mSharedCapturers.emplace(id, std::move(entry));
     91  mIdToCapturerMap.emplace(
     92      id, CaptureHandle{.mCaptureEntryNum = id, .mWindowID = aWindowID});
     93  return id;
     94 }
     95 
     96 int VideoEngine::ReleaseVideoCapture(const int32_t aId) {
     97  bool found = false;
     98 
     99 #ifdef DEBUG
    100  {
    101    auto it = mIdToCapturerMap.find(aId);
    102    MOZ_ASSERT(it != mIdToCapturerMap.end());
    103    (void)it;
    104  }
    105 #endif
    106 
    107  for (auto& it : mIdToCapturerMap) {
    108    if (it.first != aId &&
    109        it.second.mCaptureEntryNum == mIdToCapturerMap[aId].mCaptureEntryNum) {
    110      // There are other tracks still using this hardware.
    111      found = true;
    112    }
    113  }
    114 
    115  if (!found) {
    116    WithEntry(aId, [&found](CaptureEntry& cap) {
    117      cap.mVideoCaptureModule = nullptr;
    118      found = true;
    119    });
    120    MOZ_ASSERT(found);
    121    if (found) {
    122      auto it = mSharedCapturers.find(mIdToCapturerMap[aId].mCaptureEntryNum);
    123      MOZ_ASSERT(it != mSharedCapturers.end());
    124      mSharedCapturers.erase(it);
    125    }
    126  }
    127 
    128  mIdToCapturerMap.erase(aId);
    129  return found ? 0 : (-1);
    130 }
    131 
    132 std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo>
    133 VideoEngine::GetOrCreateVideoCaptureDeviceInfo() {
    134  LOG(("%s", __PRETTY_FUNCTION__));
    135  webrtc::Timestamp currentTime = webrtc::Timestamp::Micros(0);
    136 
    137  const char* capDevTypeName = EnumValueToString(mCaptureDevType);
    138 
    139  if (mDeviceInfo) {
    140    LOG(("Device cache available."));
    141    // Camera cache is invalidated by HW change detection elsewhere
    142    if (mCaptureDevType == CaptureDeviceType::Camera) {
    143      LOG(("returning cached CaptureDeviceInfo of type %s", capDevTypeName));
    144      return mDeviceInfo;
    145    }
    146    // Screen sharing cache is invalidated after the expiration time
    147    currentTime = WebrtcSystemTime();
    148    LOG(("Checking expiry, fetched current time of: %" PRId64,
    149         currentTime.ms()));
    150    LOG(("device cache expiration is %" PRId64, mExpiryTime.ms()));
    151    if (currentTime <= mExpiryTime) {
    152      LOG(("returning cached CaptureDeviceInfo of type %s", capDevTypeName));
    153      return mDeviceInfo;
    154    }
    155  }
    156 
    157  if (currentTime.IsZero()) {
    158    currentTime = WebrtcSystemTime();
    159    LOG(("Fetched current time of: %" PRId64, currentTime.ms()));
    160  }
    161  mExpiryTime = currentTime + webrtc::TimeDelta::Millis(kCacheExpiryPeriodMs);
    162  LOG(("new device cache expiration is %" PRId64, mExpiryTime.ms()));
    163  LOG(("creating a new VideoCaptureDeviceInfo of type %s", capDevTypeName));
    164 
    165 #ifdef MOZ_WIDGET_ANDROID
    166  if (mCaptureDevType == CaptureDeviceType::Camera) {
    167    if (SetAndroidObjects()) {
    168      LOG(("VideoEngine::SetAndroidObjects Failed"));
    169      return mDeviceInfo;
    170    }
    171  }
    172 #endif
    173 
    174  if (mDeviceInfo) {
    175    mDeviceInfo->DeRegisterVideoInputFeedBack(this);
    176  }
    177 
    178  mDeviceInfo = mVideoCaptureFactory->CreateDeviceInfo(mId, mCaptureDevType);
    179 
    180  if (mDeviceInfo && mCaptureDevType == CaptureDeviceType::Camera) {
    181    mDeviceInfo->RegisterVideoInputFeedBack(this);
    182  }
    183 
    184  LOG(("EXIT %s", __PRETTY_FUNCTION__));
    185  return mDeviceInfo;
    186 }
    187 
    188 void VideoEngine::ClearVideoCaptureDeviceInfo() {
    189  LOG(("%s", __PRETTY_FUNCTION__));
    190  if (mDeviceInfo) {
    191    mDeviceInfo->DeRegisterVideoInputFeedBack(this);
    192    OnDeviceChange();
    193  }
    194  mDeviceInfo.reset();
    195  mVideoCaptureFactory->Invalidate();
    196 }
    197 
    198 already_AddRefed<VideoEngine> VideoEngine::Create(
    199    const CaptureDeviceType& aCaptureDeviceType,
    200    RefPtr<VideoCaptureFactory> aVideoCaptureFactory) {
    201  LOG(("%s", __PRETTY_FUNCTION__));
    202  return do_AddRef(
    203      new VideoEngine(aCaptureDeviceType, std::move(aVideoCaptureFactory)));
    204 }
    205 
    206 VideoEngine::CaptureEntry::CaptureEntry(
    207    int32_t aCapnum, webrtc::scoped_refptr<webrtc::VideoCaptureModule> aCapture,
    208    webrtc::DesktopCaptureImpl* aDesktopImpl)
    209    : mCapnum(aCapnum),
    210      mVideoCaptureModule(std::move(aCapture)),
    211      mDesktopImpl(aDesktopImpl) {}
    212 
    213 webrtc::scoped_refptr<webrtc::VideoCaptureModule>
    214 VideoEngine::CaptureEntry::VideoCapture() {
    215  return mVideoCaptureModule;
    216 }
    217 
    218 mozilla::MediaEventSource<void>*
    219 VideoEngine::CaptureEntry::CaptureEndedEvent() {
    220  if (!mDesktopImpl) {
    221    return nullptr;
    222  }
    223 #if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_IOS)
    224  return mDesktopImpl->CaptureEndedEvent();
    225 #else
    226  return nullptr;
    227 #endif
    228 }
    229 
    230 int32_t VideoEngine::CaptureEntry::Capnum() const { return mCapnum; }
    231 
    232 bool VideoEngine::WithEntry(
    233    const int32_t entryCapnum,
    234    const std::function<void(CaptureEntry& entry)>&& fn) {
    235 #ifdef DEBUG
    236  {
    237    auto it = mIdToCapturerMap.find(entryCapnum);
    238    MOZ_ASSERT(it != mIdToCapturerMap.end());
    239    (void)it;
    240  }
    241 #endif
    242 
    243  auto it =
    244      mSharedCapturers.find(mIdToCapturerMap[entryCapnum].mCaptureEntryNum);
    245  MOZ_ASSERT(it != mSharedCapturers.end());
    246  if (it == mSharedCapturers.end()) {
    247    return false;
    248  }
    249  fn(it->second);
    250  return true;
    251 }
    252 
    253 bool VideoEngine::IsWindowCapturing(uint64_t aWindowID,
    254                                    const nsCString& aUniqueIdUTF8) {
    255  Maybe<int32_t> sharedId;
    256  for (auto& [id, entry] : mSharedCapturers) {
    257    if (entry.VideoCapture() && entry.VideoCapture()->CurrentDeviceName() &&
    258        strcmp(entry.VideoCapture()->CurrentDeviceName(),
    259               aUniqueIdUTF8.get()) == 0) {
    260      sharedId = Some(id);
    261      break;
    262    }
    263  }
    264  if (!sharedId) {
    265    return false;
    266  }
    267  for (auto& [id, handle] : mIdToCapturerMap) {
    268    if (handle.mCaptureEntryNum == *sharedId && handle.mWindowID == aWindowID) {
    269      return true;
    270    }
    271  }
    272  return false;
    273 }
    274 
    275 int32_t VideoEngine::GenerateId() {
    276  // XXX Something better than this (a map perhaps, or a simple boolean TArray,
    277  // given the number in-use is O(1) normally!)
    278  static int sId = 0;
    279  return mId = sId++;
    280 }
    281 
    282 void VideoEngine::OnDeviceChange() { mDeviceChangeEvent.Notify(); }
    283 
    284 VideoEngine::VideoEngine(const CaptureDeviceType& aCaptureDeviceType,
    285                         RefPtr<VideoCaptureFactory> aVideoCaptureFactory)
    286    : mId(0),
    287      mCaptureDevType(aCaptureDeviceType),
    288      mVideoCaptureFactory(std::move(aVideoCaptureFactory)),
    289      mDeviceInfo(nullptr) {
    290  MOZ_ASSERT(mVideoCaptureFactory);
    291  LOG(("%s", __PRETTY_FUNCTION__));
    292  LOG(("Creating new VideoEngine with CaptureDeviceType %s",
    293       EnumValueToString(mCaptureDevType)));
    294 }
    295 
    296 VideoEngine::~VideoEngine() {
    297  MOZ_ASSERT(mSharedCapturers.empty());
    298  MOZ_ASSERT(mIdToCapturerMap.empty());
    299 }
    300 
    301 #undef LOG
    302 #undef LOG_ENABLED
    303 
    304 }  // namespace mozilla::camera