start encoding a frame to video

This commit is contained in:
DavidVentura
2024-01-27 22:55:31 +01:00
parent be42eb5b5d
commit 1401af6e81
3 changed files with 148 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
import fs from "node:fs";
import beamcoder from "beamcoder";
//let outFile = fs.createWriteStream("a.h264");
let endcode = Buffer.from([0, 0, 1, 0xb7]);
async function imageToVideo(imagePath, duration, frameRate = 30) {
// Create a demuxer for the JPEG image
let demuxer = await beamcoder.demuxer(imagePath);
// Read the image packet
let packet = await demuxer.read();
// Create a decoder for the image
let decoder = beamcoder.decoder({ demuxer: demuxer, name: "mjpeg" });
// Decode the image to get the frame
let frames = await decoder.decode(packet);
// decoder.flush();
//console.log("frames", frames);
let frame = frames.frames[0];
//console.log("frame", frame.width, frame.height);
// Create an H.264 encoder
let encoder = beamcoder.encoder({
name: "libx264",
width: frame.width,
height: frame.height,
bit_rate: 500000,
time_base: [1, frameRate],
framerate: [frameRate, 1],
pix_fmt: "yuv420p",
preset: "faster",
gop_size: 10,
max_b_frames: 1,
});
let stream = beamcoder.muxerStream({});
stream.pipe(fs.createWriteStream("test.mp4"));
// Create a muxer for the output video
let muxer = stream.muxer({ format_name: "mpegts" }); //"mp4" });
//console.log(demuxer.streams[0].codecpar.extradata); // null
let vstr = muxer.newStream({
name: "ts", //"h264",
time_base: [1, 90000], //frameRate],
interleaved: true,
codecpar: {
width: encoder.width,
height: encoder.height,
format: encoder.pix_fmt,
//extradata: demuxer.streams[0].codecpar.extradata,
},
});
//await muxer.openIO();
//await muxer.initOutput({ movflags: "frag_keyframe+empty_moov+default_base_moof" });
console.log("inited");
// Add a video stream to the muxer
await muxer.writeHeader();
console.log("header written");
// Number of frames to encode
let totalFrames = duration * frameRate;
console.log("making frames", totalFrames);
for (let i = 0; i < totalFrames; i++) {
// Encode the frame
frame.pts = i;
let encodedPackets = await encoder.encode(frame);
// console.log(encodedPackets);
// Write the encoded packets to the output file
for (let packet of encodedPackets.packets) {
packet.duration = 1;
packet.stream_index = vstr.index;
packet.pts = (packet.pts * 90000) / frameRate;
packet.dts = (packet.dts * 90000) / frameRate;
//packet.pts = i;
await muxer.writeFrame(packet);
// outFile.write(packet.data);
}
}
// Finalize the encoder and muxer
let encodedPackets = await encoder.flush();
// after flushing the encoder, we may hve some more packets
// Write the encoded packets to the output file
for (let packet of encodedPackets.packets) {
packet.duration = 1;
packet.stream_index = vstr.index;
packet.pts = (packet.pts * 90000) / frameRate;
packet.dts = (packet.dts * 90000) / frameRate;
await muxer.writeFrame(packet);
}
//outFile.end(endcode);
await muxer.writeTrailer();
}
// Usage example
imageToVideo("captures/0010.jpg", 20)
.then(() => {
console.log("Video created successfully");
})
.catch(console.error);
+41
View File
@@ -4,6 +4,9 @@
"requires": true,
"packages": {
"": {
"dependencies": {
"beamcoder": "^0.7.1"
},
"devDependencies": {
"mocha": "^10.2.0",
"ts-node": "^10.9.2",
@@ -166,6 +169,16 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
"node_modules/beamcoder": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/beamcoder/-/beamcoder-0.7.1.tgz",
"integrity": "sha512-20gpjy6V+9+X3v1H5wfofzc+MbVCPXv3JE4cm9y5R28pW2LRz2LSlJptiUxZJI8ips4xBoq7oYh+1LE5HvZskQ==",
"hasInstallScript": true,
"dependencies": {
"bindings": "^1.5.0",
"segfault-handler": "^1.3.0"
}
},
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -175,6 +188,14 @@
"node": ">=8"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
@@ -381,6 +402,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -714,6 +740,11 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
"node_modules/nan": {
"version": "2.18.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w=="
},
"node_modules/nanoid": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
@@ -854,6 +885,16 @@
}
]
},
"node_modules/segfault-handler": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/segfault-handler/-/segfault-handler-1.3.0.tgz",
"integrity": "sha512-p7kVHo+4uoYkr0jmIiTBthwV5L2qmWtben/KDunDZ834mbos+tY+iO0//HpAJpOFSQZZ+wxKWuRo4DxV02B7Lg==",
"hasInstallScript": true,
"dependencies": {
"bindings": "^1.2.1",
"nan": "^2.14.0"
}
},
"node_modules/serialize-javascript": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+3
View File
@@ -7,5 +7,8 @@
"mocha": "^10.2.0",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"dependencies": {
"beamcoder": "^0.7.1"
}
}