Phase 2 — replace the 114MB node-av binary with a plain ffmpeg spawn:
Demuxer.ts: spool stream input to temp file → probe via ffmpeg stderr
(ffmpeg-headless ships NO ffprobe — parse 'Stream #0:0: Video: h264...
640x360, 30 fps' from -loglevel info) → ffmpeg -c copy -f h264 pipe:1
→ NAL-split frames. Falls back to h264 defaults when probe fails.
prepareStream.ts: resolve ffmpeg from FFMPEG_PATH env → Nix store
ffmpeg-headless (hash-prefixed entry!) → PATH; split encoder option
strings ('-forced-idr 1' → two argv) — fluent-ffmpeg used to split
automatically, spawn does not.
E2E tests (tsx, need LD_LIBRARY_PATH=/tmp/ldc-build):
- golive-demux-e2e.ts: real H264 file → 33 NAL frames + dims from probe
- golive-pipeline-e2e.ts: prepareStream → demux → 82 frames
- golive-videostream-e2e.ts: local peer pair → demux → VideoStream →
native setPacketizer/sendFrame/addTimestamp → 33 frames sent connected
Pitfalls captured: setPacketizer before negotiation breaks createOffer
('No DataChannel or Track to negotiate'); track methods are read-only
(no monkeypatching); both peers must declare audio+video tracks or
answer hangs; state() returns 'closed' after close() — snapshot first.
41 lines
1.0 KiB
TypeScript
41 lines
1.0 KiB
TypeScript
// Phase 2 E2E: Demuxer on a real ffmpeg-generated H264 file.
|
|
// Run: npx tsx tests/golive-demux-e2e.ts
|
|
|
|
import { createReadStream } from "node:fs";
|
|
import { demux } from "../src/goLive/Demuxer.js";
|
|
|
|
const input = process.argv[2] ?? "/tmp/sample.h264";
|
|
const { video, close } = await demux(createReadStream(input), {
|
|
format: "h264",
|
|
});
|
|
|
|
console.log(
|
|
"video:",
|
|
JSON.stringify({
|
|
codecName: video.codecName,
|
|
width: video.width,
|
|
height: video.height,
|
|
duration: video.duration,
|
|
fps: Math.round(video.framerate_num / video.framerate_den),
|
|
}),
|
|
);
|
|
|
|
let count = 0;
|
|
let keyframes = 0;
|
|
let bytes = 0;
|
|
video.stream.on("data", (frame: { data: Buffer; keyframe: boolean }) => {
|
|
count++;
|
|
bytes += frame.data.length;
|
|
if (frame.keyframe) keyframes++;
|
|
});
|
|
video.stream.on("end", () => {
|
|
console.log(`frames: ${count} (${keyframes} keyframes), ${bytes} bytes`);
|
|
close();
|
|
process.exit(0);
|
|
});
|
|
video.stream.on("error", (e: unknown) => {
|
|
console.error("stream error:", e);
|
|
close();
|
|
process.exit(1);
|
|
});
|