[MediaStream] An audio track should be muted when capture is interrupted by the OS.
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 6 Nov 2018 00:45:01 +0000 (00:45 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 6 Nov 2018 00:45:01 +0000 (00:45 +0000)
Source/WebCore:

https://bugs.webkit.org/show_bug.cgi?id= 191283
 <rdar://problem/45773103>

Patch by Eric Carlson <eric.carlson@apple.com> on 2018-11-05
Reviewed by Jon Lee.

Test: fast/mediastream/media-stream-track-interrupted.html

* platform/mediastream/RealtimeMediaSource.cpp:
(WebCore::RealtimeMediaSource::setInterruptedForTesting):
* platform/mediastream/RealtimeMediaSource.h:
* platform/mediastream/mac/CoreAudioCaptureSource.cpp:
(WebCore::CoreAudioCaptureSource::beginInterruption):
(WebCore::CoreAudioCaptureSource::endInterruption):
* testing/Internals.cpp:
(WebCore::Internals::setMediaStreamSourceInterrupted):
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

https://bugs.webkit.org/show_bug.cgi?id=191283
 <rdar://problem/45773103>

Patch by Eric Carlson <eric.carlson@apple.com> on 2018-11-05
Reviewed by Jon Lee.

* fast/mediastream/media-stream-track-interrupted-expected.txt: Added.
* fast/mediastream/media-stream-track-interrupted.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/fast/mediastream/media-stream-track-interrupted-expected.txt [new file with mode: 0644]
LayoutTests/fast/mediastream/media-stream-track-interrupted.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp
Source/WebCore/platform/mediastream/RealtimeMediaSource.h
Source/WebCore/platform/mediastream/mac/CoreAudioCaptureSource.cpp
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index 2368539..3f6542c 100644 (file)
@@ -1,3 +1,14 @@
+2018-11-05  Eric Carlson  <eric.carlson@apple.com>
+
+        [MediaStream] An audio track should be muted when capture is interrupted by the OS.
+        https://bugs.webkit.org/show_bug.cgi?id=191283
+         <rdar://problem/45773103>
+
+        Reviewed by Jon Lee.
+
+        * fast/mediastream/media-stream-track-interrupted-expected.txt: Added.
+        * fast/mediastream/media-stream-track-interrupted.html: Added.
+
 2018-11-05  Myles C. Maxfield  <mmaxfield@apple.com>
 
         Parsing support for text-underline-offset and text-decoration-thickness
diff --git a/LayoutTests/fast/mediastream/media-stream-track-interrupted-expected.txt b/LayoutTests/fast/mediastream/media-stream-track-interrupted-expected.txt
new file mode 100644 (file)
index 0000000..1a2ecb8
--- /dev/null
@@ -0,0 +1,6 @@
+
+
+PASS Create stream 
+PASS Interrupt video track 
+PASS Interrupt audio track 
+
diff --git a/LayoutTests/fast/mediastream/media-stream-track-interrupted.html b/LayoutTests/fast/mediastream/media-stream-track-interrupted.html
new file mode 100644 (file)
index 0000000..7b0b41d
--- /dev/null
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>Capture source interruption.</title>
+    <script src="../../resources/testharness.js"></script>
+    <script src="../../resources/testharnessreport.js"></script>
+ </head>
+<body>
+    <video width=320 height=240></video>
+
+    <script>
+
+    let video;
+
+   if (window.testRunner)
+        testRunner.setUserMediaPermission(true);
+
+    function waitForPageStateChange(numberOfTries, originalState, resolve, reject)
+    {
+        let newState = internals.pageMediaState();
+        if (newState != originalState) {
+            resolve(newState);
+            return;
+        }
+
+        if (numberOfTries <= 0) {
+            reject('Page state did not change in time.');
+            return;
+        }
+
+        setTimeout(() => { waitForPageStateChange(--numberOfTries, originalState, resolve, reject); }, 10);
+    }
+
+    function testTrack(track, title)
+    {
+        promise_test((test) => {
+            return new Promise((resolve, reject) => {
+                let isVideo = track.kind == "video";
+                if (window.internals) {
+                    assert_false(internals.pageMediaState().includes('HasMutedVideoCaptureDevice'));
+                    assert_false(internals.pageMediaState().includes('HasMutedAudioCaptureDevice'));
+                }
+
+                track.onunmute = () => reject("Got 'unmute' event unexpectedly!");
+                track.onmute = () => {
+                    new Promise((innerResolve, innerReject) => {
+                        waitForPageStateChange(10, internals.pageMediaState(), innerResolve, innerReject)
+                    }).then((pageMediaState) => {
+
+                        track.onunmute = (evt) => {
+                            waitForPageStateChange(10, internals.pageMediaState(), resolve, reject)
+                        }
+
+                        if (window.internals) {
+                            assert_true(pageMediaState.includes(isVideo ? 'HasMutedVideoCaptureDevice' : 'HasMutedAudioCaptureDevice'));
+                            assert_false(pageMediaState.includes(isVideo ? 'HasMutedAudioCaptureDevice' : 'HasMutedVideoCaptureDevice'));
+                            assert_true(pageMediaState.includes(isVideo ? 'HasActiveAudioCaptureDevice' : 'HasActiveVideoCaptureDevice'));
+                            assert_false(pageMediaState.includes(isVideo ? 'HasActiveVideoCaptureDevice' : 'HasActiveAudioCaptureDevice'));
+                            internals.setMediaStreamSourceInterrupted(track, false)
+                        }
+                    })
+                }
+
+                if (window.internals)
+                    internals.setMediaStreamSourceInterrupted(track, true);
+                setTimeout(() => reject("Muted state did not change in 1 second"), 1000);
+            });
+        }, title);
+    }
+
+    promise_test((test) => {
+        return navigator.mediaDevices.getUserMedia({ video: true, audio: true})
+            .then((stream) => {
+                testTrack(stream.getVideoTracks()[0], "Interrupt video track");
+                testTrack(stream.getAudioTracks()[0], "Interrupt audio track");
+            });
+    }, "Create stream");
+
+
+    </script>
+
+</body>
+</html>
index 1b694da..e5aaa2d 100644 (file)
@@ -1,3 +1,24 @@
+2018-11-05  Eric Carlson  <eric.carlson@apple.com>
+
+        [MediaStream] An audio track should be muted when capture is interrupted by the OS.
+        https://bugs.webkit.org/show_bug.cgi?id= 191283
+         <rdar://problem/45773103>
+
+        Reviewed by Jon Lee.
+
+        Test: fast/mediastream/media-stream-track-interrupted.html
+
+        * platform/mediastream/RealtimeMediaSource.cpp:
+        (WebCore::RealtimeMediaSource::setInterruptedForTesting):
+        * platform/mediastream/RealtimeMediaSource.h:
+        * platform/mediastream/mac/CoreAudioCaptureSource.cpp:
+        (WebCore::CoreAudioCaptureSource::beginInterruption):
+        (WebCore::CoreAudioCaptureSource::endInterruption):
+        * testing/Internals.cpp:
+        (WebCore::Internals::setMediaStreamSourceInterrupted):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2018-11-05  Myles C. Maxfield  <mmaxfield@apple.com>
 
         Parsing support for text-underline-offset and text-decoration-thickness
index f2dd46c..835a790 100644 (file)
@@ -110,6 +110,11 @@ void RealtimeMediaSource::notifyMutedChange(bool muted)
     notifyMutedObservers();
 }
 
+void RealtimeMediaSource::setInterruptedForTesting(bool interrupted)
+{
+    notifyMutedChange(interrupted);
+}
+
 void RealtimeMediaSource::forEachObserver(const WTF::Function<void(Observer&)>& apply) const
 {
     Vector<Observer*> observersCopy;
index 7aa860c..67fff69 100644 (file)
@@ -172,6 +172,7 @@ public:
 
     // Testing only
     virtual void delaySamples(Seconds) { };
+    void setInterruptedForTesting(bool);
 
 protected:
     RealtimeMediaSource(Type, String&& name, String&& deviceID = { }, String&& hashSalt = { });
index 07d233e..01b5ac9 100644 (file)
@@ -920,6 +920,7 @@ void CoreAudioCaptureSource::beginInterruption()
     scheduleDeferredTask([this, &unit] {
         m_suspendType = unit.isProducingData() ? SuspensionType::WhilePlaying : SuspensionType::WhilePaused;
         unit.suspend();
+        notifyMutedChange(true);
         m_suspendPending = false;
     });
 }
@@ -940,8 +941,10 @@ void CoreAudioCaptureSource::endInterruption()
     scheduleDeferredTask([this, type, &unit] {
         if (m_reconfigurationState == ReconfigurationState::Required)
             unit.reconfigureAudioUnit();
-        if (type == SuspensionType::WhilePlaying)
+        if (type == SuspensionType::WhilePlaying) {
             unit.resume();
+            notifyMutedChange(false);
+        }
         m_reconfigurationState = ReconfigurationState::None;
         m_resumePending = false;
     });
index c2829c2..9bdabe5 100644 (file)
@@ -4512,6 +4512,12 @@ void Internals::setMediaStreamTrackIdentifier(MediaStreamTrack& track, String&&
 {
     track.setIdForTesting(WTFMove(id));
 }
+
+void Internals::setMediaStreamSourceInterrupted(MediaStreamTrack& track, bool interrupted)
+{
+    track.source().setInterruptedForTesting(interrupted);
+}
+
 #endif
 
 String Internals::audioSessionCategory() const
index e5276a5..9e19583 100644 (file)
@@ -678,6 +678,7 @@ public:
     void removeMediaStreamTrack(MediaStream&, MediaStreamTrack&);
     void simulateMediaStreamTrackCaptureSourceFailure(MediaStreamTrack&);
     void setMediaStreamTrackIdentifier(MediaStreamTrack&, String&& id);
+    void setMediaStreamSourceInterrupted(MediaStreamTrack&, bool);
 #endif
 
     String audioSessionCategory() const;
index 198e3d3..745585c 100644 (file)
@@ -663,6 +663,7 @@ enum CompositingPolicy {
     [Conditional=MEDIA_STREAM] void removeMediaStreamTrack(MediaStream stream, MediaStreamTrack track);
     [Conditional=MEDIA_STREAM] void simulateMediaStreamTrackCaptureSourceFailure(MediaStreamTrack track);
     [Conditional=MEDIA_STREAM] void setMediaStreamTrackIdentifier(MediaStreamTrack track, DOMString identifier);
+    [Conditional=MEDIA_STREAM] void setMediaStreamSourceInterrupted(MediaStreamTrack track, boolean interrupted);
 
     unsigned long long documentIdentifier(Document document);
     boolean isDocumentAlive(unsigned long long documentIdentifier);