Skip to content

Commit b0a8275

Browse files
committed
new: working dataToVideo, videoToImages for dev/integration with imageToData
1 parent 8bafb8a commit b0a8275

File tree

2 files changed

+252
-0
lines changed

2 files changed

+252
-0
lines changed

main/dataToVideo.js

+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
#!/usr/bin/env node
2+
"use strict";
3+
4+
const fs = require("fs");
5+
const fsExtra = require("fs-extra");
6+
const path = require("path");
7+
const { spawn } = require("child_process");
8+
const prompt = require("prompt-sync")();
9+
const ProgressManager = require("./progressManager");
10+
11+
// Konfigurace videa
12+
const videoWidth = 1920;
13+
const videoHeight = 1080;
14+
const frameSize = videoWidth * videoHeight * 3;
15+
16+
// Kódovací barevná mapa (stejná, jakou používáme pro převod nibble na pixel)
17+
const colorMap = {
18+
"0": { r: 255, g: 255, b: 255 },
19+
"1": { r: 0, g: 0, b: 0 },
20+
"2": { r: 255, g: 0, b: 0 },
21+
"3": { r: 0, g: 255, b: 0 },
22+
"4": { r: 0, g: 0, b: 255 },
23+
"5": { r: 255, g: 255, b: 0 },
24+
"6": { r: 0, g: 255, b: 255 },
25+
"7": { r: 255, g: 0, b: 255 },
26+
"8": { r: 128, g: 0, b: 0 },
27+
"9": { r: 0, g: 128, b: 0 },
28+
"A": { r: 0, g: 0, b: 128 },
29+
"B": { r: 255, g: 165, b: 0 },
30+
"C": { r: 75, g: 0, b: 130 },
31+
"D": { r: 173, g: 255, b: 47 },
32+
"E": { r: 255, g: 20, b: 147 },
33+
"F": { r: 192, g: 192, b: 192 }
34+
};
35+
36+
// Pomocná funkce: převod řetězce na nibble – každý znak se převede na dvě 4-bitové hodnoty
37+
function stringToNibbles(str) {
38+
let nibbles = [];
39+
for (const char of str) {
40+
const binary = char.charCodeAt(0).toString(2).padStart(8, "0");
41+
nibbles.push(binary.substring(0, 4));
42+
nibbles.push(binary.substring(4, 8));
43+
}
44+
return nibbles;
45+
}
46+
47+
// Vrací barvu podle nibble – nibble se nejprve převede z binárního řetězce na hexadecimální znak.
48+
function getColorFromNibble(nibble) {
49+
if (!colorMap[nibble]) {
50+
console.warn(`Invalid nibble: ${nibble}`);
51+
return { r: 0, g: 0, b: 0 };
52+
}
53+
return colorMap[nibble];
54+
}
55+
56+
// Funkce vloží do pixelového proudu informaci o názvu souboru pomocí markerů
57+
function pushFilename(filename, appendPixel) {
58+
// Marker začátku – použijeme pixel {128,128,128}
59+
appendPixel({ r: 128, g: 128, b: 128 });
60+
const nibbles = stringToNibbles(filename);
61+
for (const nib of nibbles) {
62+
const hexDigit = parseInt(nib, 2).toString(16).toUpperCase();
63+
appendPixel(getColorFromNibble(hexDigit));
64+
}
65+
// Marker konce názvu
66+
appendPixel({ r: 128, g: 128, b: 128 });
67+
}
68+
69+
// Globální buffer a offset pro aktuální snímek
70+
let currentFrameBuffer = Buffer.alloc(frameSize);
71+
let currentOffset = 0;
72+
73+
// Funkce, která přidá pixel do aktuálního snímkového bufferu a v případě, že je snímek plný, ho ihned odešle do ffmpeg.
74+
function appendPixel(pixel, ffmpegStdin) {
75+
if (currentOffset + 3 > frameSize) {
76+
flushFrameBuffer(ffmpegStdin);
77+
}
78+
currentFrameBuffer[currentOffset] = pixel.r;
79+
currentFrameBuffer[currentOffset + 1] = pixel.g;
80+
currentFrameBuffer[currentOffset + 2] = pixel.b;
81+
currentOffset += 3;
82+
if (currentOffset === frameSize) {
83+
ffmpegStdin.write(currentFrameBuffer);
84+
currentFrameBuffer = Buffer.alloc(frameSize);
85+
currentOffset = 0;
86+
}
87+
}
88+
89+
// Pokud zbyde částečný snímek, doplní ho černou barvou a odešle.
90+
function flushFrameBuffer(ffmpegStdin) {
91+
if (currentOffset > 0) {
92+
currentFrameBuffer.fill(0, currentOffset);
93+
ffmpegStdin.write(currentFrameBuffer);
94+
currentFrameBuffer = Buffer.alloc(frameSize);
95+
currentOffset = 0;
96+
}
97+
}
98+
99+
// Převod datového chunku na pole pixelů (každý byte se převede na dvě barvy podle hexadecimální hodnoty)
100+
function processDataChunk(chunk) {
101+
let pixels = [];
102+
for (let i = 0; i < chunk.length; i++) {
103+
const byte = chunk[i];
104+
const highNibble = (byte >> 4).toString(16).toUpperCase();
105+
const lowNibble = (byte & 0x0F).toString(16).toUpperCase();
106+
pixels.push(getColorFromNibble(highNibble));
107+
pixels.push(getColorFromNibble(lowNibble));
108+
}
109+
return pixels;
110+
}
111+
112+
// Získá všechny soubory z adresáře (rekurzivně)
113+
function getAllFiles(dir, fileList = []) {
114+
const files = fs.readdirSync(dir);
115+
files.forEach(file => {
116+
const fullPath = path.join(dir, file);
117+
if (fs.statSync(fullPath).isDirectory()) {
118+
getAllFiles(fullPath, fileList);
119+
} else {
120+
fileList.push(fullPath);
121+
}
122+
});
123+
return fileList;
124+
}
125+
126+
async function main() {
127+
const inputPath = process.argv[2] || prompt("Define input file/directory: ");
128+
const outputVideoPath = path.join(__dirname, "./../output_video/out.mkv");
129+
let files = [];
130+
if (fs.statSync(inputPath).isDirectory()) {
131+
files = getAllFiles(inputPath);
132+
} else {
133+
files.push(inputPath);
134+
}
135+
136+
console.log(`Budu zpracovávat ${files.length} souborů.`);
137+
138+
// Spuštění ffmpeg pro kódování raw videa do lossless videa pomocí ffv1
139+
const ffmpegArgs = [
140+
"-y",
141+
"-f",
142+
"rawvideo",
143+
"-pixel_format",
144+
"rgb24",
145+
"-video_size",
146+
`${videoWidth}x${videoHeight}`,
147+
"-framerate",
148+
"30",
149+
"-i",
150+
"-",
151+
"-c:v",
152+
"ffv1",
153+
"-preset",
154+
"ultrafast",
155+
outputVideoPath,
156+
];
157+
const ffmpeg = spawn("ffmpeg", ffmpegArgs);
158+
159+
ffmpeg.on("error", (err) => {
160+
console.error("Chyba ffmpeg:", err);
161+
process.exit(1);
162+
});
163+
164+
ffmpeg.stderr.on("data", (data) => {
165+
// Volitelně můžeš logovat progres; zatím zakomentováno
166+
// console.error(data.toString());
167+
});
168+
169+
// Použijeme číslovaný cyklus, abychom věděli, jestli zpracováváme poslední soubor
170+
for (let i = 0; i < files.length; i++) {
171+
const file = files[i];
172+
console.log(`Zpracovávám soubor: ${file}`);
173+
const fileSize = fs.statSync(file).size;
174+
const progressManager = new ProgressManager(fileSize, path.basename(file));
175+
176+
// Vložíme název souboru jako marker
177+
pushFilename(file, (pixel) => {
178+
appendPixel(pixel, ffmpeg.stdin);
179+
});
180+
181+
await new Promise((resolve, reject) => {
182+
const stream = fs.createReadStream(file, { highWaterMark: 1024 * 1024 });
183+
stream.on("data", (chunk) => {
184+
progressManager.update(chunk.length);
185+
const pixels = processDataChunk(chunk);
186+
for (const pixel of pixels) {
187+
appendPixel(pixel, ffmpeg.stdin);
188+
}
189+
});
190+
stream.on("end", () => {
191+
// Vložíme terminátor – marker D: pixel s hodnotami {128, 0, 128}
192+
appendPixel({ r: 128, g: 0, b: 128 }, ffmpeg.stdin);
193+
// Pokud je to poslední soubor, doplníme aktuální snímek (neúplný blok) černými pixely a flushneme ho.
194+
if (i === files.length - 1) {
195+
flushFrameBuffer(ffmpeg.stdin);
196+
}
197+
console.log(`Dokončeno: ${file}`);
198+
resolve();
199+
});
200+
stream.on("error", (err) => reject(err));
201+
});
202+
}
203+
// Po zpracování všech souborů flushneme zůstalé pixely a ukončíme stdin ffmpeg
204+
flushFrameBuffer(ffmpeg.stdin);
205+
ffmpeg.stdin.end();
206+
207+
ffmpeg.on("close", (code) => {
208+
if (code === 0) {
209+
console.log("Video bylo úspěšně vytvořeno!");
210+
} else {
211+
console.error(`FFmpeg skončil s kódem ${code}`);
212+
}
213+
});
214+
}
215+
216+
main().catch((err) => {
217+
console.error("Chyba v dataToVideo.js:", err);
218+
});

main/videoToImages.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"use strict";
2+
3+
const { spawn } = require("child_process");
4+
const fs = require("fs");
5+
const fsExtra = require("fs-extra");
6+
const path = require("path");
7+
8+
// Cesty k videu a složce, kam se uloží obrázky
9+
const videoFilePath = path.join(__dirname, "./../output_video/out.mkv");
10+
const imagesFolder = path.join(__dirname, "./../images");
11+
fsExtra.emptyDirSync(imagesFolder);
12+
13+
// Pokud složka neexistuje, vytvoříme ji
14+
if (!fs.existsSync(imagesFolder)) {
15+
fs.mkdirSync(imagesFolder, { recursive: true });
16+
}
17+
18+
// Příkaz FFmpeg: Použije vstupní video a uloží každý frame jako obrázek (názvy: 1.png, 2.png, …)
19+
const ffmpeg = spawn("ffmpeg", [
20+
"-i", videoFilePath,
21+
path.join(imagesFolder, "%d.png")
22+
]);
23+
24+
ffmpeg.stdout.on("data", (data) => {
25+
console.log(`stdout: ${data}`);
26+
});
27+
28+
ffmpeg.stderr.on("data", (data) => {
29+
console.log(`stderr: ${data}`);
30+
});
31+
32+
ffmpeg.on("close", (code) => {
33+
console.log(`FFmpeg proces skončil s kódem ${code}`);
34+
});

0 commit comments

Comments
 (0)