feat(01-08): swap mergeVideoSegments -> await remuxSegments at call site
- src/background/index.ts now imports remuxSegments from './webm-remux'
and awaits it in createArchive instead of synchronously calling the
retired file-concat mergeVideoSegments.
- mergeVideoSegments function declaration deleted entirely; only a
retirement comment remains naming Plan 01-08 D-14-remux as the
superseding decision.
- EmptyVideoBufferError throw paths preserved on (a) zero segments
AND (b) zero-byte output. Error message free-text changed from
"merged video blob is zero bytes" to "remuxed video blob is zero
bytes"; pre-flight grep (W-01 fix from plan checker pass)
confirmed no downstream consumer matches on the legacy string —
request-id-protocol.test.ts asserts on error.code ('empty-video-
buffer'), not the free-text message.
- createArchive remains async (was already declared async); saveArchive
already awaits createArchive so no upstream signature changes.
- Stale comment in decodeBufferSegments referencing mergeVideoSegments
updated to reflect the new remux pipeline (Rule 3: keep forward-
references accurate).
- CONTEXT.md amendment provenance verified intact via 4 grep checks
(B-01 fix from plan checker, folded from retired Task 6):
(a) D-14-remux disambiguated marker present (1 match)
(b) original D-13 line preserved (1 match)
(c) D-17-port-lifecycle amendment intact (1 match)
(d) webm-remux.ts replaces citation present (1 match)
No CONTEXT.md mutation by this task — verify-only step.
- npm run build exit 0; main SW bundle 374.56 KB (108.44 KB gzipped,
matches the d13 library survey's ~100 KB estimate for ts-ebml +
webm-muxer combined).
- Full suite: 13 files / 60 GREEN + 2 RED (webm-playback duration
assertions waiting on Task 5 fixture regen). tsc exit 0.
This commit is contained in:
@@ -7,6 +7,7 @@ import type {
|
||||
SessionMetadata,
|
||||
VideoBufferResponse
|
||||
} from '../shared/types';
|
||||
import { remuxSegments } from './webm-remux';
|
||||
import JSZip from 'jszip';
|
||||
|
||||
// Default MIME applied when a wire chunk somehow lacks a type
|
||||
@@ -130,9 +131,10 @@ function decodeBufferSegments(
|
||||
wireSegments: TransferredVideoSegment[],
|
||||
): VideoSegment[] {
|
||||
// WR-07 fix: filter empty wire segments BEFORE base64 decode. An empty
|
||||
// wire.data would decode to a zero-byte Blob; mergeVideoSegments would
|
||||
// then concat it into the output WebM, producing a stray empty EBML
|
||||
// segment that breaks Chrome playback. Two passes (filter -> decode ->
|
||||
// wire.data would decode to a zero-byte Blob; the remux pipeline
|
||||
// (src/background/webm-remux.ts, Plan 01-08 D-14-remux) would try
|
||||
// to parse it via ts-ebml and either fail loudly or emit zero frames,
|
||||
// either way wasting a parse cycle. Two passes (filter -> decode ->
|
||||
// filter-non-empty) keep the iteration semantics declarative.
|
||||
const nonEmptyWires = wireSegments.filter((wire) => {
|
||||
const isEmpty = !wire.data || wire.data.length === 0;
|
||||
@@ -382,43 +384,14 @@ async function captureScreenshot(): Promise<Blob> {
|
||||
return cachedScreenshot;
|
||||
}
|
||||
|
||||
// Склейка сегментов в один WebM-файл.
|
||||
//
|
||||
// Под D-13 каждый VideoSegment — это самодостаточный ~10-секундный WebM
|
||||
// (собственный EBML-заголовок + seed keyframe). Сортируем по timestamp
|
||||
// и склеиваем подряд: получаем multi-EBML-header файл, который Chrome
|
||||
// проигрывает последовательно (см. SPEC §10 #7 — требуется проигрывание
|
||||
// в Chrome, кросс-плейер совместимость вне scope Phase 1).
|
||||
//
|
||||
// Никакой логики «pin первого чанка» или header-retention больше нет
|
||||
// — это снято вместе с D-09..D-11 и активацией D-13 в recorder.ts.
|
||||
function mergeVideoSegments(segments: VideoSegment[]): Blob {
|
||||
logger.log(`Merging ${segments.length} segments`);
|
||||
|
||||
// Сортируем по времени, чтобы сохранить правильный порядок (старшие
|
||||
// сегменты — раньше). Под D-13 порядок уже задаётся offscreen-стороной,
|
||||
// но сортируем оборонительно — стоимость copy+sort ничтожна.
|
||||
const sortedSegments = [...segments].sort((a, b) => a.timestamp - b.timestamp);
|
||||
|
||||
logger.log(
|
||||
`Segments sorted, first timestamp: ${sortedSegments[0]?.timestamp}, ` +
|
||||
`last: ${sortedSegments[sortedSegments.length - 1]?.timestamp}`,
|
||||
);
|
||||
|
||||
// Каждый сегмент уже валидный WebM — конкатенация безопасна.
|
||||
const blobs: Blob[] = sortedSegments.map((segment, index) => {
|
||||
logger.log(`Adding segment ${index}, size: ${segment.data.size} bytes`);
|
||||
return segment.data;
|
||||
});
|
||||
|
||||
const finalBlob = new Blob(blobs, { type: 'video/webm' });
|
||||
logger.log(
|
||||
`Final video blob size: ${finalBlob.size} bytes, ` +
|
||||
`total segments merged: ${blobs.length}`,
|
||||
);
|
||||
|
||||
return finalBlob;
|
||||
}
|
||||
// mergeVideoSegments (D-13 file-concat) retired in Plan 01-08 (D-14-remux):
|
||||
// see src/background/webm-remux.ts for the single-EBML remux path.
|
||||
// The concat-of-self-contained-WebM-segments approach produced a
|
||||
// multi-EBML-header file that mpv / Chrome / ffprobe truncated to
|
||||
// the first segment's local Info.Duration ~9.94 s; the remux path
|
||||
// emits a single-EBML WebM whose Info.Duration covers the full ~30 s
|
||||
// timeline. D-13's recorder-side restart-segments lifecycle is
|
||||
// preserved — only the merge step is replaced.
|
||||
|
||||
// Создание архива
|
||||
async function createArchive(
|
||||
@@ -446,14 +419,17 @@ async function createArchive(
|
||||
'no video segments available — buffer fetch returned empty (port replacement timed out, or recorder never started)',
|
||||
);
|
||||
}
|
||||
const videoBlob = mergeVideoSegments(videoBufferResponse.segments);
|
||||
// Plan 01-08 D-14-remux: replaces the retired mergeVideoSegments()
|
||||
// file-concat with the new single-EBML WebM remux. Async now —
|
||||
// ts-ebml parse + webm-muxer write happen on the SW thread.
|
||||
const videoBlob = await remuxSegments(videoBufferResponse.segments);
|
||||
if (videoBlob.size === 0) {
|
||||
throw new EmptyVideoBufferError(
|
||||
`merged video blob is zero bytes (segment count=${videoBufferResponse.segments.length})`,
|
||||
`remuxed video blob is zero bytes (segment count=${videoBufferResponse.segments.length})`,
|
||||
);
|
||||
}
|
||||
zip.file('video/last_30sec.webm', videoBlob);
|
||||
logger.log(`✓ Added video: ${videoBlob.size} bytes`);
|
||||
logger.log(`✓ Added video (remuxed): ${videoBlob.size} bytes`);
|
||||
|
||||
// Добавляем rrweb события
|
||||
const rrwebJson = JSON.stringify(rrwebEvents, null, 2);
|
||||
|
||||
Reference in New Issue
Block a user