fune/testing/web-platform/tests/webcodecs/audio-encoder.any.js
Eugene Zemtsov 4f04a2da5f Bug 1692387 [wpt PR 27600] - webcodecs: Make AudioEncoder emit decoder config with extra_data, a=testonly
Automatic update from web-platform-tests
webcodecs: Make AudioEncoder emit decoder config with extra_data

Bug: 1177021
Change-Id: I6d6fe69433b954b65cfec3fd6bc6dc78564ee168
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2691321
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
Reviewed-by: Thomas Guilbert <tguilbert@chromium.org>
Cr-Commit-Position: refs/heads/master@{#853747}

--

wpt-commits: d419c96b5cf1f8be6747f62379d87f9759aa0a70
wpt-pr: 27600
2021-02-16 12:57:41 +00:00

244 lines
No EOL
7.1 KiB
JavaScript

// META: global=window
// META: script=/webcodecs/utils.js
function make_audio_frame(timestamp, channels, sampleRate, length) {
let buffer = new AudioBuffer({
length: length,
numberOfChannels: channels,
sampleRate: sampleRate
});
for (var channel = 0; channel < buffer.numberOfChannels; channel++) {
// This gives us the actual array that contains the data
var array = buffer.getChannelData(channel);
let hz = 100 + channel * 50; // sound frequency
for (var i = 0; i < array.length; i++) {
let t = (i / sampleRate) * hz * (Math.PI * 2);
array[i] = Math.sin(t);
}
}
return new AudioFrame({
timestamp: timestamp,
buffer: buffer
});
}
// Merge all audio buffers into a new big one with all the data.
function join_buffers(buffers) {
assert_greater_than_equal(buffers.length, 0);
let total_length = 0;
let base_buffer = buffers[0];
for (const buffer of buffers) {
assert_not_equals(buffer, null);
assert_equals(buffer.sampleRate, base_buffer.sampleRate);
assert_equals(buffer.numberOfChannels, base_buffer.numberOfChannels);
total_length += buffer.length;
}
let result = new AudioBuffer({
length: total_length,
numberOfChannels: base_buffer.numberOfChannels,
sampleRate: base_buffer.sampleRate
});
for (let i = 0; i < base_buffer.numberOfChannels; i++) {
let channel = result.getChannelData(i);
let position = 0;
for (const buffer of buffers) {
channel.set(buffer.getChannelData(i), position);
position += buffer.length;
}
assert_equals(position, total_length);
}
return result;
}
function clone_frame(frame) {
return new AudioFrame({
timestamp: frame.timestamp,
buffer: join_buffers([frame.buffer])
});
}
promise_test(async t => {
let sample_rate = 48000;
let total_duration_s = 2;
let frame_count = 20;
let outputs = [];
let init = {
error: e => {
assert_unreached("error: " + e);
},
output: chunk => {
outputs.push(chunk);
}
};
let encoder = new AudioEncoder(init);
assert_equals(encoder.state, "unconfigured");
let config = {
codec: 'opus',
sampleRate: sample_rate,
numberOfChannels: 2,
bitrate: 256000 //256kbit
};
encoder.configure(config);
let timestamp_us = 0;
for (let i = 0; i < frame_count; i++) {
let frame_duration_s = total_duration_s / frame_count;
let length = frame_duration_s * config.sampleRate;
let frame = make_audio_frame(timestamp_us, config.numberOfChannels,
config.sampleRate, length);
encoder.encode(frame);
timestamp_us += frame_duration_s * 1_000_000;
}
await encoder.flush();
encoder.close();
assert_greater_than_equal(outputs.length, frame_count);
assert_equals(outputs[0].timestamp, 0, "first chunk timestamp");
for (chunk of outputs) {
assert_greater_than(chunk.data.byteLength, 0);
assert_greater_than(timestamp_us, chunk.timestamp);
}
}, 'Simple audio encoding');
promise_test(async t => {
let sample_rate = 48000;
let total_duration_s = 2;
let frame_count = 20;
let input_frames = [];
let output_frames = [];
let decoder_init = {
error: t.unreached_func("Decode error"),
output: frame => {
output_frames.push(frame);
}
};
let decoder = new AudioDecoder(decoder_init);
let encoder_init = {
error: t.unreached_func("Encoder error"),
output: (chunk, config) => {
if (config)
decoder.configure(config);
decoder.decode(chunk);
}
};
let encoder = new AudioEncoder(encoder_init);
let config = {
codec: 'opus',
sampleRate: sample_rate,
numberOfChannels: 2,
bitrate: 256000, //256kbit
};
encoder.configure(config);
let timestamp_us = 0;
const frame_duration_s = total_duration_s / frame_count;
const frame_length = frame_duration_s * config.sampleRate;
for (let i = 0; i < frame_count; i++) {
let frame = make_audio_frame(timestamp_us, config.numberOfChannels,
config.sampleRate, frame_length);
input_frames.push(clone_frame(frame));
encoder.encode(frame);
timestamp_us += frame_duration_s * 1_000_000;
}
await encoder.flush();
encoder.close();
await decoder.flush();
decoder.close();
let total_input = join_buffers(input_frames.map(f => f.buffer));
let total_output = join_buffers(output_frames.map(f => f.buffer));
assert_equals(total_output.numberOfChannels, 2);
assert_equals(total_output.sampleRate, sample_rate);
// Output can be slightly longer that the input due to padding
assert_greater_than_equal(total_output.length, total_input.length);
assert_greater_than_equal(total_output.duration, total_duration_s);
assert_approx_equals(total_output.duration, total_duration_s, 0.1);
// Compare waveform before and after encoding
for (let channel = 0; channel < total_input.numberOfChannels; channel++) {
let input_data = total_input.getChannelData(channel);
let output_data = total_output.getChannelData(channel);
for (let i = 0; i < total_input.length; i++) {
assert_approx_equals(input_data[i], output_data[i], 0.5,
"Difference between input and output is too large."
+ " index: " + i
+ " input: " + input_data[i]
+ " output: " + output_data[i]);
}
}
}, 'Encoding and decoding');
promise_test(async t => {
let output_count = 0;
let encoder_config = {
codec: 'opus',
sampleRate: 24000,
numberOfChannels: 1,
bitrate: 96000
};
let decoder_config = null;
let init = {
error: t.unreached_func("Encoder error"),
output: (chunk, config) => {
// Only the first invocation of the output callback is supposed to have
// a |config| in it.
output_count++;
if (output_count == 1) {
assert_not_equals(config, null);
decoder_config = config;
} else {
assert_equals(config, null);
}
}
};
let encoder = new AudioEncoder(init);
encoder.configure(encoder_config);
let long_frame = make_audio_frame(0, encoder_config.numberOfChannels,
encoder_config.sampleRate, encoder_config.sampleRate);
encoder.encode(clone_frame(long_frame));
await encoder.flush();
// Long frame produced more than one output, and we've got decoder_config
assert_greater_than(output_count, 1);
assert_not_equals(decoder_config, null);
assert_equals(decoder_config.codec, encoder_config.codec);
assert_equals(decoder_config.sampleRate, encoder_config.sampleRate);
assert_equals(decoder_config.numberOfChannels, encoder_config.numberOfChannels);
// Check that description start with 'Opus'
let extra_data = new Uint8Array(decoder_config.description);
assert_equals(extra_data[0], 0x4f);
assert_equals(extra_data[1], 0x70);
assert_equals(extra_data[2], 0x75);
assert_equals(extra_data[3], 0x73);
decoder_config = null;
output_count = 0;
encoder_config.bitrate = 256000;
encoder.configure(encoder_config);
encoder.encode(clone_frame(long_frame));
await encoder.flush();
// After reconfiguring encoder should produce decoder config again
assert_greater_than(output_count, 1);
assert_not_equals(decoder_config, null);
assert_not_equals(decoder_config.description, null);
encoder.close();
}, "Emit decoder config and extra data.");