gst-kit
v0.2.2
Published
Node.js binding for GStreamer, providing high-level APIs for multimedia streaming and processing
Maintainers
Readme
GStreamer Kit
A modern Node.js binding for GStreamer, providing high-level APIs for multimedia streaming and processing. This project modernizes the legacy node-gstreamer-superficial library with contemporary technologies and improved runtime support.
Project Goals & Modernization
This project represents a complete modernization of the old node-gstreamer-superficial library, featuring:
Modern Runtime Support
- N-API instead of NAN: Uses Node-API (N-API) for better runtime stability and version independence
- Multi-runtime compatibility: Supports Node.js 16+, Bun 1.0+, and other V8-based runtimes
- Version independence: Not bound to specific V8 versions, ensuring longevity
Modern Build System
- Rollup bundling: Generates both CommonJS and ESM modules for maximum compatibility
- TypeScript-first: Complete TypeScript support with full type definitions
- GYP build system: Robust C++ compilation with proper dependency management
- Modern testing: Uses Vitest for fast, concurrent testing instead of legacy test frameworks
Enhanced Developer Experience
- Full TypeScript support: Complete type definitions for all APIs
- ESM/CJS dual packaging: Works with both
importandrequirestatements - Comprehensive testing: Extensive test coverage with modern test runner
- Better documentation: Clear examples and API documentation
Complete Feature Set
Core Pipeline Features
- Pipeline Management: Create, play, pause, stop GStreamer pipelines
- State Management: Comprehensive state change handling with detailed results
- Element Access: Get elements by name with proper typing
- Property System: Get/set element properties with type safety
Advanced Data Access
- App Sources & Sinks: Two approaches for data access:
- Pull-based: Explicitly request samples with
getSample()(async, controlled timing) - Push-based: Reactive callbacks with
onSample()(automatic, real-time)
- Pull-based: Explicitly request samples with
- Pad Probes: Add/remove event-driven callbacks to intercept comprehensive buffer data
- Buffer Analysis: Extract raw data, timing information, flags, caps, and metadata
Media Processing
- RTP Support: Extract RTP metadata including timestamps, sequence numbers, SSRC, payload type
- Seeking: Frame-accurate seeking with success feedback
- Query System: Position and duration queries in seconds
- Message Bus: Handle GStreamer messages (EOS, errors, warnings, state changes)
Runtime Features
- Multi-format Support: Video, audio, containers, streaming protocols
- Codec Support: H.264, H.265, VP8, VP9, AV1, and more through GStreamer plugins
- Network Streaming: RTP, RTSP, HLS, DASH, WebRTC protocols
- Hardware Acceleration: GPU-accelerated encoding/decoding where available
Installation
npm install gst-kitSystem Requirements
- Runtime: Node.js 16+ or Bun 1.0+ (Deno is not supported)
- System: GStreamer 1.14 or higher (1.26+ recommended)
- Build Tools: Python 2.7 or 3.x (for node-gyp), pkg-config
- Dependencies: GStreamer development packages and plugins
Platform-Specific Installation Guide
Cross-Platform Alternative: For any operating system, you can use the setup-cpp tool to automatically install compilers, build tools, and package managers:
# Install development tools across platforms (Windows, macOS, Linux)
npx setup-cpp --compiler auto --pkg-config true
# On Windows, this can also install Chocolatey:
npx setup-cpp --compiler msvc --pkg-config true --choco trueUbuntu/Debian (Recommended for Production)
Option A: Package Manager (Traditional)
# Update package list
sudo apt-get update
# Install GStreamer core development packages
sudo apt-get install -y \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
libgstreamer-plugins-bad1.0-dev \
pkg-config
# Install GStreamer plugins (essential for most use cases)
sudo apt-get install -y \
gstreamer1.0-plugins-base \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly \
gstreamer1.0-libav
# Install build tools
sudo apt-get install -y build-essential python3Option B: Cross-Platform Setup with setup-cpp (Ubuntu)
# Install build tools automatically
npx setup-cpp --compiler auto --pkg-config true
# Then install GStreamer packages
sudo apt-get update
sudo apt-get install -y \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
libgstreamer-plugins-bad1.0-dev \
gstreamer1.0-plugins-base \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly \
gstreamer1.0-libavVerification:
pkg-config --cflags --libs gstreamer-1.0
gst-launch-1.0 --versionmacOS (Homebrew)
Option A: Homebrew (Traditional)
# Install GStreamer and plugins
brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly pkg-config
# Set environment variables (add to ~/.zshrc or ~/.bash_profile)
export PKG_CONFIG_PATH="$(brew --prefix)/lib/pkgconfig:$PKG_CONFIG_PATH"
export LIBRARY_PATH="$(brew --prefix)/lib:$LIBRARY_PATH"
export LD_LIBRARY_PATH="$(brew --prefix)/lib:$LD_LIBRARY_PATH"Option B: Cross-Platform Setup with setup-cpp (macOS)
# Install build tools automatically
npx setup-cpp --compiler auto --pkg-config true
# Then install GStreamer via Homebrew
brew install gstreamer gst-plugins-base gst-plugins-good gst-plugins-bad gst-plugins-ugly
# Set environment variables (add to ~/.zshrc or ~/.bash_profile)
export PKG_CONFIG_PATH="$(brew --prefix)/lib/pkgconfig:$PKG_CONFIG_PATH"
export LIBRARY_PATH="$(brew --prefix)/lib:$LIBRARY_PATH"
export LD_LIBRARY_PATH="$(brew --prefix)/lib:$LD_LIBRARY_PATH"Verification:
pkg-config --cflags --libs gstreamer-1.0
gst-launch-1.0 --versionWindows
Complete Installation:
Install Build Tools:
Option A: Manual Installation
# Install Visual Studio Build Tools 2019/2022 (Community edition is free) # Download from: https://visualstudio.microsoft.com/downloads/ # Install pkg-config (REQUIRED for build to work) choco install pkgconfigliteOption B: Cross-Platform Setup with setup-cpp (Recommended)
# Use setup-cpp for automated toolchain installation (works on Windows, macOS, Linux) # This tool can install compilers, package managers (including Chocolatey), and build systems npx setup-cpp --compiler msvc --pkg-config true --choco trueInstall GStreamer 1.26.2:
# Download both runtime and development MSI packages from: # https://gstreamer.freedesktop.org/download/ # For 64-bit systems, download and install: # - gstreamer-1.0-msvc-x86_64-1.26.2.msi (runtime) # - gstreamer-1.0-devel-msvc-x86_64-1.26.2.msi (development) # Install both MSI files by double-clicking or using: # msiexec /i gstreamer-1.0-msvc-x86_64-1.26.2.msi /quiet # msiexec /i gstreamer-1.0-devel-msvc-x86_64-1.26.2.msi /quietSet Environment Variables:
# Add to system PATH (via System Properties → Environment Variables): C:\Program Files\gstreamer\1.0\msvc_x86_64\bin # Add system environment variables: GSTREAMER_1_0_ROOT_MSVC_X86_64=C:\Program Files\gstreamer\1.0\msvc_x86_64 PKG_CONFIG_PATH=C:\Program Files\gstreamer\1.0\msvc_x86_64\lib\pkgconfig
Verification:
# Verify pkg-config can find GStreamer (most important for building)
pkg-config --exists gstreamer-1.0; if ($LASTEXITCODE -eq 0) { Write-Host "GStreamer found" } else { Write-Host "GStreamer NOT found" }
pkg-config --cflags --libs gstreamer-1.0Windows-Specific Troubleshooting:
If you encounter build errors after installation, try these common fixes:
PKG_CONFIG_PATH Issues:
# If GStreamer was installed to default location but pkg-config can't find it: setx PKG_CONFIG_PATH "C:\Program Files\gstreamer\1.0\msvc_x86_64\lib\pkgconfig" # Restart your terminal/command prompt after setting this variable # Verify the fix: pkg-config --exists gstreamer-1.0; if ($LASTEXITCODE -eq 0) { Write-Host "GStreamer found" } else { Write-Host "GStreamer NOT found" }Missing pkg-config:
# pkg-config is absolutely required - install if missing: choco install pkgconfiglite # Or refresh your PATH if already installed: refreshenvGStreamer Location Mismatch:
# If GStreamer was installed to a different location, update PKG_CONFIG_PATH accordingly: # For example, if installed to C:\gstreamer: setx PKG_CONFIG_PATH "C:\gstreamer\1.0\msvc_x86_64\lib\pkgconfig"
Common Installation Issues
Problem: "Cannot find gstreamer-1.0"
Solution:
# Ubuntu/Debian
sudo apt-get install libgstreamer1.0-dev pkg-config
# macOS
brew install gstreamer pkg-config
export PKG_CONFIG_PATH="$(brew --prefix)/lib/pkgconfig:$PKG_CONFIG_PATH"
# Windows (PowerShell)
# Ensure pkg-config is installed:
choco install pkgconfiglite
# Ensure environment variables are set correctly:
# GSTREAMER_1_0_ROOT_MSVC_X86_64=C:\Program Files\gstreamer\1.0\msvc_x86_64
# PKG_CONFIG_PATH=C:\Program Files\gstreamer\1.0\msvc_x86_64\lib\pkgconfig
# If GStreamer is installed but pkg-config can't find it, set PKG_CONFIG_PATH:
setx PKG_CONFIG_PATH "C:\Program Files\gstreamer\1.0\msvc_x86_64\lib\pkgconfig"
# Verify pkg-config can find GStreamer
pkg-config --exists gstreamer-1.0; if ($LASTEXITCODE -eq 0) { Write-Host "GStreamer found" } else { Write-Host "GStreamer NOT found" }Problem: Missing plugins (playbin/decodebin errors)
Solution:
# Ubuntu/Debian - install plugin packages
sudo apt-get install gstreamer1.0-plugins-{base,good,bad,ugly} gstreamer1.0-libav
# macOS - install plugin packages
brew install gst-plugins-{base,good,bad,ugly}
# List available plugins
gst-inspect-1.0 | grep -i pluginProblem: "Permission denied" on Linux
Solution:
# Add user to audio/video groups
sudo usermod -a -G audio,video $USER
# Logout and login again
# Or install PulseAudio for audio support
sudo apt-get install pulseaudio pulseaudio-utilsDocker Installation
For containerized applications:
# Ubuntu-based container
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \
nodejs npm \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
libgstreamer-plugins-bad1.0-dev \
gstreamer1.0-plugins-base \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly \
gstreamer1.0-libav \
build-essential pkg-config python3 \
&& rm -rf /var/lib/apt/lists/*
# Install your application
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run buildQuick Start
Basic Pipeline
import { Pipeline } from "gst-kit";
const pipeline = new Pipeline("videotestsrc ! autovideosink");
await pipeline.play();
// Stop after 5 seconds
setTimeout(async () => {
await pipeline.stop();
}, 5000);Checking Element Availability
import { Pipeline } from "gst-kit";
// Check if specific GStreamer elements are available before using them
if (Pipeline.elementExists("x264enc")) {
console.log("H.264 encoder is available");
const pipeline = new Pipeline("videotestsrc ! x264enc ! mp4mux ! filesink location=output.mp4");
} else {
console.log("x264enc not found, using alternative encoder");
const pipeline = new Pipeline("videotestsrc ! vp8enc ! webmmux ! filesink location=output.webm");
}
// Check hardware acceleration support
const hasVaapi = Pipeline.elementExists("vaapih264enc");
const hasNvenc = Pipeline.elementExists("nvh264enc");
console.log(`VAAPI support: ${hasVaapi}, NVENC support: ${hasNvenc}`);Working with AppSink (Pull-based Approach)
import { Pipeline } from "gst-kit";
const pipeline = new Pipeline("videotestsrc num-buffers=10 ! videoconvert ! appsink name=sink");
const sink = pipeline.getElementByName("sink");
if (sink?.type === "app-sink-element") {
await pipeline.play();
while (true) {
const sample = await sink.getSample(); // Explicitly request samples
if (!sample) break;
console.log("Received frame:", sample.buffer?.length, "bytes");
}
await pipeline.stop();
}Working with AppSink (Event-Driven/Push Approach)
import { Pipeline } from "gst-kit";
const pipeline = new Pipeline("videotestsrc num-buffers=10 ! videoconvert ! appsink name=sink");
const sink = pipeline.getElementByName("sink");
if (sink?.type === "app-sink-element") {
let frameCount = 0;
// Set up reactive callback - samples are pushed automatically
const unsubscribe = sink.onSample(sample => {
frameCount++;
console.log(`Frame ${frameCount}:`, sample.buffer?.length, "bytes");
if (frameCount === 10) {
unsubscribe();
pipeline.stop();
}
});
await pipeline.play();
}Working with AppSrc (Source Input)
import { Pipeline } from "gst-kit";
const pipeline = new Pipeline("appsrc name=source ! videoconvert ! autovideosink");
const source = pipeline.getElementByName("source");
if (source?.type === "app-src-element") {
source.setElementProperty("caps", "video/x-raw,format=RGB,width=320,height=240");
source.setElementProperty("is-live", true);
await pipeline.play();
// Push random RGB frames
setInterval(() => {
const buffer = Buffer.alloc(320 * 240 * 3);
buffer.fill(Math.floor(Math.random() * 255));
source.push(buffer);
}, 33); // ~30 FPS
}Working with AppSrc for Custom Data Sources (with EOS)
Use AppSrc with EOS when you need to process data that can't be handled by standard GStreamer elements like filesrc:
import { Pipeline } from "gst-kit";
import { createReadStream } from "fs";
// Real-world scenarios where AppSrc + EOS is needed:
// 1. Custom encrypted file formats
// 2. Network streams with custom protocols
// 3. Database BLOBs containing media data
// 4. Programmatically generated content with known duration
// 5. Multi-source aggregation
async function streamEncryptedVideoFile(encryptedFilePath) {
const pipeline = new Pipeline("appsrc name=source ! h264parse ! avdec_h264 ! autovideosink");
const source = pipeline.getElementByName("source");
if (source?.type === "app-src-element") {
source.setElementProperty("caps", "video/x-h264,stream-format=byte-stream");
source.setElementProperty("is-live", false); // File-like behavior
await pipeline.play();
// Read and decrypt file chunks
const encryptedStream = createReadStream(encryptedFilePath);
for await (const encryptedChunk of encryptedStream) {
// Decrypt the chunk (your custom decryption logic)
const decryptedBuffer = await decryptChunk(encryptedChunk);
source.push(decryptedBuffer);
}
// IMPORTANT: Signal end-of-stream when file is fully processed
source.endOfStream();
// Wait for natural completion
while (true) {
const message = await pipeline.busPop(1000);
if (message?.type === "eos") {
console.log("Encrypted file playback completed");
break;
}
}
await pipeline.stop();
}
}
// Example: Stream from database BLOB
async function streamFromDatabase(mediaId) {
const pipeline = new Pipeline("appsrc name=source ! decodebin ! autovideosink");
const source = pipeline.getElementByName("source");
if (source?.type === "app-src-element") {
// Note: Let decodebin auto-detect format
source.setElementProperty("is-live", false);
await pipeline.play();
// Stream media data from database in chunks
const mediaChunks = await fetchMediaFromDatabase(mediaId);
for (const chunk of mediaChunks) {
source.push(chunk);
}
source.endOfStream(); // Signal completion
// Handle completion
while (true) {
const message = await pipeline.busPop(1000);
if (message?.type === "eos") break;
}
await pipeline.stop();
}
}
// Placeholder functions - implement according to your needs
async function decryptChunk(encryptedData) {
// Your decryption logic here
return encryptedData; // Return decrypted buffer
}
async function fetchMediaFromDatabase(mediaId) {
// Your database fetch logic here
return []; // Return array of media chunks
}Recording Programmatically Generated Streams to Files
For recording programmatically generated content (procedural video, custom visualizations, etc.) to video files:
import { Pipeline } from "gst-kit";
import path from "path";
import fs from "fs";
async function recordGeneratedVideoToFile() {
const outputFile = path.join(process.cwd(), "recording.ogv");
// Remove existing file if it exists
if (fs.existsSync(outputFile)) {
fs.unlinkSync(outputFile);
}
// Create recording pipeline (OGV/Theora is most reliable for programmatic content)
const pipeline = new Pipeline(`
appsrc name=mysource !
videoconvert !
theoraenc !
oggmux !
filesink location=${outputFile}
`);
const appsrc = pipeline.getElementByName("mysource");
if (appsrc?.type === "app-src-element") {
// Configure for file recording (not live streaming)
appsrc.setElementProperty("caps", "video/x-raw,format=RGB,width=640,height=480,framerate=30/1");
appsrc.setElementProperty("format", "time");
appsrc.setElementProperty("is-live", false);
appsrc.setElementProperty("do-timestamp", true);
await pipeline.play();
// Generate and push frames
const frameCount = 300; // 10 seconds at 30fps
const width = 640;
const height = 480;
const frameSize = width * height * 3; // RGB
for (let i = 0; i < frameCount; i++) {
// Generate frame data (replace with your content generation logic)
const buffer = generateFrame(i, width, height);
appsrc.push(buffer);
if (i % 30 === 0) {
console.log(`📹 Recorded ${i} frames (${i / 30} seconds)`);
}
}
// CRITICAL: Signal end-of-stream when done generating content
appsrc.endOfStream();
// Wait for recording completion
while (true) {
const message = await pipeline.busPop(1000);
if (!message) continue;
if (message.type === "eos") {
console.log("🎉 Recording completed!");
break;
} else if (message.type === "error") {
console.error("❌ Recording error:", message.message);
break;
}
}
await pipeline.stop();
// Verify file was created
if (fs.existsSync(outputFile)) {
const stats = fs.statSync(outputFile);
console.log(`✅ File size: ${(stats.size / 1024 / 1024).toFixed(2)} MB`);
}
}
}
function generateFrame(frameNumber, width, height) {
// Example: Generate animated colored frames
const buffer = Buffer.alloc(width * height * 3);
const red = (frameNumber * 5) % 256;
const green = (frameNumber * 3) % 256;
const blue = (frameNumber * 7) % 256;
for (let i = 0; i < buffer.length; i += 3) {
buffer[i] = red; // R
buffer[i + 1] = green; // G
buffer[i + 2] = blue; // B
}
return buffer;
}
// Usage
recordGeneratedVideoToFile();Key Points for File Recording:
- EOS is Required: Always call
endOfStream()when done pushing data - OGV/Theora: Most reliable format for programmatic content
- Non-live Mode: Set
is-live: falsefor file-like behavior - Timestamping: Enable
do-timestamp: truefor proper timing - Error Handling: Monitor bus messages for completion and errors
Extracting Buffer Data with Pad Probes
import { Pipeline } from "gst-kit";
const pipeline = new Pipeline(
"videotestsrc ! videoconvert ! x264enc ! rtph264pay name=pay ! fakesink"
);
const payloader = pipeline.getElementByName("pay");
if (payloader) {
// Add probe to capture comprehensive buffer data from pad
const unsubscribe = payloader.addPadProbe("src", bufferData => {
console.log("Buffer Data:", {
// Raw buffer data
buffer: bufferData.buffer, // Buffer object with raw data
size: bufferData.buffer?.length, // Buffer size in bytes
// Timing information (nanoseconds)
pts: bufferData.pts, // Presentation timestamp
dts: bufferData.dts, // Decode timestamp
duration: bufferData.duration,
offset: bufferData.offset,
offsetEnd: bufferData.offsetEnd,
// Buffer metadata
flags: bufferData.flags, // GStreamer buffer flags
// Stream format information
caps: bufferData.caps, // Caps object with format details
// RTP data (only for RTP streams)
rtp: bufferData.rtp
? {
timestamp: bufferData.rtp.timestamp,
sequence: bufferData.rtp.sequence,
ssrc: bufferData.rtp.ssrc,
payloadType: bufferData.rtp.payloadType,
}
: undefined,
});
});
await pipeline.play();
// ... later, remove the probe
unsubscribe();
await pipeline.stop();
}Pipeline State Management
import { Pipeline } from "gst-kit";
const pipeline = new Pipeline("videotestsrc ! autovideosink");
// State change operations return detailed results
const playResult = await pipeline.play();
console.log("Play result:", playResult.result); // 'success', 'async', 'failure', etc.
console.log("Current state:", playResult.finalState);
// Check if pipeline is playing
console.log("Is playing:", pipeline.playing());
// Pause and resume
await pipeline.pause();
await pipeline.play();
// Stop pipeline
await pipeline.stop();Position and Duration Queries
import { Pipeline } from "gst-kit";
const pipeline = new Pipeline("videotestsrc ! timeoverlay ! autovideosink");
await pipeline.play();
setInterval(() => {
const position = pipeline.queryPosition(); // Position in seconds
const duration = pipeline.queryDuration(); // Duration in seconds
console.log(`Position: ${position}s / Duration: ${duration}s`);
}, 1000);Seeking
import { Pipeline } from "gst-kit";
const pipeline = new Pipeline("videotestsrc ! timeoverlay ! autovideosink");
await pipeline.play();
// Seek to 60 seconds
const seekSuccess = pipeline.seek(60);
console.log("Seek successful:", seekSuccess);Message Bus Handling
import { Pipeline } from "gst-kit";
const pipeline = new Pipeline("videotestsrc num-buffers=100 ! autovideosink");
await pipeline.play();
// Listen for bus messages
while (true) {
const message = await pipeline.busPop(1000); // 1 second timeout
if (message) {
console.log("Message:", message.type, message.srcElementName);
if (message.type === "eos") {
console.log("End of stream");
break;
} else if (message.type === "error") {
console.error("Error:", message.errorMessage);
break;
}
}
}Element Property Manipulation
import { Pipeline } from "gst-kit";
const pipeline = new Pipeline("videotestsrc name=source ! capsfilter name=filter ! autovideosink");
const source = pipeline.getElementByName("source");
const filter = pipeline.getElementByName("filter");
// Set various property types
source?.setElementProperty("pattern", "ball");
source?.setElementProperty("is-live", true);
source?.setElementProperty("num-buffers", 100);
filter?.setElementProperty("caps", "video/x-raw,width=1280,height=720,framerate=30/1");
// Get property values
const patternResult = source?.getElementProperty("pattern");
const capsResult = filter?.getElementProperty("caps");
// Access the actual values using the standardized format
const pattern = patternResult?.value;
const caps = capsResult?.value;Pad Manipulation
import { Pipeline } from "gst-kit";
const pipeline = new Pipeline(
"input-selector name=sel ! autovideosink videotestsrc pattern=0 ! sel.sink_0 videotestsrc pattern=1 ! sel.sink_1"
);
const selector = pipeline.getElementByName("sel");
await pipeline.play();
// Switch between input pads
setInterval(() => {
const activePad = Math.random() > 0.5 ? "sink_0" : "sink_1";
selector?.setPad("active-pad", activePad);
console.log("Switched to:", activePad);
}, 2000);
// Get pad information
const srcPad = selector?.getPad("src");
console.log("Pad info:", srcPad?.name, srcPad?.direction, srcPad?.caps);API Reference
Pipeline Class
class Pipeline {
constructor(description: string);
// Static methods
static elementExists(elementName: string): boolean;
// State management
play(timeoutMs?: number): Promise<StateChangeResult>;
pause(timeoutMs?: number): Promise<StateChangeResult>;
stop(timeoutMs?: number): Promise<StateChangeResult>;
playing(): boolean;
// Element access
getElementByName(name: string): Element | AppSinkElement | AppSrcElement | null;
// Position and seeking
queryPosition(): number;
queryDuration(): number;
seek(positionSeconds: number): boolean;
// Message handling
busPop(timeoutMs?: number): Promise<GstMessage | null>;
}Element Types
// Base element with common functionality
interface Element {
readonly type: "element";
getElementProperty(key: string): GStreamerPropertyResult;
setElementProperty(key: string, value: GStreamerPropertyValue): void;
addPadProbe(padName: string, callback: (bufferData: BufferData) => void): () => void;
setPad(attribute: string, padName: string): void;
getPad(padName: string): GstPad | null;
}
// AppSink element for receiving data
interface AppSinkElement extends Element {
readonly type: "app-sink-element";
getSample(timeoutMs?: number): Promise<GStreamerSample | null>;
onSample(callback: (sample: GStreamerSample) => void): () => void;
}
// AppSrc element for providing data
interface AppSrcElement extends Element {
readonly type: "app-src-element";
push(buffer: Buffer, pts?: Buffer | number): void;
endOfStream(): void;
}Buffer Flags Reference
import { GstBufferFlags } from "gst-kit";
// Check buffer flags
if (bufferData.flags & GstBufferFlags.GST_BUFFER_FLAG_DELTA_UNIT) {
console.log("This is a delta frame (not a keyframe)");
}
if (bufferData.flags & GstBufferFlags.GST_BUFFER_FLAG_HEADER) {
console.log("This buffer contains header data");
}Available flags include:
GST_BUFFER_FLAG_LIVE: Buffer from live sourceGST_BUFFER_FLAG_DECODE_ONLY: Buffer should only be decoded, not displayedGST_BUFFER_FLAG_DISCONT: Buffer represents discontinuityGST_BUFFER_FLAG_DELTA_UNIT: Buffer is delta unit (not keyframe)GST_BUFFER_FLAG_HEADER: Buffer contains header information- And more...
Property System
Standardized Property Results
The getElementProperty() method returns a standardized object with type information:
// Property result format
const result = element.getElementProperty("property-name");
if (result === null) {
console.log("Property value is null");
} else {
console.log("Property type:", result.type); // "primitive" | "array" | "object" | "buffer" | "sample"
console.log("Property value:", result.value); // The actual value
}Property Types
primitive: strings, numbers, booleans, bigint (typically uint64s used to store nanoseconds)array: Arrays of primitive values (strings, numbers, booleans, bigints)object: GStreamer structures (like stats)buffer: Raw binary datasample: Media samples with buffer, caps, and metadata
Usage Examples
// String property
const patternResult = videotestsrc.getElementProperty("pattern");
if (patternResult?.type === "primitive") {
console.log("Pattern:", patternResult.value); // e.g., "ball"
}
// Boolean property
const isLiveResult = videotestsrc.getElementProperty("is-live");
if (isLiveResult?.type === "primitive") {
console.log("Is live:", isLiveResult.value); // true/false
}
// Structure/Object property (like stats)
const statsResult = rtpdepay.getElementProperty("stats");
if (statsResult?.type === "object") {
const stats = statsResult.value;
console.log("RTP timestamp:", stats.timestamp);
}
// Sample property
const sampleResult = fakesink.getElementProperty("last-sample");
if (sampleResult?.type === "sample") {
const sample = sampleResult.value;
console.log("Buffer size:", sample.buffer.length);
console.log("Caps:", sample.caps);
}Build System & Tools
Native Code (C++)
- GYP: Cross-platform build configuration system
- N-API: Node-API for runtime-independent native bindings
- GStreamer Integration: Full integration with GStreamer 1.0 ecosystem
- Compiler Support: GCC, Clang, MSVC with C++20 standard
TypeScript/JavaScript
- Rollup: Module bundler generating both ESM and CJS outputs
- TypeScript: Full type definitions and compilation
- Dual Packaging: Supports both
importandrequirestatements - Source Maps: Full debugging support
Testing & Quality
- Vitest: Modern, fast test runner with concurrent execution
- ESLint: Code linting with TypeScript support
- Prettier: Code formatting
- Coverage: Built-in test coverage reporting
Project Structure
gst-kit/
├── src/
│ ├── cpp/ # C++ native implementation
│ │ ├── addon.cpp # N-API module entry point
│ │ ├── pipeline.cpp # Pipeline class implementation
│ │ ├── element.cpp # Element class implementation
│ │ ├── async-workers.cpp # Async operation workers
│ │ └── type-conversion.cpp # Type conversion utilities
│ └── ts/ # TypeScript implementation
│ ├── index.ts # Main API exports and types
│ └── *.test.ts # Comprehensive test suite
├── examples/ # Usage examples
│ ├── basic-pipeline.mjs # Simple pipeline example
│ ├── appsink.mjs # AppSink usage
│ ├── appsrc.mjs # AppSrc usage
│ ├── appsrc-eos.mjs # AppSrc with end-of-stream
│ ├── record-to-file.mjs # Recording to file example
│ ├── rtp-timestamp.mjs # RTP handling
│ ├── bus.mjs # Message bus handling
│ ├── seek.mjs # Seeking functionality
│ ├── query.mjs # Position/duration queries
│ ├── set-pad.mjs # Pad manipulation
│ ├── fakesink.mjs # Fakesink usage
│ └── glshader.mjs # OpenGL shader example
├── build/ # GYP build output
├── dist/ # Rollup build output
│ ├── esm/ # ES modules
│ ├── cjs/ # CommonJS modules
│ └── index.d.ts # Type definitions
├── scripts/ # Build and utility scripts
├── binding.gyp # GYP build configuration
├── rollup.config.mjs # Rollup bundler configuration
├── vitest.config.ts # Vitest test configuration
├── tsconfig.json # TypeScript configuration
└── package.json # Node.js package configurationPerformance Considerations
- Concurrent Testing: All tests run concurrently for faster execution
- Efficient Memory Management: Proper buffer lifecycle management
- Async Operations: Non-blocking operations for better performance
- Type Safety: Compile-time error detection reduces runtime overhead
Runtime Compatibility
| Runtime | Support Level | Notes | | ----------- | ---------------- | --------------------------- | | Node.js 16+ | ✅ Full | Minimal version | | Node.js 22+ | ✅ Full | Latest stable support | | Bun 1.0+ | ✅ Full | Alternative runtime support | | Deno | ❌ Not supported | Native module limitations |
Platform Compatibility
| Platform | Local Development | Production Ready | Notes | | ------------ | ----------------- | ---------------- | -------------------------- | | Ubuntu/Linux | ✅ Full | ✅ Full | Excellent for servers | | macOS | ✅ Full | ✅ Full | Intel and Apple Silicon | | Windows | ✅ Full | ✅ Full | Requires environment setup | | Docker | ✅ Full | ✅ Full | Ubuntu-based containers |
License
MIT License - see LICENSE file for details.
Contributing
See CONTRIBUTING.md for development setup and contribution guidelines.
