Title: [272050] trunk
Revision
272050
Author
[email protected]
Date
2021-01-29 00:47:58 -0800 (Fri, 29 Jan 2021)

Log 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:

Modified Paths

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>
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to