Отладка производительности
This commit is contained in:
@@ -10,93 +10,80 @@ function toUint8Array(input: KeyInput): Uint8Array {
|
|||||||
return new Uint8Array(u8.slice().buffer);
|
return new Uint8Array(u8.slice().buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildNonce(timestamp: unknown): Uint8Array {
|
/**
|
||||||
|
* Переиспользуемый процессор фреймов.
|
||||||
|
* Один экземпляр на sender/receiver — нет аллокаций на каждый фрейм.
|
||||||
|
*/
|
||||||
|
function createFrameProcessor(key: Uint8Array) {
|
||||||
|
// Переиспользуемые буферы — не создаём новые на каждый фрейм
|
||||||
const nonce = new Uint8Array(12);
|
const nonce = new Uint8Array(12);
|
||||||
const ts = typeof timestamp === "number"
|
const nonceView = new DataView(nonce.buffer);
|
||||||
? timestamp
|
|
||||||
: typeof timestamp === "bigint"
|
return function processFrame(data: ArrayBuffer): ArrayBuffer {
|
||||||
? Number(timestamp)
|
// Переиспользуем nonce буфер
|
||||||
: 0;
|
nonce.fill(0);
|
||||||
new DataView(nonce.buffer).setUint32(8, ts >>> 0, false);
|
nonceView.setUint32(8, (data.byteLength ^ (data.byteLength << 8)) >>> 0, false);
|
||||||
return nonce;
|
|
||||||
|
const input = new Uint8Array(data);
|
||||||
|
const output = chacha20(key, nonce, input);
|
||||||
|
return output.buffer as ArrayBuffer;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function processFrame(data: ArrayBuffer, key: Uint8Array, timestamp: unknown): ArrayBuffer {
|
function createTransform(processFrame: (data: ArrayBuffer) => ArrayBuffer) {
|
||||||
const nonce = buildNonce(timestamp);
|
return new TransformStream<any, any>({
|
||||||
const input = new Uint8Array(data);
|
transform(frame, controller) {
|
||||||
// ChaCha20 симметричный: encrypt === decrypt, тот же размер
|
try {
|
||||||
const output = chacha20(key, nonce, input);
|
const start = performance.now();
|
||||||
return output.buffer as ArrayBuffer;
|
frame.data = processFrame(frame.data);
|
||||||
|
const elapsed = performance.now() - start;
|
||||||
|
if (elapsed > 1) {
|
||||||
|
console.warn(`E2EE slow frame: ${elapsed.toFixed(2)}ms`);
|
||||||
|
}
|
||||||
|
controller.enqueue(frame);
|
||||||
|
} catch (e) {
|
||||||
|
// не рвём поток — пропускаем фрейм как есть
|
||||||
|
console.error("E2EE frame failed:", e);
|
||||||
|
controller.enqueue(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function attachSenderE2EE(sender: RTCRtpSender, keyInput: KeyInput): Promise<void> {
|
export async function attachSenderE2EE(sender: RTCRtpSender, keyInput: KeyInput): Promise<void> {
|
||||||
if (senderAttached.has(sender)) {
|
if (senderAttached.has(sender)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
senderAttached.add(sender);
|
senderAttached.add(sender);
|
||||||
|
|
||||||
const key = toUint8Array(keyInput);
|
const key = toUint8Array(keyInput);
|
||||||
if (key.byteLength !== 32) {
|
if (key.byteLength !== 32) throw new Error(`E2EE key must be 32 bytes, got ${key.byteLength}`);
|
||||||
throw new Error(`E2EE key must be 32 bytes, got ${key.byteLength}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const anySender = sender as any;
|
const anySender = sender as any;
|
||||||
if (!anySender.createEncodedStreams) {
|
if (!anySender.createEncodedStreams) throw new Error("createEncodedStreams not available on RTCRtpSender");
|
||||||
throw new Error("createEncodedStreams is not available on RTCRtpSender");
|
|
||||||
}
|
|
||||||
|
|
||||||
const { readable, writable } = anySender.createEncodedStreams();
|
const { readable, writable } = anySender.createEncodedStreams();
|
||||||
|
const processFrame = createFrameProcessor(key);
|
||||||
|
|
||||||
const enc = new TransformStream<any, any>({
|
readable
|
||||||
// Синхронный transform — нет async, нет накопления очереди
|
.pipeThrough(createTransform(processFrame))
|
||||||
transform(frame, controller) {
|
.pipeTo(writable)
|
||||||
try {
|
.catch((e) => console.error("Sender E2EE pipeline failed:", e));
|
||||||
frame.data = processFrame(frame.data, key, frame.timestamp);
|
|
||||||
controller.enqueue(frame);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Sender E2EE frame failed:", e);
|
|
||||||
controller.enqueue(frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
readable.pipeThrough(enc).pipeTo(writable).catch((e) => {
|
|
||||||
console.error("Sender E2EE pipeline failed:", e);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function attachReceiverE2EE(receiver: RTCRtpReceiver, keyInput: KeyInput): Promise<void> {
|
export async function attachReceiverE2EE(receiver: RTCRtpReceiver, keyInput: KeyInput): Promise<void> {
|
||||||
if (receiverAttached.has(receiver)) {
|
if (receiverAttached.has(receiver)) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
receiverAttached.add(receiver);
|
receiverAttached.add(receiver);
|
||||||
|
|
||||||
const key = toUint8Array(keyInput);
|
const key = toUint8Array(keyInput);
|
||||||
if (key.byteLength !== 32) {
|
if (key.byteLength !== 32) throw new Error(`E2EE key must be 32 bytes, got ${key.byteLength}`);
|
||||||
throw new Error(`E2EE key must be 32 bytes, got ${key.byteLength}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const anyReceiver = receiver as any;
|
const anyReceiver = receiver as any;
|
||||||
if (!anyReceiver.createEncodedStreams) {
|
if (!anyReceiver.createEncodedStreams) throw new Error("createEncodedStreams not available on RTCRtpReceiver");
|
||||||
throw new Error("createEncodedStreams is not available on RTCRtpReceiver");
|
|
||||||
}
|
|
||||||
|
|
||||||
const { readable, writable } = anyReceiver.createEncodedStreams();
|
const { readable, writable } = anyReceiver.createEncodedStreams();
|
||||||
|
const processFrame = createFrameProcessor(key);
|
||||||
|
|
||||||
const dec = new TransformStream<any, any>({
|
readable
|
||||||
// Синхронный transform — нет async, нет накопления очереди
|
.pipeThrough(createTransform(processFrame))
|
||||||
transform(frame, controller) {
|
.pipeTo(writable)
|
||||||
try {
|
.catch((e) => console.error("Receiver E2EE pipeline failed:", e));
|
||||||
frame.data = processFrame(frame.data, key, frame.timestamp);
|
|
||||||
controller.enqueue(frame);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Receiver E2EE frame failed:", e);
|
|
||||||
controller.enqueue(frame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
readable.pipeThrough(dec).pipeTo(writable).catch((e) => {
|
|
||||||
console.error("Receiver E2EE pipeline failed:", e);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user