--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -34,16 +34,23 @@
mozilla::LazyLogModule gCamerasParentLog("CamerasParent");
#define LOG(args) MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Debug, args)
#define LOG_VERBOSE(args) MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Verbose, args)
#define LOG_ENABLED() MOZ_LOG_TEST(gCamerasParentLog, mozilla::LogLevel::Debug)
namespace mozilla {
namespace camera {
+RefPtr<VideoEngine> CamerasParent::sEngines[CaptureEngine::MaxEngine];
+int32_t CamerasParent::sNumOfOpenCamerasParentEngines = 0;
+int32_t CamerasParent::sNumOfCamerasParents = 0;
+base::Thread* CamerasParent::sVideoCaptureThread = nullptr;
+Monitor* CamerasParent::sThreadMonitor = nullptr;
+StaticMutex CamerasParent::sMutex;
+
// 3 threads are involved in this code:
// - the main thread for some setups, and occassionally for video capture setup
// calls that don't work correctly elsewhere.
// - the IPC thread on which PBackground is running and which receives and
// sends messages
// - a thread which will execute the actual (possibly slow) camera access
// called "VideoCapture". On Windows this is a thread with an event loop
// suitable for UI access.
@@ -147,68 +154,68 @@ CamerasParent::Observe(nsISupports *aSub
StopVideoCapture();
return NS_OK;
}
nsresult
CamerasParent::DispatchToVideoCaptureThread(Runnable* event)
{
// Don't try to dispatch if we're already on the right thread.
- // There's a potential deadlock because the mThreadMonitor is likely
+ // There's a potential deadlock because the sThreadMonitor is likely
// to be taken already.
- MOZ_ASSERT(!mVideoCaptureThread ||
- mVideoCaptureThread->thread_id() != PlatformThread::CurrentId());
+ MOZ_ASSERT(!sVideoCaptureThread ||
+ sVideoCaptureThread->thread_id() != PlatformThread::CurrentId());
- MonitorAutoLock lock(mThreadMonitor);
+ MonitorAutoLock lock(*sThreadMonitor);
while(mChildIsAlive && mWebRTCAlive &&
- (!mVideoCaptureThread || !mVideoCaptureThread->IsRunning())) {
- mThreadMonitor.Wait();
+ (!sVideoCaptureThread || !sVideoCaptureThread->IsRunning())) {
+ sThreadMonitor->Wait();
}
- if (!mVideoCaptureThread || !mVideoCaptureThread->IsRunning()) {
+ if (!sVideoCaptureThread || !sVideoCaptureThread->IsRunning()) {
return NS_ERROR_FAILURE;
}
RefPtr<Runnable> addrefedEvent = event;
- mVideoCaptureThread->message_loop()->PostTask(addrefedEvent.forget());
+ sVideoCaptureThread->message_loop()->PostTask(addrefedEvent.forget());
return NS_OK;
}
void
CamerasParent::StopVideoCapture()
{
LOG((__PRETTY_FUNCTION__));
// We are called from the main thread (xpcom-shutdown) or
// from PBackground (when the Actor shuts down).
// Shut down the WebRTC stack (on the capture thread)
RefPtr<CamerasParent> self(this);
RefPtr<Runnable> webrtc_runnable =
media::NewRunnableFrom([self]() -> nsresult {
- MonitorAutoLock lock(self->mThreadMonitor);
+ MonitorAutoLock lock(*(self->sThreadMonitor));
self->CloseEngines();
- self->mThreadMonitor.NotifyAll();
+ self->sThreadMonitor->NotifyAll();
return NS_OK;
});
DebugOnly<nsresult> rv = DispatchToVideoCaptureThread(webrtc_runnable);
#ifdef DEBUG
// It's ok for the dispatch to fail if the cleanup it has to do
// has been done already.
MOZ_ASSERT(NS_SUCCEEDED(rv) || !mWebRTCAlive);
#endif
// Hold here until the WebRTC thread is gone. We need to dispatch
// the thread deletion *now*, or there will be no more possibility
// to get to the main thread.
- MonitorAutoLock lock(mThreadMonitor);
+ MonitorAutoLock lock(*sThreadMonitor);
while (mWebRTCAlive) {
- mThreadMonitor.Wait();
+ sThreadMonitor->Wait();
}
// After closing the WebRTC stack, clean up the
// VideoCapture thread.
- if (self->mVideoCaptureThread) {
- base::Thread *thread = self->mVideoCaptureThread;
- self->mVideoCaptureThread = nullptr;
+ if (sNumOfOpenCamerasParentEngines == 0 && self->sVideoCaptureThread) {
+ base::Thread *thread = self->sVideoCaptureThread;
+ self->sVideoCaptureThread = nullptr;
RefPtr<Runnable> threadShutdown =
media::NewRunnableFrom([thread]() -> nsresult {
if (thread->IsRunning()) {
thread->Stop();
}
delete thread;
return NS_OK;
});
@@ -302,109 +309,110 @@ CamerasParent::RecvReleaseFrame(mozilla:
mShmemPool.Put(ShmemBuffer(s));
return IPC_OK();
}
bool
CamerasParent::SetupEngine(CaptureEngine aCapEngine)
{
LOG((__PRETTY_FUNCTION__));
- MOZ_ASSERT(mVideoCaptureThread->thread_id() == PlatformThread::CurrentId());
- RefPtr<mozilla::camera::VideoEngine>* engine = &mEngines[aCapEngine];
+ MOZ_ASSERT(sVideoCaptureThread->thread_id() == PlatformThread::CurrentId());
+ RefPtr<mozilla::camera::VideoEngine>* engine = &sEngines[aCapEngine];
+
+ if (!engine->get()) {
+ webrtc::CaptureDeviceInfo *captureDeviceInfo = nullptr;
+ UniquePtr<webrtc::Config> config(new webrtc::Config);
- // Already initialized
- if (engine->get()) {
- return true;
+ switch (aCapEngine) {
+ case ScreenEngine:
+ captureDeviceInfo =
+ new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Screen);
+ break;
+ case BrowserEngine:
+ captureDeviceInfo =
+ new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Browser);
+ break;
+ case WinEngine:
+ captureDeviceInfo =
+ new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Window);
+ break;
+ case AppEngine:
+ captureDeviceInfo =
+ new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Application);
+ break;
+ case CameraEngine:
+ captureDeviceInfo =
+ new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Camera);
+ break;
+ default:
+ LOG(("Invalid webrtc Video engine"));
+ MOZ_CRASH();
+ break;
+ }
+
+ config->Set<webrtc::CaptureDeviceInfo>(captureDeviceInfo);
+ *engine = mozilla::camera::VideoEngine::Create(UniquePtr<const webrtc::Config>(config.release()));
+
+ if (!engine->get()) {
+ LOG(("VideoEngine::Create failed"));
+ return false;
+ }
}
- webrtc::CaptureDeviceInfo *captureDeviceInfo = nullptr;
- UniquePtr<webrtc::Config> config(new webrtc::Config);
-
- switch (aCapEngine) {
- case ScreenEngine:
- captureDeviceInfo =
- new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Screen);
- break;
- case BrowserEngine:
- captureDeviceInfo =
- new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Browser);
- break;
- case WinEngine:
- captureDeviceInfo =
- new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Window);
- break;
- case AppEngine:
- captureDeviceInfo =
- new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Application);
- break;
- case CameraEngine:
- captureDeviceInfo =
- new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Camera);
- break;
- default:
- LOG(("Invalid webrtc Video engine"));
- MOZ_CRASH();
- break;
- }
-
- config->Set<webrtc::CaptureDeviceInfo>(captureDeviceInfo);
- *engine = mozilla::camera::VideoEngine::Create(UniquePtr<const webrtc::Config>(config.release()));
-
- if (!engine->get()) {
- LOG(("VideoEngine::Create failed"));
- return false;
- }
-
- RefPtr<InputObserver>* observer = mObservers.AppendElement(new InputObserver(this));
- auto device_info = engine->get()->GetOrCreateVideoCaptureDeviceInfo();
- MOZ_ASSERT(device_info);
- if (device_info) {
- device_info->RegisterVideoInputFeedBack(*(observer->get()));
+ if (aCapEngine == CameraEngine && !mCameraObserver) {
+ mCameraObserver = new InputObserver(this);
+ auto device_info = engine->get()->GetOrCreateVideoCaptureDeviceInfo();
+ MOZ_ASSERT(device_info);
+ if (device_info) {
+ device_info->RegisterVideoInputFeedBack(mCameraObserver.get());
+ }
}
return true;
}
void
CamerasParent::CloseEngines()
{
LOG((__PRETTY_FUNCTION__));
if (!mWebRTCAlive) {
return;
}
- MOZ_ASSERT(mVideoCaptureThread->thread_id() == PlatformThread::CurrentId());
+ MOZ_ASSERT(sVideoCaptureThread->thread_id() == PlatformThread::CurrentId());
// Stop the callers
while (mCallbacks.Length()) {
auto capEngine = mCallbacks[0]->mCapEngine;
auto streamNum = mCallbacks[0]->mStreamId;
LOG(("Forcing shutdown of engine %d, capturer %d", capEngine, streamNum));
StopCapture(capEngine, streamNum);
Unused << ReleaseCaptureDevice(capEngine, streamNum);
}
- for (int i = 0; i < CaptureEngine::MaxEngine; i++) {
- if (auto engine = mEngines[i].get() ){
- if (engine->IsRunning()) {
- LOG(("Being closed down while engine %d is running!", i));
- }
+ auto engine = sEngines[CameraEngine].get();
+ if (engine && mCameraObserver) {
+ auto device_info = engine->GetOrCreateVideoCaptureDeviceInfo();
+ MOZ_ASSERT(device_info);
+ if (device_info) {
+ device_info->DeRegisterVideoInputFeedBack(mCameraObserver.get());
+ }
+ mCameraObserver = nullptr;
+ }
- auto device_info = engine->GetOrCreateVideoCaptureDeviceInfo();
- MOZ_ASSERT(device_info);
- if (device_info) {
- device_info->DeRegisterVideoInputFeedBack();
+ // CloseEngines() is protected by sThreadMonitor
+ sNumOfOpenCamerasParentEngines--;
+ if (sNumOfOpenCamerasParentEngines == 0) {
+ for (auto& engine : sEngines) {
+ if (engine.get()) {
+ mozilla::camera::VideoEngine::Delete(engine.get());
+ engine = nullptr;
}
- mozilla::camera::VideoEngine::Delete(engine);
- mEngines[i] = nullptr;
}
}
- // the observers hold references to us
- mObservers.Clear();
-
mWebRTCAlive = false;
}
VideoEngine *
CamerasParent::EnsureInitialized(int aEngine)
{
LOG_VERBOSE((__PRETTY_FUNCTION__));
// We're shutting down, don't try to do new WebRTC ops.
@@ -412,17 +420,17 @@ CamerasParent::EnsureInitialized(int aEn
return nullptr;
}
CaptureEngine capEngine = static_cast<CaptureEngine>(aEngine);
if (!SetupEngine(capEngine)) {
LOG(("CamerasParent failed to initialize engine"));
return nullptr;
}
- return mEngines[aEngine];
+ return sEngines[aEngine];
}
// Dispatch the runnable to do the camera operation on the
// specific Cameras thread, preventing us from blocking, and
// chain a runnable to send back the result on the IPC thread.
// It would be nice to get rid of the code duplication here,
// perhaps via Promises.
mozilla::ipc::IPCResult
@@ -707,17 +715,17 @@ CamerasParent::RecvAllocateCaptureDevice
// After retrieving the permission (or not) on the main thread,
// bounce to the WebRTC thread to allocate the device (or not),
// then bounce back to the IPC thread for the reply to content.
RefPtr<Runnable> webrtc_runnable =
media::NewRunnableFrom([self, allowed, aCapEngine, unique_id]() -> nsresult {
int numdev = -1;
int error = -1;
if (allowed && self->EnsureInitialized(aCapEngine)) {
- auto engine = self->mEngines[aCapEngine].get();
+ auto engine = self->sEngines[aCapEngine].get();
engine->CreateVideoCapture(numdev, unique_id.get());
engine->WithEntry(numdev, [&error](VideoEngine::CaptureEntry& cap) {
if (cap.VideoCapture()) {
error = 0;
}
});
}
RefPtr<nsIRunnable> ipc_runnable =
@@ -802,33 +810,32 @@ CamerasParent::RecvStartCapture(const Ca
LOG((__PRETTY_FUNCTION__));
CallbackHelper** cbh;
VideoEngine* engine = nullptr;
int error = -1;
if (self->EnsureInitialized(aCapEngine)) {
cbh = self->mCallbacks.AppendElement(
new CallbackHelper(static_cast<CaptureEngine>(aCapEngine), capnum, self));
- engine = self->mEngines[aCapEngine];
- engine->WithEntry(capnum, [&engine, &error, &ipcCaps, &cbh](VideoEngine::CaptureEntry& cap) {
+ engine = self->sEngines[aCapEngine];
+ engine->WithEntry(capnum, [&error, &ipcCaps, &cbh](VideoEngine::CaptureEntry& cap) {
error = 0;
webrtc::VideoCaptureCapability capability;
capability.width = ipcCaps.width();
capability.height = ipcCaps.height();
capability.maxFPS = ipcCaps.maxFPS();
capability.expectedCaptureDelay = ipcCaps.expectedCaptureDelay();
capability.rawType = static_cast<webrtc::RawVideoType>(ipcCaps.rawType());
capability.codecType = static_cast<webrtc::VideoCodecType>(ipcCaps.codecType());
capability.interlaced = ipcCaps.interlaced();
if (!error) {
error = cap.VideoCapture()->StartCapture(capability);
}
if (!error) {
- engine->Startup();
cap.VideoCapture()->RegisterCaptureDataCallback(static_cast<rtc::VideoSinkInterface<webrtc::VideoFrame>*>(*cbh));
}
});
}
RefPtr<nsIRunnable> ipc_runnable =
media::NewRunnableFrom([self, error]() -> nsresult {
if (self->IsShuttingDown()) {
return NS_ERROR_FAILURE;
@@ -848,32 +855,35 @@ CamerasParent::RecvStartCapture(const Ca
return IPC_OK();
}
void
CamerasParent::StopCapture(const CaptureEngine& aCapEngine,
const int& capnum)
{
if (auto engine = EnsureInitialized(aCapEngine)) {
- engine->WithEntry(capnum,[](VideoEngine::CaptureEntry& cap){
- if (cap.VideoCapture()) {
- cap.VideoCapture()->StopCapture();
- cap.VideoCapture()->DeRegisterCaptureDataCallback();
- }
- });
// we're removing elements, iterate backwards
for (size_t i = mCallbacks.Length(); i > 0; i--) {
if (mCallbacks[i - 1]->mCapEngine == aCapEngine &&
mCallbacks[i - 1]->mStreamId == (uint32_t)capnum) {
+
+ CallbackHelper* cbh = mCallbacks[i-1];
+ engine->WithEntry(capnum,[cbh](VideoEngine::CaptureEntry& cap) {
+ if (cap.VideoCapture()) {
+ cap.VideoCapture()->DeRegisterCaptureDataCallback(
+ static_cast<rtc::VideoSinkInterface<webrtc::VideoFrame>*>(cbh));
+ cap.VideoCapture()->StopCaptureIfAllClientsClose();
+ }
+ });
+
delete mCallbacks[i - 1];
mCallbacks.RemoveElementAt(i - 1);
break;
}
}
- engine->Shutdown();
}
}
mozilla::ipc::IPCResult
CamerasParent::RecvStopCapture(const CaptureEngine& aCapEngine,
const int& capnum)
{
LOG((__PRETTY_FUNCTION__));
@@ -936,23 +946,26 @@ CamerasParent::ActorDestroy(ActorDestroy
StopIPC();
// Shut down WebRTC (if we're not in full shutdown, else this
// will already have happened)
StopVideoCapture();
}
CamerasParent::CamerasParent()
: mShmemPool(CaptureEngine::MaxEngine),
- mThreadMonitor("CamerasParent::mThreadMonitor"),
- mVideoCaptureThread(nullptr),
mChildIsAlive(true),
mDestroyed(false),
mWebRTCAlive(true)
{
LOG(("CamerasParent: %p", this));
+ StaticMutexAutoLock slock(sMutex);
+
+ if (sNumOfCamerasParents++ == 0) {
+ sThreadMonitor = new Monitor("CamerasParent::sThreadMonitor");
+ }
mPBackgroundEventTarget = GetCurrentThreadSerialEventTarget();
MOZ_ASSERT(mPBackgroundEventTarget != nullptr,
"GetCurrentThreadEventTarget failed");
LOG(("Spinning up WebRTC Cameras Thread"));
RefPtr<CamerasParent> self(this);
@@ -964,47 +977,45 @@ CamerasParent::CamerasParent()
return NS_ERROR_FAILURE;
}
nsresult rv =
obs->AddObserver(self, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Start the thread
- MonitorAutoLock lock(self->mThreadMonitor);
- self->mVideoCaptureThread = new base::Thread("VideoCapture");
- base::Thread::Options options;
+ MonitorAutoLock lock(*(self->sThreadMonitor));
+ if (self->sVideoCaptureThread == nullptr) {
+ MOZ_ASSERT(sNumOfOpenCamerasParentEngines == 0);
+ self->sVideoCaptureThread = new base::Thread("VideoCapture");
+ base::Thread::Options options;
#if defined(_WIN32)
- options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD;
+ options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD;
#else
-
- options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINTHREAD;
+ options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINTHREAD;
#endif
- if (!self->mVideoCaptureThread->StartWithOptions(options)) {
- MOZ_CRASH();
+ if (!self->sVideoCaptureThread->StartWithOptions(options)) {
+ MOZ_CRASH();
+ }
}
- self->mThreadMonitor.NotifyAll();
+ sNumOfOpenCamerasParentEngines++;
+ self->sThreadMonitor->NotifyAll();
return NS_OK;
});
NS_DispatchToMainThread(threadStart);
}
CamerasParent::~CamerasParent()
{
LOG(("~CamerasParent: %p", this));
-
-#ifdef DEBUG
- // Verify we have shut down the webrtc engines, this is
- // supposed to happen in ActorDestroy.
- // That runnable takes a ref to us, so it must have finished
- // by the time we get here.
- for (int i = 0; i < CaptureEngine::MaxEngine; i++) {
- MOZ_ASSERT(!mEngines[i]);
+ StaticMutexAutoLock slock(sMutex);
+ if (--sNumOfCamerasParents == 0) {
+ delete sThreadMonitor;
+ sThreadMonitor = nullptr;
}
-#endif
}
already_AddRefed<CamerasParent>
CamerasParent::Create() {
mozilla::ipc::AssertIsOnBackgroundThread();
RefPtr<CamerasParent> camerasParent = new CamerasParent();
return camerasParent.forget();
}
--- a/dom/media/systemservices/CamerasParent.h
+++ b/dom/media/systemservices/CamerasParent.h
@@ -130,38 +130,45 @@ protected:
bool SetupEngine(CaptureEngine aCapEngine);
VideoEngine* EnsureInitialized(int aEngine);
void CloseEngines();
void StopIPC();
void StopVideoCapture();
// Can't take already_AddRefed because it can fail in stupid ways.
nsresult DispatchToVideoCaptureThread(Runnable* event);
- RefPtr<VideoEngine> mEngines[CaptureEngine::MaxEngine];
+ // sEngines will be accessed by VideoCapture thread only
+ // sNumOfCamerasParent, sNumOfOpenCamerasParentEngines, and sVideoCaptureThread will
+ // be accessed by main thread / PBackground thread / VideoCapture thread
+ // all variables are protected by sThreadMonitor while sThreadMonitor
+ // creation and deletion is protected by sMutex
+ static RefPtr<VideoEngine> sEngines[CaptureEngine::MaxEngine];
+ static int32_t sNumOfOpenCamerasParentEngines;
+ static int32_t sNumOfCamerasParents;
nsTArray<CallbackHelper*> mCallbacks;
// image buffers
mozilla::ShmemPool mShmemPool;
// PBackground parent thread
nsCOMPtr<nsISerialEventTarget> mPBackgroundEventTarget;
- // Monitors creation of the thread below
- Monitor mThreadMonitor;
+ static StaticMutex sMutex;
+ static Monitor* sThreadMonitor;
// video processing thread - where webrtc.org capturer code runs
- base::Thread* mVideoCaptureThread;
+ static base::Thread* sVideoCaptureThread;
// Shutdown handling
bool mChildIsAlive;
bool mDestroyed;
// Above 2 are PBackground only, but this is potentially
// read cross-thread.
mozilla::Atomic<bool> mWebRTCAlive;
- nsTArray<RefPtr<InputObserver>> mObservers;
+ RefPtr<InputObserver> mCameraObserver;
};
PCamerasParent* CreateCamerasParent();
} // namespace camera
} // namespace mozilla
#endif // mozilla_CameraParent_h
--- a/dom/media/systemservices/VideoEngine.cpp
+++ b/dom/media/systemservices/VideoEngine.cpp
@@ -38,42 +38,78 @@ int VideoEngine::SetAndroidObjects(JavaV
#endif
return 0;
}
#endif
void
VideoEngine::CreateVideoCapture(int32_t& id, const char* deviceUniqueIdUTF8) {
LOG((__PRETTY_FUNCTION__));
+
id = GenerateId();
LOG(("CaptureDeviceInfo.type=%s id=%d",mCaptureDevInfo.TypeName(),id));
+
+ for (auto &it : mCaps) {
+ if (strcmp(it.second.VideoCapture()->CurrentDeviceName(), deviceUniqueIdUTF8) == 0) {
+ mIdMap.emplace(id, it.first);
+ return;
+ }
+ }
+
CaptureEntry entry = {-1, nullptr};
if (mCaptureDevInfo.type == webrtc::CaptureDeviceType::Camera) {
entry = CaptureEntry(id,
webrtc::VideoCaptureFactory::Create(deviceUniqueIdUTF8));
} else {
#ifndef WEBRTC_ANDROID
entry = CaptureEntry(
id,
webrtc::DesktopCaptureImpl::Create(id, deviceUniqueIdUTF8, mCaptureDevInfo.type));
#else
MOZ_ASSERT("CreateVideoCapture NO DESKTOP CAPTURE IMPL ON ANDROID" == nullptr);
#endif
}
mCaps.emplace(id, std::move(entry));
+ mIdMap.emplace(id, id);
}
int
VideoEngine::ReleaseVideoCapture(const int32_t id) {
bool found = false;
- WithEntry(id, [&found](CaptureEntry& cap) {
- cap.mVideoCaptureModule = nullptr;
- found = true;
- });
+
+#ifdef DEBUG
+ {
+ auto it = mIdMap.find(id);
+ MOZ_ASSERT(it != mIdMap.end());
+ Unused << it;
+ }
+#endif
+
+ for (auto &it : mIdMap) {
+ if (it.first != id && it.second == mIdMap[id]) {
+ // There are other tracks still using this hardware.
+ found = true;
+ }
+ }
+
+ if (!found) {
+ WithEntry(id, [&found](CaptureEntry& cap) {
+ cap.mVideoCaptureModule = nullptr;
+ found = true;
+ });
+ MOZ_ASSERT(found);
+ if (found) {
+ auto it = mCaps.find(mIdMap[id]);
+ MOZ_ASSERT(it != mCaps.end());
+ mCaps.erase(it);
+ }
+ }
+
+ mIdMap.erase(id);
return found ? 0 : (-1);
}
std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo>
VideoEngine::GetOrCreateVideoCaptureDeviceInfo() {
LOG((__PRETTY_FUNCTION__));
int64_t currentTime = 0;
@@ -160,17 +196,26 @@ VideoEngine::CaptureEntry::VideoCapture(
int32_t
VideoEngine::CaptureEntry::Capnum() const {
return mCapnum;
}
bool VideoEngine::WithEntry(const int32_t entryCapnum,
const std::function<void(CaptureEntry &entry)>&& fn) {
- auto it = mCaps.find(entryCapnum);
+#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
--- a/dom/media/systemservices/VideoEngine.h
+++ b/dom/media/systemservices/VideoEngine.h
@@ -54,28 +54,16 @@ public:
* @return on failure the shared_ptr will be null, otherwise it will contain
* a DeviceInfo.
* @see bug 1305212 https://bugzilla.mozilla.org/show_bug.cgi?id=1305212
*/
std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo> GetOrCreateVideoCaptureDeviceInfo();
const UniquePtr<const webrtc::Config>& GetConfiguration();
- void Startup() {
- mIsRunning = true;
- }
-
- void Shutdown() {
- mIsRunning = false;
- }
-
- bool IsRunning() const {
- return mIsRunning;
- }
-
class CaptureEntry {
public:
CaptureEntry(int32_t aCapnum,
rtc::scoped_refptr<webrtc::VideoCaptureModule> aCapture);
int32_t Capnum() const;
rtc::scoped_refptr<webrtc::VideoCaptureModule> VideoCapture();
private:
int32_t mCapnum;
@@ -83,22 +71,22 @@ public:
friend class VideoEngine;
};
// Returns true iff an entry for capnum exists
bool WithEntry(const int32_t entryCapnum, const std::function<void(CaptureEntry &entry)>&& fn);
private:
explicit VideoEngine(UniquePtr<const webrtc::Config>&& aConfig);
- bool mIsRunning;
int32_t mId;
webrtc::CaptureDeviceInfo mCaptureDevInfo;
std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo> mDeviceInfo;
UniquePtr<const webrtc::Config> mConfig;
std::map<int32_t, CaptureEntry> mCaps;
+ std::map<int32_t, int32_t> mIdMap;
// The validity period for non-camera capture device infos`
int64_t mExpiryTimeInMs = 0;
int32_t GenerateId();
static int32_t sId;
};
}
}
#endif
--- a/media/webrtc/trunk/webrtc/base/platform_thread.cc
+++ b/media/webrtc/trunk/webrtc/base/platform_thread.cc
@@ -139,16 +139,17 @@ PlatformThread::~PlatformThread() {
RTC_DCHECK(!thread_id_);
#endif // defined(WEBRTC_WIN)
}
#if defined(WEBRTC_WIN)
bool PlatformUIThread::InternalInit() {
// Create an event window for use in generating callbacks to capture
// objects.
+ CritScope scoped_lock(&cs_);
if (hwnd_ == NULL) {
WNDCLASSW wc;
HMODULE hModule = GetModuleHandle(NULL);
if (!GetClassInfoW(hModule, kThreadWindow, &wc)) {
ZeroMemory(&wc, sizeof(WNDCLASSW));
wc.hInstance = hModule;
wc.lpfnWndProc = EventWindowProc;
wc.lpszClassName = kThreadWindow;
@@ -170,18 +171,23 @@ bool PlatformUIThread::InternalInit() {
void PlatformUIThread::RequestCallback() {
RTC_DCHECK(hwnd_);
RTC_DCHECK(static_reg_windows_msg);
PostMessage(hwnd_, static_reg_windows_msg, 0, 0);
}
bool PlatformUIThread::RequestCallbackTimer(unsigned int milliseconds) {
+ CritScope scoped_lock(&cs_);
if (!hwnd_) {
- RTC_DCHECK(!thread_);
+ // There is a condition that thread_ (PlatformUIThread) has been
+ // created but PlatformUIThread::Run() hasn't been run yet (hwnd_ is
+ // null while thread_ is not). If we do RTC_DCHECK(!thread_) here,
+ // it would lead to crash in this condition.
+
// set timer once thread starts
} else {
if (timerid_) {
KillTimer(hwnd_, timerid_);
}
timerid_ = SetTimer(hwnd_, kTimerId, milliseconds, NULL);
}
timeout_ = milliseconds;
--- a/media/webrtc/trunk/webrtc/base/platform_thread.h
+++ b/media/webrtc/trunk/webrtc/base/platform_thread.h
@@ -91,16 +91,17 @@ class PlatformThread {
const std::string name_;
rtc::ThreadChecker thread_checker_;
#if defined(WEBRTC_WIN)
static DWORD WINAPI StartThread(void* param);
bool stop_;
HANDLE thread_;
DWORD thread_id_;
+ CriticalSection cs_;
#else
static void* StartThread(void* param);
rtc::Event stop_event_;
pthread_t thread_;
#endif // defined(WEBRTC_WIN)
RTC_DISALLOW_COPY_AND_ASSIGN(PlatformThread);
--- a/media/webrtc/trunk/webrtc/modules/video_capture/video_capture.h
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/video_capture.h
@@ -11,16 +11,17 @@
#ifndef WEBRTC_MODULES_VIDEO_CAPTURE_VIDEO_CAPTURE_H_
#define WEBRTC_MODULES_VIDEO_CAPTURE_VIDEO_CAPTURE_H_
#include "webrtc/modules/audio_processing/include/config.h"
#include "webrtc/api/video/video_rotation.h"
#include "webrtc/media/base/videosinkinterface.h"
#include "webrtc/modules/include/module.h"
#include "webrtc/modules/video_capture/video_capture_defines.h"
+#include <set>
#if defined(ANDROID)
#include <jni.h>
#endif
namespace webrtc {
// Mozilla addition
@@ -79,24 +80,29 @@ protected:
class VideoCaptureModule: public rtc::RefCountInterface {
public:
// Interface for receiving information about available camera devices.
class DeviceInfo {
public:
virtual uint32_t NumberOfDevices() = 0;
virtual int32_t Refresh() = 0;
virtual void DeviceChange() {
- if (_inputCallBack)
- _inputCallBack->OnDeviceChange();
+ for (auto inputCallBack : _inputCallBacks) {
+ inputCallBack->OnDeviceChange();
+ }
+ }
+ virtual void RegisterVideoInputFeedBack(VideoInputFeedBack* callBack) {
+ _inputCallBacks.insert(callBack);
}
- virtual void RegisterVideoInputFeedBack(VideoInputFeedBack& callBack) {
- _inputCallBack = &callBack;
- }
- virtual void DeRegisterVideoInputFeedBack() {
- _inputCallBack = NULL;
+
+ virtual void DeRegisterVideoInputFeedBack(VideoInputFeedBack* callBack) {
+ auto it = _inputCallBacks.find(callBack);
+ if (it != _inputCallBacks.end()) {
+ _inputCallBacks.erase(it);
+ }
}
// Returns the available capture devices.
// deviceNumber - Index of capture device.
// deviceNameUTF8 - Friendly name of the capture device.
// deviceUniqueIdUTF8 - Unique name of the capture device if it exist.
// Otherwise same as deviceNameUTF8.
// productUniqueIdUTF8 - Unique product id if it exist.
@@ -140,30 +146,33 @@ class VideoCaptureModule: public rtc::Re
const char* deviceUniqueIdUTF8,
const char* dialogTitleUTF8,
void* parentWindow,
uint32_t positionX,
uint32_t positionY) = 0;
virtual ~DeviceInfo() {}
private:
- VideoInputFeedBack* _inputCallBack = NULL;
+ std::set<VideoInputFeedBack*> _inputCallBacks;
};
// Register capture data callback
virtual void RegisterCaptureDataCallback(
rtc::VideoSinkInterface<VideoFrame> *dataCallback) = 0;
// Remove capture data callback
- virtual void DeRegisterCaptureDataCallback() = 0;
+ virtual void DeRegisterCaptureDataCallback(
+ rtc::VideoSinkInterface<VideoFrame> *dataCallback) = 0;
// Start capture device
virtual int32_t StartCapture(
const VideoCaptureCapability& capability) = 0;
+ virtual int32_t StopCaptureIfAllClientsClose() = 0;
+
virtual int32_t StopCapture() = 0;
// Returns the name of the device used by this module.
virtual const char* CurrentDeviceName() const = 0;
// Returns true if the capture device is running
virtual bool CaptureStarted() = 0;
--- a/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_impl.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_impl.cc
@@ -81,52 +81,64 @@ int32_t VideoCaptureImpl::RotationInDegr
VideoCaptureImpl::VideoCaptureImpl()
: _deviceUniqueId(NULL),
_apiCs(*CriticalSectionWrapper::CreateCriticalSection()),
_captureDelay(0),
_requestedCapability(),
_lastProcessTimeNanos(rtc::TimeNanos()),
_lastFrameRateCallbackTimeNanos(rtc::TimeNanos()),
- _dataCallBack(NULL),
_lastProcessFrameTimeNanos(rtc::TimeNanos()),
_rotateFrame(kVideoRotation_0),
apply_rotation_(true) {
_requestedCapability.width = kDefaultWidth;
_requestedCapability.height = kDefaultHeight;
_requestedCapability.maxFPS = 30;
_requestedCapability.rawType = kVideoI420;
_requestedCapability.codecType = kVideoCodecUnknown;
memset(_incomingFrameTimesNanos, 0, sizeof(_incomingFrameTimesNanos));
}
VideoCaptureImpl::~VideoCaptureImpl()
{
- DeRegisterCaptureDataCallback();
delete &_apiCs;
if (_deviceUniqueId)
delete[] _deviceUniqueId;
}
void VideoCaptureImpl::RegisterCaptureDataCallback(
- rtc::VideoSinkInterface<VideoFrame>* dataCallBack) {
- CriticalSectionScoped cs(&_apiCs);
- _dataCallBack = dataCallBack;
+ rtc::VideoSinkInterface<VideoFrame>* dataCallBack) {
+ CriticalSectionScoped cs(&_apiCs);
+ _dataCallBacks.insert(dataCallBack);
}
-void VideoCaptureImpl::DeRegisterCaptureDataCallback() {
- CriticalSectionScoped cs(&_apiCs);
- _dataCallBack = NULL;
+void VideoCaptureImpl::DeRegisterCaptureDataCallback(
+ rtc::VideoSinkInterface<VideoFrame>* dataCallBack) {
+ CriticalSectionScoped cs(&_apiCs);
+
+ auto it = _dataCallBacks.find(dataCallBack);
+ if (it != _dataCallBacks.end()) {
+ _dataCallBacks.erase(it);
+ }
}
+
+int32_t VideoCaptureImpl::StopCaptureIfAllClientsClose() {
+ if (_dataCallBacks.empty()) {
+ return StopCapture();
+ } else {
+ return 0;
+ }
+}
+
int32_t VideoCaptureImpl::DeliverCapturedFrame(VideoFrame& captureFrame) {
UpdateFrameCount(); // frame count used for local frame rate callback.
- if (_dataCallBack) {
- _dataCallBack->OnFrame(captureFrame);
+ for (auto dataCallBack : _dataCallBacks) {
+ dataCallBack->OnFrame(captureFrame);
}
return 0;
}
int32_t VideoCaptureImpl::IncomingFrame(
uint8_t* videoFrame,
size_t videoFrameLength,
--- a/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_impl.h
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/video_capture_impl.h
@@ -15,16 +15,17 @@
* video_capture_impl.h
*/
#include "webrtc/api/video/video_frame.h"
#include "webrtc/base/scoped_ref_ptr.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/modules/video_capture/video_capture.h"
#include "webrtc/modules/video_capture/video_capture_config.h"
+#include <set>
namespace webrtc
{
class CriticalSectionWrapper;
namespace videocapturemodule {
// Class definitions
class VideoCaptureImpl: public VideoCaptureModule, public VideoCaptureExternal
@@ -54,18 +55,20 @@ public:
// Helpers for converting between (integral) degrees and
// VideoRotation values. Return 0 on success.
static int32_t RotationFromDegrees(int degrees, VideoRotation* rotation);
static int32_t RotationInDegrees(VideoRotation rotation, int* degrees);
//Call backs
void RegisterCaptureDataCallback(
rtc::VideoSinkInterface<VideoFrame>* dataCallback) override;
- void DeRegisterCaptureDataCallback() override;
+ void DeRegisterCaptureDataCallback(
+ rtc::VideoSinkInterface<VideoFrame>* dataCallback) override;
+ int32_t StopCaptureIfAllClientsClose() override;
int32_t SetCaptureRotation(VideoRotation rotation) override;
bool SetApplyRotation(bool enable) override;
bool GetApplyRotation() override {
return apply_rotation_;
}
const char* CurrentDeviceName() const override;
@@ -100,17 +103,17 @@ private:
void UpdateFrameCount();
uint32_t CalculateFrameRate(int64_t now_ns);
// last time the module process function was called.
int64_t _lastProcessTimeNanos;
// last time the frame rate callback function was called.
int64_t _lastFrameRateCallbackTimeNanos;
- rtc::VideoSinkInterface<VideoFrame>* _dataCallBack;
+ std::set<rtc::VideoSinkInterface<VideoFrame>*> _dataCallBacks;
int64_t _lastProcessFrameTimeNanos;
// timestamp for local captured frames
int64_t _incomingFrameTimesNanos[kFrameRateCountHistorySize];
VideoRotation _rotateFrame; // Set if the frame should be rotated by the
// capture module.
// Indicate whether rotation should be applied before delivered externally.
--- a/media/webrtc/trunk/webrtc/test/vcm_capturer.cc
+++ b/media/webrtc/trunk/webrtc/test/vcm_capturer.cc
@@ -88,17 +88,17 @@ void VcmCapturer::RemoveSink(rtc::VideoS
sink_ = nullptr;
}
void VcmCapturer::Destroy() {
if (!vcm_)
return;
vcm_->StopCapture();
- vcm_->DeRegisterCaptureDataCallback();
+ vcm_->DeRegisterCaptureDataCallback(this);
// Release reference to VCM.
vcm_ = nullptr;
}
VcmCapturer::~VcmCapturer() { Destroy(); }
void VcmCapturer::OnFrame(const VideoFrame& frame) {
rtc::CritScope lock(&crit_);
--- a/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc
+++ b/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.cc
@@ -433,17 +433,16 @@ int32_t DesktopCaptureImpl::Init(const c
}
DesktopCaptureImpl::DesktopCaptureImpl(const int32_t id)
: _id(id),
_deviceUniqueId(""),
_apiCs(*CriticalSectionWrapper::CreateCriticalSection()),
_requestedCapability(),
_callBackCs(*CriticalSectionWrapper::CreateCriticalSection()),
- _dataCallBack(NULL),
_rotateFrame(kVideoRotation_0),
last_capture_time_(rtc::TimeNanos()/rtc::kNumNanosecsPerMillisec),
// XXX Note that this won't capture drift!
delta_ntp_internal_ms_(Clock::GetRealTimeClock()->CurrentNtpInMilliseconds() -
last_capture_time_),
time_event_(EventWrapper::Create()),
mRefCount(0),
#if defined(_WIN32)
@@ -461,33 +460,44 @@ DesktopCaptureImpl::DesktopCaptureImpl(c
_requestedCapability.codecType = kVideoCodecUnknown;
memset(_incomingFrameTimesNanos, 0, sizeof(_incomingFrameTimesNanos));
}
DesktopCaptureImpl::~DesktopCaptureImpl() {
time_event_->Set();
capturer_thread_->Stop();
- DeRegisterCaptureDataCallback();
delete &_callBackCs;
delete &_apiCs;
}
void DesktopCaptureImpl::RegisterCaptureDataCallback(rtc::VideoSinkInterface<VideoFrame> *dataCallback)
{
CriticalSectionScoped cs(&_apiCs);
CriticalSectionScoped cs2(&_callBackCs);
- _dataCallBack = dataCallback;
+ _dataCallBacks.insert(dataCallback);
}
-void DesktopCaptureImpl::DeRegisterCaptureDataCallback()
+void DesktopCaptureImpl::DeRegisterCaptureDataCallback(
+ rtc::VideoSinkInterface<VideoFrame> *dataCallback)
{
CriticalSectionScoped cs(&_apiCs);
CriticalSectionScoped cs2(&_callBackCs);
- _dataCallBack = nullptr;
+ auto it = _dataCallBacks.find(dataCallback);
+ if (it != _dataCallBacks.end()) {
+ _dataCallBacks.erase(it);
+ }
+}
+
+int32_t DesktopCaptureImpl::StopCaptureIfAllClientsClose() {
+ if (_dataCallBacks.empty()) {
+ return StopCapture();
+ } else {
+ return 0;
+ }
}
int32_t DesktopCaptureImpl::DeliverCapturedFrame(webrtc::VideoFrame& captureFrame,
int64_t capture_time) {
UpdateFrameCount(); // frame count used for local frame rate callback.
// Set the capture time
if (capture_time != 0) {
@@ -497,18 +507,18 @@ int32_t DesktopCaptureImpl::DeliverCaptu
}
if (captureFrame.render_time_ms() == last_capture_time_) {
// We don't allow the same capture time for two frames, drop this one.
return -1;
}
last_capture_time_ = captureFrame.render_time_ms();
- if (_dataCallBack) {
- _dataCallBack->OnFrame(captureFrame);
+ for (auto dataCallBack : _dataCallBacks) {
+ dataCallBack->OnFrame(captureFrame);
}
return 0;
}
// Copied from VideoCaptureImpl::IncomingFrame. See Bug 1038324
int32_t DesktopCaptureImpl::IncomingFrame(uint8_t* videoFrame,
size_t videoFrameLength,
@@ -684,16 +694,20 @@ uint32_t DesktopCaptureImpl::CalculateFr
int32_t DesktopCaptureImpl::StartCapture(const VideoCaptureCapability& capability) {
_requestedCapability = capability;
#if defined(_WIN32)
uint32_t maxFPSNeeded = 1000/_requestedCapability.maxFPS;
capturer_thread_->RequestCallbackTimer(maxFPSNeeded);
#endif
+ if (started_) {
+ return 0;
+ }
+
desktop_capturer_cursor_composer_->Start(this);
capturer_thread_->Start();
started_ = true;
return 0;
}
int32_t DesktopCaptureImpl::StopCapture() {
--- a/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.h
+++ b/media/webrtc/trunk/webrtc/video_engine/desktop_capture_impl.h
@@ -22,16 +22,17 @@
#include "webrtc/base/scoped_ref_ptr.h"
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/modules/video_capture/video_capture_config.h"
#include "webrtc/modules/desktop_capture/shared_memory.h"
#include "webrtc/base/platform_thread.h"
#include "webrtc/system_wrappers/include/event_wrapper.h"
#include "webrtc/modules/desktop_capture/desktop_device_info.h"
#include "webrtc/modules/desktop_capture/desktop_and_cursor_composer.h"
+#include <set>
using namespace webrtc::videocapturemodule;
namespace webrtc {
class CriticalSectionWrapper;
class VideoCaptureEncodeInterface;
@@ -168,17 +169,18 @@ public:
static VideoCaptureModule::DeviceInfo* CreateDeviceInfo(const int32_t id, const CaptureDeviceType type);
int32_t Init(const char* uniqueId, const CaptureDeviceType type);
//RefCounting for RefCountedModule
virtual int32_t AddRef() const override;
virtual int32_t Release() const override;
//Call backs
virtual void RegisterCaptureDataCallback(rtc::VideoSinkInterface<VideoFrame> *dataCallback) override;
- virtual void DeRegisterCaptureDataCallback() override;
+ virtual void DeRegisterCaptureDataCallback(rtc::VideoSinkInterface<VideoFrame> *dataCallback) override;
+ virtual int32_t StopCaptureIfAllClientsClose() override;
virtual int32_t SetCaptureRotation(VideoRotation rotation) override;
virtual bool SetApplyRotation(bool enable) override;
virtual bool GetApplyRotation() override { return true; }
virtual const char* CurrentDeviceName() const override;
// Implement VideoCaptureExternal
@@ -208,17 +210,17 @@ protected:
VideoCaptureCapability _requestedCapability; // Should be set by platform dependent code in StartCapture.
private:
void UpdateFrameCount();
uint32_t CalculateFrameRate(int64_t now_ns);
CriticalSectionWrapper& _callBackCs;
- rtc::VideoSinkInterface<VideoFrame>* _dataCallBack;
+ std::set<rtc::VideoSinkInterface<VideoFrame>*> _dataCallBacks;
int64_t _incomingFrameTimesNanos[kFrameRateCountHistorySize];// timestamp for local captured frames
VideoRotation _rotateFrame; //Set if the frame should be rotated by the capture module.
// Used to make sure incoming timestamp is increasing for every frame.
int64_t last_capture_time_;
// Delta used for translating between NTP and internal timestamps.