try to send exactly 10ms packets
This commit is contained in:
@@ -1,15 +1,12 @@
|
|||||||
const SAMPLE_RATE = 48000;
|
const SAMPLE_RATE = 48000;
|
||||||
const PACKET_MS = 10;
|
const PACKET_SAMPLES = 480;
|
||||||
const PACKET_FRAMES = PACKET_MS / 1000 * SAMPLE_RATE;
|
|
||||||
//const PACKET_FRAMES = 2400;
|
|
||||||
console.log("Frames per packet:", PACKET_FRAMES);
|
|
||||||
|
|
||||||
class RustWorklet extends AudioWorkletProcessor {
|
class RustWorklet extends AudioWorkletProcessor {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super();
|
super();
|
||||||
this.module = options.processorOptions;
|
this.module = options.processorOptions;
|
||||||
this.timestamp = null;
|
this.timestamp = null;
|
||||||
this.buffer = new Float32Array(PACKET_FRAMES);
|
this.buffer = new Float32Array(PACKET_SAMPLES * 2);
|
||||||
this.buffer_offset = 0;
|
this.buffer_offset = 0;
|
||||||
if (sampleRate != SAMPLE_RATE) {
|
if (sampleRate != SAMPLE_RATE) {
|
||||||
throw Error(`sample rate ${sampleRate} should be ${SAMPLE_RATE}`);
|
throw Error(`sample rate ${sampleRate} should be ${SAMPLE_RATE}`);
|
||||||
@@ -23,15 +20,16 @@ class RustWorklet extends AudioWorkletProcessor {
|
|||||||
const data = {
|
const data = {
|
||||||
format: 'f32',
|
format: 'f32',
|
||||||
sampleRate: SAMPLE_RATE,
|
sampleRate: SAMPLE_RATE,
|
||||||
//numberOfFrames: this.buffer_offset,
|
|
||||||
numberOfFrames: this.buffer_offset,
|
numberOfFrames: this.buffer_offset,
|
||||||
numberOfChannels: 1,
|
numberOfChannels: 1,
|
||||||
timestamp: this.timestamp,
|
timestamp: this.timestamp,
|
||||||
//timestamp: null,
|
data: this.buffer.slice(0, PACKET_SAMPLES),
|
||||||
data: this.buffer.slice(0, this.buffer_offset),
|
|
||||||
};
|
};
|
||||||
this.port.postMessage(data);
|
this.port.postMessage(data);
|
||||||
this.buffer_offset = 0;
|
if (this.buffer_offset > PACKET_SAMPLES) {
|
||||||
|
this.buffer.copyWithin(0, PACKET_SAMPLES, this.buffer_offset);
|
||||||
|
}
|
||||||
|
this.buffer_offset -= PACKET_SAMPLES;
|
||||||
this.timestamp = null;
|
this.timestamp = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,18 +50,12 @@ class RustWorklet extends AudioWorkletProcessor {
|
|||||||
|
|
||||||
const frames = input[0];
|
const frames = input[0];
|
||||||
|
|
||||||
if (this.buffer_offset + frames.length > this.buffer.length) {
|
|
||||||
// too full, send now
|
|
||||||
this.do_send();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.buffer.set(frames, this.buffer_offset);
|
this.buffer.set(frames, this.buffer_offset);
|
||||||
this.buffer_offset += frames.length;
|
this.buffer_offset += frames.length;
|
||||||
|
if (this.buffer_offset >= PACKET_SAMPLES) {
|
||||||
if (this.buffer_offset + 128 > this.buffer.length) {
|
|
||||||
// full enough, send now
|
|
||||||
this.do_send();
|
this.do_send();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+70
-83
@@ -46,89 +46,75 @@ use web_sys::WorkletOptions;
|
|||||||
|
|
||||||
mod ass {
|
mod ass {
|
||||||
|
|
||||||
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
|
use ogg::PacketWriter;
|
||||||
|
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
const VER: &str = "ballz";
|
||||||
use ogg::PacketWriter;
|
|
||||||
|
|
||||||
const VER: &str = "ballz";
|
const fn to_samples<const S_PS: u32>(ms: u32) -> usize {
|
||||||
|
((S_PS * ms) / 1000) as usize
|
||||||
const fn to_samples<const S_PS: u32>(ms: u32) -> usize {
|
|
||||||
((S_PS * ms) / 1000) as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn calc_sr_u64(val: u64, from: u32, to: u32) -> u64 {
|
|
||||||
(val * to as u64) / from as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encode(
|
|
||||||
pre_encoded_frames: Vec<Vec<u8>>,
|
|
||||||
frame_size: usize,
|
|
||||||
skip: u16,
|
|
||||||
) -> Vec<u8>{
|
|
||||||
let mut buffer: Vec<u8> = Vec::new();
|
|
||||||
let mut packet_writer = PacketWriter::new(&mut buffer);
|
|
||||||
|
|
||||||
// Hardcoded serial number
|
|
||||||
let serial = 12345;
|
|
||||||
|
|
||||||
let skip_48 = calc_sr_u64(skip.into(), 48000, 48000);
|
|
||||||
|
|
||||||
let opus_head: [u8; 19] = [
|
|
||||||
b'O', b'p', b'u', b's', b'H', b'e', b'a', b'd', 1, 1, // NUM_CHANNELS = 1
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
];
|
|
||||||
|
|
||||||
let mut head = opus_head;
|
|
||||||
LittleEndian::write_u16(&mut head[10..12], skip_48 as u16);
|
|
||||||
LittleEndian::write_u32(&mut head[12..16], 48000);
|
|
||||||
|
|
||||||
let mut opus_tags: Vec<u8> = Vec::with_capacity(60);
|
|
||||||
let vendor_str = format!("ogg-opus {}", VER);
|
|
||||||
opus_tags.extend(b"OpusTags");
|
|
||||||
let mut len_bf = [0u8; 4];
|
|
||||||
LittleEndian::write_u32(&mut len_bf, vendor_str.len() as u32);
|
|
||||||
opus_tags.extend(&len_bf);
|
|
||||||
opus_tags.extend(vendor_str.bytes());
|
|
||||||
opus_tags.extend(&[0u8; 4]);
|
|
||||||
|
|
||||||
packet_writer.write_packet(&head, serial, ogg::PacketWriteEndInfo::EndPage, 0);
|
|
||||||
packet_writer.write_packet(
|
|
||||||
&opus_tags,
|
|
||||||
serial,
|
|
||||||
ogg::PacketWriteEndInfo::EndPage,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
for (i, frame) in pre_encoded_frames.iter().enumerate() {
|
|
||||||
let is_last = i == pre_encoded_frames.len() - 1;
|
|
||||||
//let granule_pos = 0;
|
|
||||||
let granule_pos = calc_sr_u64(
|
|
||||||
(skip as usize + (i + 1) * frame_size) as u64,
|
|
||||||
48000,
|
|
||||||
48000,
|
|
||||||
);
|
|
||||||
|
|
||||||
packet_writer.write_packet(
|
|
||||||
frame.clone(),
|
|
||||||
serial,
|
|
||||||
if is_last {
|
|
||||||
ogg::PacketWriteEndInfo::EndStream
|
|
||||||
} else {
|
|
||||||
ogg::PacketWriteEndInfo::NormalPacket
|
|
||||||
},
|
|
||||||
granule_pos,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer
|
const fn calc_sr_u64(val: u64, from: u32, to: u32) -> u64 {
|
||||||
}
|
(val * to as u64) / from as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(pre_encoded_frames: Vec<Vec<u8>>, frame_size: usize, skip: u16) -> Vec<u8> {
|
||||||
|
let mut buffer: Vec<u8> = Vec::new();
|
||||||
|
let mut packet_writer = PacketWriter::new(&mut buffer);
|
||||||
|
|
||||||
|
// Hardcoded serial number
|
||||||
|
let serial = 12345;
|
||||||
|
|
||||||
|
let skip_48 = calc_sr_u64(skip.into(), 48000, 48000);
|
||||||
|
|
||||||
|
let opus_head: [u8; 19] = [
|
||||||
|
b'O', b'p', b'u', b's', b'H', b'e', b'a', b'd', 1, 1, // NUM_CHANNELS = 1
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut head = opus_head;
|
||||||
|
LittleEndian::write_u16(&mut head[10..12], skip_48 as u16);
|
||||||
|
LittleEndian::write_u32(&mut head[12..16], 48000);
|
||||||
|
|
||||||
|
let mut opus_tags: Vec<u8> = Vec::with_capacity(60);
|
||||||
|
let vendor_str = format!("ogg-opus {}", VER);
|
||||||
|
opus_tags.extend(b"OpusTags");
|
||||||
|
let mut len_bf = [0u8; 4];
|
||||||
|
LittleEndian::write_u32(&mut len_bf, vendor_str.len() as u32);
|
||||||
|
opus_tags.extend(&len_bf);
|
||||||
|
opus_tags.extend(vendor_str.bytes());
|
||||||
|
opus_tags.extend(&[0u8; 4]);
|
||||||
|
|
||||||
|
packet_writer.write_packet(&head, serial, ogg::PacketWriteEndInfo::EndPage, 0);
|
||||||
|
packet_writer.write_packet(&opus_tags, serial, ogg::PacketWriteEndInfo::EndPage, 0);
|
||||||
|
|
||||||
|
for (i, frame) in pre_encoded_frames.iter().enumerate() {
|
||||||
|
let is_last = i == pre_encoded_frames.len() - 1;
|
||||||
|
//let granule_pos = 0;
|
||||||
|
let granule_pos =
|
||||||
|
calc_sr_u64((skip as usize + (i + 1) * frame_size) as u64, 48000, 48000);
|
||||||
|
|
||||||
|
packet_writer.write_packet(
|
||||||
|
frame.clone(),
|
||||||
|
serial,
|
||||||
|
if is_last {
|
||||||
|
ogg::PacketWriteEndInfo::EndStream
|
||||||
|
} else {
|
||||||
|
ogg::PacketWriteEndInfo::NormalPacket
|
||||||
|
},
|
||||||
|
granule_pos,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to download data as a file
|
// Function to download data as a file
|
||||||
pub fn download_data(data: Vec<u8>, filename: &str) -> Result<(), JsValue> {
|
pub fn download_data(data: Vec<u8>, filename: &str) -> Result<(), JsValue> {
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use web_sys::{Blob, Url, HtmlAnchorElement, window};
|
use web_sys::{window, Blob, HtmlAnchorElement, Url};
|
||||||
// Create a new Blob from the data
|
// Create a new Blob from the data
|
||||||
let array = web_sys::js_sys::Uint8Array::from(&data[..]);
|
let array = web_sys::js_sys::Uint8Array::from(&data[..]);
|
||||||
let blob = Blob::new_with_u8_array_sequence(&vec![array].into())?;
|
let blob = Blob::new_with_u8_array_sequence(&vec![array].into())?;
|
||||||
@@ -138,7 +124,9 @@ pub fn download_data(data: Vec<u8>, filename: &str) -> Result<(), JsValue> {
|
|||||||
|
|
||||||
// Create an anchor element and set its href to the Blob URL
|
// Create an anchor element and set its href to the Blob URL
|
||||||
let document = window().unwrap().document().unwrap();
|
let document = window().unwrap().document().unwrap();
|
||||||
let a = document.create_element("a")?.dyn_into::<HtmlAnchorElement>()?;
|
let a = document
|
||||||
|
.create_element("a")?
|
||||||
|
.dyn_into::<HtmlAnchorElement>()?;
|
||||||
a.set_href(&url);
|
a.set_href(&url);
|
||||||
a.set_download(filename);
|
a.set_download(filename);
|
||||||
|
|
||||||
@@ -153,7 +141,6 @@ pub fn download_data(data: Vec<u8>, filename: &str) -> Result<(), JsValue> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Borrowed from
|
// Borrowed from
|
||||||
// https://github.com/security-union/videocall-rs/blob/main/videocall-client/src/decode/config.rs#L6
|
// https://github.com/security-union/videocall-rs/blob/main/videocall-client/src/decode/config.rs#L6
|
||||||
fn configure_audio_context() -> AudioContext {
|
fn configure_audio_context() -> AudioContext {
|
||||||
@@ -207,9 +194,7 @@ async fn create_encoder_worklet(
|
|||||||
|
|
||||||
let error: Closure<dyn FnMut(JsValue)> = Closure::new(|e| console::error_1(&e));
|
let error: Closure<dyn FnMut(JsValue)> = Closure::new(|e| console::error_1(&e));
|
||||||
|
|
||||||
|
let mut download_buffer = std::cell::RefCell::new(Vec::new());
|
||||||
let mut download_buffer = std::cell::RefCell::new(Vec::new());
|
|
||||||
|
|
||||||
|
|
||||||
// This knows what MediaStreamTrackGenerator to use as it closes around it
|
// This knows what MediaStreamTrackGenerator to use as it closes around it
|
||||||
let mut sequence_num = 0;
|
let mut sequence_num = 0;
|
||||||
@@ -221,11 +206,13 @@ async fn create_encoder_worklet(
|
|||||||
download_buffer.borrow_mut().push(array.clone());
|
download_buffer.borrow_mut().push(array.clone());
|
||||||
if download_buffer.borrow().len() > 200 {
|
if download_buffer.borrow().len() > 200 {
|
||||||
//download_data(download_buffer.borrow().to_vec(), "download_buffer.opus");
|
//download_data(download_buffer.borrow().to_vec(), "download_buffer.opus");
|
||||||
download_data(ass::encode(download_buffer.borrow().to_vec(), 960, 0), "download_buffer.opus");
|
download_data(
|
||||||
|
ass::encode(download_buffer.borrow().to_vec(), 960, 0),
|
||||||
|
"download_buffer.opus",
|
||||||
|
);
|
||||||
download_buffer.borrow_mut().clear();
|
download_buffer.borrow_mut().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let _ = packets.try_send(ControlPacket::UDPTunnel(Box::new(VoicePacket::Audio {
|
let _ = packets.try_send(ControlPacket::UDPTunnel(Box::new(VoicePacket::Audio {
|
||||||
_dst: std::marker::PhantomData,
|
_dst: std::marker::PhantomData,
|
||||||
target: 0,
|
target: 0,
|
||||||
@@ -234,7 +221,7 @@ async fn create_encoder_worklet(
|
|||||||
payload: VoicePacketPayload::Opus(array.into(), false),
|
payload: VoicePacketPayload::Opus(array.into(), false),
|
||||||
position_info: None,
|
position_info: None,
|
||||||
})));
|
})));
|
||||||
sequence_num = sequence_num.wrapping_add(2);
|
sequence_num = sequence_num.wrapping_add(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
let audio_encoder = AudioEncoder::new(&AudioEncoderInit::new(
|
let audio_encoder = AudioEncoder::new(&AudioEncoderInit::new(
|
||||||
@@ -254,14 +241,14 @@ async fn create_encoder_worklet(
|
|||||||
audio_encoder.configure(&encoder_config);
|
audio_encoder.configure(&encoder_config);
|
||||||
console::log_1(&"Created Audio Encoder".into());
|
console::log_1(&"Created Audio Encoder".into());
|
||||||
|
|
||||||
let mut download_buffer = std::cell::RefCell::new(Vec::new());
|
let mut download_buffer = std::cell::RefCell::new(Vec::new());
|
||||||
|
|
||||||
let onmessage: Closure<dyn FnMut(MessageEvent)> = Closure::new(move |event: MessageEvent| {
|
let onmessage: Closure<dyn FnMut(MessageEvent)> = Closure::new(move |event: MessageEvent| {
|
||||||
match AudioData::new(event.data().unchecked_ref()) {
|
match AudioData::new(event.data().unchecked_ref()) {
|
||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
let x = web_sys::AudioDataCopyToOptions::new(0);
|
let x = web_sys::AudioDataCopyToOptions::new(0);
|
||||||
x.set_format(web_sys::AudioSampleFormat::F32);
|
x.set_format(web_sys::AudioSampleFormat::F32);
|
||||||
let mut sub_buffer = vec![0;data.allocation_size(&x).unwrap() as usize];
|
let mut sub_buffer = vec![0; data.allocation_size(&x).unwrap() as usize];
|
||||||
data.copy_to_with_u8_array(&mut sub_buffer, &x);
|
data.copy_to_with_u8_array(&mut sub_buffer, &x);
|
||||||
download_buffer.borrow_mut().append(&mut sub_buffer);
|
download_buffer.borrow_mut().append(&mut sub_buffer);
|
||||||
if download_buffer.borrow().len() > 48000 * 10 * 4 {
|
if download_buffer.borrow().len() > 48000 * 10 * 4 {
|
||||||
|
|||||||
Reference in New Issue
Block a user