1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
<!DOCTYPE HTML>
<html>
<head>
<script src="mediaStreamPlayback.js"></script>
<script src="constraints.js"></script>
</head>
<body>
<pre id="test">
<script type="application/javascript">
createHTML({ title: "Test getUserMedia constraints", bug: "882145" });
/**
Tests covering gUM constraints API for audio, video and fake video. Exercise
successful parsing code and ensure that unknown required constraints and
overconstraining cases produce appropriate errors.
*/
var tests = [
// Each test here tests a different constraint or codepath.
{ message: "unknown required constraint on video ignored",
constraints: { video: { somethingUnknown: { exact: 0 } } },
error: null },
{ message: "unknown required constraint on audio ignored",
constraints: { audio: { somethingUnknown: { exact: 0 } } },
error: null },
{ message: "audio overconstrained by facingMode ignored",
constraints: { audio: { facingMode: { exact: 'left' } } },
error: null },
{ message: "full screensharing requires permission",
constraints: { video: { mediaSource: 'screen' } },
error: "NotAllowedError" },
{ message: "application screensharing no longer exists",
constraints: { video: { mediaSource: 'application' } },
error: "OverconstrainedError" },
{ message: "window screensharing requires permission",
constraints: { video: { mediaSource: 'window' } },
error: "NotAllowedError" },
{ message: "browser screensharing requires permission",
constraints: { video: { mediaSource: 'browser' } },
error: "NotAllowedError" },
{ message: "unknown mediaSource in video fails",
constraints: { video: { mediaSource: 'uncle' } },
error: "OverconstrainedError",
constraint: "mediaSource" },
{ message: "unknown mediaSource in audio fails",
constraints: { audio: { mediaSource: 'uncle' } },
error: "OverconstrainedError",
constraint: "mediaSource" },
{ message: "emtpy constraint fails",
constraints: { },
error: "TypeError" },
{ message: "Triggering mock failure in default video device fails",
constraints: { video: { deviceId: 'bad device' } },
error: "NotReadableError" },
{ message: "Triggering mock failure in default audio device fails",
constraints: { audio: { deviceId: 'bad device' } },
error: "NotReadableError" },
{ message: "Success-path: optional video facingMode + audio ignoring facingMode",
constraints: { audio: { mediaSource: 'microphone',
facingMode: 'left',
foo: 0,
advanced: [{ facingMode: 'environment' },
{ facingMode: 'user' },
{ bar: 0 }] },
video: { mediaSource: 'camera',
foo: 0,
advanced: [{ facingMode: 'environment' },
{ facingMode: ['user'] },
{ facingMode: ['left', 'right', 'user'] },
{ bar: 0 }] } },
error: null },
{ message: "legacy facingMode ignored",
constraints: { video: { mandatory: { facingMode: 'left' } } },
error: null },
];
var mustSupport = [
'width', 'height', 'frameRate', 'facingMode', 'deviceId', 'groupId',
'echoCancellation', 'noiseSuppression', 'autoGainControl', 'channelCount',
// Yet to add:
// 'aspectRatio', 'volume', 'sampleRate', 'sampleSize', 'latency'
// http://fluffy.github.io/w3c-screen-share/#screen-based-video-constraints
// OBE by http://w3c.github.io/mediacapture-screen-share
'mediaSource',
// Experimental https://bugzilla.mozilla.org/show_bug.cgi?id=1131568#c3
'browserWindow', 'scrollWithPage',
'viewportOffsetX', 'viewportOffsetY', 'viewportWidth', 'viewportHeight',
];
var mustFailWith = (msg, reason, constraint, f) =>
f().then(() => ok(false, msg + " must fail"), e => {
is(e.name, reason, msg + " must fail: " + e.message);
if (constraint !== undefined) {
is(e.constraint, constraint, msg + " must fail w/correct constraint.");
}
});
/**
* Starts the test run by running through each constraint
* test by verifying that the right resolution and rejection is fired.
*/
runTest(() => pushPrefs(
// This test expects fake devices, particularly for the 'triggering mock
// failure *' steps. So explicitly disable loopback and setup fakes
['media.audio_loopback_dev', ''],
['media.video_loopback_dev', ''],
['media.navigator.streams.fake', true]
)
.then(() => {
// Check supported constraints first.
var dict = navigator.mediaDevices.getSupportedConstraints();
var supported = Object.keys(dict);
mustSupport.forEach(key => ok(supported.includes(key) && dict[key],
"Supports " + key));
var unexpected = supported.filter(key => !mustSupport.includes(key));
is(unexpected.length, 0,
"Unanticipated support (please update test): " + unexpected);
})
.then(() => pushPrefs(["media.getusermedia.browser.enabled", false],
["media.getusermedia.screensharing.enabled", false]))
.then(() => tests.reduce((p, test) => p.then(
() => {
SpecialPowers.wrap(document).notifyUserGestureActivation();
return getUserMedia(test.constraints);
})
.then(stream => {
is(null, test.error, test.message);
stream.getTracks().forEach(t => t.stop());
}, e => {
is(e.name, test.error, test.message + ": " + e.message);
if (test.constraint) {
is(e.constraint, test.constraint,
test.message + " w/correct constraint.");
}
}), Promise.resolve()))
.then(() => getUserMedia({video: true, audio: true}))
.then(stream => stream.getVideoTracks()[0].applyConstraints({ width: 320 })
.then(() => stream.getAudioTracks()[0].applyConstraints({ }))
.then(() => {
stream.getTracks().forEach(track => track.stop());
ok(true, "applyConstraints code exercised");
}))
// TODO: Test outcome once fake devices support constraints (Bug 1088621)
.then(() => mustFailWith("applyConstraints fails on non-Gum tracks",
"OverconstrainedError", "",
() => (new AudioContext())
.createMediaStreamDestination().stream
.getAudioTracks()[0].applyConstraints()))
.then(() => mustFailWith(
"getUserMedia with unsatisfied required constraint",
"OverconstrainedError", "deviceId",
() => getUserMedia({ audio: true,
video: { deviceId: { exact: "unheardof" } } })))
.then(() => mustFailWith(
"getUserMedia with unsatisfied required constraint array",
"OverconstrainedError", "deviceId",
() => getUserMedia({ audio: true,
video: { deviceId: { exact: ["a", "b"] } } }))));
</script>
</pre>
</body>
</html>
|