| // META: global=window,dedicatedworker | |
| // META: script=/webcodecs/utils.js | |
| const invalidConfigs = [ | |
| { | |
| comment: 'Missing codec', | |
| config: { | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| }, | |
| }, | |
| { | |
| comment: 'Empty codec', | |
| config: { | |
| codec: '', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| }, | |
| }, | |
| { | |
| comment: 'Missing sampleRate', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 48000, | |
| }, | |
| }, | |
| { | |
| comment: 'Missing numberOfChannels', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 48000, | |
| }, | |
| }, | |
| { | |
| comment: 'Zero sampleRate', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 0, | |
| numberOfChannels: 2, | |
| }, | |
| }, | |
| { | |
| comment: 'Zero channels', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 8000, | |
| numberOfChannels: 0, | |
| }, | |
| }, | |
| { | |
| comment: 'Bit rate too big', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 8000, | |
| numberOfChannels: 2, | |
| bitrate: 6e9, | |
| }, | |
| }, | |
| { | |
| comment: 'Bit rate present but equal to zero', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 8000, | |
| numberOfChannels: 2, | |
| bitrate: 0, | |
| }, | |
| }, | |
| { | |
| comment: 'Opus complexity too big', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 8000, | |
| numberOfChannels: 2, | |
| opus: { | |
| complexity: 11, | |
| }, | |
| }, | |
| }, | |
| { | |
| comment: 'Opus packetlossperc too big', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 8000, | |
| numberOfChannels: 2, | |
| opus: { | |
| packetlossperc: 101, | |
| }, | |
| }, | |
| }, | |
| { | |
| comment: 'Opus frame duration too small', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 8000, | |
| numberOfChannels: 2, | |
| opus: { | |
| frameDuration: 0, | |
| }, | |
| }, | |
| }, | |
| { | |
| comment: 'Opus frame duration too big', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 8000, | |
| numberOfChannels: 2, | |
| opus: { | |
| frameDuration: 120 * 1000 + 1, | |
| }, | |
| }, | |
| }, | |
| { | |
| comment: 'Invalid Opus frameDuration', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 8000, | |
| numberOfChannels: 2, | |
| opus: { | |
| frameDuration: 2501, | |
| }, | |
| }, | |
| }, | |
| { | |
| comment: 'Bitrate is too low for Opus', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| bitrate: 1, | |
| }, | |
| }, | |
| ]; | |
| invalidConfigs.forEach(entry => { | |
| promise_test( | |
| t => { | |
| return promise_rejects_js( | |
| t, TypeError, AudioEncoder.isConfigSupported(entry.config)); | |
| }, | |
| 'Test that AudioEncoder.isConfigSupported() rejects invalid config: ' + | |
| entry.comment); | |
| }); | |
| invalidConfigs.forEach(entry => { | |
| async_test( | |
| t => { | |
| let codec = new AudioEncoder(getDefaultCodecInit(t)); | |
| assert_throws_js(TypeError, () => { | |
| codec.configure(entry.config); | |
| }); | |
| t.done(); | |
| }, | |
| 'Test that AudioEncoder.configure() rejects invalid config: ' + | |
| entry.comment); | |
| }); | |
| const validButUnsupportedConfigs = [ | |
| { | |
| comment: 'Unrecognized codec', | |
| config: { | |
| codec: 'bogus', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| }, | |
| }, | |
| { | |
| comment: 'Sample rate is too small', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 1, | |
| numberOfChannels: 2, | |
| }, | |
| }, | |
| { | |
| comment: 'Sample rate is too large', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 10000000, | |
| numberOfChannels: 2, | |
| }, | |
| }, | |
| { | |
| comment: 'Way too many channels', | |
| config: { | |
| codec: 'opus', | |
| sampleRate: 8000, | |
| numberOfChannels: 1024, | |
| bitrate: 128000, | |
| }, | |
| }, | |
| { | |
| comment: 'Possible future opus codec string', | |
| config: { | |
| codec: 'opus.123', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| } | |
| }, | |
| { | |
| comment: 'Possible future aac codec string', | |
| config: { | |
| codec: 'mp4a.FF.9', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| } | |
| }, | |
| { | |
| comment: 'codec with spaces', | |
| config: { | |
| codec: ' opus ', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| } | |
| }, | |
| ]; | |
| validButUnsupportedConfigs.forEach(entry => { | |
| promise_test( | |
| async t => { | |
| let support = await AudioEncoder.isConfigSupported(entry.config); | |
| assert_false(support.supported); | |
| let config = support.config; | |
| assert_equals(config.codec, entry.config.codec); | |
| assert_equals(config.sampleRate, entry.config.sampleRate); | |
| assert_equals(config.numberOfChannels, entry.config.numberOfChannels); | |
| }, | |
| 'Test that AudioEncoder.isConfigSupported() doesn\'t support config: ' + | |
| entry.comment); | |
| }); | |
| validButUnsupportedConfigs.forEach(entry => { | |
| promise_test( | |
| t => { | |
| let isErrorCallbackCalled = false; | |
| let codec = new AudioEncoder({ | |
| output: t.unreached_func('unexpected output'), | |
| error: t.step_func_done(e => { | |
| isErrorCallbackCalled = true; | |
| assert_true(e instanceof DOMException); | |
| assert_equals(e.name, 'NotSupportedError'); | |
| assert_equals(codec.state, 'closed', 'state'); | |
| }) | |
| }); | |
| codec.configure(entry.config); | |
| return codec.flush() | |
| .then(t.unreached_func('flush succeeded unexpectedly')) | |
| .catch(t.step_func(e => { | |
| assert_true(isErrorCallbackCalled, "isErrorCallbackCalled"); | |
| assert_true(e instanceof DOMException); | |
| assert_equals(e.name, 'NotSupportedError'); | |
| assert_equals(codec.state, 'closed', 'state'); | |
| })); | |
| }, | |
| 'Test that AudioEncoder.configure() doesn\'t support config: ' + | |
| entry.comment); | |
| }); | |
| const validConfigs = [ | |
| { | |
| codec: 'opus', | |
| sampleRate: 8000, | |
| numberOfChannels: 1, | |
| }, | |
| { | |
| codec: 'opus', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| }, | |
| { | |
| codec: 'opus', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| bitrate: 128000, | |
| bitrateMode: "constant", | |
| bogus: 123 | |
| }, | |
| { | |
| codec: 'opus', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| bitrate: 128000, | |
| bitrateMode: "variable", | |
| bogus: 123 | |
| }, | |
| { | |
| codec: 'opus', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| opus: { | |
| complexity: 5, | |
| signal: 'music', | |
| application: 'audio', | |
| frameDuration: 20000, | |
| packetlossperc: 10, | |
| useinbandfec: true, | |
| }, | |
| }, | |
| { | |
| codec: 'opus', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| opus: { | |
| format: 'opus', | |
| signal: 'voice', | |
| application: 'lowdelay', | |
| complexity: 10, | |
| frameDuration: 60000, | |
| packetlossperc: 20, // Irrelevant without useinbandfec, but still valid. | |
| usedtx: true, | |
| bogus: 456, | |
| }, | |
| }, | |
| { | |
| codec: 'opus', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| opus: {}, // Use default values. | |
| }, | |
| { | |
| codec: 'opus', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| opus: { | |
| frameDuration: 7500, // Test multiples of valid Opus frame durations. | |
| } | |
| }, | |
| { | |
| codec: 'opus', | |
| sampleRate: 48000, | |
| numberOfChannels: 2, | |
| opus: { | |
| frameDuration: 120000, | |
| } | |
| }, | |
| ]; | |
| validConfigs.forEach(config => { | |
| promise_test(async t => { | |
| let support = await AudioEncoder.isConfigSupported(config); | |
| assert_true(support.supported); | |
| let new_config = support.config; | |
| assert_equals(new_config.codec, config.codec); | |
| assert_equals(new_config.sampleRate, config.sampleRate); | |
| assert_equals(new_config.numberOfChannels, config.numberOfChannels); | |
| if (config.bitrate) | |
| assert_equals(new_config.bitrate, config.bitrate); | |
| if (config.opus) { | |
| let opus_config = config.opus; | |
| let new_opus_config = new_config.opus; | |
| assert_equals(new_opus_config.format, opus_config.format ?? 'opus'); | |
| assert_equals( | |
| new_opus_config.frameDuration, opus_config.frameDuration ?? 20000); | |
| assert_equals( | |
| new_opus_config.packetlossperc, opus_config.packetlossperc ?? 0); | |
| assert_equals( | |
| new_opus_config.useinbandfec, opus_config.useinbandfec ?? false); | |
| assert_equals(new_opus_config.usedtx, opus_config.usedtx ?? false); | |
| assert_false(new_opus_config.hasOwnProperty('bogus')); | |
| if (opus_config.complexity) { | |
| assert_equals(new_opus_config.complexity, opus_config.complexity); | |
| } else { | |
| // Default complexity is 5 for mobile/ARM platforms, and 9 otherwise. | |
| assert_true( | |
| new_opus_config.complexity == 5 || new_opus_config.complexity == 9); | |
| } | |
| } else { | |
| assert_false(new_config.hasOwnProperty('opus')); | |
| } | |
| assert_false(new_config.hasOwnProperty('bogus')); | |
| }, 'AudioEncoder.isConfigSupported() supports: ' + JSON.stringify(config)); | |
| }); |