From 1401af6e81108fe10bf36b2058a0503392083bb4 Mon Sep 17 00:00:00 2001 From: DavidVentura Date: Sat, 27 Jan 2024 22:55:31 +0100 Subject: [PATCH] start encoding a frame to video --- a.js | 104 ++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 41 ++++++++++++++++++ package.json | 3 ++ 3 files changed, 148 insertions(+) create mode 100644 a.js diff --git a/a.js b/a.js new file mode 100644 index 0000000..2a906f5 --- /dev/null +++ b/a.js @@ -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); diff --git a/package-lock.json b/package-lock.json index 00478f9..c585545 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index a57c88d..66509ba 100644 --- a/package.json +++ b/package.json @@ -7,5 +7,8 @@ "mocha": "^10.2.0", "ts-node": "^10.9.2", "typescript": "^5.3.3" + }, + "dependencies": { + "beamcoder": "^0.7.1" } }