commit 903b2ec85840bfe79c003c8de14bc276e60a5ce0
parent 5f30f72c4a2b57a6a830a82d292a9c440df60458
Author: Andreas Pehrson <apehrson@mozilla.com>
Date: Tue, 11 Nov 2025 08:20:26 +0000
Bug 1771789 - Disregard failure to send Stop in MERVS state machine. r=jib
Failure to send stop does not mean the capturer is still running. Treat it as
stopped.
Differential Revision: https://phabricator.services.mozilla.com/D268858
Diffstat:
3 files changed, 81 insertions(+), 36 deletions(-)
diff --git a/dom/media/systemservices/CamerasChild.cpp b/dom/media/systemservices/CamerasChild.cpp
@@ -141,35 +141,42 @@ mozilla::ipc::IPCResult CamerasChild::RecvReplyNumberOfCapabilities(
template <class T = int>
class LockAndDispatch {
public:
+ using Result = CamerasChild::DispatchToParentResult;
+
LockAndDispatch(CamerasChild* aCamerasChild, const char* aRequestingFunc,
- nsIRunnable* aRunnable, T aFailureValue,
+ nsIRunnable* aRunnable, T aFailureValue, T aIPCFailureValue,
const T& aSuccessValue)
: mCamerasChild(aCamerasChild),
mRequestingFunc(aRequestingFunc),
mRunnable(aRunnable),
mReplyLock(aCamerasChild->mReplyMonitor),
mRequestLock(aCamerasChild->mRequestMutex),
- mSuccess(true),
+ mStatus(Result::SUCCESS),
mFailureValue(aFailureValue),
+ mIPCFailureValue(aIPCFailureValue),
mSuccessValue(aSuccessValue) {
Dispatch();
}
T ReturnValue() const {
- if (mSuccess) {
+ if (mStatus == Result::SUCCESS) {
return mSuccessValue;
- } else {
+ }
+ if (mStatus == Result::FAILURE) {
return mFailureValue;
}
+ MOZ_ASSERT(mStatus == Result::DISCONNECTED);
+ return mIPCFailureValue;
}
- const bool& Success() const { return mSuccess; }
+ bool Success() const { return mStatus == Result::SUCCESS; }
+ bool Disconnected() const { return mStatus == Result::DISCONNECTED; }
private:
void Dispatch() {
- if (!mCamerasChild->DispatchToParent(mRunnable, mReplyLock)) {
+ mStatus = mCamerasChild->DispatchToParent(mRunnable, mReplyLock);
+ if (mStatus != Result::SUCCESS) {
LOG(("Cameras dispatch for IPC failed in %s", mRequestingFunc));
- mSuccess = false;
}
}
@@ -181,13 +188,15 @@ class LockAndDispatch {
// the reply to be filled in, necessitating the additional mRequestLock/Mutex;
MonitorAutoLock mReplyLock;
MutexAutoLock mRequestLock;
- bool mSuccess;
+ CamerasChild::DispatchToParentResult mStatus;
const T mFailureValue;
+ const T mIPCFailureValue;
const T& mSuccessValue;
};
-bool CamerasChild::DispatchToParent(nsIRunnable* aRunnable,
- MonitorAutoLock& aMonitor) {
+auto CamerasChild::DispatchToParent(nsIRunnable* aRunnable,
+ MonitorAutoLock& aMonitor)
+ -> DispatchToParentResult {
CamerasSingleton::Mutex().AssertCurrentThreadOwns();
mReplyMonitor.AssertCurrentThreadOwns();
CamerasSingleton::Thread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
@@ -197,11 +206,12 @@ bool CamerasChild::DispatchToParent(nsIRunnable* aRunnable,
do {
// If the parent has been shut down, then we won't receive a reply.
if (!mIPCIsAlive) {
- return false;
+ return DispatchToParentResult::DISCONNECTED;
}
aMonitor.Wait();
} while (!mReceivedReply);
- return mReplySuccess;
+ return mReplySuccess ? DispatchToParentResult::SUCCESS
+ : DispatchToParentResult::FAILURE;
}
int CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine,
@@ -213,7 +223,7 @@ int CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine,
mozilla::NewRunnableMethod<CaptureEngine, nsCString>(
"camera::PCamerasChild::SendNumberOfCapabilities", this,
&CamerasChild::SendNumberOfCapabilities, aCapEngine, unique_id);
- LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
+ LockAndDispatch<> dispatcher(this, __func__, runnable, 0, 0, mReplyInteger);
LOG(("Capture capability count: %d", dispatcher.ReturnValue()));
return dispatcher.ReturnValue();
}
@@ -223,7 +233,7 @@ int CamerasChild::NumberOfCaptureDevices(CaptureEngine aCapEngine) {
nsCOMPtr<nsIRunnable> runnable = mozilla::NewRunnableMethod<CaptureEngine>(
"camera::PCamerasChild::SendNumberOfCaptureDevices", this,
&CamerasChild::SendNumberOfCaptureDevices, aCapEngine);
- LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
+ LockAndDispatch<> dispatcher(this, __func__, runnable, 0, 0, mReplyInteger);
LOG(("Capture Devices: %d", dispatcher.ReturnValue()));
return dispatcher.ReturnValue();
}
@@ -244,8 +254,8 @@ int CamerasChild::EnsureInitialized(CaptureEngine aCapEngine) {
nsCOMPtr<nsIRunnable> runnable = mozilla::NewRunnableMethod<CaptureEngine>(
"camera::PCamerasChild::SendEnsureInitialized", this,
&CamerasChild::SendEnsureInitialized, aCapEngine);
- LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
- LOG(("Capture Devices: %d", dispatcher.ReturnValue()));
+ LockAndDispatch<> dispatcher(this, __func__, runnable, 0, 0, mReplyInteger);
+ LOG(("Initialized: %d", dispatcher.ReturnValue()));
return dispatcher.ReturnValue();
}
@@ -262,7 +272,8 @@ int CamerasChild::GetCaptureCapability(
&CamerasChild::SendGetCaptureCapability, aCapEngine, unique_id,
capability_number);
mReplyCapability = capability;
- LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero);
+ LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
+ kSuccess);
mReplyCapability = nullptr;
return dispatcher.ReturnValue();
}
@@ -292,7 +303,8 @@ int CamerasChild::GetCaptureDevice(
mozilla::NewRunnableMethod<CaptureEngine, unsigned int>(
"camera::PCamerasChild::SendGetCaptureDevice", this,
&CamerasChild::SendGetCaptureDevice, aCapEngine, list_number);
- LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero);
+ LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
+ kSuccess);
if (dispatcher.Success()) {
base::strlcpy(device_nameUTF8, mReplyDeviceName.get(),
device_nameUTF8Length);
@@ -328,7 +340,8 @@ int CamerasChild::AllocateCapture(CaptureEngine aCapEngine,
mozilla::NewRunnableMethod<CaptureEngine, nsCString, uint64_t>(
"camera::PCamerasChild::SendAllocateCapture", this,
&CamerasChild::SendAllocateCapture, aCapEngine, unique_id, aWindowID);
- LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mReplyInteger);
+ LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
+ mReplyInteger);
if (dispatcher.Success()) {
LOG(("Capture Device allocated: %d", mReplyInteger));
}
@@ -353,7 +366,8 @@ int CamerasChild::ReleaseCapture(CaptureEngine aCapEngine,
mozilla::NewRunnableMethod<CaptureEngine, int>(
"camera::PCamerasChild::SendReleaseCapture", this,
&CamerasChild::SendReleaseCapture, aCapEngine, capture_id);
- LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero);
+ LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
+ kSuccess);
return dispatcher.ReturnValue();
}
@@ -401,7 +415,8 @@ int CamerasChild::StartCapture(CaptureEngine aCapEngine, const int capture_id,
"camera::PCamerasChild::SendStartCapture", this,
&CamerasChild::SendStartCapture, aCapEngine, capture_id, capCap,
constraints, resize_mode);
- LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero);
+ LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
+ kSuccess);
return dispatcher.ReturnValue();
}
@@ -412,7 +427,8 @@ int CamerasChild::FocusOnSelectedSource(CaptureEngine aCapEngine,
mozilla::NewRunnableMethod<CaptureEngine, int>(
"camera::PCamerasChild::SendFocusOnSelectedSource", this,
&CamerasChild::SendFocusOnSelectedSource, aCapEngine, aCaptureId);
- LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero);
+ LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
+ kSuccess);
return dispatcher.ReturnValue();
}
@@ -422,8 +438,9 @@ int CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id) {
mozilla::NewRunnableMethod<CaptureEngine, int>(
"camera::PCamerasChild::SendStopCapture", this,
&CamerasChild::SendStopCapture, aCapEngine, capture_id);
- LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero);
- if (dispatcher.Success()) {
+ LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
+ kSuccess);
+ if (dispatcher.Success() || dispatcher.Disconnected()) {
RemoveCallback(capture_id);
}
return dispatcher.ReturnValue();
@@ -526,7 +543,6 @@ CamerasChild::CamerasChild()
mReplyMonitor("mozilla::cameras::CamerasChild::mReplyMonitor"),
mReceivedReply(false),
mReplySuccess(false),
- mZero(0),
mReplyInteger(0),
mReplyScary(false) {
LOG(("CamerasChild: %p", this));
diff --git a/dom/media/systemservices/CamerasChild.h b/dom/media/systemservices/CamerasChild.h
@@ -45,6 +45,10 @@ class CamerasChild;
template <class T>
class LockAndDispatch;
+static constexpr int kSuccess = 0;
+static constexpr int kError = -1;
+static constexpr int kIpcError = -2;
+
// We emulate the sync webrtc.org API with the help of singleton
// CamerasSingleton, which manages a pointer to an IPC object, a thread
// where IPC operations should run on, and a mutex.
@@ -223,7 +227,13 @@ class CamerasChild final : public PCamerasChild {
~CamerasChild();
// Dispatch a Runnable to the PCamerasParent, by executing it on the
// decidecated Cameras IPC/PBackground thread.
- bool DispatchToParent(nsIRunnable* aRunnable, MonitorAutoLock& aMonitor);
+ enum class DispatchToParentResult : int8_t {
+ SUCCESS = 0,
+ FAILURE = -1,
+ DISCONNECTED = -2,
+ };
+ DispatchToParentResult DispatchToParent(nsIRunnable* aRunnable,
+ MonitorAutoLock& aMonitor);
void AddCallback(int capture_id, FrameRelay* render);
void RemoveCallback(int capture_id);
@@ -250,7 +260,6 @@ class CamerasChild final : public PCamerasChild {
bool mReceivedReply;
// Async responses data contents;
bool mReplySuccess;
- const int mZero;
int mReplyInteger;
webrtc::VideoCaptureCapability* mReplyCapability = nullptr;
nsCString mReplyDeviceName;
diff --git a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -369,12 +369,22 @@ nsresult MediaEngineRemoteVideoSource::Deallocate() {
LOG("Video device %d deallocated", mCaptureId);
- if (camera::GetChildAndCall(&camera::CamerasChild::ReleaseCapture, mCapEngine,
- mCaptureId)) {
- // Failure can occur when the parent process is shutting down.
- return NS_ERROR_FAILURE;
+ int error = camera::GetChildAndCall(&camera::CamerasChild::ReleaseCapture,
+ mCapEngine, mCaptureId);
+
+ if (error == camera::kSuccess) {
+ return NS_OK;
}
- return NS_OK;
+
+ if (error == camera::kIpcError) {
+ // Failure can occur when the parent process is shutting down, and the IPC
+ // channel is down. We still consider the capturer deallocated in this
+ // case, since it cannot deliver frames without the IPC channel open.
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(error == camera::kError);
+ return NS_ERROR_FAILURE;
}
void MediaEngineRemoteVideoSource::SetTrack(const RefPtr<MediaTrack>& aTrack,
@@ -468,9 +478,11 @@ nsresult MediaEngineRemoteVideoSource::Stop() {
MOZ_ASSERT(mState == kStarted);
- if (camera::GetChildAndCall(&camera::CamerasChild::StopCapture, mCapEngine,
- mCaptureId)) {
- // Failure can occur when the parent process is shutting down.
+ int error = camera::GetChildAndCall(&camera::CamerasChild::StopCapture,
+ mCapEngine, mCaptureId);
+
+ if (error == camera::kError) {
+ // CamerasParent replied with error. The capturer is still running.
return NS_ERROR_FAILURE;
}
@@ -479,7 +491,15 @@ nsresult MediaEngineRemoteVideoSource::Stop() {
mState = kStopped;
}
- return NS_OK;
+ if (error == camera::kSuccess) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(error == camera::kIpcError);
+ // Failure can occur when the parent process is shutting down, and the IPC
+ // channel is down. We still consider the capturer stopped in this case,
+ // since it cannot deliver frames without the IPC channel open.
+ return NS_ERROR_FAILURE;
}
nsresult MediaEngineRemoteVideoSource::Reconfigure(