Changeset 272050 in webkit
- Timestamp:
- Jan 29, 2021, 12:47:58 AM (4 years ago)
- Location:
- trunk
- Files:
-
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebKit/ChangeLog
r272042 r272050 1 2021-01-29 Youenn Fablet <youenn@apple.com> 2 3 Recover audio and video capture from GPUProcess crash 4 https://bugs.webkit.org/show_bug.cgi?id=221086 5 6 Reviewed by Eric Carlson. 7 8 Make RemoteRealtimeMediaSource observe GPU process crash. 9 If RemoteRealtimeMediaSource is live and capturing in GPUProcess, restart capture from WebProcess. 10 If GPU process crashes, make sure to send back the necessary entitlements and information to GPUProcess 11 of which processes are allowed to capture. 12 13 Covered by API test. 14 15 * UIProcess/WebPageProxy.cpp: 16 (WebKit::WebPageProxy::gpuProcessCrashed): 17 * WebProcess/cocoa/RemoteCaptureSampleManager.cpp: 18 (WebKit::RemoteCaptureSampleManager::didUpdateSourceConnection): 19 * WebProcess/cocoa/RemoteCaptureSampleManager.h: 20 * WebProcess/cocoa/RemoteRealtimeMediaSource.cpp: 21 (WebKit::RemoteRealtimeMediaSource::create): 22 (WebKit::RemoteRealtimeMediaSource::createRemoteMediaSource): 23 (WebKit::RemoteRealtimeMediaSource::~RemoteRealtimeMediaSource): 24 (WebKit::RemoteRealtimeMediaSource::gpuProcessConnectionDidClose): 25 * WebProcess/cocoa/RemoteRealtimeMediaSource.h: 26 * WebProcess/cocoa/UserMediaCaptureManager.cpp: 27 (WebKit::UserMediaCaptureManager::AudioFactory::createAudioCaptureSource): 28 (WebKit::UserMediaCaptureManager::VideoFactory::createVideoCaptureSource): 29 (WebKit::UserMediaCaptureManager::DisplayFactory::createDisplayCaptureSource): 30 (WebKit::UserMediaCaptureManager::didUpdateSourceConnection): 31 * WebProcess/cocoa/UserMediaCaptureManager.h: 32 1 33 2021-01-28 Megan Gardner <megan_gardner@apple.com> 2 34 -
trunk/Source/WebKit/UIProcess/WebPageProxy.cpp
r272042 r272050 10363 10363 { 10364 10364 pageClient().gpuProcessCrashed(); 10365 10366 #if ENABLE(MEDIA_STREAM) 10367 bool shouldAllowAudioCapture = isCapturingAudio() && preferences().captureAudioInGPUProcessEnabled(); 10368 bool shouldAllowVideoCapture = isCapturingVideo() && preferences().captureVideoInGPUProcessEnabled(); 10369 bool shouldAllowDisplayCapture = false; 10370 if (shouldAllowAudioCapture || shouldAllowVideoCapture) { 10371 auto& gpuProcess = process().processPool().ensureGPUProcess(); 10372 gpuProcess.updateCaptureAccess(shouldAllowAudioCapture, shouldAllowVideoCapture, shouldAllowDisplayCapture, m_process->coreProcessIdentifier(), [] { }); 10373 } 10374 #endif 10365 10375 } 10366 10376 #endif -
trunk/Source/WebKit/WebProcess/cocoa/RemoteCaptureSampleManager.cpp
r271477 r272050 83 83 } 84 84 85 void RemoteCaptureSampleManager::didUpdateSourceConnection(RemoteRealtimeMediaSource& source) 86 { 87 ASSERT(WTF::isMainRunLoop()); 88 setConnection(source.connection()); 89 } 90 85 91 void RemoteCaptureSampleManager::dispatchToThread(Function<void()>&& callback) 86 92 { -
trunk/Source/WebKit/WebProcess/cocoa/RemoteCaptureSampleManager.h
r270663 r272050 49 49 void removeSource(WebCore::RealtimeMediaSourceIdentifier); 50 50 51 void didUpdateSourceConnection(RemoteRealtimeMediaSource&); 52 51 53 void didReceiveMessage(IPC::Connection&, IPC::Decoder&); 52 54 -
trunk/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.cpp
r267237 r272050 47 47 using namespace WebCore; 48 48 49 Ref<RealtimeMediaSource> RemoteRealtimeMediaSource::create(const WebCore::CaptureDevice& device, const WebCore::MediaConstraints& constraints, String&& name, String&& hashSalt, UserMediaCaptureManager& manager, bool shouldCaptureInGPUProcess)49 Ref<RealtimeMediaSource> RemoteRealtimeMediaSource::create(const CaptureDevice& device, const MediaConstraints& constraints, String&& name, String&& hashSalt, UserMediaCaptureManager& manager, bool shouldCaptureInGPUProcess) 50 50 { 51 51 auto source = adoptRef(*new RemoteRealtimeMediaSource(RealtimeMediaSourceIdentifier::generate(), device.type(), WTFMove(name), WTFMove(hashSalt), manager, shouldCaptureInGPUProcess)); 52 52 manager.addSource(source.copyRef()); 53 source->connection()->sendWithAsyncReply(Messages::UserMediaCaptureManagerProxy::CreateMediaSourceForCaptureDeviceWithConstraints(source->identifier(), device, source->deviceIDHashSalt(), constraints), [source = source.copyRef()](bool succeeded, auto&& errorMessage, auto&& settings, auto&& capabilities) { 54 if (!succeeded) { 55 source->didFail(WTFMove(errorMessage)); 56 return; 57 } 58 source->setName(String { settings.label().string() }); 59 source->setSettings(WTFMove(settings)); 60 source->setCapabilities(WTFMove(capabilities)); 61 source->setAsReady(); 62 }); 53 source->createRemoteMediaSource(device, constraints); 63 54 return source; 64 55 } … … 107 98 } 108 99 100 void RemoteRealtimeMediaSource::createRemoteMediaSource(const CaptureDevice& device, const MediaConstraints& constraints) 101 { 102 if (m_shouldCaptureInGPUProcess) { 103 m_device = device; 104 m_constraints = constraints; 105 } 106 107 connection()->sendWithAsyncReply(Messages::UserMediaCaptureManagerProxy::CreateMediaSourceForCaptureDeviceWithConstraints(identifier(), device, deviceIDHashSalt(), constraints), [this, protectedThis = makeRef(*this)](bool succeeded, auto&& errorMessage, auto&& settings, auto&& capabilities) { 108 if (!succeeded) { 109 didFail(WTFMove(errorMessage)); 110 return; 111 } 112 setName(String { settings.label().string() }); 113 setSettings(WTFMove(settings)); 114 setCapabilities(WTFMove(capabilities)); 115 setAsReady(); 116 if (m_shouldCaptureInGPUProcess) 117 WebProcess::singleton().ensureGPUProcessConnection().addClient(*this); 118 }); 119 } 120 109 121 RemoteRealtimeMediaSource::~RemoteRealtimeMediaSource() 110 122 { 123 if (m_shouldCaptureInGPUProcess) 124 WebProcess::singleton().ensureGPUProcessConnection().removeClient(*this); 125 111 126 switch (m_deviceType) { 112 127 case CaptureDevice::DeviceType::Microphone: … … 306 321 } 307 322 308 } 309 310 #endif 323 #if ENABLE(GPU_PROCESS) 324 void RemoteRealtimeMediaSource::gpuProcessConnectionDidClose(GPUProcessConnection&) 325 { 326 ASSERT(m_shouldCaptureInGPUProcess); 327 if (isEnded()) 328 return; 329 330 m_manager.didUpdateSourceConnection(*this); 331 createRemoteMediaSource(m_device, m_constraints); 332 // FIXME: We should update the track according current settings. 333 if (isProducingData()) 334 startProducingData(); 335 } 336 #endif 337 338 } 339 340 #endif -
trunk/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.h
r267237 r272050 28 28 #if PLATFORM(COCOA) && ENABLE(MEDIA_STREAM) 29 29 30 #include "GPUProcessConnection.h" 30 31 #include <WebCore/CaptureDevice.h> 31 32 #include <WebCore/RealtimeMediaSource.h> … … 48 49 class UserMediaCaptureManager; 49 50 50 class RemoteRealtimeMediaSource : public WebCore::RealtimeMediaSource { 51 class RemoteRealtimeMediaSource : public WebCore::RealtimeMediaSource 52 #if ENABLE(GPU_PROCESS) 53 , public GPUProcessConnection::Client 54 #endif 55 { 51 56 public: 52 static Ref<WebCore::RealtimeMediaSource> create(const WebCore::CaptureDevice&, const WebCore::MediaConstraints&, String&& name, String&& hashSalt, UserMediaCaptureManager&, bool shouldCaptureInGPUProcess = false);57 static Ref<WebCore::RealtimeMediaSource> create(const WebCore::CaptureDevice&, const WebCore::MediaConstraints&, String&& name, String&& hashSalt, UserMediaCaptureManager&, bool shouldCaptureInGPUProcess); 53 58 ~RemoteRealtimeMediaSource(); 54 59 … … 87 92 Ref<RealtimeMediaSource> clone() final; 88 93 94 #if ENABLE(GPU_PROCESS) 95 // GPUProcessConnection::Client 96 void gpuProcessConnectionDidClose(GPUProcessConnection&) final; 97 #endif 98 99 void createRemoteMediaSource(const WebCore::CaptureDevice&, const WebCore::MediaConstraints&); 89 100 void didFail(String&& errorMessage); 90 101 void setAsReady(); … … 105 116 String m_errorMessage; 106 117 CompletionHandler<void(String)> m_callback; 118 WebCore::CaptureDevice m_device; 119 WebCore::MediaConstraints m_constraints; 107 120 }; 108 121 -
trunk/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.cpp
r261375 r272050 159 159 #endif 160 160 161 return RemoteRealtimeMediaSource::create(device, *constraints, { }, WTFMove(hashSalt), m_manager );161 return RemoteRealtimeMediaSource::create(device, *constraints, { }, WTFMove(hashSalt), m_manager, m_shouldCaptureInGPUProcess); 162 162 } 163 163 … … 177 177 #endif 178 178 179 return RemoteRealtimeMediaSource::create(device, *constraints, { }, WTFMove(hashSalt), m_manager );179 return RemoteRealtimeMediaSource::create(device, *constraints, { }, WTFMove(hashSalt), m_manager, m_shouldCaptureInGPUProcess); 180 180 } 181 181 … … 192 192 return { }; 193 193 194 return RemoteRealtimeMediaSource::create(device, *constraints, { }, { }, m_manager); 195 } 196 197 } 198 199 #endif 194 return RemoteRealtimeMediaSource::create(device, *constraints, { }, { }, m_manager, false); 195 } 196 197 void UserMediaCaptureManager::didUpdateSourceConnection(RemoteRealtimeMediaSource& source) 198 { 199 m_remoteCaptureSampleManager.didUpdateSourceConnection(source); 200 } 201 202 } 203 204 #endif -
trunk/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.h
r266166 r272050 60 60 void addSource(Ref<RemoteRealtimeMediaSource>&&); 61 61 void removeSource(WebCore::RealtimeMediaSourceIdentifier); 62 void didUpdateSourceConnection(RemoteRealtimeMediaSource&); 62 63 63 64 private: -
trunk/Tools/TestWebKitAPI/Tests/WebKit/GetUserMedia.mm
r271670 r272050 39 39 #import <WebKit/WKWebViewConfigurationPrivate.h> 40 40 #import <WebKit/WKWebViewPrivateForTesting.h> 41 #import <WebKit/_WKInternalDebugFeature.h> 41 42 #import <WebKit/_WKProcessPoolConfiguration.h> 42 43 #import <wtf/text/StringBuilder.h> … … 373 374 #endif 374 375 376 #if ENABLE(GPU_PROCESS) 377 TEST(WebKit2, CrashGPUProcessWhileCapturing) 378 { 379 auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]); 380 auto preferences = [configuration preferences]; 381 382 for (_WKInternalDebugFeature *feature in [WKPreferences _internalDebugFeatures]) { 383 if ([feature.key isEqualToString:@"CaptureAudioInGPUProcessEnabled"]) 384 [preferences _setEnabled:YES forInternalDebugFeature:feature]; 385 if ([feature.key isEqualToString:@"CaptureAudioInUIProcessEnabled"]) 386 [preferences _setEnabled:NO forInternalDebugFeature:feature]; 387 if ([feature.key isEqualToString:@"CaptureVideoInGPUProcessEnabled"]) 388 [preferences _setEnabled:YES forInternalDebugFeature:feature]; 389 } 390 391 preferences._mediaCaptureRequiresSecureConnection = NO; 392 configuration.get()._mediaCaptureEnabled = YES; 393 preferences._mockCaptureDevicesEnabled = YES; 394 395 auto messageHandler = adoptNS([[GUMMessageHandler alloc] init]); 396 [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"gum"]; 397 398 auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400) configuration:configuration.get()]); 399 400 auto delegate = adoptNS([[GetUserMediaCaptureUIDelegate alloc] init]); 401 webView.get().UIDelegate = delegate.get(); 402 403 [webView loadTestPageNamed:@"getUserMedia"]; 404 EXPECT_TRUE(waitUntilCaptureState(webView.get(), _WKMediaCaptureStateActiveCamera)); 405 406 done = false; 407 [webView stringByEvaluatingJavaScript:@"captureAudioAndVideo(true)"]; 408 TestWebKitAPI::Util::run(&done); 409 410 auto webViewPID = [webView _webProcessIdentifier]; 411 412 // The GPU process should get launched. 413 auto* processPool = configuration.get().processPool; 414 unsigned timeout = 0; 415 while (![processPool _gpuProcessIdentifier] && timeout++ < 100) 416 TestWebKitAPI::Util::sleep(0.1); 417 418 EXPECT_NE([processPool _gpuProcessIdentifier], 0); 419 if (![processPool _gpuProcessIdentifier]) 420 return; 421 auto gpuProcessPID = [processPool _gpuProcessIdentifier]; 422 423 // Kill the GPU Process. 424 kill(gpuProcessPID, 9); 425 426 // GPU Process should get relaunched. 427 timeout = 0; 428 while ((![processPool _gpuProcessIdentifier] || [processPool _gpuProcessIdentifier] == gpuProcessPID) && timeout++ < 100) 429 TestWebKitAPI::Util::sleep(0.1); 430 EXPECT_NE([processPool _gpuProcessIdentifier], 0); 431 EXPECT_NE([processPool _gpuProcessIdentifier], gpuProcessPID); 432 gpuProcessPID = [processPool _gpuProcessIdentifier]; 433 434 // Make sure the WebProcess did not crash. 435 EXPECT_EQ(webViewPID, [webView _webProcessIdentifier]); 436 437 done = false; 438 [webView stringByEvaluatingJavaScript:@"createConnection()"]; 439 TestWebKitAPI::Util::run(&done); 440 441 done = false; 442 [webView stringByEvaluatingJavaScript:@"checkVideoStatus()"]; 443 TestWebKitAPI::Util::run(&done); 444 445 done = false; 446 [webView stringByEvaluatingJavaScript:@"checkAudioStatus()"]; 447 TestWebKitAPI::Util::run(&done); 448 449 EXPECT_EQ(gpuProcessPID, [processPool _gpuProcessIdentifier]); 450 EXPECT_EQ(webViewPID, [webView _webProcessIdentifier]); 451 } 452 #endif 453 375 454 } // namespace TestWebKitAPI 376 455 -
trunk/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html
r256377 r272050 62 62 } 63 63 64 function captureAudioAndVideo( )64 function captureAudioAndVideo(notifySuccess) 65 65 { 66 navigator.mediaDevices.getUserMedia({audio: true, video: true}).then(s => stream = s); 66 navigator.mediaDevices.getUserMedia({audio: true, video: true}).then(s => { 67 if (notifySuccess) 68 window.webkit.messageHandlers.gum.postMessage("PASS"); 69 stream = s; 70 }); 67 71 } 68 72 … … 93 97 } 94 98 99 function getStats(connection, type, kind) 100 { 101 return connection.getStats().then((report) => { 102 var stats; 103 report.forEach((statItem) => { 104 if (statItem.type === type && statItem.kind === kind) { 105 stats = statItem; 106 } 107 }); 108 return stats; 109 }); 110 } 111 112 var pc1, pc2; 113 function createConnection() { 114 pc1 = new RTCPeerConnection(); 115 pc2 = new RTCPeerConnection(); 116 117 pc1.onicecandidate = (e) => { if (e.candidate) pc2.addIceCandidate(e.candidate) } 118 pc2.onicecandidate = (e) => { if (e.candidate) pc1.addIceCandidate(e.candidate) } 119 120 stream.getTracks().forEach(track => pc1.addTrack(track, stream)); 121 122 pc1.createOffer() 123 .then((o) => pc1.setLocalDescription(o)) 124 .then(() => pc2.setRemoteDescription(pc1.localDescription)) 125 .then(() => pc2.createAnswer()) 126 .then((a) => pc2.setLocalDescription(a)) 127 .then((a) => pc1.setRemoteDescription(pc2.localDescription)) 128 .then(() => { 129 window.webkit.messageHandlers.gum.postMessage("PASS"); 130 }); 131 } 132 133 function checkVideoStatus(counter) { 134 if (!counter) 135 counter = 0; 136 else if (counter > 100) { 137 window.webkit.messageHandlers.gum.postMessage("FAIL"); 138 return; 139 } 140 getStats(pc1, "outbound-rtp", "video").then((stats) => { 141 if (stats && stats.framesEncoded) { 142 window.webkit.messageHandlers.gum.postMessage("PASS"); 143 return; 144 } 145 setTimeout(() => checkVideoStatus(++counter), 50); 146 }); 147 } 148 149 function checkAudioStatus(counter) { 150 if (!counter) 151 counter = 0; 152 else if (counter > 100) { 153 window.webkit.messageHandlers.gum.postMessage("FAIL"); 154 return; 155 } 156 getStats(pc2, "inbound-rtp", "audio").then((stats) => { 157 if (stats && stats.audioLevel > 0) { 158 window.webkit.messageHandlers.gum.postMessage("PASS"); 159 return; 160 } 161 setTimeout(() => checkAudioStatus(++counter), 50); 162 }); 163 } 95 164 </script> 96 165 <head>
Note:
See TracChangeset
for help on using the changeset viewer.