AX: <svg> elements with labels and no accessible contents are exposed as empty AXGroups
[WebKit-https.git] / LayoutTests / webrtc / routines.js
1 // Test inspired from https://webrtc.github.io/samples/
2 var localConnection;
3 var remoteConnection;
4
5 function createConnections(setupLocalConnection, setupRemoteConnection, options = { }) {
6     localConnection = new RTCPeerConnection();
7     remoteConnection = new RTCPeerConnection();
8     remoteConnection.onicecandidate = (event) => { iceCallback2(event, options.filterOutICECandidate) };
9
10     localConnection.onicecandidate = (event) => { iceCallback1(event, options.filterOutICECandidate) };
11
12     Promise.resolve(setupLocalConnection(localConnection)).then(() => {
13         return Promise.resolve(setupRemoteConnection(remoteConnection));
14     }).then(() => {
15         localConnection.createOffer().then((desc) => gotDescription1(desc, options), onCreateSessionDescriptionError);
16     });
17
18     return [localConnection, remoteConnection]
19 }
20
21 function closeConnections()
22 {
23     localConnection.close();
24     remoteConnection.close();
25 }
26
27 function onCreateSessionDescriptionError(error)
28 {
29     assert_unreached();
30 }
31
32 function gotDescription1(desc, options)
33 {
34     if (options.observeOffer) {
35         const result = options.observeOffer(desc);
36         if (result)
37             desc = result;
38     }
39
40     localConnection.setLocalDescription(desc);
41     remoteConnection.setRemoteDescription(desc).then(() => {
42         remoteConnection.createAnswer().then((desc) => gotDescription2(desc, options), onCreateSessionDescriptionError);
43     });
44 }
45
46 function gotDescription2(desc, options)
47 {
48     if (options.observeAnswer)
49         options.observeAnswer(desc);
50
51     remoteConnection.setLocalDescription(desc);
52     localConnection.setRemoteDescription(desc);
53 }
54
55 function iceCallback1(event, filterOutICECandidate)
56 {
57     if (filterOutICECandidate && filterOutICECandidate(event.candidate))
58         return;
59
60     remoteConnection.addIceCandidate(event.candidate).then(onAddIceCandidateSuccess, onAddIceCandidateError);
61 }
62
63 function iceCallback2(event, filterOutICECandidate)
64 {
65     if (filterOutICECandidate && filterOutICECandidate(event.candidate))
66         return;
67
68     localConnection.addIceCandidate(event.candidate).then(onAddIceCandidateSuccess, onAddIceCandidateError);
69 }
70
71 function onAddIceCandidateSuccess()
72 {
73 }
74
75 function onAddIceCandidateError(error)
76 {
77     console.log("addIceCandidate error: " + error)
78     assert_unreached();
79 }
80
81 function analyseAudio(stream, duration, context)
82 {
83     return new Promise((resolve, reject) => {
84         var sourceNode = context.createMediaStreamSource(stream);
85
86         var analyser = context.createAnalyser();
87         var gain = context.createGain();
88
89         var results = { heardHum: false, heardBip: false, heardBop: false };
90
91         analyser.fftSize = 2048;
92         analyser.smoothingTimeConstant = 0;
93         analyser.minDecibels = -100;
94         analyser.maxDecibels = 0;
95         gain.gain.value = 0;
96
97         sourceNode.connect(analyser);
98         analyser.connect(gain);
99         gain.connect(context.destination);
100
101        function analyse() {
102            var freqDomain = new Uint8Array(analyser.frequencyBinCount);
103            analyser.getByteFrequencyData(freqDomain);
104
105            var hasFrequency = expectedFrequency => {
106                 var bin = Math.floor(expectedFrequency * analyser.fftSize / context.sampleRate);
107                 return bin < freqDomain.length && freqDomain[bin] >= 150;
108            };
109
110            if (!results.heardHum)
111                 results.heardHum = hasFrequency(150);
112
113            if (!results.heardBip)
114                results.heardBip = hasFrequency(1500);
115
116            if (!results.heardBop)
117                 results.heardBop = hasFrequency(500);
118
119             if (results.heardHum && results.heardBip && results.heardBop)
120                 done();
121         };
122
123        function done() {
124             clearTimeout(timeout);
125             clearInterval(interval);
126             resolve(results);
127        }
128
129         var timeout = setTimeout(done, 3 * duration);
130         var interval = setInterval(analyse, duration / 30);
131         analyse();
132     });
133 }
134
135 function waitFor(duration)
136 {
137     return new Promise((resolve) => setTimeout(resolve, duration));
138 }
139
140 function waitForVideoSize(video, width, height, count)
141 {
142     if (video.videoWidth === width && video.videoHeight === height)
143         return Promise.resolve("video has expected size");
144
145     if (count === undefined)
146         count = 0;
147     if (++count > 20)
148         return Promise.reject("waitForVideoSize timed out, expected " + width + "x"+ height + " but got " + video.videoWidth + "x" + video.videoHeight);
149
150     return waitFor(100).then(() => {
151         return waitForVideoSize(video, width, height, count);
152     });
153 }
154
155 async function doHumAnalysis(stream, expected)
156 {
157     var context = new webkitAudioContext();
158     for (var cptr = 0; cptr < 20; cptr++) {
159         var results = await analyseAudio(stream, 200, context);
160         if (results.heardHum === expected)
161             return true;
162         await waitFor(50);
163     }
164     await context.close();
165     return false;
166 }
167
168 function isVideoBlack(canvas, video, startX, startY, grabbedWidth, grabbedHeight)
169 {
170     canvas.width = video.videoWidth;
171     canvas.height = video.videoHeight;
172     if (!grabbedHeight) {
173         startX = 10;
174         startY = 10;
175         grabbedWidth = canvas.width - 20;
176         grabbedHeight = canvas.height - 20;
177     }
178
179     canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
180
181     imageData = canvas.getContext('2d').getImageData(startX, startY, grabbedWidth, grabbedHeight);
182     data = imageData.data;
183     for (var cptr = 0; cptr < grabbedWidth * grabbedHeight; ++cptr) {
184         // Approximatively black pixels.
185         if (data[4 * cptr] > 30 || data[4 * cptr + 1] > 30 || data[4 * cptr + 2] > 30)
186             return false;
187     }
188     return true;
189 }
190
191 async function checkVideoBlack(expected, canvas, video, errorMessage, counter)
192 {
193     if (isVideoBlack(canvas, video) === expected)
194         return Promise.resolve();
195
196     if (counter === undefined)
197         counter = 0;
198     if (counter > 400) {
199         if (!errorMessage)
200             errorMessage = "checkVideoBlack timed out expecting " + expected;
201         return Promise.reject(errorMessage);
202     }
203
204     await waitFor(50);
205     return checkVideoBlack(expected, canvas, video, errorMessage, ++counter);
206 }
207
208 function setCodec(sdp, codec)
209 {
210     return sdp.split('\r\n').filter(line => {
211         return line.indexOf('a=fmtp') === -1 && line.indexOf('a=rtcp-fb') === -1 && (line.indexOf('a=rtpmap') === -1 || line.indexOf(codec) !== -1);
212     }).join('\r\n');
213 }
214
215 async function getTypedStats(connection, type)
216 {
217     const report = await connection.getStats();
218     var stats;
219     report.forEach((statItem) => {
220         if (statItem.type === type)
221             stats = statItem;
222     });
223     return stats;
224 }