diff --git a/src/background/index.ts b/src/background/index.ts index 5e3f19b..3702657 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -1,12 +1,19 @@ import { Logger } from '../shared/logger'; +import { base64ToBlob } from '../shared/binary'; import type { Message, + TransferredVideoChunk, VideoChunk, SessionMetadata, VideoBufferResponse } from '../shared/types'; import JSZip from 'jszip'; +// Default MIME applied when a wire chunk somehow lacks a type +// field (defense-in-depth: in normal operation the offscreen recorder +// always populates it from chunk.data.type). Matches D-20 strict codec. +const VIDEO_MIME_FALLBACK = 'video/webm;codecs=vp9'; + const logger = new Logger('Main'); // Состояние @@ -88,6 +95,11 @@ chrome.runtime.onConnect.addListener((port) => { // per-request listener installed in getVideoBufferFromOffscreen). }); +// 2 s budget covers the worst-case round-trip: offscreen base64-encodes +// up to ~15 chunks of ~100 KB each (~1.5 MB raw → ~2 MB base64) in +// well under 100 ms, post-message + JSON parse adds < 50 ms, leaving +// plenty of headroom. Bumping later is cheap if real-world recordings +// produce significantly larger buffers; today this is sufficient. const BUFFER_FETCH_TIMEOUT_MS = 2_000; async function getVideoBufferFromOffscreen(): Promise { @@ -110,7 +122,30 @@ async function getVideoBufferFromOffscreen(): Promise { ) { clearTimeout(timer); port.onMessage.removeListener(handler); - const chunks = (msg as { chunks?: VideoChunk[] }).chunks ?? []; + // D-12 fix: chunks arrive as TransferredVideoChunk[] (base64 + // string + MIME). Decode each back into a VideoChunk so + // mergeVideoChunks keeps operating on real Blobs. See + // src/shared/binary.ts and the GREEN block of + // tests/offscreen/port-serialization.test.ts. + const wireChunks = + (msg as { chunks?: TransferredVideoChunk[] }).chunks ?? []; + const chunks: VideoChunk[] = []; + for (const wire of wireChunks) { + try { + chunks.push({ + data: base64ToBlob(wire.data, wire.type || VIDEO_MIME_FALLBACK), + timestamp: wire.timestamp, + isFirst: wire.isFirst, + }); + } catch (err) { + logger.warn( + 'base64ToBlob failed; skipping chunk', + 'timestamp:', wire.timestamp, + 'isFirst:', wire.isFirst, + 'error:', err, + ); + } + } resolve({ chunks }); } };