Initial commit

This commit is contained in:
Kuba Orlik 2022-07-12 22:39:00 +02:00
commit e6836a8ab3
5 changed files with 1528 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/node_modules/
/lib/

1370
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

18
package.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "record",
"version": "1.0.0",
"description": "",
"main": "lib/record.js",
"scripts": {
"start": "npm run build && node lib/record.js",
"build": "tsc"
},
"type": "module",
"author": "",
"license": "ISC",
"devDependencies": {
"ts-node": "^10.8.2",
"typescript": "^4.7.4",
"zx": "^7.0.7"
}
}

125
record.ts Executable file
View File

@ -0,0 +1,125 @@
#!/usr/bin/env -S ts-node-esm --compilerOptions='{"module": "es2022"}'
$.verbose = false;
import { $ } from "zx";
// Or
import "zx/globals";
import { spawn } from "child_process";
import { PassThrough } from "stream";
function sleep(time: number) {
return new Promise<void>((resolve) => {
setTimeout(() => resolve(), time);
});
}
void (async function () {
const source = spawn("ffmpeg", [
"-f",
"video4linux2",
"-input_format",
"nv12",
"-thread_queue_size",
"512",
"-video_size",
"3840x2160",
"-i",
"/dev/video2",
"-f",
"nut",
"-c",
"copy",
"pipe:1",
]);
const preview = spawn("ffplay", [
"-f",
"nut",
"-use_wallclock_as_timestamps",
"1",
"-",
]);
const convert_and_add_audio = spawn("ffmpeg", [
"-y",
"-vaapi_device",
"/dev/dri/renderD128",
"-f",
"nut",
"-i",
"-",
"-f",
"pulse",
"-thread_queue_size",
"512",
"-i",
"alsa_input.usb-Elgato_Cam_Link_4K_0123456789000-03.analog-stereo",
"-vf",
"format=nv12,hwupload",
"-c:v",
"h264_vaapi",
"-b:v",
"12M",
"-f",
"mp4",
"output.mp4",
]);
console.log("started ffmpeg...");
source.stderr.on("data", (data) => {
// console.error("source", data.toString());
});
convert_and_add_audio.stderr.on("data", (data) => {
// console.error("convert", data.toString());
});
preview.stderr.on("data", (data) => {
// console.error("preview", data.toString());
});
// preview.stderr.on("data", (data) => console.error(data.toString()));
const source_stream_for_convert = new PassThrough();
const source_stream_for_preview = new PassThrough();
// const source_stream_for_dump = new PassThrough();
// source.stdout.pipe(source_stream_for_convert);
// // source.stdout.pipe(source_stream_for_preview);
// source.stdout.pipe(source_stream_for_dump);
//
source_stream_for_convert.pipe(convert_and_add_audio.stdin);
// source_stream_for_preview.pipe(preview.stdin);
let preview_ok = true;
const stats = {};
let count = 0;
let preview_stopped = false;
let preview_can_resume = false;
let preview_should_stop_asap = false;
source.stdout.on("data", (data) => {
// this implements framedropping. we detect when a frame starts and emit it only when the stdin is drained
count++;
// console.log("got data!", a++);
const beginning = data.slice(0, 4).join("");
if (beginning == "7875228173") {
//means the beginning of the frame (counted the most frequent start of data and that was the most frequent)
if (preview_can_resume) {
preview_stopped = false;
}
if (preview_should_stop_asap) {
preview_should_stop_asap = false;
preview_stopped = true;
}
}
if (!preview_stopped) {
let preview_result = preview.stdin.write(data);
if (!preview_result && !preview_should_stop_asap) {
preview_should_stop_asap = true;
preview_can_resume = false;
preview.stdin.once("drain", () => {
preview_can_resume = true;
});
}
}
convert_and_add_audio.stdin.write(data);
});
})();

13
tsconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "nodenext",
"esModuleInterop": true,
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"outDir": "./lib"
},
"files": ["record.ts"],
"exclude": ["node_modules/**/*"]
}