diff options
Diffstat (limited to 'dom/media/systemservices/VideoEngine.cpp')
-rw-r--r-- | dom/media/systemservices/VideoEngine.cpp | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/dom/media/systemservices/VideoEngine.cpp b/dom/media/systemservices/VideoEngine.cpp new file mode 100644 index 0000000000..1d4bde6344 --- /dev/null +++ b/dom/media/systemservices/VideoEngine.cpp @@ -0,0 +1,245 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et ft=cpp : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "VideoEngine.h" +#include "libwebrtcglue/SystemTime.h" +#include "video_engine/desktop_capture_impl.h" +#include "system_wrappers/include/clock.h" +#ifdef WEBRTC_ANDROID +# include "modules/video_capture/video_capture.h" +#endif + +#ifdef MOZ_WIDGET_ANDROID +# include "mozilla/jni/Utils.h" +#endif + +namespace mozilla::camera { + +#undef LOG +#undef LOG_ENABLED +mozilla::LazyLogModule gVideoEngineLog("VideoEngine"); +#define LOG(args) MOZ_LOG(gVideoEngineLog, mozilla::LogLevel::Debug, args) +#define LOG_ENABLED() MOZ_LOG_TEST(gVideoEngineLog, mozilla::LogLevel::Debug) + +#if defined(ANDROID) +int VideoEngine::SetAndroidObjects() { + LOG(("%s", __PRETTY_FUNCTION__)); + + JavaVM* const javaVM = mozilla::jni::GetVM(); + if (!javaVM || webrtc::SetCaptureAndroidVM(javaVM) != 0) { + LOG(("Could not set capture Android VM")); + return -1; + } +# ifdef WEBRTC_INCLUDE_INTERNAL_VIDEO_RENDER + if (webrtc::SetRenderAndroidVM(javaVM) != 0) { + LOG(("Could not set render Android VM")); + return -1; + } +# endif + return 0; +} +#endif + +int32_t VideoEngine::CreateVideoCapture(const char* aDeviceUniqueIdUTF8) { + LOG(("%s", __PRETTY_FUNCTION__)); + MOZ_ASSERT(aDeviceUniqueIdUTF8); + + int32_t id = GenerateId(); + LOG(("CaptureDeviceInfo.type=%s id=%d", mCaptureDevInfo.TypeName(), id)); + + for (auto& it : mCaps) { + if (it.second.VideoCapture() && + it.second.VideoCapture()->CurrentDeviceName() && + strcmp(it.second.VideoCapture()->CurrentDeviceName(), + aDeviceUniqueIdUTF8) == 0) { + mIdMap.emplace(id, it.first); + return id; + } + } + + CaptureEntry entry = {-1, nullptr}; + + if (mCaptureDevInfo.type == CaptureDeviceType::Camera) { + entry = CaptureEntry( + id, webrtc::VideoCaptureFactory::Create(aDeviceUniqueIdUTF8)); + if (entry.VideoCapture()) { + entry.VideoCapture()->SetApplyRotation(true); + } + } else { +#ifndef WEBRTC_ANDROID + entry = CaptureEntry( + id, rtc::scoped_refptr<webrtc::VideoCaptureModule>( + webrtc::DesktopCaptureImpl::Create(id, aDeviceUniqueIdUTF8, + mCaptureDevInfo.type))); +#else + MOZ_ASSERT("CreateVideoCapture NO DESKTOP CAPTURE IMPL ON ANDROID" == + nullptr); +#endif + } + mCaps.emplace(id, std::move(entry)); + mIdMap.emplace(id, id); + return id; +} + +int VideoEngine::ReleaseVideoCapture(const int32_t aId) { + bool found = false; + +#ifdef DEBUG + { + auto it = mIdMap.find(aId); + MOZ_ASSERT(it != mIdMap.end()); + Unused << it; + } +#endif + + for (auto& it : mIdMap) { + if (it.first != aId && it.second == mIdMap[aId]) { + // There are other tracks still using this hardware. + found = true; + } + } + + if (!found) { + WithEntry(aId, [&found](CaptureEntry& cap) { + cap.mVideoCaptureModule = nullptr; + found = true; + }); + MOZ_ASSERT(found); + if (found) { + auto it = mCaps.find(mIdMap[aId]); + MOZ_ASSERT(it != mCaps.end()); + mCaps.erase(it); + } + } + + mIdMap.erase(aId); + return found ? 0 : (-1); +} + +std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo> +VideoEngine::GetOrCreateVideoCaptureDeviceInfo() { + LOG(("%s", __PRETTY_FUNCTION__)); + webrtc::Timestamp currentTime = webrtc::Timestamp::Micros(0); + + const char* capDevTypeName = + CaptureDeviceInfo(mCaptureDevInfo.type).TypeName(); + + if (mDeviceInfo) { + LOG(("Device cache available.")); + // Camera cache is invalidated by HW change detection elsewhere + if (mCaptureDevInfo.type == CaptureDeviceType::Camera) { + LOG(("returning cached CaptureDeviceInfo of type %s", capDevTypeName)); + return mDeviceInfo; + } + // Screen sharing cache is invalidated after the expiration time + currentTime = WebrtcSystemTime(); + LOG(("Checking expiry, fetched current time of: %" PRId64, + currentTime.ms())); + LOG(("device cache expiration is %" PRId64, mExpiryTime.ms())); + if (currentTime <= mExpiryTime) { + LOG(("returning cached CaptureDeviceInfo of type %s", capDevTypeName)); + return mDeviceInfo; + } + } + + if (currentTime.IsZero()) { + currentTime = WebrtcSystemTime(); + LOG(("Fetched current time of: %" PRId64, currentTime.ms())); + } + mExpiryTime = currentTime + webrtc::TimeDelta::Millis(kCacheExpiryPeriodMs); + LOG(("new device cache expiration is %" PRId64, mExpiryTime.ms())); + LOG(("creating a new VideoCaptureDeviceInfo of type %s", capDevTypeName)); + + switch (mCaptureDevInfo.type) { + case CaptureDeviceType::Camera: { +#ifdef MOZ_WIDGET_ANDROID + if (SetAndroidObjects()) { + LOG(("VideoEngine::SetAndroidObjects Failed")); + break; + } +#endif + mDeviceInfo.reset(webrtc::VideoCaptureFactory::CreateDeviceInfo()); + LOG(("CaptureDeviceType::Camera: Finished creating new device.")); + break; + } + // Window and Screen and Browser (tab) types are handled by DesktopCapture + case CaptureDeviceType::Browser: + case CaptureDeviceType::Window: + case CaptureDeviceType::Screen: { +#if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_IOS) + mDeviceInfo = webrtc::DesktopCaptureImpl::CreateDeviceInfo( + mId, mCaptureDevInfo.type); + LOG(("screen capture: Finished creating new device.")); +#else + MOZ_ASSERT( + "GetVideoCaptureDeviceInfo NO DESKTOP CAPTURE IMPL ON ANDROID" == + nullptr); + mDeviceInfo.reset(); +#endif + break; + } + } + LOG(("EXIT %s", __PRETTY_FUNCTION__)); + return mDeviceInfo; +} + +already_AddRefed<VideoEngine> VideoEngine::Create( + const CaptureDeviceType& aCaptureDeviceType) { + LOG(("%s", __PRETTY_FUNCTION__)); + return do_AddRef(new VideoEngine(aCaptureDeviceType)); +} + +VideoEngine::CaptureEntry::CaptureEntry( + int32_t aCapnum, rtc::scoped_refptr<webrtc::VideoCaptureModule> aCapture) + : mCapnum(aCapnum), mVideoCaptureModule(aCapture) {} + +rtc::scoped_refptr<webrtc::VideoCaptureModule> +VideoEngine::CaptureEntry::VideoCapture() { + return mVideoCaptureModule; +} + +int32_t VideoEngine::CaptureEntry::Capnum() const { return mCapnum; } + +bool VideoEngine::WithEntry( + const int32_t entryCapnum, + const std::function<void(CaptureEntry& entry)>&& fn) { +#ifdef DEBUG + { + auto it = mIdMap.find(entryCapnum); + MOZ_ASSERT(it != mIdMap.end()); + Unused << it; + } +#endif + + auto it = mCaps.find(mIdMap[entryCapnum]); + MOZ_ASSERT(it != mCaps.end()); + if (it == mCaps.end()) { + return false; + } + fn(it->second); + return true; +} + +int32_t VideoEngine::GenerateId() { + // XXX Something better than this (a map perhaps, or a simple boolean TArray, + // given the number in-use is O(1) normally!) + static int sId = 0; + return mId = sId++; +} + +VideoEngine::VideoEngine(const CaptureDeviceType& aCaptureDeviceType) + : mId(0), mCaptureDevInfo(aCaptureDeviceType), mDeviceInfo(nullptr) { + LOG(("%s", __PRETTY_FUNCTION__)); + LOG(("Creating new VideoEngine with CaptureDeviceType %s", + mCaptureDevInfo.TypeName())); +} + +VideoEngine::~VideoEngine() { + MOZ_ASSERT(mCaps.empty()); + MOZ_ASSERT(mIdMap.empty()); +} + +} // namespace mozilla::camera |