audiocontext-sinkid-state-change.https.html (3613B)
1 <!DOCTYPE html> 2 <head> 3 <title>Test AudioContext.setSinkId() state change</title> 4 </head> 5 <script src=/resources/testharness.js></script> 6 <script src=/resources/testharnessreport.js></script> 7 <script> 8 "use strict"; 9 10 let outputDeviceList = null; 11 let firstDeviceId = null; 12 13 // Setup: Get permission via getUserMedia() and a list of audio output devices. 14 promise_setup(async t => { 15 await navigator.mediaDevices.getUserMedia({ audio: true }); 16 const deviceList = await navigator.mediaDevices.enumerateDevices(); 17 outputDeviceList = 18 deviceList.filter(({kind}) => kind === 'audiooutput'); 19 assert_greater_than(outputDeviceList.length, 1, 20 'the system must have more than 1 device.'); 21 firstDeviceId = outputDeviceList[1].deviceId; 22 }, 'Get permission via getUserMedia() and a list of audio output devices.'); 23 24 // Test the sink change when from a suspended context. 25 promise_test(async t => { 26 let events = []; 27 const audioContext = new AudioContext(); 28 await audioContext.suspend(); 29 30 // Step 6. Set wasRunning to false if the [[rendering thread state]] on the 31 // AudioContext is "suspended". 32 assert_equals(audioContext.state, 'suspended'); 33 34 // Step 11.5. Fire an event named sinkchange at the associated AudioContext. 35 audioContext.onsinkchange = t.step_func(() => { 36 events.push('sinkchange'); 37 assert_equals(audioContext.sinkId, firstDeviceId); 38 }); 39 40 await audioContext.setSinkId(firstDeviceId); 41 assert_equals(events[0], 'sinkchange'); 42 t.done(); 43 }, 'Calling setSinkId() on a suspended AudioContext should fire only sink ' + 44 'change events.'); 45 46 // Test the sink change on a running AudioContext. 47 promise_test(async t => { 48 let events = []; 49 let silentSinkOption = {type: 'none'}; 50 const audioContext = new AudioContext(); 51 52 // Make sure the context is "running". This also will fire a state change 53 // event upon resolution. 54 await audioContext.resume(); 55 56 return new Promise(async resolve => { 57 58 function eventCheckpoint() { 59 // We're expecting 4 events from AudioContext. 60 if (events.length === 4) { 61 // The initial context state was "running". 62 assert_equals(events[0], 'statechange:running'); 63 assert_equals(events[1], 'statechange:suspended'); 64 assert_equals(events[2], 'sinkchange'); 65 assert_equals(events[3], 'statechange:running'); 66 resolve(); 67 } 68 } 69 70 // This is to catch a sink change event: 71 // - Step 11.5. Fire an event named sinkchange at the associated 72 // AudioContext. 73 audioContext.onsinkchange = t.step_func(() => { 74 assert_equals(audioContext.sinkId.type, silentSinkOption.type); 75 events.push('sinkchange'); 76 eventCheckpoint(); 77 }); 78 79 // The following event handler will catch 3 state change events: 80 // - The initial 'running' state change. 81 // - Step 9.2.1. Set the state attribute of the AudioContext to "suspended". 82 // Fire an event named statechange at the associated AudioContext. 83 // - Step 12.2. Set the state attribute of the AudioContext to "running". 84 // Fire an event named statechange at the associated AudioContext. 85 audioContext.onstatechange = async () => { 86 events.push(`statechange:${audioContext.state}`); 87 eventCheckpoint(); 88 }; 89 90 // To trigger a series of state changes, we need a device change. The 91 // context started with the default device, and this call changes it to a 92 // silent sink. 93 audioContext.setSinkId(silentSinkOption); 94 }); 95 }, 'Calling setSinkId() on a running AudioContext should fire both state ' + 96 'and sink change events.'); 97 </script> 98 </html>