tor-browser

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

commit 82e8303b6670528eeb75d6af14e6404a6256694a
parent e6ac33e70d49b8475756fb7c936bc0052c50077f
Author: Andreas Pehrson <apehrson@mozilla.com>
Date:   Tue, 14 Oct 2025 18:35:44 +0000

Bug 1771789 - In CamerasParent use one ShmemPool per source. r=jib

This mitigates the risk of exhausting the ShmemPool when many distinct sources
are live.

Differential Revision: https://phabricator.services.mozilla.com/D266405

Diffstat:
Mdom/media/systemservices/CamerasChild.cpp | 14+++++++-------
Mdom/media/systemservices/CamerasChild.h | 5+++--
Mdom/media/systemservices/CamerasParent.cpp | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mdom/media/systemservices/CamerasParent.h | 15++++++++++-----
Mdom/media/systemservices/PCameras.ipdl | 4++--
5 files changed, 86 insertions(+), 37 deletions(-)

diff --git a/dom/media/systemservices/CamerasChild.cpp b/dom/media/systemservices/CamerasChild.cpp @@ -492,18 +492,18 @@ mozilla::ipc::IPCResult CamerasChild::RecvCaptureEnded( } mozilla::ipc::IPCResult CamerasChild::RecvDeliverFrame( - nsTArray<int>&& capIds, mozilla::ipc::Shmem&& shmem, - const VideoFrameProperties& prop) { + const int& aCaptureId, nsTArray<int>&& aStreamIds, + mozilla::ipc::Shmem&& aShmem, const VideoFrameProperties& aProps) { MutexAutoLock lock(mCallbackMutex); - for (int capId : capIds) { - if (auto* cb = Callback(capId)) { - unsigned char* image = shmem.get<unsigned char>(); - cb->DeliverFrame(image, prop); + for (const int& streamId : aStreamIds) { + if (auto* cb = Callback(streamId)) { + unsigned char* image = aShmem.get<unsigned char>(); + cb->DeliverFrame(image, aProps); } else { LOG(("DeliverFrame called with dead callback")); } } - SendReleaseFrame(std::move(shmem)); + SendReleaseFrame(aCaptureId, std::move(aShmem)); return IPC_OK(); } diff --git a/dom/media/systemservices/CamerasChild.h b/dom/media/systemservices/CamerasChild.h @@ -149,8 +149,9 @@ class CamerasChild final : public PCamerasChild { mozilla::ipc::IPCResult RecvCaptureEnded( nsTArray<int>&& aCaptureIds) override; mozilla::ipc::IPCResult RecvDeliverFrame( - nsTArray<int>&& capIds, mozilla::ipc::Shmem&& shmem, - const VideoFrameProperties& prop) override; + const int& aCaptureId, nsTArray<int>&& aStreamIds, + mozilla::ipc::Shmem&& aShmem, + const VideoFrameProperties& aProps) override; mozilla::ipc::IPCResult RecvDeviceChange() override; diff --git a/dom/media/systemservices/CamerasParent.cpp b/dom/media/systemservices/CamerasParent.cpp @@ -260,13 +260,14 @@ void CamerasParent::OnDeviceChange() { class DeliverFrameRunnable : public mozilla::Runnable { public: DeliverFrameRunnable(CamerasParent* aParent, CaptureEngine aEngine, - nsTArray<int>&& aStreamIds, + int aCaptureId, nsTArray<int>&& aStreamIds, const TrackingId& aTrackingId, const webrtc::VideoFrame& aFrame, const VideoFrameProperties& aProperties) : Runnable("camera::DeliverFrameRunnable"), mParent(aParent), mCapEngine(aEngine), + mCaptureId(aCaptureId), mStreamIds(std::move(aStreamIds)), mTrackingId(aTrackingId), mProperties(aProperties) { @@ -286,12 +287,13 @@ class DeliverFrameRunnable : public mozilla::Runnable { } DeliverFrameRunnable(CamerasParent* aParent, CaptureEngine aEngine, - nsTArray<int>&& aStreamIds, + int aCaptureId, nsTArray<int>&& aStreamIds, const TrackingId& aTrackingId, ShmemBuffer aBuffer, VideoFrameProperties& aProperties) : Runnable("camera::DeliverFrameRunnable"), mParent(aParent), mCapEngine(aEngine), + mCaptureId(aCaptureId), mStreamIds(std::move(aStreamIds)), mTrackingId(aTrackingId), mBuffer(std::move(aBuffer)), @@ -305,15 +307,16 @@ class DeliverFrameRunnable : public mozilla::Runnable { // Communication channel is being torn down return NS_OK; } - mParent->DeliverFrameOverIPC(mCapEngine, mStreamIds, mTrackingId, - std::move(mBuffer), mAlternateBuffer.get(), - mProperties); + mParent->DeliverFrameOverIPC(mCapEngine, mCaptureId, mStreamIds, + mTrackingId, std::move(mBuffer), + mAlternateBuffer.get(), mProperties); return NS_OK; } private: const RefPtr<CamerasParent> mParent; const CaptureEngine mCapEngine; + const int mCaptureId; const nsTArray<int> mStreamIds; const TrackingId mTrackingId; ShmemBuffer mBuffer; @@ -321,7 +324,7 @@ class DeliverFrameRunnable : public mozilla::Runnable { const VideoFrameProperties mProperties; }; -int CamerasParent::DeliverFrameOverIPC(CaptureEngine aCapEngine, +int CamerasParent::DeliverFrameOverIPC(CaptureEngine aCapEngine, int aCaptureId, const Span<const int>& aStreamIds, const TrackingId& aTrackingId, ShmemBuffer aBuffer, @@ -333,7 +336,15 @@ int CamerasParent::DeliverFrameOverIPC(CaptureEngine aCapEngine, // buffer of the right size. if (aAltBuffer != nullptr) { // Get a shared memory buffer from the pool, at least size big - ShmemBuffer shMemBuff = mShmemPool.Get(this, aProps.bufferSize()); + ShmemBuffer shMemBuff; + { + auto guard = mShmemPools.Lock(); + auto it = guard->find(aCaptureId); + if (it != guard->end()) { + auto& [_, pool] = *it; + shMemBuff = pool.Get(this, aProps.bufferSize()); + } + } if (!shMemBuff.Valid()) { LOG("No usable Video shmem in DeliverFrame (out of buffers?)"); @@ -348,14 +359,16 @@ int CamerasParent::DeliverFrameOverIPC(CaptureEngine aCapEngine, memcpy(shMemBuff.GetBytes(), aAltBuffer, aProps.bufferSize()); rec.Record(); - if (!SendDeliverFrame(aStreamIds, std::move(shMemBuff.Get()), aProps)) { + if (!SendDeliverFrame(aCaptureId, aStreamIds, std::move(shMemBuff.Get()), + aProps)) { return -1; } } else { MOZ_ASSERT(aBuffer.Valid()); // ShmemBuffer was available, we're all good. A single copy happened // in the original webrtc callback. - if (!SendDeliverFrame(aStreamIds, std::move(aBuffer.Get()), aProps)) { + if (!SendDeliverFrame(aCaptureId, aStreamIds, std::move(aBuffer.Get()), + aProps)) { return -1; } } @@ -380,8 +393,14 @@ bool CamerasParent::IsWindowCapturing(uint64_t aWindowId, return false; } -ShmemBuffer CamerasParent::GetBuffer(size_t aSize) { - return mShmemPool.GetIfAvailable(aSize); +ShmemBuffer CamerasParent::GetBuffer(int aCaptureId, size_t aSize) { + auto guard = mShmemPools.Lock(); + auto it = guard->find(aCaptureId); + if (it == guard->end()) { + return ShmemBuffer(); + } + auto& [_, pool] = *it; + return pool.GetIfAvailable(aSize); } /*static*/ @@ -645,7 +664,8 @@ void AggregateCapturer::OnFrame(const webrtc::VideoFrame& aVideoFrame) { LOG_VERBOSE("CamerasParent(%p)::%s", parent, __func__); // Get a shared memory buffer to copy the frame data into - ShmemBuffer shMemBuffer = parent->GetBuffer(properties.bufferSize()); + ShmemBuffer shMemBuffer = + parent->GetBuffer(mCaptureId, properties.bufferSize()); if (!shMemBuffer.Valid()) { // Either we ran out of buffers or they're not the right size yet LOG("Correctly sized Video shmem not available in DeliverFrame"); @@ -660,23 +680,34 @@ void AggregateCapturer::OnFrame(const webrtc::VideoFrame& aVideoFrame) { VideoFrameUtils::CopyVideoFrameBuffers( shMemBuffer.GetBytes(), properties.bufferSize(), aVideoFrame); rec.Record(); - runnable = new DeliverFrameRunnable(parent, mCapEngine, std::move(ids), - mTrackingId, std::move(shMemBuffer), - properties); + runnable = new DeliverFrameRunnable(parent, mCapEngine, mCaptureId, + std::move(ids), mTrackingId, + std::move(shMemBuffer), properties); } if (!runnable) { - runnable = new DeliverFrameRunnable(parent, mCapEngine, std::move(ids), - mTrackingId, aVideoFrame, properties); + runnable = new DeliverFrameRunnable(parent, mCapEngine, mCaptureId, + std::move(ids), mTrackingId, + aVideoFrame, properties); } nsIEventTarget* target = parent->GetBackgroundEventTarget(); target->Dispatch(runnable, NS_DISPATCH_NORMAL); } } -ipc::IPCResult CamerasParent::RecvReleaseFrame(ipc::Shmem&& aShmem) { +ipc::IPCResult CamerasParent::RecvReleaseFrame(const int& aCaptureId, + ipc::Shmem&& aShmem) { MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread()); - mShmemPool.Put(ShmemBuffer(aShmem)); + auto guard = mShmemPools.Lock(); + auto it = guard->find(aCaptureId); + if (it == guard->end()) { + MOZ_ASSERT_UNREACHABLE( + "Releasing shmem but pool is already gone. Shmem must have been " + "deallocated."); + return IPC_FAIL(this, "Shmem was already deallocated"); + } + auto& [_, pool] = *it; + pool.Put(ShmemBuffer(aShmem)); return IPC_OK(); } @@ -1313,12 +1344,18 @@ auto CamerasParent::GetOrCreateCapturer( -> GetOrCreateCapturerResult { MOZ_ASSERT(mVideoCaptureThread->IsOnCurrentThread()); VideoEngine* engine = EnsureInitialized(aEngine); + const auto ensureShmemPool = [&](int aCaptureId) { + auto guard = mShmemPools.Lock(); + constexpr size_t kMaxShmemBuffers = 1; + guard->try_emplace(aCaptureId, kMaxShmemBuffers); + }; for (auto& capturer : *mCapturers) { if (capturer->mCapEngine != aEngine) { continue; } if (capturer->mUniqueId.Equals(aUniqueId)) { int streamId = engine->GenerateId(); + ensureShmemPool(capturer->mCaptureId); capturer->AddStream(this, streamId, aWindowId); return {.mCapturer = capturer.get(), .mStreamId = streamId}; } @@ -1326,6 +1363,7 @@ auto CamerasParent::GetOrCreateCapturer( NotNull capturer = mCapturers->AppendElement( AggregateCapturer::Create(mVideoCaptureThread, aEngine, engine, aUniqueId, aWindowId, std::move(aCapabilities), this)); + ensureShmemPool(capturer->get()->mCaptureId); return {.mCapturer = capturer->get(), .mStreamId = capturer->get()->mCaptureId}; } @@ -1428,7 +1466,12 @@ void CamerasParent::ActorDestroy(ActorDestroyReason aWhy) { LOG_FUNCTION(); // Release shared memory now, it's our last chance - mShmemPool.Cleanup(this); + { + auto guard = mShmemPools.Lock(); + for (auto& [captureId, pool] : *guard) { + pool.Cleanup(this); + } + } // We don't want to receive callbacks or anything if we can't // forward them anymore anyway. mDestroyed = true; @@ -1461,7 +1504,7 @@ CamerasParent::CamerasParent() mEngines(sEngines), mCapturers(sCapturers), mVideoCaptureFactory(EnsureVideoCaptureFactory()), - mShmemPool(CaptureEngine::MaxEngine), + mShmemPools("CamerasParent::mShmemPools"), mPBackgroundEventTarget(GetCurrentSerialEventTarget()), mDestroyed(false) { MOZ_ASSERT(mPBackgroundEventTarget != nullptr, diff --git a/dom/media/systemservices/CamerasParent.h b/dom/media/systemservices/CamerasParent.h @@ -193,7 +193,7 @@ class CamerasParent final : public PCamerasParent { mozilla::ipc::IPCResult RecvStopCapture(const CaptureEngine& aCapEngine, const int& aStreamId) override; mozilla::ipc::IPCResult RecvReleaseFrame( - mozilla::ipc::Shmem&& aShmem) override; + const int& aCaptureId, mozilla::ipc::Shmem&& aShmem) override; void ActorDestroy(ActorDestroyReason aWhy) override; mozilla::ipc::IPCResult RecvEnsureInitialized( const CaptureEngine& aCapEngine) override; @@ -207,10 +207,10 @@ class CamerasParent final : public PCamerasParent { MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread()); return mDestroyed; }; - ShmemBuffer GetBuffer(size_t aSize); + ShmemBuffer GetBuffer(int aCaptureId, size_t aSize); // helper to forward to the PBackground thread - int DeliverFrameOverIPC(CaptureEngine aCapEngine, + int DeliverFrameOverIPC(CaptureEngine aCapEngine, int aCaptureId, const Span<const int>& aStreamId, const TrackingId& aTrackingId, ShmemBuffer aBuffer, unsigned char* aAltBuffer, @@ -272,8 +272,13 @@ class CamerasParent final : public PCamerasParent { // capture thread only. const RefPtr<VideoCaptureFactory> mVideoCaptureFactory; - // image buffers - ShmemPool mShmemPool; + // Image buffers. One pool per CamerasParent instance and capture id (i.e. + // unique source). Multiple CamerasParent instances capturing the same source + // need distinct ShmemPools as ShmemBuffers are tied to the IPC channel. + // Access is on the PBackground thread for mutations and + // allocating shmem buffers, and on the callback thread (varies by capture + // backend) for querying an existing pool for an available buffer. + DataMutex<std::map<int, ShmemPool>> mShmemPools; // PBackgroundParent thread const nsCOMPtr<nsISerialEventTarget> mPBackgroundEventTarget; diff --git a/dom/media/systemservices/PCameras.ipdl b/dom/media/systemservices/PCameras.ipdl @@ -63,7 +63,7 @@ async protocol PCameras child: async CaptureEnded(int[] streamIds); // transfers ownership of |buffer| from parent to child - async DeliverFrame(int[] streamIds, Shmem buffer, VideoFrameProperties props); + async DeliverFrame(int captureId, int[] streamIds, Shmem buffer, VideoFrameProperties props); async DeviceChange(); async ReplyNumberOfCaptureDevices(int deviceCount); async ReplyNumberOfCapabilities(int capabilityCount); @@ -92,7 +92,7 @@ parent: async FocusOnSelectedSource(CaptureEngine engine, int streamId); async StopCapture(CaptureEngine engine, int streamId); // transfers frame back - async ReleaseFrame(Shmem s); + async ReleaseFrame(int captureId, Shmem s); // setup camera engine async EnsureInitialized(CaptureEngine engine);