Changeset 272050 in webkit


Ignore:
Timestamp:
Jan 29, 2021 12:47:58 AM (3 years ago)
Author:
youenn@apple.com
Message:

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:
Location:
trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r272042 r272050  
     12021-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
    1332021-01-28  Megan Gardner  <megan_gardner@apple.com>
    234
  • trunk/Source/WebKit/UIProcess/WebPageProxy.cpp

    r272042 r272050  
    1036310363{
    1036410364    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
    1036510375}
    1036610376#endif
  • trunk/Source/WebKit/WebProcess/cocoa/RemoteCaptureSampleManager.cpp

    r271477 r272050  
    8383}
    8484
     85void RemoteCaptureSampleManager::didUpdateSourceConnection(RemoteRealtimeMediaSource& source)
     86{
     87    ASSERT(WTF::isMainRunLoop());
     88    setConnection(source.connection());
     89}
     90
    8591void RemoteCaptureSampleManager::dispatchToThread(Function<void()>&& callback)
    8692{
  • trunk/Source/WebKit/WebProcess/cocoa/RemoteCaptureSampleManager.h

    r270663 r272050  
    4949    void removeSource(WebCore::RealtimeMediaSourceIdentifier);
    5050
     51    void didUpdateSourceConnection(RemoteRealtimeMediaSource&);
     52
    5153    void didReceiveMessage(IPC::Connection&, IPC::Decoder&);
    5254
  • trunk/Source/WebKit/WebProcess/cocoa/RemoteRealtimeMediaSource.cpp

    r267237 r272050  
    4747using namespace WebCore;
    4848
    49 Ref<RealtimeMediaSource> RemoteRealtimeMediaSource::create(const WebCore::CaptureDevice& device, const WebCore::MediaConstraints& constraints, String&& name, String&& hashSalt, UserMediaCaptureManager& manager, bool shouldCaptureInGPUProcess)
     49Ref<RealtimeMediaSource> RemoteRealtimeMediaSource::create(const CaptureDevice& device, const MediaConstraints& constraints, String&& name, String&& hashSalt, UserMediaCaptureManager& manager, bool shouldCaptureInGPUProcess)
    5050{
    5151    auto source = adoptRef(*new RemoteRealtimeMediaSource(RealtimeMediaSourceIdentifier::generate(), device.type(), WTFMove(name), WTFMove(hashSalt), manager, shouldCaptureInGPUProcess));
    5252    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);
    6354    return source;
    6455}
     
    10798}
    10899
     100void 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
    109121RemoteRealtimeMediaSource::~RemoteRealtimeMediaSource()
    110122{
     123    if (m_shouldCaptureInGPUProcess)
     124        WebProcess::singleton().ensureGPUProcessConnection().removeClient(*this);
     125
    111126    switch (m_deviceType) {
    112127    case CaptureDevice::DeviceType::Microphone:
     
    306321}
    307322
    308 }
    309 
    310 #endif
     323#if ENABLE(GPU_PROCESS)
     324void 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  
    2828#if PLATFORM(COCOA) && ENABLE(MEDIA_STREAM)
    2929
     30#include "GPUProcessConnection.h"
    3031#include <WebCore/CaptureDevice.h>
    3132#include <WebCore/RealtimeMediaSource.h>
     
    4849class UserMediaCaptureManager;
    4950
    50 class RemoteRealtimeMediaSource : public WebCore::RealtimeMediaSource {
     51class RemoteRealtimeMediaSource : public WebCore::RealtimeMediaSource
     52#if ENABLE(GPU_PROCESS)
     53    , public GPUProcessConnection::Client
     54#endif
     55{
    5156public:
    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);
    5358    ~RemoteRealtimeMediaSource();
    5459
     
    8792    Ref<RealtimeMediaSource> clone() final;
    8893
     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&);
    89100    void didFail(String&& errorMessage);
    90101    void setAsReady();
     
    105116    String m_errorMessage;
    106117    CompletionHandler<void(String)> m_callback;
     118    WebCore::CaptureDevice m_device;
     119    WebCore::MediaConstraints m_constraints;
    107120};
    108121
  • trunk/Source/WebKit/WebProcess/cocoa/UserMediaCaptureManager.cpp

    r261375 r272050  
    159159#endif
    160160
    161     return RemoteRealtimeMediaSource::create(device, *constraints, { }, WTFMove(hashSalt), m_manager);
     161    return RemoteRealtimeMediaSource::create(device, *constraints, { }, WTFMove(hashSalt), m_manager, m_shouldCaptureInGPUProcess);
    162162}
    163163
     
    177177#endif
    178178
    179     return RemoteRealtimeMediaSource::create(device, *constraints, { }, WTFMove(hashSalt), m_manager);
     179    return RemoteRealtimeMediaSource::create(device, *constraints, { }, WTFMove(hashSalt), m_manager, m_shouldCaptureInGPUProcess);
    180180}
    181181
     
    192192        return { };
    193193
    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
     197void 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  
    6060    void addSource(Ref<RemoteRealtimeMediaSource>&&);
    6161    void removeSource(WebCore::RealtimeMediaSourceIdentifier);
     62    void didUpdateSourceConnection(RemoteRealtimeMediaSource&);
    6263
    6364private:
  • trunk/Tools/TestWebKitAPI/Tests/WebKit/GetUserMedia.mm

    r271670 r272050  
    3939#import <WebKit/WKWebViewConfigurationPrivate.h>
    4040#import <WebKit/WKWebViewPrivateForTesting.h>
     41#import <WebKit/_WKInternalDebugFeature.h>
    4142#import <WebKit/_WKProcessPoolConfiguration.h>
    4243#import <wtf/text/StringBuilder.h>
     
    373374#endif
    374375
     376#if ENABLE(GPU_PROCESS)
     377TEST(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
    375454} // namespace TestWebKitAPI
    376455
  • trunk/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html

    r256377 r272050  
    6262            }
    6363
    64             function captureAudioAndVideo()
     64            function captureAudioAndVideo(notifySuccess)
    6565            {
    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                });
    6771            }
    6872
     
    9397            }
    9498
     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            }
    95164        </script>
    96165    <head>
Note: See TracChangeset for help on using the changeset viewer.