audio-decoder.https.any.js (6244B)
1 // META: global=window,dedicatedworker 2 // META: script=/webcodecs/utils.js 3 4 5 const detachedArrayBuffer = new ArrayBuffer(4); 6 var b = detachedArrayBuffer.transferToFixedLength(); 7 8 const invalidConfigs = [ 9 { 10 comment: 'Missing codec', 11 config: { 12 sampleRate: 48000, 13 numberOfChannels: 2, 14 }, 15 }, 16 { 17 comment: 'Empty codec', 18 config: { 19 codec: '', 20 sampleRate: 48000, 21 numberOfChannels: 2, 22 }, 23 }, 24 { 25 comment: 'Missing sampleRate', 26 config: { 27 codec: 'opus', 28 sampleRate: 48000, 29 }, 30 }, 31 { 32 comment: 'Missing numberOfChannels', 33 config: { 34 codec: 'opus', 35 sampleRate: 48000, 36 }, 37 }, 38 { 39 comment: 'Zero sampleRate', 40 config: { 41 codec: 'opus', 42 sampleRate: 0, 43 numberOfChannels: 2, 44 }, 45 }, 46 { 47 comment: 'Zero channels', 48 config: { 49 codec: 'opus', 50 sampleRate: 8000, 51 numberOfChannels: 0, 52 }, 53 }, 54 { 55 comment: 'Opus with >2 channels but no description', 56 config: { 57 codec: 'opus', 58 sampleRate: 48000, 59 numberOfChannels: 6, 60 } 61 }, 62 { 63 comment: 'Valid configuration except detached description', 64 config: { 65 codec: 'opus', 66 sampleRate: 8000, 67 numberOfChannels: 1, 68 description: detachedArrayBuffer, 69 }, 70 }, 71 ]; 72 73 invalidConfigs.forEach(entry => { 74 promise_test( 75 t => { 76 return promise_rejects_js( 77 t, TypeError, AudioDecoder.isConfigSupported(entry.config)); 78 }, 79 'Test that AudioDecoder.isConfigSupported() rejects invalid config: ' + 80 entry.comment); 81 }); 82 83 invalidConfigs.forEach(entry => { 84 async_test( 85 t => { 86 let codec = new AudioDecoder(getDefaultCodecInit(t)); 87 assert_throws_js(TypeError, () => { 88 codec.configure(entry.config); 89 }); 90 t.done(); 91 }, 92 'Test that AudioDecoder.configure() rejects invalid config: ' + 93 entry.comment); 94 }); 95 96 const validButUnsupportedConfigs = [ 97 { 98 comment: 'Unrecognized codec', 99 config: { 100 codec: 'bogus', 101 sampleRate: 48000, 102 numberOfChannels: 2, 103 }, 104 }, 105 { 106 comment: 'Video codec', 107 config: { 108 codec: 'vp8', 109 sampleRate: 48000, 110 numberOfChannels: 2, 111 }, 112 }, 113 { 114 comment: 'Ambiguous codec', 115 config: { 116 codec: 'vp9', 117 sampleRate: 48000, 118 numberOfChannels: 2, 119 }, 120 }, 121 { 122 comment: 'Codec with MIME type', 123 config: { 124 codec: 'audio/webm; codecs="opus"', 125 sampleRate: 48000, 126 numberOfChannels: 2, 127 }, 128 }, 129 { 130 comment: 'Possible future opus codec string', 131 config: { 132 codec: 'opus.123', 133 sampleRate: 48000, 134 numberOfChannels: 2, 135 } 136 }, 137 { 138 comment: 'Possible future aac codec string', 139 config: { 140 codec: 'mp4a.FF.9', 141 sampleRate: 48000, 142 numberOfChannels: 2, 143 } 144 }, 145 { 146 comment: 'codec with spaces', 147 config: { 148 codec: ' mp3 ', 149 sampleRate: 48000, 150 numberOfChannels: 2, 151 } 152 }, 153 ]; 154 155 // Those configurations are supported, but attempting to configure an 156 // AudioDecoder will fail, because `description` is invalid for this particular 157 // codec 158 var supportedButErrorOnConfiguration = [ 159 { 160 comment: 'Opus with more than two channels and without description', 161 config: { 162 codec: 'opus', 163 sampleRate: '48000', 164 numberOfChannels: 3, 165 }, 166 }, 167 { 168 comment: 'Opus with more than two channels and with a description that is too short', 169 config: { 170 codec: 'opus', 171 sampleRate: '48000', 172 numberOfChannels: 3, 173 description: new Uint8Array(9), // at least 10 bytes are required for multichannel 174 }, 175 }, 176 { 177 comment: 'vorbis requires a description', 178 config: { 179 codec: 'vorbis', 180 sampleRate: '48000', 181 numberOfChannels: 2 182 }, 183 }, 184 { 185 comment: 'flac requires a description', 186 config: { 187 codec: 'flac', 188 sampleRate: '48000', 189 numberOfChannels: 2 190 }, 191 }, 192 ]; 193 194 validButUnsupportedConfigs.forEach(entry => { 195 promise_test( 196 t => { 197 return AudioDecoder.isConfigSupported(entry.config).then(support => { 198 assert_false(support.supported); 199 }); 200 }, 201 'Test that AudioDecoder.isConfigSupported() doesn\'t support config: ' + 202 entry.comment); 203 }); 204 205 var shouldError = validButUnsupportedConfigs.concat(supportedButErrorOnConfiguration); 206 207 shouldError.forEach(entry => { 208 promise_test( 209 t => { 210 let isErrorCallbackCalled = false; 211 let supported = AudioDecoder.isConfigSupported(entry.config); 212 assert_implements_optional(supported, entry.config.codec + ' unsupported'); 213 let codec = new AudioDecoder({ 214 output: t.unreached_func('unexpected output'), 215 error: t.step_func_done(e => { 216 isErrorCallbackCalled = true; 217 assert_true(e instanceof DOMException); 218 assert_equals(e.name, 'NotSupportedError'); 219 assert_equals(codec.state, 'closed', 'state'); 220 }) 221 }); 222 codec.configure(entry.config); 223 return codec.flush() 224 .then(t.unreached_func('flush succeeded unexpectedly')) 225 .catch(t.step_func(e => { 226 assert_true(isErrorCallbackCalled, "isErrorCallbackCalled"); 227 assert_true(e instanceof DOMException); 228 assert_equals(e.name, 'NotSupportedError'); 229 assert_equals(codec.state, 'closed', 'state'); 230 })); 231 }, 232 'Test that AudioDecoder.configure() doesn\'t support config: ' + 233 entry.comment); 234 }); 235 236 function getFakeChunk() { 237 return new EncodedAudioChunk( 238 {type: 'key', timestamp: 0, data: Uint8Array.of(0)}); 239 } 240 241 promise_test(t => { 242 // AudioDecoderInit lacks required fields. 243 assert_throws_js(TypeError, () => { 244 new AudioDecoder({}); 245 }); 246 247 // AudioDecoderInit has required fields. 248 let decoder = new AudioDecoder(getDefaultCodecInit(t)); 249 250 assert_equals(decoder.state, 'unconfigured'); 251 decoder.close(); 252 253 return endAfterEventLoopTurn(); 254 }, 'Test AudioDecoder construction'); 255 256 promise_test(t => { 257 let decoder = new AudioDecoder(getDefaultCodecInit(t)); 258 return testUnconfiguredCodec(t, decoder, getFakeChunk()); 259 }, 'Verify unconfigured AudioDecoder operations');