WebAudioSourceProviderAVFObjC::provideInput should set its WebAudioBufferList paramet...
authoryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 16 Oct 2019 16:48:48 +0000 (16:48 +0000)
committeryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 16 Oct 2019 16:48:48 +0000 (16:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=202930
<rdar://problem/56006776>

Reviewed by Eric Carlson.

Source/WebCore:

There is a time where the bus channel number and audio source channel numbers may be different.
In case the bus channel number is less than the audio source channel number, initialization of
the WebAudioBufferList might not be fully done.
In that case, output silence and return early.
Reduce the number of frames to process based on the number of frames the output audio bus plans to process.

Partially covered by new API test (this a race so we cannot reproduce the crash easily).

* Modules/webaudio/MediaStreamAudioSourceNode.cpp:
(WebCore::MediaStreamAudioSourceNode::process):
Make sure to process the number of frames the output bus expect.
* platform/mediastream/mac/WebAudioSourceProviderAVFObjC.mm:
(WebCore::WebAudioSourceProviderAVFObjC::provideInput):

Tools:

Add a test that has an audio track that goes from 1 to 2 channels while being piped to a WebAudio pipeline.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKit/GetUserMedia.mm:
(-[GUMMessageHandler userContentController:didReceiveScriptMessage:]):
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebKit/getUserMedia-webaudio.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@251188 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/Modules/webaudio/MediaStreamAudioSourceNode.cpp
Source/WebCore/platform/mediastream/mac/WebAudioSourceProviderAVFObjC.mm
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKit/GetUserMedia.mm
Tools/TestWebKitAPI/Tests/WebKit/getUserMedia-webaudio.html [new file with mode: 0644]

index eac9820..a948555 100644 (file)
@@ -1,3 +1,25 @@
+2019-10-16  Youenn Fablet  <youenn@apple.com>
+
+        WebAudioSourceProviderAVFObjC::provideInput should set its WebAudioBufferList parameters correctly
+        https://bugs.webkit.org/show_bug.cgi?id=202930
+        <rdar://problem/56006776>
+
+        Reviewed by Eric Carlson.
+
+        There is a time where the bus channel number and audio source channel numbers may be different.
+        In case the bus channel number is less than the audio source channel number, initialization of
+        the WebAudioBufferList might not be fully done.
+        In that case, output silence and return early.
+        Reduce the number of frames to process based on the number of frames the output audio bus plans to process.
+
+        Partially covered by new API test (this a race so we cannot reproduce the crash easily).
+
+        * Modules/webaudio/MediaStreamAudioSourceNode.cpp:
+        (WebCore::MediaStreamAudioSourceNode::process):
+        Make sure to process the number of frames the output bus expect.
+        * platform/mediastream/mac/WebAudioSourceProviderAVFObjC.mm:
+        (WebCore::WebAudioSourceProviderAVFObjC::provideInput):
+
 2019-10-16  Zalan Bujtas  <zalan@apple.com>
 
         [LFC][TFC] TableFormattingContext::distributeExtraHorizontalSpace should not ignore fixed width columns
index e43f544..7ba59bf 100644 (file)
@@ -131,6 +131,9 @@ void MediaStreamAudioSourceNode::process(size_t numberOfFrames)
         return;
     }
 
+    if (numberOfFrames > outputBus->length())
+        numberOfFrames = outputBus->length();
+
     if (m_multiChannelResampler.get()) {
         ASSERT(m_sourceSampleRate != sampleRate());
         m_multiChannelResampler->process(provider, outputBus, numberOfFrames);
index 7bc8b75..ad862eb 100644 (file)
@@ -79,8 +79,13 @@ void WebAudioSourceProviderAVFObjC::provideInput(AudioBus* bus, size_t framesToP
     }
 
     WebAudioBufferList list { m_outputDescription.value() };
+    if (bus->numberOfChannels() < list.bufferCount()) {
+        bus->zero();
+        return;
+    }
+
     for (unsigned i = 0; i < bus->numberOfChannels(); ++i) {
-        AudioChannel& channel = *bus->channel(i);
+        auto& channel = *bus->channel(i);
         if (i >= list.bufferCount()) {
             channel.zero();
             continue;
@@ -91,6 +96,7 @@ void WebAudioSourceProviderAVFObjC::provideInput(AudioBus* bus, size_t framesToP
         buffer->mDataByteSize = channel.length() * sizeof(float);
     }
 
+    ASSERT(framesToProcess <= bus->length());
     m_dataSource->pullSamples(*list.list(), framesToProcess, m_readCount, 0, AudioSampleDataSource::Copy);
     m_readCount += framesToProcess;
 }
index 072f817..afde174 100644 (file)
@@ -1,3 +1,19 @@
+2019-10-16  Youenn Fablet  <youenn@apple.com>
+
+        WebAudioSourceProviderAVFObjC::provideInput should set its WebAudioBufferList parameters correctly
+        https://bugs.webkit.org/show_bug.cgi?id=202930
+        <rdar://problem/56006776>
+
+        Reviewed by Eric Carlson.
+
+        Add a test that has an audio track that goes from 1 to 2 channels while being piped to a WebAudio pipeline.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKit/GetUserMedia.mm:
+        (-[GUMMessageHandler userContentController:didReceiveScriptMessage:]):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebKit/getUserMedia-webaudio.html: Added.
+
 2019-10-15  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [GTK][WPE] Add user messages API
index 18b5bf2..945eddb 100644 (file)
                4135FB842011FAA700332139 /* InjectInternals_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4135FB832011FAA300332139 /* InjectInternals_Bundle.cpp */; };
                4135FB852011FABF00332139 /* libWebCoreTestSupport.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4135FB862011FABF00332139 /* libWebCoreTestSupport.dylib */; };
                414AD6862285D1C000777F2D /* StorageQuota.mm in Sources */ = {isa = PBXBuildFile; fileRef = 414AD6852285D1B000777F2D /* StorageQuota.mm */; };
+               41661C662355E85E00D33C27 /* getUserMedia-webaudio.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 41661C652355D98B00D33C27 /* getUserMedia-webaudio.html */; };
                41882F0321010C0D002FF288 /* ProcessPreWarming.mm in Sources */ = {isa = PBXBuildFile; fileRef = 41882F0221010A70002FF288 /* ProcessPreWarming.mm */; };
                44077BB123144B5000179E2D /* DataDetectorsTestIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 44077BB0231449D200179E2D /* DataDetectorsTestIOS.mm */; };
                4433A396208044140091ED57 /* SynchronousTimeoutTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4433A395208044130091ED57 /* SynchronousTimeoutTests.mm */; };
                        dstPath = TestWebKitAPI.resources;
                        dstSubfolderSpec = 7;
                        files = (
+                               41661C662355E85E00D33C27 /* getUserMedia-webaudio.html in Copy Resources */,
                                55A817FF2181021A0004A39A /* 100x100-red.tga in Copy Resources */,
                                1A9E52C913E65EF4006917F5 /* 18-characters.html in Copy Resources */,
                                55A81800218102210004A39A /* 400x400-green.png in Copy Resources */,
                4135FB832011FAA300332139 /* InjectInternals_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = InjectInternals_Bundle.cpp; path = Tests/InjectInternals_Bundle.cpp; sourceTree = SOURCE_ROOT; };
                4135FB862011FABF00332139 /* libWebCoreTestSupport.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libWebCoreTestSupport.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
                414AD6852285D1B000777F2D /* StorageQuota.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = StorageQuota.mm; sourceTree = "<group>"; };
+               41661C652355D98B00D33C27 /* getUserMedia-webaudio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "getUserMedia-webaudio.html"; sourceTree = "<group>"; };
                41882F0221010A70002FF288 /* ProcessPreWarming.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ProcessPreWarming.mm; sourceTree = "<group>"; };
                41973B5C1AF22875006C7B36 /* SharedBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SharedBuffer.cpp; sourceTree = "<group>"; };
                44077BB0231449D200179E2D /* DataDetectorsTestIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DataDetectorsTestIOS.mm; sourceTree = "<group>"; };
                                26F52EB018288F0F0023D412 /* geolocationWatchPosition.html */,
                                26F52EB118288F0F0023D412 /* geolocationWatchPositionWithHighAccuracy.html */,
                                4A410F4D19AF7BEF002EBAB5 /* getUserMedia.html */,
+                               41661C652355D98B00D33C27 /* getUserMedia-webaudio.html */,
                                4A410F4D19AF7BEF002EBAC5 /* getUserMediaAudioVideoCapture.html */,
                                BCBD372E125ABBE600D2C29F /* icon.png */,
                                CE3524F51B142BBB0028A7C5 /* input-focus-blur.html */,
index c946234..6c523b5 100644 (file)
 #import "PlatformUtilities.h"
 #import "Test.h"
 #import "TestWKWebView.h"
+#import "WKWebViewConfigurationExtras.h"
 #import <WebKit/WKPreferencesPrivate.h>
+#import <WebKit/WKProcessPoolPrivate.h>
 #import <WebKit/WKUIDelegatePrivate.h>
 #import <WebKit/WKWebView.h>
 #import <WebKit/WKWebViewConfiguration.h>
 #import <WebKit/_WKProcessPoolConfiguration.h>
 
+static bool done;
+
 @interface GetUserMediaCaptureUIDelegate : NSObject<WKUIDelegate>
 - (void)_webView:(WKWebView *)webView requestMediaCaptureAuthorization: (_WKCaptureDevices)devices decisionHandler:(void (^)(BOOL))decisionHandler;
 - (void)_webView:(WKWebView *)webView checkUserMediaPermissionForURL:(NSURL *)url mainFrameURL:(NSURL *)mainFrameURL frameIdentifier:(NSUInteger)frameIdentifier decisionHandler:(void (^)(NSString *salt, BOOL authorized))decisionHandler;
 }
 @end
 
+@interface GUMMessageHandler : NSObject <WKScriptMessageHandler>
+@end
+
+@implementation GUMMessageHandler
+- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
+{
+    EXPECT_WK_STREQ(@"PASS", [message body]);
+    done = true;
+}
+@end
+
 namespace TestWebKitAPI {
 
 void waitUntilCaptureState(WKWebView *webView, _WKMediaCaptureState expectedState)
@@ -112,6 +127,37 @@ TEST(WebKit2, CaptureMute)
     waitUntilCaptureState(webView, _WKMediaCaptureStateNone);
 }
 
+#if WK_HAVE_C_SPI
+TEST(WebKit, WebAudioAndGetUserMedia)
+{
+    done = false;
+
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto context = adoptWK(TestWebKitAPI::Util::createContextForInjectedBundleTest("InternalsInjectedBundleTest"));
+    configuration.get().processPool = (WKProcessPool *)context.get();
+    configuration.get().processPool._configuration.shouldCaptureAudioInUIProcess = NO;
+
+    auto preferences = [configuration preferences];
+    preferences._mediaCaptureRequiresSecureConnection = NO;
+    preferences._mediaDevicesEnabled = YES;
+    preferences._mockCaptureDevicesEnabled = YES;
+
+    auto messageHandler = adoptNS([[GUMMessageHandler alloc] init]);
+    [[configuration.get() userContentController] addScriptMessageHandler:messageHandler.get() name:@"gum"];
+
+    auto webView = [[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration.get()];
+
+    auto delegate = adoptNS([[GetUserMediaCaptureUIDelegate alloc] init]);
+    webView.UIDelegate = delegate.get();
+
+    auto url = adoptWK(Util::createURLForResource("getUserMedia-webaudio", "html"));
+    [webView loadTestPageNamed:@"getUserMedia-webaudio"];
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+}
+#endif
+
 } // namespace TestWebKitAPI
 
 #endif // ENABLE(MEDIA_STREAM)
diff --git a/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia-webaudio.html b/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia-webaudio.html
new file mode 100644 (file)
index 0000000..1342986
--- /dev/null
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script>
+async function capture()
+{
+    try {
+        const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
+        internals.setMockAudioTrackChannelNumber(stream.getAudioTracks()[0], 2);
+
+        var audioContext = new webkitAudioContext();
+        var analyzer = audioContext.createAnalyser();
+        analyzer.fftSize = 256;
+        let source = audioContext.createMediaStreamSource(stream);
+        source.connect(analyzer);
+        analyzer.connect(audioContext.destination);
+
+        for (let cptr = 0; cptr < 1000; cptr++) {
+          internals.setMockAudioTrackChannelNumber(stream.getAudioTracks()[0], (cptr % 2) ? 1 : 2);
+          await new Promise(resolve => setTimeout(resolve, 20));
+        }
+
+        source.disconnect(analyzer);
+        analyzer.disconnect(audioContext.destination);
+
+        window.webkit.messageHandlers.gum.postMessage("PASS");
+    } catch (e) {
+        window.webkit.messageHandlers.gum.postMessage("FAIL: " + e);
+    }
+}
+        </script>
+    <head>
+
+    <body onload="capture()">
+    </body>
+</html>