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