Diff
Modified: trunk/Source/WebKit/ChangeLog (272049 => 272050)
--- trunk/Source/WebKit/ChangeLog 2021-01-29 08:04:55 UTC (rev 272049)
+++ trunk/Source/WebKit/ChangeLog 2021-01-29 08:47:58 UTC (rev 272050)
@@ -1,3 +1,35 @@
+2021-01-29 Youenn Fablet <[email protected]>
+
+ Recover audio and video capture from GPUProcess crash
+ https://bugs.webkit.org/show_bug.cgi?id=221086
+
+ Reviewed by Eric Carlson.
+
+ Make RemoteRealtimeMediaSource observe GPU process crash.
+ If RemoteRealtimeMediaSource is live and capturing in GPUProcess, restart capture from WebProcess.
+ If GPU process crashes, make sure to send back the necessary entitlements and information to GPUProcess
+ of which processes are allowed to capture.
+
+ Covered by API test.
+
+ * UIProcess/WebPageProxy.cpp:
+ (WebKit::WebPageProxy::gpuProcessCrashed):
+ * WebProcess/cocoa/RemoteCaptureSampleManager.cpp:
+ (WebKit::RemoteCaptureSampleManager::didUpdateSourceConnection):
+ * WebProcess/cocoa/RemoteCaptureSampleManager.h:
+ * WebProcess/cocoa/RemoteRealtimeMediaSource.cpp:
+ (WebKit::RemoteRealtimeMediaSource::create):
+ (WebKit::RemoteRealtimeMediaSource::createRemoteMediaSource):
+ (WebKit::RemoteRealtimeMediaSource::~RemoteRealtimeMediaSource):
+ (WebKit::RemoteRealtimeMediaSource::gpuProcessConnectionDidClose):
+ * WebProcess/cocoa/RemoteRealtimeMediaSource.h:
+ * WebProcess/cocoa/UserMediaCaptureManager.cpp:
+ (WebKit::UserMediaCaptureManager::AudioFactory::createAudioCaptureSource):
+ (WebKit::UserMediaCaptureManager::VideoFactory::createVideoCaptureSource):
+ (WebKit::UserMediaCaptureManager::DisplayFactory::createDisplayCaptureSource):
+ (WebKit::UserMediaCaptureManager::didUpdateSourceConnection):
+ * WebProcess/cocoa/UserMediaCaptureManager.h:
+
2021-01-28 Megan Gardner <[email protected]>
Add plumbing to allow AppHighlights to be stored.
Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (272049 => 272050)
--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2021-01-29 08:04:55 UTC (rev 272049)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2021-01-29 08:47:58 UTC (rev 272050)
@@ -10362,6 +10362,16 @@
void WebPageProxy::gpuProcessCrashed()
{
pageClient().gpuProcessCrashed();
+
+#if ENABLE(MEDIA_STREAM)
+ bool shouldAllowAudioCapture = isCapturingAudio() && preferences().captureAudioInGPUProcessEnabled();
+ bool shouldAllowVideoCapture = isCapturingVideo() && preferences().captureVideoInGPUProcessEnabled();
+ bool shouldAllowDisplayCapture = false;
+ if (shouldAllowAudioCapture || shouldAllowVideoCapture) {
+ auto& gpuProcess = process().processPool().ensureGPUProcess();
+ gpuProcess.updateCaptureAccess(shouldAllowAudioCapture, shouldAllowVideoCapture, shouldAllowDisplayCapture, m_process->coreProcessIdentifier(), [] { });
+ }
+#endif
}
#endif
Modified: trunk/Source/WebKit/WebProcess/cocoa/RemoteCaptureSampleManager.cpp (272049 => 272050)
--- trunk/Source/WebKit/WebProcess/cocoa/RemoteCaptureSampleManager.cpp 2021-01-29 08:04:55 UTC (rev 272049)
+++ trunk/Source/WebKit/WebProcess/cocoa/RemoteCaptureSampleManager.cpp 2021-01-29 08:47:58 UTC (rev 272050)
@@ -82,6 +82,12 @@
});
}
+void RemoteCaptureSampleManager::didUpdateSourceConnection(RemoteRealtimeMediaSource& source)
+{
+ ASSERT(WTF::isMainRunLoop());
+ setConnection(source.connection());
+}
+
void RemoteCaptureSampleManager::dispatchToThread(Function<void()>&& callback)
{
m_queue->dispatch(WTFMove(callback));
Modified: trunk/Source/WebKit/WebProcess/cocoa/RemoteCaptureSampleManager.h (272049 => 272050)
--- trunk/Source/WebKit/WebProcess/cocoa/RemoteCaptureSampleManager.h 2021-01-29 08:04:55 UTC (rev 272049)
+++ trunk/Source/WebKit/WebProcess/cocoa/RemoteCaptureSampleManager.h 2021-01-29 08:47:58 UTC (rev 272050)
@@ -48,6 +48,8 @@
void addSource(Ref<RemoteRealtimeMediaSource>&&);
void removeSource(WebCore::RealtimeMediaSourceIdentifier);
+ void didUpdateSourceConnection(RemoteRealtimeMediaSource&);
+
void didReceiveMessage(IPC::Connection&, IPC::Decoder&);
private:
Modified: trunk/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.cpp (272049 => 272050)
--- trunk/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.cpp 2021-01-29 08:04:55 UTC (rev 272049)
+++ trunk/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.cpp 2021-01-29 08:47:58 UTC (rev 272050)
@@ -46,20 +46,11 @@
using namespace PAL;
using namespace WebCore;
-Ref<RealtimeMediaSource> RemoteRealtimeMediaSource::create(const WebCore::CaptureDevice& device, const WebCore::MediaConstraints& constraints, String&& name, String&& hashSalt, UserMediaCaptureManager& manager, bool shouldCaptureInGPUProcess)
+Ref<RealtimeMediaSource> RemoteRealtimeMediaSource::create(const CaptureDevice& device, const MediaConstraints& constraints, String&& name, String&& hashSalt, UserMediaCaptureManager& manager, bool shouldCaptureInGPUProcess)
{
auto source = adoptRef(*new RemoteRealtimeMediaSource(RealtimeMediaSourceIdentifier::generate(), device.type(), WTFMove(name), WTFMove(hashSalt), manager, shouldCaptureInGPUProcess));
manager.addSource(source.copyRef());
- source->connection()->sendWithAsyncReply(Messages::UserMediaCaptureManagerProxy::CreateMediaSourceForCaptureDeviceWithConstraints(source->identifier(), device, source->deviceIDHashSalt(), constraints), [source = source.copyRef()](bool succeeded, auto&& errorMessage, auto&& settings, auto&& capabilities) {
- if (!succeeded) {
- source->didFail(WTFMove(errorMessage));
- return;
- }
- source->setName(String { settings.label().string() });
- source->setSettings(WTFMove(settings));
- source->setCapabilities(WTFMove(capabilities));
- source->setAsReady();
- });
+ source->createRemoteMediaSource(device, constraints);
return source;
}
@@ -106,8 +97,32 @@
}
}
+void RemoteRealtimeMediaSource::createRemoteMediaSource(const CaptureDevice& device, const MediaConstraints& constraints)
+{
+ if (m_shouldCaptureInGPUProcess) {
+ m_device = device;
+ m_constraints = constraints;
+ }
+
+ connection()->sendWithAsyncReply(Messages::UserMediaCaptureManagerProxy::CreateMediaSourceForCaptureDeviceWithConstraints(identifier(), device, deviceIDHashSalt(), constraints), [this, protectedThis = makeRef(*this)](bool succeeded, auto&& errorMessage, auto&& settings, auto&& capabilities) {
+ if (!succeeded) {
+ didFail(WTFMove(errorMessage));
+ return;
+ }
+ setName(String { settings.label().string() });
+ setSettings(WTFMove(settings));
+ setCapabilities(WTFMove(capabilities));
+ setAsReady();
+ if (m_shouldCaptureInGPUProcess)
+ WebProcess::singleton().ensureGPUProcessConnection().addClient(*this);
+ });
+}
+
RemoteRealtimeMediaSource::~RemoteRealtimeMediaSource()
{
+ if (m_shouldCaptureInGPUProcess)
+ WebProcess::singleton().ensureGPUProcessConnection().removeClient(*this);
+
switch (m_deviceType) {
case CaptureDevice::DeviceType::Microphone:
#if PLATFORM(IOS_FAMILY)
@@ -305,6 +320,21 @@
}
}
+#if ENABLE(GPU_PROCESS)
+void RemoteRealtimeMediaSource::gpuProcessConnectionDidClose(GPUProcessConnection&)
+{
+ ASSERT(m_shouldCaptureInGPUProcess);
+ if (isEnded())
+ return;
+
+ m_manager.didUpdateSourceConnection(*this);
+ createRemoteMediaSource(m_device, m_constraints);
+ // FIXME: We should update the track according current settings.
+ if (isProducingData())
+ startProducingData();
}
+#endif
+}
+
#endif
Modified: trunk/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.h (272049 => 272050)
--- trunk/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.h 2021-01-29 08:04:55 UTC (rev 272049)
+++ trunk/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.h 2021-01-29 08:47:58 UTC (rev 272050)
@@ -27,6 +27,7 @@
#if PLATFORM(COCOA) && ENABLE(MEDIA_STREAM)
+#include "GPUProcessConnection.h"
#include <WebCore/CaptureDevice.h>
#include <WebCore/RealtimeMediaSource.h>
#include <WebCore/RealtimeMediaSourceIdentifier.h>
@@ -47,9 +48,13 @@
class UserMediaCaptureManager;
-class RemoteRealtimeMediaSource : public WebCore::RealtimeMediaSource {
+class RemoteRealtimeMediaSource : public WebCore::RealtimeMediaSource
+#if ENABLE(GPU_PROCESS)
+ , public GPUProcessConnection::Client
+#endif
+{
public:
- static Ref<WebCore::RealtimeMediaSource> create(const WebCore::CaptureDevice&, const WebCore::MediaConstraints&, String&& name, String&& hashSalt, UserMediaCaptureManager&, bool shouldCaptureInGPUProcess = false);
+ static Ref<WebCore::RealtimeMediaSource> create(const WebCore::CaptureDevice&, const WebCore::MediaConstraints&, String&& name, String&& hashSalt, UserMediaCaptureManager&, bool shouldCaptureInGPUProcess);
~RemoteRealtimeMediaSource();
WebCore::RealtimeMediaSourceIdentifier identifier() const { return m_identifier; }
@@ -86,6 +91,12 @@
WebCore::CaptureDevice::DeviceType deviceType() const final { return m_deviceType; }
Ref<RealtimeMediaSource> clone() final;
+#if ENABLE(GPU_PROCESS)
+ // GPUProcessConnection::Client
+ void gpuProcessConnectionDidClose(GPUProcessConnection&) final;
+#endif
+
+ void createRemoteMediaSource(const WebCore::CaptureDevice&, const WebCore::MediaConstraints&);
void didFail(String&& errorMessage);
void setAsReady();
void setCapabilities(WebCore::RealtimeMediaSourceCapabilities&&);
@@ -104,6 +115,8 @@
bool m_isReady { false };
String m_errorMessage;
CompletionHandler<void(String)> m_callback;
+ WebCore::CaptureDevice m_device;
+ WebCore::MediaConstraints m_constraints;
};
} // namespace WebKit
Modified: trunk/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.cpp (272049 => 272050)
--- trunk/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.cpp 2021-01-29 08:04:55 UTC (rev 272049)
+++ trunk/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.cpp 2021-01-29 08:47:58 UTC (rev 272050)
@@ -158,7 +158,7 @@
DeprecatedGlobalSettings::setShouldManageAudioSessionCategory(true);
#endif
- return RemoteRealtimeMediaSource::create(device, *constraints, { }, WTFMove(hashSalt), m_manager);
+ return RemoteRealtimeMediaSource::create(device, *constraints, { }, WTFMove(hashSalt), m_manager, m_shouldCaptureInGPUProcess);
}
void UserMediaCaptureManager::AudioFactory::setShouldCaptureInGPUProcess(bool value)
@@ -176,7 +176,7 @@
return CaptureSourceOrError { "Video capture in GPUProcess is not implemented"_s };
#endif
- return RemoteRealtimeMediaSource::create(device, *constraints, { }, WTFMove(hashSalt), m_manager);
+ return RemoteRealtimeMediaSource::create(device, *constraints, { }, WTFMove(hashSalt), m_manager, m_shouldCaptureInGPUProcess);
}
#if PLATFORM(IOS_FAMILY)
@@ -191,9 +191,14 @@
if (!constraints)
return { };
- return RemoteRealtimeMediaSource::create(device, *constraints, { }, { }, m_manager);
+ return RemoteRealtimeMediaSource::create(device, *constraints, { }, { }, m_manager, false);
}
+void UserMediaCaptureManager::didUpdateSourceConnection(RemoteRealtimeMediaSource& source)
+{
+ m_remoteCaptureSampleManager.didUpdateSourceConnection(source);
}
+}
+
#endif
Modified: trunk/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.h (272049 => 272050)
--- trunk/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.h 2021-01-29 08:04:55 UTC (rev 272049)
+++ trunk/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.h 2021-01-29 08:47:58 UTC (rev 272050)
@@ -59,6 +59,7 @@
void addSource(Ref<RemoteRealtimeMediaSource>&&);
void removeSource(WebCore::RealtimeMediaSourceIdentifier);
+ void didUpdateSourceConnection(RemoteRealtimeMediaSource&);
private:
// WebCore::RealtimeMediaSource factories
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit/GetUserMedia.mm (272049 => 272050)
--- trunk/Tools/TestWebKitAPI/Tests/WebKit/GetUserMedia.mm 2021-01-29 08:04:55 UTC (rev 272049)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit/GetUserMedia.mm 2021-01-29 08:47:58 UTC (rev 272050)
@@ -38,6 +38,7 @@
#import <WebKit/WKWebViewConfiguration.h>
#import <WebKit/WKWebViewConfigurationPrivate.h>
#import <WebKit/WKWebViewPrivateForTesting.h>
+#import <WebKit/_WKInternalDebugFeature.h>
#import <WebKit/_WKProcessPoolConfiguration.h>
#import <wtf/text/StringBuilder.h>
#import <wtf/text/WTFString.h>
@@ -372,6 +373,84 @@
}
#endif
+#if ENABLE(GPU_PROCESS)
+TEST(WebKit2, CrashGPUProcessWhileCapturing)
+{
+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+ auto preferences = [configuration preferences];
+
+ for (_WKInternalDebugFeature *feature in [WKPreferences _internalDebugFeatures]) {
+ if ([feature.key isEqualToString:@"CaptureAudioInGPUProcessEnabled"])
+ [preferences _setEnabled:YES forInternalDebugFeature:feature];
+ if ([feature.key isEqualToString:@"CaptureAudioInUIProcessEnabled"])
+ [preferences _setEnabled:NO forInternalDebugFeature:feature];
+ if ([feature.key isEqualToString:@"CaptureVideoInGPUProcessEnabled"])
+ [preferences _setEnabled:YES forInternalDebugFeature:feature];
+ }
+
+ preferences._mediaCaptureRequiresSecureConnection = NO;
+ configuration.get()._mediaCaptureEnabled = YES;
+ preferences._mockCaptureDevicesEnabled = YES;
+
+ auto messageHandler = adoptNS([[GUMMessageHandler alloc] init]);
+ [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"gum"];
+
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400) configuration:configuration.get()]);
+
+ auto delegate = adoptNS([[GetUserMediaCaptureUIDelegate alloc] init]);
+ webView.get().UIDelegate = delegate.get();
+
+ [webView loadTestPageNamed:@"getUserMedia"];
+ EXPECT_TRUE(waitUntilCaptureState(webView.get(), _WKMediaCaptureStateActiveCamera));
+
+ done = false;
+ [webView stringByEvaluatingJavaScript:@"captureAudioAndVideo(true)"];
+ TestWebKitAPI::Util::run(&done);
+
+ auto webViewPID = [webView _webProcessIdentifier];
+
+ // The GPU process should get launched.
+ auto* processPool = configuration.get().processPool;
+ unsigned timeout = 0;
+ while (![processPool _gpuProcessIdentifier] && timeout++ < 100)
+ TestWebKitAPI::Util::sleep(0.1);
+
+ EXPECT_NE([processPool _gpuProcessIdentifier], 0);
+ if (![processPool _gpuProcessIdentifier])
+ return;
+ auto gpuProcessPID = [processPool _gpuProcessIdentifier];
+
+ // Kill the GPU Process.
+ kill(gpuProcessPID, 9);
+
+ // GPU Process should get relaunched.
+ timeout = 0;
+ while ((![processPool _gpuProcessIdentifier] || [processPool _gpuProcessIdentifier] == gpuProcessPID) && timeout++ < 100)
+ TestWebKitAPI::Util::sleep(0.1);
+ EXPECT_NE([processPool _gpuProcessIdentifier], 0);
+ EXPECT_NE([processPool _gpuProcessIdentifier], gpuProcessPID);
+ gpuProcessPID = [processPool _gpuProcessIdentifier];
+
+ // Make sure the WebProcess did not crash.
+ EXPECT_EQ(webViewPID, [webView _webProcessIdentifier]);
+
+ done = false;
+ [webView stringByEvaluatingJavaScript:@"createConnection()"];
+ TestWebKitAPI::Util::run(&done);
+
+ done = false;
+ [webView stringByEvaluatingJavaScript:@"checkVideoStatus()"];
+ TestWebKitAPI::Util::run(&done);
+
+ done = false;
+ [webView stringByEvaluatingJavaScript:@"checkAudioStatus()"];
+ TestWebKitAPI::Util::run(&done);
+
+ EXPECT_EQ(gpuProcessPID, [processPool _gpuProcessIdentifier]);
+ EXPECT_EQ(webViewPID, [webView _webProcessIdentifier]);
+}
+#endif
+
} // namespace TestWebKitAPI
#endif // ENABLE(MEDIA_STREAM)
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html (272049 => 272050)
--- trunk/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html 2021-01-29 08:04:55 UTC (rev 272049)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html 2021-01-29 08:47:58 UTC (rev 272050)
@@ -61,9 +61,13 @@
navigator.mediaDevices.getUserMedia({video: true}).then(s => video.srcObject = s);
}
- function captureAudioAndVideo()
+ function captureAudioAndVideo(notifySuccess)
{
- navigator.mediaDevices.getUserMedia({audio: true, video: true}).then(s => stream = s);
+ navigator.mediaDevices.getUserMedia({audio: true, video: true}).then(s => {
+ if (notifySuccess)
+ window.webkit.messageHandlers.gum.postMessage("PASS");
+ stream = s;
+ });
}
function notifyEndedEvent()
@@ -92,6 +96,71 @@
window.webkit.messageHandlers.gum.postMessage(RTCRtpSender.getCapabilities('video') != null ? "PASS" : "FAIL");
}
+ function getStats(connection, type, kind)
+ {
+ return connection.getStats().then((report) => {
+ var stats;
+ report.forEach((statItem) => {
+ if (statItem.type === type && statItem.kind === kind) {
+ stats = statItem;
+ }
+ });
+ return stats;
+ });
+ }
+
+ var pc1, pc2;
+ function createConnection() {
+ pc1 = new RTCPeerConnection();
+ pc2 = new RTCPeerConnection();
+
+ pc1._onicecandidate_ = (e) => { if (e.candidate) pc2.addIceCandidate(e.candidate) }
+ pc2._onicecandidate_ = (e) => { if (e.candidate) pc1.addIceCandidate(e.candidate) }
+
+ stream.getTracks().forEach(track => pc1.addTrack(track, stream));
+
+ pc1.createOffer()
+ .then((o) => pc1.setLocalDescription(o))
+ .then(() => pc2.setRemoteDescription(pc1.localDescription))
+ .then(() => pc2.createAnswer())
+ .then((a) => pc2.setLocalDescription(a))
+ .then((a) => pc1.setRemoteDescription(pc2.localDescription))
+ .then(() => {
+ window.webkit.messageHandlers.gum.postMessage("PASS");
+ });
+ }
+
+ function checkVideoStatus(counter) {
+ if (!counter)
+ counter = 0;
+ else if (counter > 100) {
+ window.webkit.messageHandlers.gum.postMessage("FAIL");
+ return;
+ }
+ getStats(pc1, "outbound-rtp", "video").then((stats) => {
+ if (stats && stats.framesEncoded) {
+ window.webkit.messageHandlers.gum.postMessage("PASS");
+ return;
+ }
+ setTimeout(() => checkVideoStatus(++counter), 50);
+ });
+ }
+
+ function checkAudioStatus(counter) {
+ if (!counter)
+ counter = 0;
+ else if (counter > 100) {
+ window.webkit.messageHandlers.gum.postMessage("FAIL");
+ return;
+ }
+ getStats(pc2, "inbound-rtp", "audio").then((stats) => {
+ if (stats && stats.audioLevel > 0) {
+ window.webkit.messageHandlers.gum.postMessage("PASS");
+ return;
+ }
+ setTimeout(() => checkAudioStatus(++counter), 50);
+ });
+ }
</script>
<head>