From 118affaefe08708b628dace902b7fc0a5fc961dd Mon Sep 17 00:00:00 2001 From: Denys Bezmenov Date: Mon, 25 Nov 2024 12:54:43 -0800 Subject: [PATCH] Krec ts & ffmpeg fix (#4) * krec-ts * added ffmpeg null output; gitignore --- .gitignore | 4 +++ Cargo.toml | 2 +- krec-ts/package.json | 23 +++++++++++++ krec-ts/src/example.ts | 40 +++++++++++++++++++++ krec-ts/src/generated/.keep | 0 krec-ts/src/krec.ts | 67 ++++++++++++++++++++++++++++++++++++ krec-ts/tsconfig.json | 14 ++++++++ krec/bindings/pyproject.toml | 2 +- src/ffmpeg.rs | 10 +++++- 9 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 krec-ts/package.json create mode 100644 krec-ts/src/example.ts create mode 100644 krec-ts/src/generated/.keep create mode 100644 krec-ts/src/krec.ts create mode 100644 krec-ts/tsconfig.json diff --git a/.gitignore b/.gitignore index 8c07eaf..16d3bea 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,7 @@ out*/ # Rust Cargo.lock target/ + +# Node +krec-ts/node_modules/ +krec-ts/src/generated/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 0d9d1d6..2a8cd95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,5 +28,5 @@ resolver = "2" [workspace.package] -version = "0.2.7" +version = "0.2.8" edition = "2021" diff --git a/krec-ts/package.json b/krec-ts/package.json new file mode 100644 index 0000000..9fe3926 --- /dev/null +++ b/krec-ts/package.json @@ -0,0 +1,23 @@ +{ + "name": "krec-ts", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "generate-proto": "pbjs -t static-module -w commonjs -o src/generated/proto.js ../proto/krec.proto && pbts -o src/generated/proto.d.ts src/generated/proto.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@types/node": "^18.19.65", + "long": "^5.2.3", + "protobufjs": "^7.4.0" + }, + "devDependencies": { + "@types/long": "^5.0.0", + "ts-node": "^10.9.2", + "typescript": "^5.8.0" + } +} diff --git a/krec-ts/src/example.ts b/krec-ts/src/example.ts new file mode 100644 index 0000000..ff61ce9 --- /dev/null +++ b/krec-ts/src/example.ts @@ -0,0 +1,40 @@ +import { KRec } from "./krec"; +import { promises as fs } from "fs"; +import { Long } from "protobufjs/minimal"; + +// Helper function to convert Long timestamp to Date +function longToDate(timestamp: Long): Date { + // Convert nanoseconds to milliseconds + const ms = Number(timestamp) / 1_000_000; + return new Date(ms); +} + +// Helper function to format Long to number (safe for frame numbers) +function longToNumber(value: Long): number { + return Number(value); +} + +async function main() { + const buffer = await fs.readFile("./test.krec"); + + const krec = await KRec.load(buffer); + + console.log("Recording UUID:", krec.header.uuid); + console.log("Number of frames:", krec.frames.length); + + if (krec.frames.length > 0) { + const frame = krec.frames[0]; + console.log("First frame:", { + timestamp: longToDate(frame.videoTimestamp as Long).toISOString(), + frameNumber: longToNumber(frame.frameNumber as Long), + actuatorStates: frame.actuatorStates?.map(state => ({ + online: state.online, + position: state.position, + velocity: state.velocity, + torque: state.torque + })) + }); + } +} + +main().catch(console.error); \ No newline at end of file diff --git a/krec-ts/src/generated/.keep b/krec-ts/src/generated/.keep new file mode 100644 index 0000000..e69de29 diff --git a/krec-ts/src/krec.ts b/krec-ts/src/krec.ts new file mode 100644 index 0000000..c22a170 --- /dev/null +++ b/krec-ts/src/krec.ts @@ -0,0 +1,67 @@ +import { Reader } from "protobufjs"; +import { krec } from "./generated/proto"; + +export class KRec { + header: krec.proto.IKRecHeader; + frames: krec.proto.IKRecFrame[]; + + constructor(header: krec.proto.IKRecHeader) { + this.header = header; + this.frames = []; + } + + static async load(buffer: Buffer): Promise { + let pos = 0; + + // Read header length and decode header + if (buffer.length < 4) { + throw new Error(`File too short: ${buffer.length} bytes`); + } + + const headerLen = buffer.readUInt32LE(0); + pos += 4; + + if (pos + headerLen > buffer.length) { + throw new Error( + `Incomplete header data: need ${headerLen} bytes, have ${buffer.length - pos} bytes` + ); + } + + const header = krec.proto.KRecHeader.decode( + Reader.create(buffer.subarray(pos, pos + headerLen)) + ); + pos += headerLen; + + const frames: krec.proto.IKRecFrame[] = []; + + // Read frames + while (pos + 4 <= buffer.length) { + const frameLen = buffer.readUInt32LE(pos); + pos += 4; + + if (pos + frameLen > buffer.length) { + throw new Error( + `Incomplete frame data: at position ${pos}, need ${frameLen} bytes, have ${ + buffer.length - pos + } bytes remaining` + ); + } + + const frame = krec.proto.KRecFrame.decode( + Reader.create(buffer.subarray(pos, pos + frameLen)) + ); + pos += frameLen; + frames.push(frame); + } + + if (pos !== buffer.length) { + throw new Error( + `Trailing data: ${buffer.length - pos} bytes remaining after position ${pos}` + ); + } + + const krecInstance = new KRec(header); + krecInstance.frames = frames; + return krecInstance; + } +} \ No newline at end of file diff --git a/krec-ts/tsconfig.json b/krec-ts/tsconfig.json new file mode 100644 index 0000000..66862c8 --- /dev/null +++ b/krec-ts/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "outDir": "./dist", + "declaration": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/krec/bindings/pyproject.toml b/krec/bindings/pyproject.toml index d4d3c3c..7da912b 100644 --- a/krec/bindings/pyproject.toml +++ b/krec/bindings/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "bindings" -version = "0.1.4" +version = "0.1.5" description = "Python bindings for KRec" authors = [{ name = "Denys Bezmenov", email = "denys@kscale.dev" }] requires-python = ">=3.7" diff --git a/src/ffmpeg.rs b/src/ffmpeg.rs index d55a50b..7ebfafd 100644 --- a/src/ffmpeg.rs +++ b/src/ffmpeg.rs @@ -83,7 +83,15 @@ pub fn combine_with_video( pub fn extract_from_video(video_path: &str, output_path: &str) -> Result<(), FFmpegError> { let status = std::process::Command::new("ffmpeg") - .args(["-dump_attachment:t:0", output_path, "-i", video_path]) + .args([ + "-dump_attachment:t:0", + output_path, + "-i", + video_path, + "-f", + "null", + "/dev/null", + ]) .status() .map_err(|e| FFmpegError::FFmpeg(e.to_string()))?;