[MediaStream] applyConstraints pt. 1 - mandatory constraints
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Sep 2016 17:36:29 +0000 (17:36 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 2 Sep 2016 17:36:29 +0000 (17:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=161469
<rdar://problem/28109325>

Reviewed by Jer Noble.

Source/WebCore:

Tests: fast/mediastream/apply-constraints-audio.html
       fast/mediastream/apply-constraints-video.html

* Modules/mediastream/MediaStreamTrack.cpp:
(WebCore::MediaStreamTrack::MediaStreamTrack): Initialize the weak pointer factory.
(WebCore::MediaStreamTrack::applyConstraints): Make it work.
* Modules/mediastream/MediaStreamTrack.h:
* Modules/mediastream/MediaStreamTrack.idl:

* WebCore.xcodeproj/project.pbxproj: Add JSMediaDevicesCustom.h.

* bindings/js/JSMediaDevicesCustom.cpp:
(WebCore::createStringConstraint): Add name parameter.
(WebCore::createBooleanConstraint): Ditto.
(WebCore::createDoubleConstraint): Ditto.
(WebCore::createIntConstraint): Ditto.
(WebCore::parseMediaTrackConstraintSetForKey): Drop type parameter because we don't need to
  filter by source media type.
(WebCore::parseAdvancedConstraints): Ditto.
(WebCore::parseMediaConstraintsDictionary): Renamed from parseConstraints.
(WebCore::JSMediaDevices::getUserMedia): Don't throw exceptions, always return a promise.
(WebCore::parseConstraints): Deleted.
* bindings/js/JSMediaDevicesCustom.h: Added.

* bindings/js/JSMediaStreamTrackCustom.cpp:
(WebCore::JSMediaStreamTrack::getSettings): Don't include aspect ratio if the value is 0.
(WebCore::capabilityValue): asULong -> asInt.
(WebCore::JSMediaStreamTrack::applyConstraints): New.
(WebCore::JSMediaStreamTrack::getConstraints): New.

* bindings/js/WebCoreBuiltinNames.h: Add "mediaStreamTrackConstraints".

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::setSrcObject): Drive by fix: don't call DOMURL::createPublicURL(null).

* platform/mediastream/MediaConstraints.cpp:
(WebCore::MediaConstraint::create): Pass name to constructors.
(WebCore::StringConstraint::find): New.
* platform/mediastream/MediaConstraints.h:

* platform/mediastream/MediaStreamTrackPrivate.cpp:
(WebCore::MediaStreamTrackPrivate::applyConstraints): Add callback parameters.
* platform/mediastream/MediaStreamTrackPrivate.h:

* platform/mediastream/RealtimeMediaSource.cpp:
(WebCore::RealtimeMediaSource::RealtimeMediaSource): Initialize weak pointer factory.
(WebCore::RealtimeMediaSource::settingsDidChange): Don't call observers immediately so we can
 coalesce multiple changes in the same runloop cycle.
(WebCore::RealtimeMediaSource::supportsConstraint): New.
(WebCore::value): Return the most appropriate value from a numeric constraint.
(WebCore::RealtimeMediaSource::applyConstraint): New, apply one constraint.
(WebCore::RealtimeMediaSource::applyConstraints): New, validate and apply constraints.
(WebCore::RealtimeMediaSource::setWidth): New.
(WebCore::RealtimeMediaSource::setHeight): New.
(WebCore::RealtimeMediaSource::setFrameRate): New.
(WebCore::RealtimeMediaSource::setAspectRatio): New.
(WebCore::RealtimeMediaSource::setFacingMode): New.
(WebCore::RealtimeMediaSource::setVolume): New.
(WebCore::RealtimeMediaSource::setSampleRate): New.
(WebCore::RealtimeMediaSource::setSampleSize): New.
(WebCore::RealtimeMediaSource::setEchoCancellation) New.:
(WebCore::RealtimeMediaSource::scheduleDeferredTask): New.
* platform/mediastream/RealtimeMediaSource.h:

* platform/mediastream/RealtimeMediaSourceCapabilities.h:
(WebCore::CapabilityValueOrRange::CapabilityValueOrRange): "unsigned long" -> "int"

* platform/mediastream/RealtimeMediaSourceSettings.cpp:
(WebCore::userFacing): New.
(WebCore::environmentFacing): New.
(WebCore::leftFacing): New.
(WebCore::rightFacing): New.
(WebCore::RealtimeMediaSourceSettings::facingMode):
(WebCore::RealtimeMediaSourceSettings::videoFacingModeEnum):
* platform/mediastream/RealtimeMediaSourceSettings.h:

* platform/mediastream/mac/AVAudioCaptureSource.mm:
(WebCore::AVAudioCaptureSource::initializeCapabilities): Volume range is 0.0 .. 1.0.

* platform/mediastream/mac/AVMediaCaptureSource.h:
(WebCore::AVMediaCaptureSource::createWeakPtr): Deleted.
* platform/mediastream/mac/AVMediaCaptureSource.mm:
(WebCore::AVMediaCaptureSource::AVMediaCaptureSource): Don't need the weak ptr factory, it is
  in the base class.
(WebCore::AVMediaCaptureSource::scheduleDeferredTask): Deleted.

* platform/mediastream/mac/AVVideoCaptureSource.h:
* platform/mediastream/mac/AVVideoCaptureSource.mm:
(WebCore::AVVideoCaptureSource::applySize): New.
(WebCore::AVVideoCaptureSource::applyFrameRate): New.
(WebCore::AVVideoCaptureSource::setupCaptureSession):
(WebCore::AVVideoCaptureSource::setFrameRateConstraint): Deleted.
(WebCore::AVVideoCaptureSource::applyConstraints): Deleted.

* platform/mock/MockRealtimeAudioSource.cpp:
(WebCore::MockRealtimeAudioSource::updateSettings): Set volume and echoCancellation to the
  current values.
(WebCore::MockRealtimeAudioSource::initializeCapabilities): Volume takes a float, not an int.
* platform/mock/MockRealtimeAudioSource.h:

* platform/mock/MockRealtimeMediaSource.cpp: Minor cleanup.
* platform/mock/MockRealtimeMediaSource.h:

* platform/mock/MockRealtimeVideoSource.cpp:
(WebCore::MockRealtimeVideoSource::MockRealtimeVideoSource): Initialize frame rate.
(WebCore::MockRealtimeVideoSource::startProducingData): m_size -> size().
(WebCore::MockRealtimeVideoSource::updateSettings): Use accessors because instance variables
  have been moved to the base class.
(WebCore::MockRealtimeVideoSource::initializeCapabilities): Ditto.
(WebCore::MockRealtimeVideoSource::applyFrameRate): New.
(WebCore::MockRealtimeVideoSource::applySize):
(WebCore::MockRealtimeVideoSource::drawAnimation):
(WebCore::MockRealtimeVideoSource::drawBoxes):
(WebCore::MockRealtimeVideoSource::drawText):
(WebCore::MockRealtimeVideoSource::generateFrame):
(WebCore::MockRealtimeVideoSource::imageBuffer):
(WebCore::MockRealtimeVideoSource::setFrameRate): Deleted.
(WebCore::MockRealtimeVideoSource::setSize): Deleted.
* platform/mock/MockRealtimeVideoSource.h:
(WebCore::MockRealtimeVideoSource::size): Deleted.

LayoutTests:

* fast/mediastream/apply-constraints-audio-expected.txt: Added.
* fast/mediastream/apply-constraints-audio.html: Added.
* fast/mediastream/apply-constraints-video-expected.txt: Added.
* fast/mediastream/apply-constraints-video.html: Added.
* fast/mediastream/resources/apply-constraints-utils.js: Added.

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

37 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/mediastream/apply-constraints-audio-expected.txt [new file with mode: 0644]
LayoutTests/fast/mediastream/apply-constraints-audio.html [new file with mode: 0644]
LayoutTests/fast/mediastream/apply-constraints-video-expected.txt [new file with mode: 0644]
LayoutTests/fast/mediastream/apply-constraints-video.html [new file with mode: 0644]
LayoutTests/fast/mediastream/resources/apply-constraints-utils.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediastream/MediaStreamTrack.cpp
Source/WebCore/Modules/mediastream/MediaStreamTrack.h
Source/WebCore/Modules/mediastream/MediaStreamTrack.idl
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/bindings/js/JSMediaDevicesCustom.cpp
Source/WebCore/bindings/js/JSMediaDevicesCustom.h [new file with mode: 0644]
Source/WebCore/bindings/js/JSMediaStreamTrackCustom.cpp
Source/WebCore/bindings/js/WebCoreBuiltinNames.h
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaStreamAVFObjC.mm
Source/WebCore/platform/mediastream/MediaConstraints.cpp
Source/WebCore/platform/mediastream/MediaConstraints.h
Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.cpp
Source/WebCore/platform/mediastream/MediaStreamTrackPrivate.h
Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp
Source/WebCore/platform/mediastream/RealtimeMediaSource.h
Source/WebCore/platform/mediastream/RealtimeMediaSourceCapabilities.h
Source/WebCore/platform/mediastream/RealtimeMediaSourceSettings.cpp
Source/WebCore/platform/mediastream/RealtimeMediaSourceSettings.h
Source/WebCore/platform/mediastream/mac/AVAudioCaptureSource.mm
Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.h
Source/WebCore/platform/mediastream/mac/AVMediaCaptureSource.mm
Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.h
Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm
Source/WebCore/platform/mock/MockRealtimeAudioSource.cpp
Source/WebCore/platform/mock/MockRealtimeAudioSource.h
Source/WebCore/platform/mock/MockRealtimeMediaSource.cpp
Source/WebCore/platform/mock/MockRealtimeMediaSource.h
Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp
Source/WebCore/platform/mock/MockRealtimeVideoSource.h

index 3b8dc1c..b97fa32 100644 (file)
@@ -1,3 +1,17 @@
+2016-09-02  Eric Carlson  <eric.carlson@apple.com>
+
+        [MediaStream] applyConstraints pt. 1 - mandatory constraints
+        https://bugs.webkit.org/show_bug.cgi?id=161469
+        <rdar://problem/28109325>
+
+        Reviewed by Jer Noble.
+
+        * fast/mediastream/apply-constraints-audio-expected.txt: Added.
+        * fast/mediastream/apply-constraints-audio.html: Added.
+        * fast/mediastream/apply-constraints-video-expected.txt: Added.
+        * fast/mediastream/apply-constraints-video.html: Added.
+        * fast/mediastream/resources/apply-constraints-utils.js: Added.
+
 2016-09-01  Ryosuke Niwa  <rniwa@webkit.org>
 
         Only update connected custom elements
diff --git a/LayoutTests/fast/mediastream/apply-constraints-audio-expected.txt b/LayoutTests/fast/mediastream/apply-constraints-audio-expected.txt
new file mode 100644 (file)
index 0000000..47ec55e
--- /dev/null
@@ -0,0 +1,43 @@
+Tests applyConstraints on an audio stream track.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS stream.getVideoTracks().length is 0
+PASS stream.getAudioTracks().length is 1
+PASS video.videoTracks.length is 0
+PASS video.audioTracks.length is 1
+
+** Constraint: {"volume":0.5} - set volume to a valid value.
+PASS settings['volume'] is 0.5
+PASS typeof settings['echoCancellation'] is 'boolean'
+PASS settings['echoCancellation'] is false
+
+** Constraint: {"volume":{"exact":2.1}} - the 'exact' constraint it too big, promise should reject and no settings should change.
+PASS Promise was rejected
+PASS error.constraint is "volume"
+PASS settings['volume'] is 0.5
+PASS typeof settings['echoCancellation'] is 'boolean'
+PASS settings['echoCancellation'] is false
+
+** Constraint: {"volume":{"exact":-1}} - the 'exact' constraint it too small, promise should reject and no settings should change.
+PASS Promise was rejected
+PASS error.constraint is "volume"
+PASS settings['volume'] is 0.5
+PASS typeof settings['echoCancellation'] is 'boolean'
+PASS settings['echoCancellation'] is false
+
+** Constraint: {"echoCancellation":true} - set echoCancellation to a valid value.
+PASS settings['volume'] is 0.5
+PASS typeof settings['echoCancellation'] is 'boolean'
+PASS settings['echoCancellation'] is true
+
+** Constraint: {"facingMode":"environment","frameRate":30,"volume":1} - constraint not supported by an audio track should be ignored.
+PASS settings['volume'] is 1
+PASS typeof settings['echoCancellation'] is 'boolean'
+PASS settings['echoCancellation'] is true
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/mediastream/apply-constraints-audio.html b/LayoutTests/fast/mediastream/apply-constraints-audio.html
new file mode 100644 (file)
index 0000000..e83e467
--- /dev/null
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src="../../resources/js-test-pre.js"></script>
+        <script src="resources/apply-constraints-utils.js"></script>
+        <script>
+
+            let tests = [
+                {
+                    message: "set volume to a valid value.",
+                    constraint: { volume: .5 }, 
+                    expected: { volume: .5, echoCancellation: false }, 
+                },
+                {
+                    message: "the 'exact' constraint it too big, promise should reject and no settings should change.",
+                    constraint: { volume: { exact: 2.1 } }, 
+                    expected: { volume: .5, echoCancellation: false }, 
+                    error: "volume",
+                },
+                {
+                    message: "the 'exact' constraint it too small, promise should reject and no settings should change.",
+                    constraint: { volume: { exact: -1 } }, 
+                    expected: { volume: .5, echoCancellation: false }, 
+                    error: "volume",
+                },
+                {
+                    message: "set echoCancellation to a valid value.",
+                    constraint: { echoCancellation: true }, 
+                    expected: { volume: .5, echoCancellation: true }, 
+                },
+                {
+                    message: "constraint not supported by an audio track should be ignored.",
+                    constraint: { facingMode: "environment", frameRate: 30, volume: 1.0 }, 
+                    expected: { volume: 1.0, echoCancellation: true }, 
+                },
+                
+            ];
+
+            let tester = new ConstraintsTest({ audio: true }, tests, "Tests applyConstraints on an audio stream track.")
+                .onStreamReady((s) => {
+                    stream = s;
+                    shouldBe('stream.getVideoTracks().length', '0');
+                    shouldBe('stream.getAudioTracks().length', '1');
+                    tester.setStreamTrack(stream.getAudioTracks()[0]);
+                })
+                .onVideoReady((v) => {
+                    video = v;
+                    shouldBe('video.videoTracks.length', '0');
+                    shouldBe('video.audioTracks.length', '1');
+                })
+                .start();
+
+        </script>
+        <script src="../../resources/js-test-post.js"></script>
+    </head>
+    <body>
+        <video controls id="video"</video>
+        <br>
+        <div id="div"></div>
+
+    </body>
+</html>
+
diff --git a/LayoutTests/fast/mediastream/apply-constraints-video-expected.txt b/LayoutTests/fast/mediastream/apply-constraints-video-expected.txt
new file mode 100644 (file)
index 0000000..81bf079
--- /dev/null
@@ -0,0 +1,97 @@
+Tests applyConstraints on a video stream track.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS stream.getVideoTracks().length is 1
+PASS stream.getAudioTracks().length is 0
+PASS video.videoTracks.length is 1
+PASS video.audioTracks.length is 0
+
+** Constraint: {"width":640,"height":480,"frameRate":30} - set width, height, and frame rate to valid values.
+PASS settings['width'] is 640
+PASS settings['height'] is 480
+PASS settings['frameRate'] is 30
+
+** Constraint: {"width":320,"height":240} - change width and height, frame rate should remain unchanged.
+PASS settings['width'] is 320
+PASS settings['height'] is 240
+PASS settings['frameRate'] is 30
+
+** Constraint: {"width":{"exact":2000}} - the 'exact' constraint can't be satisfied, promise should reject and no settings should change.
+PASS Promise was rejected
+PASS error.constraint is "width"
+PASS settings['width'] is 320
+PASS settings['height'] is 240
+PASS settings['frameRate'] is 30
+
+** Constraint: {"width":640,"height":{"min":3000}} - the 'min' constraint can't be satisfied, promise should reject and no settings should change.
+PASS Promise was rejected
+PASS error.constraint is "height"
+PASS settings['width'] is 320
+PASS settings['height'] is 240
+PASS settings['frameRate'] is 30
+
+** Constraint: {"frameRate":{"max":6}} - the 'max' constraint can't be satisfied, promise should reject and no settings should change.
+PASS Promise was rejected
+PASS error.constraint is "frameRate"
+PASS settings['frameRate'] is 30
+
+** Constraint: {"width":{"exact":400}} - the 'exact' constraint can be satisfied.
+PASS settings['width'] is 400
+PASS settings['height'] is 240
+
+** Constraint: {"width":{"min":300,"ideal":5000}} - the 'ideal' constraint can't be satisfied but the 'min' can, max should be chosen.
+PASS settings['width'] is 1920
+PASS settings['height'] is 240
+
+** Constraint: {"width":{"min":320,"ideal":1280},"height":{"min":480,"ideal":720}} - 'ideal' and 'min' constraints can be satisfied, 'ideal' should be chosen.
+PASS settings['width'] is 1280
+PASS settings['height'] is 720
+
+** Constraint: {"width":3000} - ideal width is greater than track capability, should be clamped to the maximum value.
+PASS settings['width'] is 1920
+
+** Constraint: {"width":160,"height":120,"frameRate":10} - all values are less than track capabilities, should be clamped to the minimum values.
+PASS settings['width'] is 320
+PASS settings['height'] is 240
+PASS settings['frameRate'] is 15
+
+** Constraint: {"facingMode":"environment"} - set facing mode, width and height should remain unchanged
+PASS settings['width'] is 320
+PASS settings['height'] is 240
+PASS settings['facingMode'] is "environment"
+
+** Constraint: {"facingMode":"USER","height":400} - illegal facing mode value should be ignored, height should change.
+PASS settings['facingMode'] is "environment"
+PASS settings['width'] is 320
+PASS settings['height'] is 400
+
+** Constraint: {"FACINGMODE":"user","frameRate":30} - unknown constraint should be ignored, frame rate should change.
+PASS settings['facingMode'] is "environment"
+PASS settings['frameRate'] is 30
+
+** Constraint: {"aspectRatio":"1.3333"} - aspect ratio should change width and height.
+PASS settings['width'] is 320
+PASS settings['height'] is 240
+
+** Constraint: {"aspectRatio":"1.7778"} - new aspect ratio should again change width and height.
+PASS settings['width'] is 320
+PASS settings['height'] is 180
+
+** Constraint: {"width":1920} - when aspect ratio has been set, changing width should change height.
+PASS settings['width'] is 1920
+PASS settings['height'] is 1080
+
+** Constraint: {"height":576} - when aspect ratio has been set, changing height should change width.
+PASS settings['width'] is 1024
+PASS settings['height'] is 576
+
+** Constraint: {"deviceId":{"exact":"20983-20o198-109283-098-09812"}} - the 'exact' deviceID doesn't match, promise should reject.
+PASS Promise was rejected
+PASS error.constraint is "deviceId"
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/mediastream/apply-constraints-video.html b/LayoutTests/fast/mediastream/apply-constraints-video.html
new file mode 100644 (file)
index 0000000..79a0d4c
--- /dev/null
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src="../../resources/js-test-pre.js"></script>
+        <script src="resources/apply-constraints-utils.js"></script>
+        <script>
+
+            let tests = [
+                {
+                    message: "set width, height, and frame rate to valid values.",
+                    constraint: { width: 640, height: 480, frameRate: 30 }, 
+                    expected: { width: 640, height: 480, frameRate: 30 }, 
+                },
+                {
+                    message: "change width and height, frame rate should remain unchanged.",
+                    constraint: { width: 320, height: 240 }, 
+                    expected: { width: 320, height: 240, frameRate: 30 }, 
+                },
+                {
+                    message: "the 'exact' constraint can't be satisfied, promise should reject and no settings should change.",
+                    constraint: { width: { exact: 2000 } }, 
+                    expected: { width: 320, height: 240, frameRate: 30 },
+                    error: "width",
+                },
+                {
+                    message: "the 'min' constraint can't be satisfied, promise should reject and no settings should change.",
+                    constraint: { width: 640, height: {min: 3000}, }, 
+                    expected: { width: 320, height: 240, frameRate: 30 },
+                    error: "height",
+                },
+                {
+                    message: "the 'max' constraint can't be satisfied, promise should reject and no settings should change.",
+                    constraint: { frameRate: {max: 6}, }, 
+                    expected: { frameRate: 30 },
+                    error: "frameRate",
+                },
+                {
+                    message: "the 'exact' constraint can be satisfied.",
+                    constraint: { width: { exact: 400 } }, 
+                    expected: { width: 400, height: 240 },
+                },
+                {
+                    message: "the 'ideal' constraint can't be satisfied but the 'min' can, max should be chosen.",
+                    constraint: { width: {min: 300, ideal: 5000} }, 
+                    expected: { width: 1920, height: 240 },
+                },
+                {
+                    message: "'ideal' and 'min' constraints can be satisfied, 'ideal' should be chosen.",
+                    constraint: { width: {min: 320, ideal: 1280}, height: {min: 480, ideal: 720}, }, 
+                    expected: { width: 1280, height: 720 },
+                },
+                {
+                    message: "ideal width is greater than track capability, should be clamped to the maximum value.",
+                    constraint: { width: 3000 }, 
+                    expected: { width: 1920},
+                },
+                {
+                    message: "all values are less than track capabilities, should be clamped to the minimum values.",
+                    constraint: { width: 160, height: 120, frameRate: 10 }, 
+                    expected: { width: 320, height: 240, frameRate: 15 },
+                },
+                {
+                    message: "set facing mode, width and height should remain unchanged",
+                    constraint: { facingMode: "environment" }, 
+                    expected: { width: 320, height: 240, facingMode: "environment" },
+                },
+                {
+                    message: "illegal facing mode value should be ignored, height should change.",
+                    constraint: { facingMode: "USER", height: 400 }, 
+                    expected: { facingMode: "environment", width: 320, height: 400 },
+                },
+                {
+                    message: "unknown constraint should be ignored, frame rate should change.",
+                    constraint: { FACINGMODE: "user", frameRate: 30 }, 
+                    expected: { facingMode: "environment", frameRate: 30 },
+                },
+                {
+                    message: "aspect ratio should change width and height.",
+                    constraint: { aspectRatio: (4/3).toFixed(4) }, 
+                    expected: { width: 320, height: 240 },
+                },
+                {
+                    message: "new aspect ratio should again change width and height.",
+                    constraint: { aspectRatio: (16/9).toFixed(4) }, 
+                    expected: { width: 320, height: 180 },
+                },
+                {
+                    message: "when aspect ratio has been set, changing width should change height.",
+                    constraint: { width: 1920 }, 
+                    expected: { width: 1920, height: 1080},
+                },
+                {
+                    message: "when aspect ratio has been set, changing height should change width.",
+                    constraint: { height: 576 }, 
+                    expected: { width: 1024, height: 576},
+                },
+                {
+                    message: "the 'exact' deviceID doesn't match, promise should reject.",
+                    constraint: { deviceId: {exact: "20983-20o198-109283-098-09812"}, }, 
+                    expected: { },
+                    error: "deviceId",
+                },
+            ];
+
+            let tester = new ConstraintsTest({ video: true }, tests, "Tests applyConstraints on a video stream track.")
+                .onStreamReady((s) => {
+                    stream = s;
+                    shouldBe('stream.getVideoTracks().length', '1');
+                    shouldBe('stream.getAudioTracks().length', '0');
+                    tester.setStreamTrack(stream.getVideoTracks()[0]);
+                })
+                .onVideoReady((v) => {
+                    video = v;
+                    shouldBe('video.videoTracks.length', '1');
+                    shouldBe('video.audioTracks.length', '0');
+                })
+                .start();
+
+        </script>
+        <script src="../../resources/js-test-post.js"></script>
+    </head>
+    <body>
+        <video controls id="video"</video>
+        <br>
+        <div id="div"></div>
+
+    </body>
+</html>
+
diff --git a/LayoutTests/fast/mediastream/resources/apply-constraints-utils.js b/LayoutTests/fast/mediastream/resources/apply-constraints-utils.js
new file mode 100644 (file)
index 0000000..1699046
--- /dev/null
@@ -0,0 +1,115 @@
+let settings;
+let error;
+
+ConstraintsTest = class ConstraintsTest {
+
+    constructor(constraints, tests, description)
+    {
+        this.constraints = constraints;
+        this.tests = tests;
+        this.description = description;
+
+        window.jsTestIsAsync = true;
+        window.successfullyParsed = true;
+        if (window.testRunner)
+            testRunner.setUserMediaPermission(true);
+    }
+
+    onStreamReady(callback)
+    {
+        if (typeof callback == "function")
+            this.streamCallback = callback;
+        return this;
+    }
+
+    onVideoReady(callback)
+    {
+        if (typeof callback == "function")
+            this.videoCallback = callback;
+        return this;
+    }
+
+    scheduleNextTest()
+    {
+        new Promise(resolved => this.runNextTest()); 
+    }
+
+    checkTrackSettings()
+    {
+        settings = this.track.getSettings();
+        for (let property in this.currentTest.expected) {
+            let expected = this.currentTest.expected[property];
+            if (typeof expected === "string")
+                shouldBeEqualToString(`settings['${property}']`, expected);
+            else
+                shouldEvaluateTo(`settings['${property}']`, expected);
+        }
+    }
+
+    runNextTest()
+    {
+        description(this.description);
+
+        debug("");
+        if (!this.tests.length) {
+            finishJSTest();
+            return;
+        }
+
+        this.currentTest = this.tests.shift();
+        debug(`** Constraint: ${JSON.stringify(this.currentTest.constraint)} - ${this.currentTest.message}`);
+        this.track.applyConstraints(this.currentTest.constraint)
+            .then(() => {
+                if (this.currentTest.error)
+                    testFailed(`Constraint '${this.currentTest.error}' should have failed to apply, is '${settings[this.currentTest.error]}'`);
+                else
+                    this.checkTrackSettings()
+                this.scheduleNextTest();
+            })
+            .catch((evt) => {
+                if (!this.currentTest.error) {
+                    testFailed("Promise was rejected");
+                    testFailed(`Constraint failed to apply: ${evt} - constraint = ${evt.constraint}`);
+                } else {
+                    testPassed("Promise was rejected");
+                    error = evt;
+                    shouldBeEqualToString("error.constraint", this.currentTest.error);
+                }
+                this.checkTrackSettings()
+                this.scheduleNextTest();
+            });
+    }
+    
+    setStreamTrack(track)
+    {
+        this.track = track;
+    }
+
+    start()
+    {
+        window.addEventListener("load", function () {
+
+            navigator.mediaDevices.getUserMedia(this.constraints)
+                .then(stream => {
+                    this.video = document.querySelector("video");
+                    this.video.srcObject = stream;
+                    if (this.streamCallback)
+                        this.streamCallback(stream);
+                })
+                .then(() => new Promise(resolve => this.video.onloadedmetadata = resolve))
+                .then(() => {
+                    if (this.videoCallback)
+                        this.videoCallback(this.video);
+                    this.runNextTest();
+                })
+                .catch(err => {
+                    testFailed(`Stream setup failed with error: ${err}`);
+                    finishJSTest();
+                });
+
+        }.bind(this), false);
+
+        return this;
+    }
+
+}
index cec7fb5..a190a33 100644 (file)
@@ -1,3 +1,132 @@
+2016-09-02  Eric Carlson  <eric.carlson@apple.com>
+
+        [MediaStream] applyConstraints pt. 1 - mandatory constraints
+        https://bugs.webkit.org/show_bug.cgi?id=161469
+        <rdar://problem/28109325>
+
+        Reviewed by Jer Noble.
+
+        Tests: fast/mediastream/apply-constraints-audio.html
+               fast/mediastream/apply-constraints-video.html
+
+        * Modules/mediastream/MediaStreamTrack.cpp:
+        (WebCore::MediaStreamTrack::MediaStreamTrack): Initialize the weak pointer factory.
+        (WebCore::MediaStreamTrack::applyConstraints): Make it work.
+        * Modules/mediastream/MediaStreamTrack.h:
+        * Modules/mediastream/MediaStreamTrack.idl:
+
+        * WebCore.xcodeproj/project.pbxproj: Add JSMediaDevicesCustom.h.
+
+        * bindings/js/JSMediaDevicesCustom.cpp:
+        (WebCore::createStringConstraint): Add name parameter.
+        (WebCore::createBooleanConstraint): Ditto.
+        (WebCore::createDoubleConstraint): Ditto.
+        (WebCore::createIntConstraint): Ditto.
+        (WebCore::parseMediaTrackConstraintSetForKey): Drop type parameter because we don't need to
+          filter by source media type.
+        (WebCore::parseAdvancedConstraints): Ditto.
+        (WebCore::parseMediaConstraintsDictionary): Renamed from parseConstraints.
+        (WebCore::JSMediaDevices::getUserMedia): Don't throw exceptions, always return a promise.
+        (WebCore::parseConstraints): Deleted.
+        * bindings/js/JSMediaDevicesCustom.h: Added.
+
+        * bindings/js/JSMediaStreamTrackCustom.cpp:
+        (WebCore::JSMediaStreamTrack::getSettings): Don't include aspect ratio if the value is 0.
+        (WebCore::capabilityValue): asULong -> asInt.
+        (WebCore::JSMediaStreamTrack::applyConstraints): New.
+        (WebCore::JSMediaStreamTrack::getConstraints): New.
+
+        * bindings/js/WebCoreBuiltinNames.h: Add "mediaStreamTrackConstraints".
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::setSrcObject): Drive by fix: don't call DOMURL::createPublicURL(null).
+
+        * platform/mediastream/MediaConstraints.cpp:
+        (WebCore::MediaConstraint::create): Pass name to constructors.
+        (WebCore::StringConstraint::find): New.
+        * platform/mediastream/MediaConstraints.h:
+
+        * platform/mediastream/MediaStreamTrackPrivate.cpp:
+        (WebCore::MediaStreamTrackPrivate::applyConstraints): Add callback parameters.
+        * platform/mediastream/MediaStreamTrackPrivate.h:
+
+        * platform/mediastream/RealtimeMediaSource.cpp:
+        (WebCore::RealtimeMediaSource::RealtimeMediaSource): Initialize weak pointer factory.
+        (WebCore::RealtimeMediaSource::settingsDidChange): Don't call observers immediately so we can
+         coalesce multiple changes in the same runloop cycle.
+        (WebCore::RealtimeMediaSource::supportsConstraint): New.
+        (WebCore::value): Return the most appropriate value from a numeric constraint.
+        (WebCore::RealtimeMediaSource::applyConstraint): New, apply one constraint.
+        (WebCore::RealtimeMediaSource::applyConstraints): New, validate and apply constraints.
+        (WebCore::RealtimeMediaSource::setWidth): New.
+        (WebCore::RealtimeMediaSource::setHeight): New.
+        (WebCore::RealtimeMediaSource::setFrameRate): New.
+        (WebCore::RealtimeMediaSource::setAspectRatio): New.
+        (WebCore::RealtimeMediaSource::setFacingMode): New.
+        (WebCore::RealtimeMediaSource::setVolume): New.
+        (WebCore::RealtimeMediaSource::setSampleRate): New.
+        (WebCore::RealtimeMediaSource::setSampleSize): New.
+        (WebCore::RealtimeMediaSource::setEchoCancellation) New.:
+        (WebCore::RealtimeMediaSource::scheduleDeferredTask): New.
+        * platform/mediastream/RealtimeMediaSource.h:
+
+        * platform/mediastream/RealtimeMediaSourceCapabilities.h:
+        (WebCore::CapabilityValueOrRange::CapabilityValueOrRange): "unsigned long" -> "int"
+
+        * platform/mediastream/RealtimeMediaSourceSettings.cpp:
+        (WebCore::userFacing): New.
+        (WebCore::environmentFacing): New.
+        (WebCore::leftFacing): New.
+        (WebCore::rightFacing): New.
+        (WebCore::RealtimeMediaSourceSettings::facingMode):
+        (WebCore::RealtimeMediaSourceSettings::videoFacingModeEnum):
+        * platform/mediastream/RealtimeMediaSourceSettings.h:
+
+        * platform/mediastream/mac/AVAudioCaptureSource.mm:
+        (WebCore::AVAudioCaptureSource::initializeCapabilities): Volume range is 0.0 .. 1.0.
+
+        * platform/mediastream/mac/AVMediaCaptureSource.h:
+        (WebCore::AVMediaCaptureSource::createWeakPtr): Deleted.
+        * platform/mediastream/mac/AVMediaCaptureSource.mm:
+        (WebCore::AVMediaCaptureSource::AVMediaCaptureSource): Don't need the weak ptr factory, it is
+          in the base class.
+        (WebCore::AVMediaCaptureSource::scheduleDeferredTask): Deleted.
+
+        * platform/mediastream/mac/AVVideoCaptureSource.h:
+        * platform/mediastream/mac/AVVideoCaptureSource.mm:
+        (WebCore::AVVideoCaptureSource::applySize): New.
+        (WebCore::AVVideoCaptureSource::applyFrameRate): New.
+        (WebCore::AVVideoCaptureSource::setupCaptureSession):
+        (WebCore::AVVideoCaptureSource::setFrameRateConstraint): Deleted.
+        (WebCore::AVVideoCaptureSource::applyConstraints): Deleted.
+
+        * platform/mock/MockRealtimeAudioSource.cpp:
+        (WebCore::MockRealtimeAudioSource::updateSettings): Set volume and echoCancellation to the
+          current values.
+        (WebCore::MockRealtimeAudioSource::initializeCapabilities): Volume takes a float, not an int.
+        * platform/mock/MockRealtimeAudioSource.h:
+
+        * platform/mock/MockRealtimeMediaSource.cpp: Minor cleanup.
+        * platform/mock/MockRealtimeMediaSource.h:
+
+        * platform/mock/MockRealtimeVideoSource.cpp:
+        (WebCore::MockRealtimeVideoSource::MockRealtimeVideoSource): Initialize frame rate.
+        (WebCore::MockRealtimeVideoSource::startProducingData): m_size -> size().
+        (WebCore::MockRealtimeVideoSource::updateSettings): Use accessors because instance variables
+          have been moved to the base class.
+        (WebCore::MockRealtimeVideoSource::initializeCapabilities): Ditto.
+        (WebCore::MockRealtimeVideoSource::applyFrameRate): New.
+        (WebCore::MockRealtimeVideoSource::applySize):
+        (WebCore::MockRealtimeVideoSource::drawAnimation):
+        (WebCore::MockRealtimeVideoSource::drawBoxes):
+        (WebCore::MockRealtimeVideoSource::drawText):
+        (WebCore::MockRealtimeVideoSource::generateFrame):
+        (WebCore::MockRealtimeVideoSource::imageBuffer):
+        (WebCore::MockRealtimeVideoSource::setFrameRate): Deleted.
+        (WebCore::MockRealtimeVideoSource::setSize): Deleted.
+        * platform/mock/MockRealtimeVideoSource.h:
+        (WebCore::MockRealtimeVideoSource::size): Deleted.
+
 2016-09-02  Brady Eidson  <beidson@apple.com>
 
         Weak link the GameController.framework on macOS.
index 3881c66..115119f 100644 (file)
 #include "EventNames.h"
 #include "ExceptionCode.h"
 #include "ExceptionCodePlaceholder.h"
+#include "JSOverconstrainedError.h"
 #include "MediaConstraintsImpl.h"
 #include "MediaSourceSettings.h"
 #include "MediaStream.h"
 #include "MediaStreamPrivate.h"
 #include "MediaTrackConstraints.h"
 #include "NotImplemented.h"
+#include "OverconstrainedError.h"
 #include "ScriptExecutionContext.h"
 #include <wtf/NeverDestroyed.h>
 
@@ -52,9 +54,9 @@ Ref<MediaStreamTrack> MediaStreamTrack::create(ScriptExecutionContext& context,
 }
 
 MediaStreamTrack::MediaStreamTrack(ScriptExecutionContext& context, MediaStreamTrackPrivate& privateTrack)
-    : RefCounted()
-    , ActiveDOMObject(&context)
+    : ActiveDOMObject(&context)
     , m_private(privateTrack)
+    , m_weakPtrFactory(this)
 {
     suspendIfNeeded();
 
@@ -167,18 +169,37 @@ RefPtr<RealtimeMediaSourceCapabilities> MediaStreamTrack::getCapabilities() cons
     return m_private->capabilities();
 }
 
-void MediaStreamTrack::applyConstraints(const Dictionary& constraints)
+void MediaStreamTrack::applyConstraints(Ref<MediaConstraints>&& constraints, ApplyConstraintsPromise&& promise)
 {
-    // FIXME: Implement correctly. https://bugs.webkit.org/show_bug.cgi?id=160579
+    if (!constraints->isValid()) {
+        promise.reject(TypeError);
+        return;
+    }
+
+    m_constraints = WTFMove(constraints);
+    m_promise = WTFMove(promise);
 
-    m_constraints->initialize(constraints);
-    m_private->applyConstraints(*m_constraints);
+    applyConstraints(*m_constraints);
 }
 
-void MediaStreamTrack::applyConstraints(const MediaConstraints&)
+void MediaStreamTrack::applyConstraints(const MediaConstraints& constraints)
 {
-    // FIXME: apply the new constraints to the track
-    // https://bugs.webkit.org/show_bug.cgi?id=122428
+    auto weakThis = createWeakPtr();
+    std::function<void(const String&, const String&)> failureHandler = [weakThis](const String& failedConstraint, const String& message) {
+        if (!weakThis || !weakThis->m_promise)
+            return;
+
+        weakThis->m_promise->reject(OverconstrainedError::create(failedConstraint, message).get());
+    };
+
+    std::function<void()> successHandler = [weakThis]() {
+        if (!weakThis || !weakThis->m_promise)
+            return;
+
+        weakThis->m_promise->resolve(nullptr);
+    };
+
+    m_private->applyConstraints(constraints, successHandler, failureHandler);
 }
 
 void MediaStreamTrack::addObserver(MediaStreamTrack::Observer* observer)
index c99f4ce..76bb03c 100644 (file)
 
 #include "ActiveDOMObject.h"
 #include "EventTarget.h"
+#include "JSDOMPromise.h"
 #include "MediaStreamTrackPrivate.h"
 #include "RealtimeMediaSource.h"
 #include "ScriptWrappable.h"
+#include <wtf/Optional.h>
 #include <wtf/RefPtr.h>
 #include <wtf/Vector.h>
+#include <wtf/WeakPtr.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
 class AudioSourceProvider;
-class Dictionary;
-class MediaConstraintsImpl;
+class MediaConstraints;
 class MediaSourceSettings;
 class MediaTrackConstraints;
 
@@ -79,7 +81,9 @@ public:
     RefPtr<MediaTrackConstraints> getConstraints() const;
     RefPtr<MediaSourceSettings> getSettings() const;
     RefPtr<RealtimeMediaSourceCapabilities> getCapabilities() const;
-    void applyConstraints(const Dictionary&);
+
+    using ApplyConstraintsPromise = DOMPromise<std::nullptr_t>;
+    void applyConstraints(Ref<MediaConstraints>&&, ApplyConstraintsPromise&&);
     void applyConstraints(const MediaConstraints&);
 
     RealtimeMediaSource& source() const { return m_private->source(); }
@@ -118,10 +122,14 @@ private:
     void trackSettingsChanged(MediaStreamTrackPrivate&) override;
     void trackEnabledChanged(MediaStreamTrackPrivate&) override;
 
+    WeakPtr<MediaStreamTrack> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
+
     Vector<Observer*> m_observers;
     Ref<MediaStreamTrackPrivate> m_private;
 
-    RefPtr<MediaConstraintsImpl> m_constraints;
+    RefPtr<MediaConstraints> m_constraints;
+    Optional<ApplyConstraintsPromise> m_promise;
+    WeakPtrFactory<MediaStreamTrack> m_weakPtrFactory;
 
     bool m_ended { false };
 };
index 1163eac..3b27bfc 100644 (file)
@@ -47,10 +47,10 @@ enum MediaStreamTrackState { "new", "live", "ended" };
     MediaStreamTrack clone();
     [ImplementedAs=stopProducingData] void stop();
 
-    MediaTrackConstraints getConstraints();
+    [Custom] MediaTrackConstraints getConstraints();
     [Custom] MediaSourceSettings getSettings();
     [Custom] MediaTrackCapabilities getCapabilities();
-    void applyConstraints(Dictionary constraints);
+    [Custom] Promise applyConstraints(Dictionary? constraints);
 
     attribute EventHandler onoverconstrained;
 };
index 6a1248b..e460077 100644 (file)
                07297FA71C1881C5003F0735 /* UserMediaPermissionCheck.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07297FA51C1881C5003F0735 /* UserMediaPermissionCheck.cpp */; };
                07297FA81C1881C5003F0735 /* UserMediaPermissionCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 07297FA61C1881C5003F0735 /* UserMediaPermissionCheck.h */; settings = {ATTRIBUTES = (Private, ); }; };
                072A70401D6E8F6200DF0AFC /* OverconstrainedErrorEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 072A703E1D6E8F6200DF0AFC /* OverconstrainedErrorEvent.h */; };
+               072A70431D7396B300DF0AFC /* JSMediaDevicesCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = 072A70421D7396B200DF0AFC /* JSMediaDevicesCustom.h */; };
                072AE1E5183C0741000A5988 /* PluginReplacement.h in Headers */ = {isa = PBXBuildFile; fileRef = 072AE1DF183C0741000A5988 /* PluginReplacement.h */; settings = {ATTRIBUTES = (Private, ); }; };
                072AE1E6183C0741000A5988 /* QuickTimePluginReplacement.mm in Sources */ = {isa = PBXBuildFile; fileRef = 072AE1E0183C0741000A5988 /* QuickTimePluginReplacement.mm */; };
                072AE1E8183C0741000A5988 /* QuickTimePluginReplacement.h in Headers */ = {isa = PBXBuildFile; fileRef = 072AE1E2183C0741000A5988 /* QuickTimePluginReplacement.h */; };
                07297FA61C1881C5003F0735 /* UserMediaPermissionCheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserMediaPermissionCheck.h; sourceTree = "<group>"; };
                072A703E1D6E8F6200DF0AFC /* OverconstrainedErrorEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OverconstrainedErrorEvent.h; sourceTree = "<group>"; };
                072A703F1D6E8F6200DF0AFC /* OverconstrainedErrorEvent.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = OverconstrainedErrorEvent.idl; sourceTree = "<group>"; };
+               072A70421D7396B200DF0AFC /* JSMediaDevicesCustom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMediaDevicesCustom.h; sourceTree = "<group>"; };
                072AE1DF183C0741000A5988 /* PluginReplacement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PluginReplacement.h; sourceTree = "<group>"; };
                072AE1E0183C0741000A5988 /* QuickTimePluginReplacement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QuickTimePluginReplacement.mm; sourceTree = "<group>"; };
                072AE1E1183C0741000A5988 /* QuickTimePluginReplacement.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = QuickTimePluginReplacement.css; sourceTree = "<group>"; };
                                7A74ECBC101839DA00BF939E /* JSInspectorFrontendHostCustom.cpp */,
                                BCE1C43F0D9830F4003B02F2 /* JSLocationCustom.cpp */,
                                1B88DD121D5AD3B200E3B7A4 /* JSMediaDevicesCustom.cpp */,
+                               072A70421D7396B200DF0AFC /* JSMediaDevicesCustom.h */,
                                AD726FE716D9F204003A4E6D /* JSMediaListCustom.h */,
                                07C59B6D17F794F6000FBCBB /* JSMediaStreamTrackCustom.cpp */,
                                07C1C0E61BFB90A700BD2256 /* JSMediaTrackSupportedConstraintsCustom.cpp */,
                                93309EA3099EB78C0056E581 /* SharedTimer.h in Headers */,
                                E48944A3180B57D800F165D8 /* SimpleLineLayout.h in Headers */,
                                585D6E041A1A792E00FA4F12 /* SimpleLineLayoutFlowContents.h in Headers */,
+                               072A70431D7396B300DF0AFC /* JSMediaDevicesCustom.h in Headers */,
                                E4E9B11D1814569C003ACCDF /* SimpleLineLayoutFunctions.h in Headers */,
                                E4E9B1191810916F003ACCDF /* SimpleLineLayoutResolver.h in Headers */,
                                582CB0531A78A14B00AFFCC4 /* SimpleLineLayoutTextFragmentIterator.h in Headers */,
index e01241c..4ce613a 100644 (file)
  */
 
 #include "config.h"
-#include "JSMediaDevices.h"
+#include "JSMediaDevicesCustom.h"
 
 #if ENABLE(MEDIA_STREAM)
 
 #include "ArrayValue.h"
 #include "Dictionary.h"
 #include "ExceptionCode.h"
+#include "JSMediaDevices.h"
 #include "Logging.h"
-#include "MediaConstraints.h"
 #include "MediaConstraintsImpl.h"
 #include "RealtimeMediaSourceCenter.h"
 #include "RealtimeMediaSourceSupportedConstraints.h"
@@ -60,7 +60,7 @@ static void initializeStringConstraintWithList(StringConstraint& constraint, voi
 
 static RefPtr<StringConstraint> createStringConstraint(const Dictionary& mediaTrackConstraintSet, const String& name, MediaConstraintType type, ConstraintSetType constraintSetType)
 {
-    auto constraint = StringConstraint::create(type);
+    auto constraint = StringConstraint::create(name, type);
 
     // Dictionary constraint value.
     Dictionary dictionaryValue;
@@ -122,7 +122,7 @@ static RefPtr<StringConstraint> createStringConstraint(const Dictionary& mediaTr
 
 static RefPtr<BooleanConstraint> createBooleanConstraint(const Dictionary& mediaTrackConstraintSet, const String& name, MediaConstraintType type, ConstraintSetType constraintSetType)
 {
-    auto constraint = BooleanConstraint::create(type);
+    auto constraint = BooleanConstraint::create(name, type);
 
     // Dictionary constraint value.
     Dictionary dictionaryValue;
@@ -161,7 +161,7 @@ static RefPtr<BooleanConstraint> createBooleanConstraint(const Dictionary& media
 
 static RefPtr<DoubleConstraint> createDoubleConstraint(const Dictionary& mediaTrackConstraintSet, const String& name, MediaConstraintType type, ConstraintSetType constraintSetType)
 {
-    auto constraint = DoubleConstraint::create(type);
+    auto constraint = DoubleConstraint::create(name, type);
 
     // Dictionary constraint value.
     Dictionary dictionaryValue;
@@ -208,7 +208,7 @@ static RefPtr<DoubleConstraint> createDoubleConstraint(const Dictionary& mediaTr
 
 static RefPtr<IntConstraint> createIntConstraint(const Dictionary& mediaTrackConstraintSet, const String& name, MediaConstraintType type, ConstraintSetType constraintSetType)
 {
-    auto constraint = IntConstraint::create(type);
+    auto constraint = IntConstraint::create(name, type);
 
     // Dictionary constraint value.
     Dictionary dictionaryValue;
@@ -253,61 +253,45 @@ static RefPtr<IntConstraint> createIntConstraint(const Dictionary& mediaTrackCon
     return nullptr;
 }
 
-static void parseMediaTrackConstraintSetForKey(const Dictionary& mediaTrackConstraintSet, const String& name, MediaTrackConstraintSetMap& map, ConstraintSetType constraintSetType, RealtimeMediaSource::Type sourceType)
+static void parseMediaTrackConstraintSetForKey(const Dictionary& mediaTrackConstraintSet, const String& name, MediaTrackConstraintSetMap& map, ConstraintSetType constraintSetType)
 {
     MediaConstraintType constraintType = RealtimeMediaSourceSupportedConstraints::constraintFromName(name);
 
     RefPtr<MediaConstraint> mediaConstraint;
-    if (sourceType == RealtimeMediaSource::Audio) {
-        switch (constraintType) {
-        case MediaConstraintType::SampleRate:
-        case MediaConstraintType::SampleSize:
-            mediaConstraint = createIntConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType);
-            break;
-        case MediaConstraintType::Volume:
-            mediaConstraint = createDoubleConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType);
-            break;
-        case MediaConstraintType::EchoCancellation:
-            mediaConstraint = createBooleanConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType);
-            break;
-        case MediaConstraintType::DeviceId:
-        case MediaConstraintType::GroupId:
-            mediaConstraint = createStringConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType);
-            break;
-        default:
-            LOG(Media, "parseMediaTrackConstraintSetForKey() - ignoring unsupported constraint '%s' for audio.", name.utf8().data());
-            mediaConstraint = nullptr;
-            break;
-        }
-    } else if (sourceType == RealtimeMediaSource::Video) {
-        switch (constraintType) {
-        case MediaConstraintType::Width:
-        case MediaConstraintType::Height:
-            mediaConstraint = createIntConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType);
-            break;
-        case MediaConstraintType::AspectRatio:
-        case MediaConstraintType::FrameRate:
-            mediaConstraint = createDoubleConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType);
-            break;
-        case MediaConstraintType::FacingMode:
-        case MediaConstraintType::DeviceId:
-        case MediaConstraintType::GroupId:
-            mediaConstraint = createStringConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType);
-            break;
-        default:
-            LOG(Media, "parseMediaTrackConstraintSetForKey() - ignoring unsupported constraint '%s' for video.", name.utf8().data());
-            mediaConstraint = nullptr;
-            break;
-        }
+    switch (constraintType) {
+    case MediaConstraintType::Width:
+    case MediaConstraintType::Height:
+    case MediaConstraintType::SampleRate:
+    case MediaConstraintType::SampleSize:
+        mediaConstraint = createIntConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType);
+        break;
+
+    case MediaConstraintType::AspectRatio:
+    case MediaConstraintType::FrameRate:
+    case MediaConstraintType::Volume:
+        mediaConstraint = createDoubleConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType);
+        break;
+
+    case MediaConstraintType::EchoCancellation:
+        mediaConstraint = createBooleanConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType);
+        break;
+
+    case MediaConstraintType::FacingMode:
+    case MediaConstraintType::DeviceId:
+    case MediaConstraintType::GroupId:
+        mediaConstraint = createStringConstraint(mediaTrackConstraintSet, name, constraintType, constraintSetType);
+        break;
+
+    case MediaConstraintType::Unknown:
+        LOG(Media, "parseMediaTrackConstraintSetForKey() - found unsupported constraint '%s'.", name.utf8().data());
+        mediaConstraint = UnknownConstraint::create(name, constraintType);
+        break;
     }
-
-    if (!mediaConstraint)
-        return;
-
+    
     map.add(name, WTFMove(mediaConstraint));
 }
 
-static void parseAdvancedConstraints(const Dictionary& mediaTrackConstraints, Vector<MediaTrackConstraintSetMap>& advancedConstraints, RealtimeMediaSource::Type sourceType)
+static void parseAdvancedConstraints(const Dictionary& mediaTrackConstraints, Vector<MediaTrackConstraintSetMap>& advancedConstraints)
 {
     ArrayValue sequenceOfMediaTrackConstraintSets;
     if (!mediaTrackConstraints.get("advanced", sequenceOfMediaTrackConstraintSets) || sequenceOfMediaTrackConstraintSets.isUndefinedOrNull()) {
@@ -333,14 +317,14 @@ static void parseAdvancedConstraints(const Dictionary& mediaTrackConstraints, Ve
         Vector<String> localKeys;
         mediaTrackConstraintSet.getOwnPropertyNames(localKeys);
         for (auto& localKey : localKeys)
-            parseMediaTrackConstraintSetForKey(mediaTrackConstraintSet, localKey, map, ConstraintSetType::Advanced, sourceType);
+            parseMediaTrackConstraintSetForKey(mediaTrackConstraintSet, localKey, map, ConstraintSetType::Advanced);
 
         if (!map.isEmpty())
             advancedConstraints.append(WTFMove(map));
     }
 }
 
-static void parseConstraints(const Dictionary& mediaTrackConstraints, MediaTrackConstraintSetMap& mandatoryConstraints, Vector<MediaTrackConstraintSetMap>& advancedConstraints, RealtimeMediaSource::Type sourceType)
+void parseMediaConstraintsDictionary(const Dictionary& mediaTrackConstraints, MediaTrackConstraintSetMap& mandatoryConstraints, Vector<MediaTrackConstraintSetMap>& advancedConstraints)
 {
     if (mediaTrackConstraints.isUndefinedOrNull())
         return;
@@ -350,9 +334,9 @@ static void parseConstraints(const Dictionary& mediaTrackConstraints, MediaTrack
 
     for (auto& key : keys) {
         if (key == "advanced")
-            parseAdvancedConstraints(mediaTrackConstraints, advancedConstraints, sourceType);
+            parseAdvancedConstraints(mediaTrackConstraints, advancedConstraints);
         else
-            parseMediaTrackConstraintSetForKey(mediaTrackConstraints, key, mandatoryConstraints, ConstraintSetType::Mandatory, sourceType);
+            parseMediaTrackConstraintSetForKey(mediaTrackConstraints, key, mandatoryConstraints, ConstraintSetType::Mandatory);
     }
 }
 
@@ -372,7 +356,7 @@ JSValue JSMediaDevices::getUserMedia(ExecState& state)
 
     Dictionary audioConstraintsDictionary;
     if (constraintsDictionary.get("audio", audioConstraintsDictionary) && !audioConstraintsDictionary.isUndefinedOrNull()) {
-        parseConstraints(audioConstraintsDictionary, mandatoryAudioConstraints, advancedAudioConstraints, RealtimeMediaSource::Audio);
+        parseMediaConstraintsDictionary(audioConstraintsDictionary, mandatoryAudioConstraints, advancedAudioConstraints);
         areAudioConstraintsValid = true;
     } else
         constraintsDictionary.get("audio", areAudioConstraintsValid);
@@ -383,7 +367,7 @@ JSValue JSMediaDevices::getUserMedia(ExecState& state)
 
     Dictionary videoConstraintsDictionary;
     if (constraintsDictionary.get("video", videoConstraintsDictionary) && !videoConstraintsDictionary.isUndefinedOrNull()) {
-        parseConstraints(videoConstraintsDictionary, mandatoryVideoConstraints, advancedVideoConstraints, RealtimeMediaSource::Video);
+        parseMediaConstraintsDictionary(videoConstraintsDictionary, mandatoryVideoConstraints, advancedVideoConstraints);
         areVideoConstraintsValid = true;
     } else
         constraintsDictionary.get("video", areVideoConstraintsValid);
diff --git a/Source/WebCore/bindings/js/JSMediaDevicesCustom.h b/Source/WebCore/bindings/js/JSMediaDevicesCustom.h
new file mode 100644 (file)
index 0000000..010ac6c
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(MEDIA_STREAM)
+
+#include "MediaConstraints.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class Dictionary;
+
+void parseMediaConstraintsDictionary(const Dictionary&, MediaTrackConstraintSetMap&, Vector<MediaTrackConstraintSetMap>&);
+
+}
+
+#endif
index 4117d35..fdf66a7 100644 (file)
 
 #if ENABLE(MEDIA_STREAM)
 
+#include "Dictionary.h"
 #include "ExceptionCode.h"
 #include "JSDOMBinding.h"
+#include "JSMediaDevicesCustom.h"
+#include "MediaConstraintsImpl.h"
 #include "MediaSourceSettings.h"
 #include "MediaStreamTrack.h"
+#include "WebCoreJSClientData.h"
 #include <runtime/JSObject.h>
 #include <runtime/ObjectConstructor.h>
 
@@ -57,7 +61,7 @@ JSC::JSValue JSMediaStreamTrack::getSettings(ExecState& state)
         object->putDirect(state.vm(), Identifier::fromString(&state, "width"), jsNumber(settings->width()), DontDelete | ReadOnly);
     if (settings->supportsHeight())
         object->putDirect(state.vm(), Identifier::fromString(&state, "height"), jsNumber(settings->height()), DontDelete | ReadOnly);
-    if (settings->supportsAspectRatio())
+    if (settings->supportsAspectRatio() && settings->aspectRatio())
         object->putDirect(state.vm(), Identifier::fromString(&state, "aspectRatio"), jsDoubleNumber(settings->aspectRatio()), DontDelete | ReadOnly);
     if (settings->supportsFrameRate())
         object->putDirect(state.vm(), Identifier::fromString(&state, "frameRate"), jsDoubleNumber(settings->frameRate()), DontDelete | ReadOnly);
@@ -90,8 +94,8 @@ static JSValue capabilityValue(const CapabilityValueOrRange& value, ExecState& s
             object->putDirect(state.vm(), Identifier::fromString(&state, "min"), jsNumber(min.asDouble));
             object->putDirect(state.vm(), Identifier::fromString(&state, "max"), jsNumber(max.asDouble));
         } else {
-            object->putDirect(state.vm(), Identifier::fromString(&state, "min"), jsNumber(min.asULong));
-            object->putDirect(state.vm(), Identifier::fromString(&state, "max"), jsNumber(max.asULong));
+            object->putDirect(state.vm(), Identifier::fromString(&state, "min"), jsNumber(min.asInt));
+            object->putDirect(state.vm(), Identifier::fromString(&state, "max"), jsNumber(max.asInt));
         }
 
         return object;
@@ -100,7 +104,7 @@ static JSValue capabilityValue(const CapabilityValueOrRange& value, ExecState& s
     if (value.type() == CapabilityValueOrRange::Double)
         return jsNumber(value.value().asDouble);
 
-    return jsNumber(value.value().asULong);
+    return jsNumber(value.value().asInt);
 }
 
 JSC::JSValue JSMediaStreamTrack::getCapabilities(ExecState& state)
@@ -162,6 +166,38 @@ JSC::JSValue JSMediaStreamTrack::getCapabilities(ExecState& state)
     return object;
 }
 
+JSValue JSMediaStreamTrack::applyConstraints(ExecState& state)
+{
+    MediaTrackConstraintSetMap mandatoryConstraints;
+    Vector<MediaTrackConstraintSetMap> advancedConstraints;
+    bool valid = false;
+
+    if (state.argumentCount() >= 1) {
+        JSValue argument = state.uncheckedArgument(0);
+
+        JSVMClientData& clientData = *static_cast<JSVMClientData*>(state.vm().clientData);
+        putDirect(state.vm(), clientData.builtinNames().mediaStreamTrackConstraintsPrivateName(), argument, DontEnum);
+
+        auto constraintsDictionary = Dictionary(&state, argument);
+        if (!constraintsDictionary.isUndefinedOrNull())
+            parseMediaConstraintsDictionary(constraintsDictionary, mandatoryConstraints, advancedConstraints);
+        valid = !advancedConstraints.isEmpty() || !mandatoryConstraints.isEmpty();
+    }
+
+    JSC::JSPromiseDeferred* promiseDeferred = JSC::JSPromiseDeferred::create(&state, globalObject());
+    auto constraints = MediaConstraintsImpl::create(WTFMove(mandatoryConstraints), WTFMove(advancedConstraints), valid);
+    wrapped().applyConstraints(WTFMove(constraints), DeferredWrapper::create(&state, globalObject(), promiseDeferred));
+
+    return promiseDeferred->promise();
+}
+
+JSValue JSMediaStreamTrack::getConstraints(ExecState& state)
+{
+    JSVMClientData& clientData = *static_cast<JSVMClientData*>(state.vm().clientData);
+    JSValue result = getDirect(state.vm(), clientData.builtinNames().mediaStreamTrackConstraintsPrivateName());
+    return !result.isEmpty() ? result : jsUndefined();
+}
+
 } // namespace WebCore
 
 #endif
index 8197d42..bd39cda 100644 (file)
@@ -58,6 +58,7 @@ namespace WebCore {
     macro(localStreams) \
     macro(makeThisTypeError) \
     macro(makeGetterTypeError) \
+    macro(mediaStreamTrackConstraints) \
     macro(operations) \
     macro(ownerReadableStream) \
     macro(privateGetStats) \
index 11992df..d2a84b0 100644 (file)
@@ -1001,7 +1001,8 @@ void HTMLMediaElement::setSrcObject(ScriptExecutionContext& context, MediaStream
     // https://bugs.webkit.org/show_bug.cgi?id=124896
 
     m_mediaStreamSrcObject = mediaStream;
-    setSrc(DOMURL::createPublicURL(context, mediaStream));
+    if (mediaStream)
+        setSrc(DOMURL::createPublicURL(context, mediaStream));
 }
 #endif
 
index 539ae85..3f565e8 100644 (file)
@@ -312,6 +312,7 @@ void MediaPlayerPrivateMediaStreamAVFObjC::play()
     m_playing = true;
     m_haveEverPlayed = true;
     scheduleDeferredTask([this] {
+        updateDisplayMode();
         updateReadyState();
     });
 }
index bd5eed3..3bace1c 100644 (file)
@@ -46,19 +46,19 @@ RefPtr<MediaConstraint> MediaConstraint::create(const String& name)
     case MediaConstraintType::Height:
     case MediaConstraintType::SampleRate:
     case MediaConstraintType::SampleSize:
-        return IntConstraint::create(constraintType);
+        return IntConstraint::create(name, constraintType);
     case MediaConstraintType::AspectRatio:
     case MediaConstraintType::FrameRate:
     case MediaConstraintType::Volume:
-        return DoubleConstraint::create(constraintType);
+        return DoubleConstraint::create(name, constraintType);
     case MediaConstraintType::EchoCancellation:
-        return BooleanConstraint::create(constraintType);
+        return BooleanConstraint::create(name, constraintType);
     case MediaConstraintType::FacingMode:
     case MediaConstraintType::DeviceId:
     case MediaConstraintType::GroupId:
-        return StringConstraint::create(constraintType);
+        return StringConstraint::create(name, constraintType);
     case MediaConstraintType::Unknown:
-        return nullptr;
+        return UnknownConstraint::create(name, constraintType);
     }
 }
 
@@ -121,6 +121,21 @@ bool StringConstraint::getIdeal(Vector<String>& ideal) const
     return true;
 }
 
+const String& StringConstraint::find(std::function<bool(ConstraintType, const String&)> filter) const
+{
+    for (auto& constraint : m_exact) {
+        if (filter(ConstraintType::ExactConstraint, constraint))
+            return constraint;
+    }
+
+    for (auto& constraint : m_ideal) {
+        if (filter(ConstraintType::IdealConstraint, constraint))
+            return constraint;
+    }
+    
+    return emptyString();
+}
+
 }
 
 #endif // ENABLE(MEDIA_STREAM)
index e0ae8d6..17a9dd0 100644 (file)
@@ -45,6 +45,8 @@ class MediaConstraint : public RefCounted<MediaConstraint> {
 public:
     static RefPtr<MediaConstraint> create(const String& name);
 
+    enum class ConstraintType { ExactConstraint, IdealConstraint, MinConstraint, MaxConstraint };
+
     virtual ~MediaConstraint() { };
     virtual bool isEmpty() const = 0;
     virtual bool isMandatory() const = 0;
@@ -53,11 +55,15 @@ public:
     virtual bool getMax(int&) const { ASSERT_NOT_REACHED(); return false; }
     virtual bool getExact(int&) const { ASSERT_NOT_REACHED(); return false; }
     virtual bool getIdeal(int&) const { ASSERT_NOT_REACHED(); return false; }
+    virtual bool validForRange(int, int) const { ASSERT_NOT_REACHED(); return false; }
+    virtual int find(std::function<bool(ConstraintType, int)>) const { ASSERT_NOT_REACHED(); return 0; }
 
     virtual bool getMin(double&) const { ASSERT_NOT_REACHED(); return false; }
     virtual bool getMax(double&) const { ASSERT_NOT_REACHED(); return false; }
     virtual bool getExact(double&) const { ASSERT_NOT_REACHED(); return false; }
     virtual bool getIdeal(double&) const { ASSERT_NOT_REACHED(); return false; }
+    virtual bool validForRange(double, double) const { ASSERT_NOT_REACHED(); return false; }
+    virtual double find(std::function<bool(ConstraintType, double)>) const { ASSERT_NOT_REACHED(); return 0; }
 
     virtual bool getMin(bool&) const { ASSERT_NOT_REACHED(); return false; }
     virtual bool getMax(bool&) const { ASSERT_NOT_REACHED(); return false; }
@@ -68,16 +74,20 @@ public:
     virtual bool getMax(Vector<String>&) const { ASSERT_NOT_REACHED(); return false; }
     virtual bool getExact(Vector<String>&) const { ASSERT_NOT_REACHED(); return false; }
     virtual bool getIdeal(Vector<String>&) const { ASSERT_NOT_REACHED(); return false; }
+    virtual const String& find(std::function<bool(ConstraintType, const String&)>) const { ASSERT_NOT_REACHED(); return emptyString(); }
 
     MediaConstraintType type() const { return m_type; }
+    const String& name() const { return m_name; }
 
 protected:
-    explicit MediaConstraint(MediaConstraintType type)
-        : m_type(type)
+    explicit MediaConstraint(const String& name, MediaConstraintType type)
+        : m_name(name)
+        , m_type(type)
     {
     }
 
 private:
+    String m_name;
     MediaConstraintType m_type;
 };
 
@@ -124,9 +134,42 @@ public:
         return true;
     }
 
+    bool validForRange(ValueType rangeMin, ValueType rangeMax) const final {
+        if (isEmpty())
+            return false;
+
+        if (m_exact && (m_exact.value() < rangeMin || m_exact.value() > rangeMax))
+            return false;
+
+        if (m_min && m_min.value() > rangeMax)
+            return false;
+
+        if (m_max && m_max.value() < rangeMin)
+            return false;
+
+        return true;
+    }
+
+    ValueType find(std::function<bool(ConstraintType, ValueType)> function) const final {
+        if (m_min && function(ConstraintType::MinConstraint, m_min.value()))
+            return m_min.value();
+
+        if (m_max && function(ConstraintType::MaxConstraint, m_max.value()))
+            return m_max.value();
+
+        if (m_exact && function(ConstraintType::ExactConstraint, m_exact.value()))
+            return m_exact.value();
+
+        if (m_ideal && function(ConstraintType::IdealConstraint, m_ideal.value()))
+            return m_ideal.value();
+
+        return 0;
+    }
+    
+
 protected:
-    explicit NumericConstraint(MediaConstraintType type)
-        : MediaConstraint(type)
+    explicit NumericConstraint(const String& name, MediaConstraintType type)
+        : MediaConstraint(name, type)
     {
     }
 
@@ -139,29 +182,29 @@ private:
 
 class IntConstraint final : public NumericConstraint<int> {
 public:
-    static Ref<IntConstraint> create(MediaConstraintType type) { return adoptRef(*new IntConstraint(type)); }
+    static Ref<IntConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new IntConstraint(name, type)); }
 
 private:
-    explicit IntConstraint(MediaConstraintType type)
-        : NumericConstraint<int>(type)
+    explicit IntConstraint(const String& name, MediaConstraintType type)
+        : NumericConstraint<int>(name, type)
     {
     }
 };
 
 class DoubleConstraint final : public NumericConstraint<double> {
 public:
-    static Ref<DoubleConstraint> create(MediaConstraintType type) { return adoptRef(*new DoubleConstraint(type)); }
+    static Ref<DoubleConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new DoubleConstraint(name, type)); }
 
 private:
-    explicit DoubleConstraint(MediaConstraintType type)
-        : NumericConstraint<double>(type)
+    explicit DoubleConstraint(const String& name, MediaConstraintType type)
+        : NumericConstraint<double>(name, type)
     {
     }
 };
 
 class BooleanConstraint final : public MediaConstraint {
 public:
-    static Ref<BooleanConstraint> create(MediaConstraintType type) { return adoptRef(*new BooleanConstraint(type)); }
+    static Ref<BooleanConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new BooleanConstraint(name, type)); }
 
     void setExact(bool value) { m_exact = value; }
     void setIdeal(bool value) { m_ideal = value; }
@@ -173,18 +216,18 @@ public:
     bool isMandatory() const final { return bool(m_exact); }
 
 private:
-    explicit BooleanConstraint(MediaConstraintType type)
-        : MediaConstraint(type)
+    explicit BooleanConstraint(const String& name, MediaConstraintType type)
+        : MediaConstraint(name, type)
     {
     }
 
-    Optional<bool> m_exact { false };
-    Optional<bool> m_ideal { false };
+    Optional<bool> m_exact;
+    Optional<bool> m_ideal;
 };
 
 class StringConstraint final : public MediaConstraint {
 public:
-    static Ref<StringConstraint> create(MediaConstraintType type) { return adoptRef(*new StringConstraint(type)); }
+    static Ref<StringConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new StringConstraint(name, type)); }
 
     void setExact(const String&);
     void appendExact(const String&);
@@ -197,9 +240,11 @@ public:
     bool isEmpty() const final { return m_exact.isEmpty() && m_ideal.isEmpty(); }
     bool isMandatory() const final { return !m_exact.isEmpty(); }
 
+    const String& find(std::function<bool(ConstraintType, const String&)>) const override;
+
 private:
-    explicit StringConstraint(MediaConstraintType type)
-        : MediaConstraint(type)
+    explicit StringConstraint(const String& name, MediaConstraintType type)
+        : MediaConstraint(name, type)
     {
     }
 
@@ -207,6 +252,20 @@ private:
     Vector<String> m_ideal;
 };
 
+class UnknownConstraint final : public MediaConstraint {
+public:
+    static Ref<UnknownConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new UnknownConstraint(name, type)); }
+
+    bool isEmpty() const final { return true; }
+    bool isMandatory() const final { return false; }
+
+private:
+    explicit UnknownConstraint(const String& name, MediaConstraintType type)
+        : MediaConstraint(name, type)
+    {
+    }
+};
+
 using MediaTrackConstraintSetMap = HashMap<String, RefPtr<MediaConstraint>>;
 
 class MediaConstraints : public RefCounted<MediaConstraints> {
index 8bbf650..a48b267 100644 (file)
@@ -172,10 +172,9 @@ void MediaStreamTrackPrivate::paintCurrentFrameInContext(GraphicsContext& contex
     }
 }
 
-void MediaStreamTrackPrivate::applyConstraints(const MediaConstraints&)
+void MediaStreamTrackPrivate::applyConstraints(const MediaConstraints& constraints, RealtimeMediaSource::SuccessHandler successHandler, RealtimeMediaSource::FailureHandler failureHandler)
 {
-    // FIXME: apply the new constraints to the track
-    // https://bugs.webkit.org/show_bug.cgi?id=122428
+    m_source->applyConstraints(constraints, successHandler, failureHandler);
 }
 
 AudioSourceProvider* MediaStreamTrackPrivate::audioSourceProvider()
index 12282d9..cd21a4e 100644 (file)
@@ -91,7 +91,7 @@ public:
     RefPtr<RealtimeMediaSourceCapabilities> capabilities() const;
 
     RefPtr<MediaConstraints> constraints() const;
-    void applyConstraints(const MediaConstraints&);
+    void applyConstraints(const MediaConstraints&, RealtimeMediaSource::SuccessHandler, RealtimeMediaSource::FailureHandler);
 
     AudioSourceProvider* audioSourceProvider();
 
index d34e3df..299ae4e 100644 (file)
 #if ENABLE(MEDIA_STREAM)
 #include "RealtimeMediaSource.h"
 
+#include "MediaConstraints.h"
+#include "NotImplemented.h"
 #include "RealtimeMediaSourceCapabilities.h"
 #include "UUID.h"
+#include <wtf/MainThread.h>
+#include <wtf/text/StringHash.h>
 
 namespace WebCore {
 
 RealtimeMediaSource::RealtimeMediaSource(const String& id, Type type, const String& name)
-    : m_id(id)
+    : m_weakPtrFactory(this)
+    , m_id(id)
     , m_type(type)
     , m_name(name)
 {
@@ -51,6 +56,7 @@ RealtimeMediaSource::RealtimeMediaSource(const String& id, Type type, const Stri
     if (m_id.isEmpty())
         m_id = createCanonicalUUIDString();
     m_persistentID = m_id;
+    m_suppressNotifications = false;
 }
 
 void RealtimeMediaSource::reset()
@@ -92,8 +98,18 @@ void RealtimeMediaSource::setMuted(bool muted)
 
 void RealtimeMediaSource::settingsDidChange()
 {
-    for (auto& observer : m_observers)
-        observer->sourceSettingsChanged();
+    ASSERT(isMainThread());
+
+    if (m_pendingSettingsDidChangeNotification || m_suppressNotifications)
+        return;
+
+    m_pendingSettingsDidChangeNotification = true;
+
+    scheduleDeferredTask([this] {
+        m_pendingSettingsDidChangeNotification = false;
+        for (auto& observer : m_observers)
+            observer->sourceSettingsChanged();
+    });
 }
 
 void RealtimeMediaSource::mediaDataUpdated(MediaSample& mediaSample)
@@ -134,6 +150,418 @@ void RealtimeMediaSource::requestStop(Observer* callingObserver)
     stop(callingObserver);
 }
 
+RealtimeMediaSource::ConstraintSupport RealtimeMediaSource::supportsConstraint(const MediaConstraint& constraint)
+{
+    RealtimeMediaSourceCapabilities& capabilities = *this->capabilities();
+
+    switch (constraint.type()) {
+    case MediaConstraintType::Width: {
+        if (!capabilities.supportsWidth())
+            return ConstraintSupport::Ignored;
+
+        auto widthRange = capabilities.width();
+        return constraint.validForRange(widthRange.rangeMin().asInt, widthRange.rangeMax().asInt) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
+        break;
+    }
+
+    case MediaConstraintType::Height: {
+        if (!capabilities.supportsHeight())
+            return ConstraintSupport::Ignored;
+
+        auto heightRange = capabilities.height();
+        return constraint.validForRange(heightRange.rangeMin().asInt, heightRange.rangeMax().asInt) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
+        break;
+    }
+
+    case MediaConstraintType::FrameRate: {
+        if (!capabilities.supportsFrameRate())
+            return ConstraintSupport::Ignored;
+
+        auto rateRange = capabilities.frameRate();
+        return constraint.validForRange(rateRange.rangeMin().asDouble, rateRange.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
+        break;
+    }
+
+    case MediaConstraintType::AspectRatio: {
+        if (!capabilities.supportsAspectRatio())
+            return ConstraintSupport::Ignored;
+
+        auto range = capabilities.aspectRatio();
+        return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
+        break;
+    }
+
+    case MediaConstraintType::Volume: {
+        if (!capabilities.supportsVolume())
+            return ConstraintSupport::Ignored;
+
+        auto range = capabilities.volume();
+        return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
+        break;
+    }
+
+    case MediaConstraintType::SampleRate: {
+        if (!capabilities.supportsSampleRate())
+            return ConstraintSupport::Ignored;
+
+        auto range = capabilities.sampleRate();
+        return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
+        break;
+    }
+
+    case MediaConstraintType::SampleSize: {
+        if (!capabilities.supportsSampleSize())
+            return ConstraintSupport::Ignored;
+
+        auto range = capabilities.sampleSize();
+        return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
+        break;
+    }
+
+    case MediaConstraintType::FacingMode: {
+        if (!capabilities.supportsFacingMode())
+            return ConstraintSupport::Ignored;
+
+        ConstraintSupport support = ConstraintSupport::Ignored;
+        auto& supportedModes = capabilities.facingMode();
+        std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [supportedModes, &support](MediaConstraint::ConstraintType type, const String& modeString) {
+            if (type == MediaConstraint::ConstraintType::ExactConstraint)
+                support = ConstraintSupport::Unsupported;
+
+            auto mode = RealtimeMediaSourceSettings::videoFacingModeEnum(modeString);
+            for (auto& supportedMode : supportedModes) {
+                if (supportedMode == mode) {
+                    support = ConstraintSupport::Supported;
+                    break;
+                }
+            }
+
+            return type == MediaConstraint::ConstraintType::ExactConstraint ? true : false;
+        };
+
+        constraint.find(filter);
+        return support;
+        break;
+    }
+
+    case MediaConstraintType::EchoCancellation:
+        if (!capabilities.supportsEchoCancellation())
+            return ConstraintSupport::Ignored;
+
+        if (capabilities.echoCancellation() == RealtimeMediaSourceCapabilities::EchoCancellation::ReadOnly)
+            return constraint.isMandatory() ? ConstraintSupport::Unsupported : ConstraintSupport::Ignored;
+
+        return ConstraintSupport::Supported;
+        break;
+
+    case MediaConstraintType::DeviceId: {
+        if (!capabilities.supportsDeviceId())
+            return ConstraintSupport::Ignored;
+
+        ConstraintSupport support = ConstraintSupport::Ignored;
+        std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [this, &support](MediaConstraint::ConstraintType type, const String& idString) {
+            if (type != MediaConstraint::ConstraintType::ExactConstraint)
+                return false; // Keep looking.
+
+            support = idString == m_id ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
+            return false;
+        };
+
+        constraint.find(filter);
+        return support;
+        break;
+    }
+
+    case MediaConstraintType::GroupId: {
+        if (!capabilities.supportsDeviceId())
+            return ConstraintSupport::Ignored;
+
+        ConstraintSupport support = ConstraintSupport::Ignored;
+        String groupId = settings().groupId();
+        std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [groupId, &support](MediaConstraint::ConstraintType type, const String& idString) {
+            if (type != MediaConstraint::ConstraintType::ExactConstraint)
+                return false; // Keep looking.
+
+            support = idString == groupId ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
+            return false;
+        };
+
+        constraint.find(filter);
+        return support;
+        break;
+    }
+
+    case MediaConstraintType::Unknown:
+        // Unknown (or unsupported) constraints should be ignored.
+        break;
+    }
+
+    return ConstraintSupport::Ignored;
+}
+
+
+template <typename T>
+T value(const MediaConstraint& constraint, T rangeMin, T rangeMax)
+{
+    T result;
+
+    if (constraint.getExact(result)) {
+        ASSERT(result >= rangeMin && result <= rangeMax);
+        return result;
+    }
+
+    if (constraint.getIdeal(result)) {
+        if (result < rangeMin)
+            result = rangeMin;
+        else if (result > rangeMax)
+            result = rangeMax;
+
+        return result;
+    }
+
+    if (constraint.getMin(result) && result > rangeMax)
+        return false;
+
+    if (constraint.getMax(result) && result < rangeMin)
+        return false;
+    
+    return result;
+}
+
+
+void RealtimeMediaSource::applyConstraint(const MediaConstraint& constraint)
+{
+    RealtimeMediaSourceCapabilities& capabilities = *this->capabilities();
+    switch (constraint.type()) {
+    case MediaConstraintType::Width: {
+        if (!capabilities.supportsWidth())
+            return;
+
+        auto widthRange = capabilities.width();
+        setWidth(value(constraint, widthRange.rangeMin().asInt, widthRange.rangeMax().asInt));
+        break;
+    }
+
+    case MediaConstraintType::Height: {
+        if (!capabilities.supportsHeight())
+            return;
+
+        auto heightRange = capabilities.height();
+        setHeight(value(constraint, heightRange.rangeMin().asInt, heightRange.rangeMax().asInt));
+        break;
+    }
+
+    case MediaConstraintType::FrameRate: {
+        if (!capabilities.supportsFrameRate())
+            return;
+
+        auto rateRange = capabilities.frameRate();
+        setFrameRate(value(constraint, rateRange.rangeMin().asDouble, rateRange.rangeMax().asDouble));
+        break;
+    }
+
+    case MediaConstraintType::AspectRatio: {
+        if (!capabilities.supportsAspectRatio())
+            return;
+
+        auto range = capabilities.aspectRatio();
+        setAspectRatio(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
+        break;
+    }
+
+    case MediaConstraintType::Volume: {
+        if (!capabilities.supportsVolume())
+            return;
+
+        auto range = capabilities.volume();
+        // std::pair<T, T> valuesForRange(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble)
+        setVolume(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
+        break;
+    }
+
+    case MediaConstraintType::SampleRate: {
+        if (!capabilities.supportsSampleRate())
+            return;
+
+        auto range = capabilities.sampleRate();
+        setSampleRate(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
+        break;
+    }
+
+    case MediaConstraintType::SampleSize: {
+        if (!capabilities.supportsSampleSize())
+            return;
+
+        auto range = capabilities.sampleSize();
+        setSampleSize(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
+        break;
+    }
+
+    case MediaConstraintType::EchoCancellation:
+        if (!capabilities.supportsEchoCancellation())
+            return;
+
+        bool setting;
+        if (constraint.getExact(setting) || constraint.getIdeal(setting))
+            setEchoCancellation(setting);
+        break;
+
+    case MediaConstraintType::FacingMode: {
+        if (!capabilities.supportsFacingMode())
+            return;
+
+        auto& supportedModes = capabilities.facingMode();
+        std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [supportedModes](MediaConstraint::ConstraintType, const String& modeString) {
+            auto mode = RealtimeMediaSourceSettings::videoFacingModeEnum(modeString);
+            for (auto& supportedMode : supportedModes) {
+                if (mode == supportedMode)
+                    return true;
+            }
+            return false;
+        };
+
+        auto modeString = constraint.find(filter);
+        if (!modeString.isEmpty())
+            setFacingMode(RealtimeMediaSourceSettings::videoFacingModeEnum(modeString));
+        break;
+    }
+
+    case MediaConstraintType::DeviceId:
+    case MediaConstraintType::GroupId:
+        // There is nothing to do here, neither can be changed.
+        break;
+
+    case MediaConstraintType::Unknown:
+        break;
+    }
+}
+
+void RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints, SuccessHandler successHandler, FailureHandler failureHandler)
+{
+    ASSERT(constraints.isValid());
+
+    auto& mandatoryConstraints = constraints.mandatoryConstraints();
+    for (auto& nameConstraintPair : mandatoryConstraints) {
+        auto& constraint = *nameConstraintPair.value;
+        if (supportsConstraint(constraint) == ConstraintSupport::Unsupported) {
+            failureHandler(constraint.name(), "Constraint not supported");
+            return;
+        }
+    }
+
+    for (auto& nameConstraintPair : mandatoryConstraints)
+        applyConstraint(*nameConstraintPair.value);
+
+    successHandler();
+}
+
+void RealtimeMediaSource::setWidth(int width)
+{
+    if (width == m_size.width())
+        return;
+
+    int height = m_aspectRatio ? width / m_aspectRatio : m_size.height();
+    if (!applySize(IntSize(width, height)))
+        return;
+
+    m_size.setWidth(width);
+    if (m_aspectRatio)
+        m_size.setHeight(width / m_aspectRatio);
+
+    settingsDidChange();
+}
+
+void RealtimeMediaSource::setHeight(int height)
+{
+    if (height == m_size.height())
+        return;
+
+    int width = m_aspectRatio ? height * m_aspectRatio : m_size.width();
+    if (!applySize(IntSize(width, height)))
+        return;
+
+    if (m_aspectRatio)
+        m_size.setWidth(width);
+    m_size.setHeight(height);
+
+    settingsDidChange();
+}
+
+void RealtimeMediaSource::setFrameRate(double rate)
+{
+    if (m_frameRate == rate || !applyFrameRate(rate))
+        return;
+
+    m_frameRate = rate;
+    settingsDidChange();
+}
+
+void RealtimeMediaSource::setAspectRatio(double ratio)
+{
+    if (m_aspectRatio == ratio || !applyAspectRatio(ratio))
+        return;
+
+    m_aspectRatio = ratio;
+    m_size.setHeight(m_size.width() / ratio);
+    settingsDidChange();
+}
+
+void RealtimeMediaSource::setFacingMode(RealtimeMediaSourceSettings::VideoFacingMode mode)
+{
+    if (m_facingMode == mode || !applyFacingMode(mode))
+        return;
+
+    m_facingMode = mode;
+    settingsDidChange();
+}
+
+void RealtimeMediaSource::setVolume(double volume)
+{
+    if (m_volume == volume || !applyVolume(volume))
+        return;
+
+    m_volume = volume;
+    settingsDidChange();
+}
+
+void RealtimeMediaSource::setSampleRate(double rate)
+{
+    if (m_sampleRate == rate || !applySampleRate(rate))
+        return;
+
+    m_sampleRate = rate;
+    settingsDidChange();
+}
+
+void RealtimeMediaSource::setSampleSize(double size)
+{
+    if (m_sampleSize == size || !applySampleSize(size))
+        return;
+
+    m_sampleSize = size;
+    settingsDidChange();
+}
+
+void RealtimeMediaSource::setEchoCancellation(bool echoCancellation)
+{
+    if (m_echoCancellation == echoCancellation || !applyEchoCancellation(echoCancellation))
+        return;
+
+    m_echoCancellation = echoCancellation;
+    settingsDidChange();
+}
+
+void RealtimeMediaSource::scheduleDeferredTask(std::function<void()>&& function)
+{
+    ASSERT(function);
+    callOnMainThread([weakThis = createWeakPtr(), function = WTFMove(function)] {
+        if (!weakThis)
+            return;
+
+        function();
+    });
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(MEDIA_STREAM)
index 0e7288a..8988db5 100644 (file)
 #include "MediaSample.h"
 #include "PlatformLayer.h"
 #include "RealtimeMediaSourceCapabilities.h"
+#include <wtf/Lock.h>
 #include <wtf/RefCounted.h>
 #include <wtf/Vector.h>
+#include <wtf/WeakPtr.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
 class FloatRect;
 class GraphicsContext;
-class MediaConstraints;
 class MediaStreamPrivate;
 class RealtimeMediaSourceSettings;
 
@@ -90,7 +91,12 @@ public:
 
     virtual RefPtr<RealtimeMediaSourceCapabilities> capabilities() = 0;
     virtual const RealtimeMediaSourceSettings& settings() = 0;
-    void settingsDidChange();
+
+    using SuccessHandler = std::function<void()>;
+    using FailureHandler = std::function<void(const String& badConstraint, const String& errorString)>;
+    void applyConstraints(const MediaConstraints&, SuccessHandler, FailureHandler);
+
+    virtual void settingsDidChange();
     void mediaDataUpdated(MediaSample&);
     
     bool stopped() const { return m_stopped; }
@@ -122,23 +128,76 @@ public:
     virtual RefPtr<Image> currentFrameImage() { return nullptr; }
     virtual void paintCurrentFrameInContext(GraphicsContext&, const FloatRect&) { }
 
+    void setWidth(int);
+    void setHeight(int);
+    const IntSize& size() const { return m_size; }
+    virtual bool applySize(const IntSize&) { return false; }
+
+    double frameRate() const { return m_frameRate; }
+    void setFrameRate(double);
+    virtual bool applyFrameRate(double) { return false; }
+
+    double aspectRatio() const { return m_aspectRatio; }
+    void setAspectRatio(double);
+    virtual bool applyAspectRatio(double) { return false; }
+
+    RealtimeMediaSourceSettings::VideoFacingMode facingMode() const { return m_facingMode; }
+    void setFacingMode(RealtimeMediaSourceSettings::VideoFacingMode);
+    virtual bool applyFacingMode(RealtimeMediaSourceSettings::VideoFacingMode) { return false; }
+
+    double volume() const { return m_volume; }
+    void setVolume(double);
+    virtual bool applyVolume(double) { return false; }
+
+    double sampleRate() const { return m_sampleRate; }
+    void setSampleRate(double);
+    virtual bool applySampleRate(double) { return false; }
+
+    double sampleSize() const { return m_sampleSize; }
+    void setSampleSize(double);
+    virtual bool applySampleSize(double) { return false; }
+
+    bool echoCancellation() const { return m_echoCancellation; }
+    void setEchoCancellation(bool);
+    virtual bool applyEchoCancellation(bool) { return false; }
+
 protected:
     RealtimeMediaSource(const String& id, Type, const String& name);
 
+    void scheduleDeferredTask(std::function<void()>&&);
+
     bool m_muted { false };
 
 private:
+    WeakPtr<RealtimeMediaSource> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
+
+    enum ConstraintSupport { Ignored, Supported, Unsupported };
+    ConstraintSupport supportsConstraint(const MediaConstraint&);
+    void applyConstraint(const MediaConstraint&);
+
+    WeakPtrFactory<RealtimeMediaSource> m_weakPtrFactory;
+    Lock m_lock;
+
     String m_id;
     String m_persistentID;
     Type m_type;
     String m_name;
-    bool m_stopped { false };
     Vector<Observer*> m_observers;
+    IntSize m_size;
+    double m_frameRate { 30 };
+    double m_aspectRatio { 0 };
+    double m_volume { 1 };
+    double m_sampleRate { 0 };
+    double m_sampleSize { 0 };
+    unsigned m_fitnessScore { 0 };
+    RealtimeMediaSourceSettings::VideoFacingMode m_facingMode { RealtimeMediaSourceSettings::User};
 
+    bool m_echoCancellation { false };
+    bool m_stopped { false };
     bool m_readonly { false };
     bool m_remote { false };
-    
-    unsigned m_fitnessScore { 0 };
+    bool m_pendingSettingsDidChangeNotification { false };
+    bool m_suppressNotifications { true };
 };
 
 } // namespace WebCore
index ac1785d..2f22d48 100644 (file)
@@ -48,7 +48,7 @@ public:
     Type type() const { return m_type; }
 
     union ValueUnion {
-        unsigned long asULong;
+        int asInt;
         double asDouble;
     };
 
@@ -63,10 +63,10 @@ public:
         m_minOrValue.asDouble = value;
     }
 
-    CapabilityValueOrRange(unsigned long value)
+    CapabilityValueOrRange(int value)
         : m_type(ULong)
     {
-        m_minOrValue.asULong = value;
+        m_minOrValue.asInt = value;
     }
 
     CapabilityValueOrRange(double min, double max)
@@ -76,11 +76,11 @@ public:
         m_max.asDouble = max;
     }
     
-    CapabilityValueOrRange(unsigned long min, unsigned long max)
+    CapabilityValueOrRange(int min, int max)
         : m_type(ULongRange)
     {
-        m_minOrValue.asULong = min;
-        m_max.asULong = max;
+        m_minOrValue.asInt = min;
+        m_max.asInt = max;
     }
 
     const ValueUnion& rangeMin() const
index aebd61d..7872901 100644 (file)
 
 namespace WebCore {
 
-const AtomicString& RealtimeMediaSourceSettings::facingMode(RealtimeMediaSourceSettings::VideoFacingMode mode)
+static const AtomicString& userFacing()
 {
     static NeverDestroyed<AtomicString> userFacing("user", AtomicString::ConstructFromLiteral);
+    return userFacing;
+}
+static const AtomicString& environmentFacing()
+{
     static NeverDestroyed<AtomicString> environmentFacing("environment", AtomicString::ConstructFromLiteral);
+    return environmentFacing;
+}
+
+static const AtomicString& leftFacing()
+{
     static NeverDestroyed<AtomicString> leftFacing("left", AtomicString::ConstructFromLiteral);
+    return leftFacing;
+}
+
+static const AtomicString& rightFacing()
+{
     static NeverDestroyed<AtomicString> rightFacing("right", AtomicString::ConstructFromLiteral);
-    
+    return rightFacing;
+}
+
+const AtomicString& RealtimeMediaSourceSettings::facingMode(RealtimeMediaSourceSettings::VideoFacingMode mode)
+{
     switch (mode) {
     case RealtimeMediaSourceSettings::User:
-        return userFacing;
+        return userFacing();
     case RealtimeMediaSourceSettings::Environment:
-        return environmentFacing;
+        return environmentFacing();
     case RealtimeMediaSourceSettings::Left:
-        return leftFacing;
+        return leftFacing();
     case RealtimeMediaSourceSettings::Right:
-        return rightFacing;
+        return rightFacing();
     case RealtimeMediaSourceSettings::Unknown:
         return emptyAtom;
     }
@@ -61,6 +79,20 @@ const AtomicString& RealtimeMediaSourceSettings::facingMode(RealtimeMediaSourceS
     return emptyAtom;
 }
 
+RealtimeMediaSourceSettings::VideoFacingMode RealtimeMediaSourceSettings::videoFacingModeEnum(const String& mode)
+{
+    if (mode == userFacing())
+        return RealtimeMediaSourceSettings::User;
+    if (mode == environmentFacing())
+        return RealtimeMediaSourceSettings::Environment;
+    if (mode == leftFacing())
+        return RealtimeMediaSourceSettings::Left;
+    if (mode == rightFacing())
+        return RealtimeMediaSourceSettings::Right;
+
+    return RealtimeMediaSourceSettings::Unknown;
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(MEDIA_STREAM)
index 7e74925..7b5dc71 100644 (file)
@@ -42,6 +42,8 @@ public:
 
     static const AtomicString& facingMode(RealtimeMediaSourceSettings::VideoFacingMode);
 
+    static RealtimeMediaSourceSettings::VideoFacingMode videoFacingModeEnum(const String&);
+
     explicit RealtimeMediaSourceSettings()
     {
     }
index e68c222..4f91baa 100644 (file)
@@ -80,7 +80,7 @@ AVAudioCaptureSource::~AVAudioCaptureSource()
 void AVAudioCaptureSource::initializeCapabilities(RealtimeMediaSourceCapabilities& capabilities)
 {
     // FIXME: finish this implementation - https://webkit.org/b/122430
-    capabilities.setVolume(CapabilityValueOrRange(0, 1.0));
+    capabilities.setVolume(CapabilityValueOrRange(0.0, 1.0));
 }
 
 void AVAudioCaptureSource::initializeSupportedConstraints(RealtimeMediaSourceSupportedConstraints& supportedConstraints)
index 74d7ffc..d05abcc 100644 (file)
@@ -33,7 +33,6 @@
 #include "Timer.h"
 #include <wtf/Function.h>
 #include <wtf/RetainPtr.h>
-#include <wtf/WeakPtr.h>
 
 OBJC_CLASS AVCaptureAudioDataOutput;
 OBJC_CLASS AVCaptureConnection;
@@ -63,8 +62,6 @@ public:
     void stopProducingData() override;
     bool isProducingData() const override { return m_isRunning; }
 
-    WeakPtr<AVMediaCaptureSource> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
-
 protected:
     AVMediaCaptureSource(AVCaptureDevice*, const AtomicString&, RealtimeMediaSource::Type, PassRefPtr<MediaConstraints>);
 
@@ -86,15 +83,12 @@ protected:
     void setVideoSampleBufferDelegate(AVCaptureVideoDataOutput*);
     void setAudioSampleBufferDelegate(AVCaptureAudioDataOutput*);
 
-    void scheduleDeferredTask(Function<void ()>&&);
-
 private:
     void setupSession();
     void reset() override;
 
     RealtimeMediaSourceSettings m_currentSettings;
     RealtimeMediaSourceSupportedConstraints m_supportedConstraints;
-    WeakPtrFactory<AVMediaCaptureSource> m_weakPtrFactory;
     RetainPtr<WebCoreAVMediaCaptureSourceObserver> m_objcObserver;
     RefPtr<MediaConstraints> m_constraints;
     RefPtr<RealtimeMediaSourceCapabilities> m_capabilities;
index b6c0fac..a91ccb8 100644 (file)
@@ -124,7 +124,6 @@ static dispatch_queue_t globaVideoCaptureSerialQueue()
 
 AVMediaCaptureSource::AVMediaCaptureSource(AVCaptureDeviceTypedef* device, const AtomicString& id, RealtimeMediaSource::Type type, PassRefPtr<MediaConstraints> constraints)
     : RealtimeMediaSource(id, type, emptyString())
-    , m_weakPtrFactory(this)
     , m_objcObserver(adoptNS([[WebCoreAVMediaCaptureSourceObserver alloc] initWithCallback:this]))
     , m_constraints(constraints)
     , m_device(device)
@@ -239,17 +238,6 @@ void AVMediaCaptureSource::setAudioSampleBufferDelegate(AVCaptureAudioDataOutput
     [audioOutput setSampleBufferDelegate:m_objcObserver.get() queue:globaAudioCaptureSerialQueue()];
 }
 
-void AVMediaCaptureSource::scheduleDeferredTask(Function<void ()>&& function)
-{
-    ASSERT(function);
-    callOnMainThread([weakThis = createWeakPtr(), function = WTFMove(function)] {
-        if (!weakThis)
-            return;
-
-        function();
-    });
-}
-
 AudioSourceProvider* AVMediaCaptureSource::audioSourceProvider()
 {
     ASSERT_NOT_REACHED();
index 247e3bf..e5bf501 100644 (file)
@@ -57,10 +57,12 @@ private:
 
     void updateSettings(RealtimeMediaSourceSettings&) override;
 
+    bool applySize(const IntSize&) override;
+    bool applyFrameRate(double) override;
+
     void initializeCapabilities(RealtimeMediaSourceCapabilities&) override;
     void initializeSupportedConstraints(RealtimeMediaSourceSupportedConstraints&) override;
 
-    bool applyConstraints(MediaConstraints*);
     bool setFrameRateConstraint(double minFrameRate, double maxFrameRate);
 
     bool updateFramerate(CMSampleBufferRef);
index 99dee54..818d2ba 100644 (file)
@@ -128,91 +128,64 @@ void AVVideoCaptureSource::updateSettings(RealtimeMediaSourceSettings& settings)
     settings.setAspectRatio(static_cast<float>(m_width) / m_height);
 }
 
-bool AVVideoCaptureSource::setFrameRateConstraint(double minFrameRate, double maxFrameRate)
+bool AVVideoCaptureSource::applySize(const IntSize& size)
 {
-    AVFrameRateRange *bestFrameRateRange = 0;
-
-    for (AVFrameRateRange *frameRateRange in [[device() activeFormat] videoSupportedFrameRateRanges]) {
-        if (!maxFrameRate) {
-            if (minFrameRate == [frameRateRange minFrameRate])
-                bestFrameRateRange = frameRateRange;
-        } else if (minFrameRate >= [frameRateRange minFrameRate] && maxFrameRate <= [frameRateRange maxFrameRate]) {
-            if (CMTIME_COMPARE_INLINE([frameRateRange minFrameDuration], >, [bestFrameRateRange minFrameDuration]))
-                bestFrameRateRange = frameRateRange;
-        }
-    }
-    
-    if (!bestFrameRateRange) {
-        LOG(Media, "AVVideoCaptureSource::setFrameRateConstraint(%p), frame rate range %f..%f not supported by video device", this, minFrameRate, maxFrameRate);
+    NSString *preset = AVCaptureSessionInfo(session()).bestSessionPresetForVideoDimensions(size.width(), size.height());
+    if (!preset || ![session() canSetSessionPreset:preset]) {
+        LOG(Media, "AVVideoCaptureSource::applySize%p), unable find or set preset for width: %i, height: %i", this, size.width(), size.height());
         return false;
     }
-    
+
     NSError *error = nil;
     @try {
-        if ([device() lockForConfiguration:&error]) {
-            [device() setActiveVideoMinFrameDuration:[bestFrameRateRange minFrameDuration]];
-            if (maxFrameRate)
-                [device() setActiveVideoMaxFrameDuration:[bestFrameRateRange maxFrameDuration]];
-            [device() unlockForConfiguration];
-        }
+        [session() setSessionPreset:preset];
+
     } @catch(NSException *exception) {
-        LOG(Media, "AVVideoCaptureSource::setFrameRateConstraint(%p), exception thrown configuring device: <%s> %s", this, [[exception name] UTF8String], [[exception reason] UTF8String]);
+        LOG(Media, "AVVideoCaptureSource::applySize(%p), exception thrown configuring device: <%s> %s", this, [[exception name] UTF8String], [[exception reason] UTF8String]);
         return false;
     }
-    
+
     if (error) {
-        LOG(Media, "AVVideoCaptureSource::setFrameRateConstraint(%p), failed to lock video device for configuration: %s", this, [[error localizedDescription] UTF8String]);
+        LOG(Media, "AVVideoCaptureSource::applySize(%p), failed to lock video device for configuration: %s", this, [[error localizedDescription] UTF8String]);
         return false;
     }
 
-    LOG(Media, "AVVideoCaptureSource::setFrameRateConstraint(%p) - set frame rate range to %f..%f", this, minFrameRate, maxFrameRate);
+    LOG(Media, "AVVideoCaptureSource::applySize(%p) - set frame size to %i x %i", this, size.width(), size.height());
     return true;
 }
 
-bool AVVideoCaptureSource::applyConstraints(MediaConstraints* constraints)
+bool AVVideoCaptureSource::applyFrameRate(double rate)
 {
-    ASSERT(constraints);
-
-    // FIXME: Below needs to be refactored for https://bugs.webkit.org/show_bug.cgi?id=160579.
-
-    auto& mandatoryConstraints = constraints->mandatoryConstraints();
-    int intValue;
-
-    String widthConstraintName = RealtimeMediaSourceSupportedConstraints::nameForConstraint(MediaConstraintType::Width);
-    auto widthConstraint = mandatoryConstraints.get(widthConstraintName);
-    Optional<int> width;
-    if (widthConstraint && widthConstraint->getExact(intValue))
-        width = intValue;
-
-    String heightConstraintName = RealtimeMediaSourceSupportedConstraints::nameForConstraint(MediaConstraintType::Height);
-    auto heightConstraint = mandatoryConstraints.get(heightConstraintName);
-    Optional<int> height;
-    if (heightConstraint && heightConstraint->getExact(intValue))
-        height = intValue;
-    
-    if (width && height) {
-        NSString *preset = AVCaptureSessionInfo(session()).bestSessionPresetForVideoDimensions(width.value(), height.value());
-        if (!preset || ![session() canSetSessionPreset:preset]) {
-            LOG(Media, "AVVideoCaptureSource::applyConstraints(%p), unable find or set preset for width: %i, height: %i", this, width.value(), height.value());
-            return false;
+    AVFrameRateRange *bestFrameRateRange = 0;
+    for (AVFrameRateRange *frameRateRange in [[device() activeFormat] videoSupportedFrameRateRanges]) {
+        if (rate >= [frameRateRange minFrameRate] && rate <= [frameRateRange maxFrameRate]) {
+            if (!bestFrameRateRange || CMTIME_COMPARE_INLINE([frameRateRange minFrameDuration], >, [bestFrameRateRange minFrameDuration]))
+                bestFrameRateRange = frameRateRange;
         }
-
-        [session() setSessionPreset:preset];
     }
 
-    String frameRateConstraintName = RealtimeMediaSourceSupportedConstraints::nameForConstraint(MediaConstraintType::FrameRate);
-    auto frameRateConstraint = mandatoryConstraints.get(frameRateConstraintName);
+    if (!bestFrameRateRange) {
+        LOG(Media, "AVVideoCaptureSource::applyFrameRate(%p), frame rate %f not supported by video device", this, rate);
+        return false;
+    }
 
-    Optional<double> frameRate;
-    double doubleValue;
-    if (frameRateConstraint && frameRateConstraint->getExact(doubleValue))
-        frameRate = doubleValue;
+    NSError *error = nil;
+    @try {
+        if ([device() lockForConfiguration:&error]) {
+            [device() setActiveVideoMinFrameDuration:[bestFrameRateRange minFrameDuration]];
+            [device() unlockForConfiguration];
+        }
+    } @catch(NSException *exception) {
+        LOG(Media, "AVVideoCaptureSource::applyFrameRate(%p), exception thrown configuring device: <%s> %s", this, [[exception name] UTF8String], [[exception reason] UTF8String]);
+        return false;
+    }
 
-    if (frameRate && !setFrameRateConstraint(frameRate.value(), 0)) {
-        LOG(Media, "AVVideoCaptureSource::applyConstraints(%p), unable set frame rate to %f", this, frameRate.value());
+    if (error) {
+        LOG(Media, "AVVideoCaptureSource::applyFrameRate(%p), failed to lock video device for configuration: %s", this, [[error localizedDescription] UTF8String]);
         return false;
     }
 
+    LOG(Media, "AVVideoCaptureSource::applyFrameRate(%p) - set frame rate range to %f", this, rate);
     return true;
 }
 
@@ -227,9 +200,6 @@ void AVVideoCaptureSource::setupCaptureSession()
 
     [session() addInput:videoIn.get()];
 
-    if (constraints())
-        applyConstraints(constraints());
-
     RetainPtr<AVCaptureVideoDataOutputType> videoOutput = adoptNS([allocAVCaptureVideoDataOutputInstance() init]);
     RetainPtr<NSDictionary> settingsDictionary = adoptNS([[NSDictionary alloc] initWithObjectsAndKeys:
                                                          [NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey
index 2e9363e..c4f3c87 100644 (file)
@@ -59,12 +59,13 @@ MockRealtimeAudioSource::MockRealtimeAudioSource(const String& name)
 
 void MockRealtimeAudioSource::updateSettings(RealtimeMediaSourceSettings& settings)
 {
-    settings.setVolume(50);
+    settings.setVolume(volume());
+    settings.setEchoCancellation(echoCancellation());
 }
 
 void MockRealtimeAudioSource::initializeCapabilities(RealtimeMediaSourceCapabilities& capabilities)
 {
-    capabilities.setVolume(CapabilityValueOrRange(0, 1.0));
+    capabilities.setVolume(CapabilityValueOrRange(0.0, 1.0));
     capabilities.setEchoCancellation(RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite);
 }
 
index 1fcb868..e338eae 100644 (file)
@@ -51,6 +51,12 @@ protected:
     MockRealtimeAudioSource(const String& name = ASCIILiteral("Mock audio device"));
 
 private:
+
+    bool applyVolume(double) override { return true; }
+    bool applySampleRate(double) override { return true; }
+    bool applySampleSize(double) override { return true; }
+    bool applyEchoCancellation(bool) override { return true; }
+
     void updateSettings(RealtimeMediaSourceSettings&) override;
     void initializeCapabilities(RealtimeMediaSourceCapabilities&) override;
     void initializeSupportedConstraints(RealtimeMediaSourceSupportedConstraints&) override;
index 1a8b883..a36a469 100644 (file)
@@ -124,7 +124,6 @@ RealtimeMediaSourceSupportedConstraints& MockRealtimeMediaSource::supportedConst
     return m_supportedConstraints;
 }
 
-
 } // namespace WebCore
 
 #endif // ENABLE(MEDIA_STREAM)
index 89b44dc..0af3417 100644 (file)
@@ -37,8 +37,6 @@
 
 namespace WebCore {
 
-class FloatRect;
-class GraphicsContext;
 class TrackSourceInfo;
 
 class MockRealtimeMediaSource : public RealtimeMediaSource {
index 2e2d95c..014de46 100644 (file)
@@ -65,6 +65,7 @@ MockRealtimeVideoSource::MockRealtimeVideoSource(const String& name)
     : MockRealtimeMediaSource(createCanonicalUUIDString(), RealtimeMediaSource::Video, name)
     , m_timer(RunLoop::current(), this, &MockRealtimeVideoSource::generateFrame)
 {
+    setFrameRate(30);
     m_dashWidths.reserveInitialCapacity(2);
     m_dashWidths.uncheckedAppend(6);
     m_dashWidths.uncheckedAppend(6);
@@ -73,11 +74,13 @@ MockRealtimeVideoSource::MockRealtimeVideoSource(const String& name)
 void MockRealtimeVideoSource::startProducingData()
 {
     MockRealtimeMediaSource::startProducingData();
-    if (m_size.isEmpty())
-        setSize(IntSize(640, 480));
+    if (size().isEmpty()) {
+        setWidth(640);
+        setHeight(480);
+    }
 
     m_startTime = monotonicallyIncreasingTime();
-    m_timer.startRepeating(std::chrono::milliseconds(lround(1000 / m_frameRate)));
+    m_timer.startRepeating(std::chrono::milliseconds(lround(1000 / frameRate())));
 }
 
 void MockRealtimeVideoSource::stopProducingData()
@@ -98,19 +101,21 @@ double MockRealtimeVideoSource::elapsedTime()
 
 void MockRealtimeVideoSource::updateSettings(RealtimeMediaSourceSettings& settings)
 {
-    settings.setFacingMode(RealtimeMediaSourceSettings::User);
-    settings.setFrameRate(m_frameRate);
-    settings.setWidth(m_size.width());
-    settings.setHeight(m_size.height());
-    settings.setAspectRatio(static_cast<float>(m_size.width()) / m_size.height());
+    settings.setFacingMode(facingMode());
+    settings.setFrameRate(frameRate());
+    IntSize size = this->size();
+    settings.setWidth(size.width());
+    settings.setHeight(size.height());
+    if (aspectRatio())
+        settings.setAspectRatio(aspectRatio());
 }
 
 void MockRealtimeVideoSource::initializeCapabilities(RealtimeMediaSourceCapabilities& capabilities)
 {
     capabilities.addFacingMode(RealtimeMediaSourceSettings::User);
     capabilities.addFacingMode(RealtimeMediaSourceSettings::Environment);
-    capabilities.setWidth(CapabilityValueOrRange(320UL, 1920UL));
-    capabilities.setHeight(CapabilityValueOrRange(240UL, 1080UL));
+    capabilities.setWidth(CapabilityValueOrRange(320, 1920));
+    capabilities.setHeight(CapabilityValueOrRange(240, 1080));
     capabilities.setFrameRate(CapabilityValueOrRange(15.0, 60.0));
     capabilities.setAspectRatio(CapabilityValueOrRange(4 / 3.0, 16 / 9.0));
 }
@@ -124,26 +129,19 @@ void MockRealtimeVideoSource::initializeSupportedConstraints(RealtimeMediaSource
     supportedConstraints.setSupportsFacingMode(true);
 }
 
-void MockRealtimeVideoSource::setFrameRate(float rate)
+bool MockRealtimeVideoSource::applyFrameRate(double rate)
 {
-    if (m_frameRate == rate)
-        return;
-
-    m_frameRate = rate;
     if (m_timer.isActive())
-        m_timer.startRepeating(std::chrono::milliseconds(lround(1000 / m_frameRate)));
+        m_timer.startRepeating(std::chrono::milliseconds(lround(1000 / rate)));
 
-    settingsDidChange();
+    updatePlatformLayer();
+    updateSampleBuffer();
+    return true;
 }
 
-void MockRealtimeVideoSource::setSize(const IntSize& size)
+bool MockRealtimeVideoSource::applySize(const IntSize& size)
 {
-    if (size == m_size)
-        return;
-
-    m_size = size;
-
-    m_baseFontSize = m_size.height() * .08;
+    m_baseFontSize = size.height() * .08;
     FontCascadeDescription fontDescription;
     fontDescription.setOneFamily("Courier");
     fontDescription.setSpecifiedSize(m_baseFontSize);
@@ -168,13 +166,13 @@ void MockRealtimeVideoSource::setSize(const IntSize& size)
     m_imageBuffer = nullptr;
     updatePlatformLayer();
 
-    settingsDidChange();
+    return true;
 }
 
 void MockRealtimeVideoSource::drawAnimation(GraphicsContext& context)
 {
-    float radius = m_size.width() * .09;
-    FloatPoint location(m_size.width() * .8, m_size.height() * .3);
+    float radius = size().width() * .09;
+    FloatPoint location(size().width() * .8, size().height() * .3);
 
     m_path.clear();
     m_path.moveTo(location);
@@ -184,7 +182,7 @@ void MockRealtimeVideoSource::drawAnimation(GraphicsContext& context)
     context.setFillRule(RULE_NONZERO);
     context.fillPath(m_path);
 
-    float endAngle = piFloat * (((fmod(m_frameNumber, m_frameRate) + 0.5) * (2.0 / m_frameRate)) + 1);
+    float endAngle = piFloat * (((fmod(m_frameNumber, frameRate()) + 0.5) * (2.0 / frameRate())) + 1);
     m_path.clear();
     m_path.moveTo(location);
     m_path.addArc(location, radius, 1.5 * piFloat, endAngle, false);
@@ -202,11 +200,12 @@ void MockRealtimeVideoSource::drawBoxes(GraphicsContext& context)
     static const RGBA32 red = 0xffff0000;
     static const RGBA32 green = 0xff008000;
 
-    float boxSize = m_size.width() * .035;
-    float boxTop = m_size.height() * .6;
+    IntSize size = this->size();
+    float boxSize = size.width() * .035;
+    float boxTop = size.height() * .6;
 
     m_path.clear();
-    FloatRect frameRect(2, 2, m_size.width() - 3, m_size.height() - 3);
+    FloatRect frameRect(2, 2, size.width() - 3, size.height() - 3);
     context.setStrokeColor(Color::white);
     context.setStrokeThickness(3);
     context.setLineDash(m_dashWidths, 0);
@@ -217,7 +216,7 @@ void MockRealtimeVideoSource::drawBoxes(GraphicsContext& context)
     context.setLineDash(DashArray(), 0);
     m_path.clear();
     m_path.moveTo(FloatPoint(0, boxTop + boxSize));
-    m_path.addLineTo(FloatPoint(m_size.width(), boxTop + boxSize));
+    m_path.addLineTo(FloatPoint(size.width(), boxTop + boxSize));
     m_path.closeSubpath();
     context.setStrokeColor(Color::white);
     context.setStrokeThickness(2);
@@ -264,7 +263,8 @@ void MockRealtimeVideoSource::drawText(GraphicsContext& context)
     unsigned minutes = seconds / 60 % 60;
     unsigned hours = minutes / 60 % 60;
 
-    FloatPoint timeLocation(m_size.width() * .05, m_size.height() * .15);
+    IntSize size = this->size();
+    FloatPoint timeLocation(size.width() * .05, size.height() * .15);
     context.setFillColor(Color::white);
     context.setTextDrawingMode(TextModeFill);
     String string = String::format("%02u:%02u:%02u.%03u", hours, minutes, seconds, milliseconds % 1000);
@@ -274,16 +274,16 @@ void MockRealtimeVideoSource::drawText(GraphicsContext& context)
     timeLocation.move(0, m_baseFontSize);
     context.drawText(m_timeFont, TextRun((StringView(string))), timeLocation);
 
-    FloatPoint statsLocation(m_size.width() * .65, m_size.height() * .75);
-    string = String::format("Frame rate: %ufps", m_frameRate);
+    FloatPoint statsLocation(size.width() * .65, size.height() * .75);
+    string = String::format("Frame rate: %ffps", frameRate());
     context.drawText(m_statsFont, TextRun((StringView(string))), statsLocation);
 
-    string = String::format("Size: %u x %u", m_size.width(), m_size.height());
+    string = String::format("Size: %u x %u", size.width(), size.height());
     statsLocation.move(0, m_statsFontSize);
     context.drawText(m_statsFont, TextRun((StringView(string))), statsLocation);
 
     const char* camera;
-    switch (settings().facingMode()) {
+    switch (facingMode()) {
     case RealtimeMediaSourceSettings::User:
         camera = "User facing";
         break;
@@ -304,7 +304,7 @@ void MockRealtimeVideoSource::drawText(GraphicsContext& context)
     statsLocation.move(0, m_statsFontSize);
     context.drawText(m_statsFont, TextRun((StringView(string))), statsLocation);
 
-    FloatPoint bipBopLocation(m_size.width() * .6, m_size.height() * .6);
+    FloatPoint bipBopLocation(size.width() * .6, size.height() * .6);
     unsigned frameMod = m_frameNumber % 60;
     if (frameMod <= 15) {
         context.setFillColor(Color::gray);
@@ -319,11 +319,16 @@ void MockRealtimeVideoSource::drawText(GraphicsContext& context)
 
 void MockRealtimeVideoSource::generateFrame()
 {
-    GraphicsContext& context = imageBuffer()->context();
+    ImageBuffer* buffer = imageBuffer();
+    if (!buffer)
+        return;
+
+    GraphicsContext& context = buffer->context();
     GraphicsContextStateSaver stateSaver(context);
 
-    FloatRect frameRect(FloatPoint(), m_size);
-    context.fillRect(FloatRect(FloatPoint(), m_size), Color::black);
+    IntSize size = this->size();
+    FloatRect frameRect(FloatPoint(), size);
+    context.fillRect(FloatRect(FloatPoint(), size), Color::black);
 
     drawText(context);
     drawAnimation(context);
@@ -338,7 +343,7 @@ ImageBuffer* MockRealtimeVideoSource::imageBuffer() const
     if (m_imageBuffer)
         return m_imageBuffer.get();
 
-    m_imageBuffer = ImageBuffer::create(m_size, Unaccelerated);
+    m_imageBuffer = ImageBuffer::create(size(), Unaccelerated);
     if (!m_imageBuffer)
         return nullptr;
 
@@ -368,7 +373,6 @@ RefPtr<Image> MockRealtimeVideoSource::currentFrameImage()
     return m_imageBuffer->copyImage(DontCopyBackingStore);
 }
 
-
 } // namespace WebCore
 
 #endif // ENABLE(MEDIA_STREAM)
index 4cd364c..a79bea2 100644 (file)
@@ -51,11 +51,6 @@ public:
 
     virtual ~MockRealtimeVideoSource() { }
 
-    void setSize(const IntSize&);
-    const IntSize& size() const { return m_size; }
-
-    void setFrameRate(float);
-
 protected:
     MockRealtimeVideoSource(const String& name = ASCIILiteral("Mock video device"));
     virtual void updatePlatformLayer() const { }
@@ -70,13 +65,18 @@ private:
     void initializeCapabilities(RealtimeMediaSourceCapabilities&) override;
     void initializeSupportedConstraints(RealtimeMediaSourceSupportedConstraints&) override;
 
-    void startProducingData() override;
-    void stopProducingData() override;
+    void startProducingData() final;
+    void stopProducingData() final;
 
     void drawAnimation(GraphicsContext&);
     void drawText(GraphicsContext&);
     void drawBoxes(GraphicsContext&);
 
+    bool applySize(const IntSize&) override;
+    bool applyFrameRate(double) override;
+    bool applyFacingMode(RealtimeMediaSourceSettings::VideoFacingMode) override { return true; }
+    bool applyAspectRatio(double) override { return true; }
+
     PlatformLayer* platformLayer() const override { return nullptr; }
     RefPtr<Image> currentFrameImage() override;
     void paintCurrentFrameInContext(GraphicsContext&, const FloatRect&) override;
@@ -94,14 +94,12 @@ private:
 
     mutable std::unique_ptr<ImageBuffer> m_imageBuffer;
 
-    IntSize m_size;
     Path m_path;
     DashArray m_dashWidths;
 
     double m_startTime { NAN };
     double m_elapsedTime { 0 };
 
-    unsigned m_frameRate { 30 };
     unsigned m_frameNumber { 0 };
 
     RunLoop::Timer<MockRealtimeVideoSource> m_timer;