livekit-ingress

star 0

Import RTMP, WHIP, and media streams into LiveKit rooms from OBS, encoders, and external sources

FutureAtoms By FutureAtoms schedule Updated 3/11/2026

name: livekit-ingress description: Import RTMP, WHIP, and media streams into LiveKit rooms from OBS, encoders, and external sources argument-hint: "" allowed-tools: Read, Write, Bash(lk, npm install, pip install), Glob, Grep

LiveKit Ingress (Streaming Input)

Import external streams into LiveKit: $ARGUMENTS

Expert Knowledge

You are a LiveKit ingress specialist with expertise in:

  • RTMP streaming from OBS/encoders
  • WHIP protocol integration
  • Simulcast and transcoding
  • URL/file ingestion
  • Broadcasting workflows

Ingress Overview

Input Type Protocol Transcoding Use Case
RTMP Real-Time Messaging Protocol Required OBS, hardware encoders
WHIP WebRTC-HTTP Ingestion Protocol Optional Low-latency WebRTC
URL HTTP/HTTPS Required Pre-recorded content

Creating Ingress

RTMP Ingress

import { IngressClient, IngressInput } from 'livekit-server-sdk';

const ingressClient = new IngressClient(
  process.env.LIVEKIT_URL!,
  process.env.LIVEKIT_API_KEY!,
  process.env.LIVEKIT_API_SECRET!
);

// Create RTMP ingress
const ingress = await ingressClient.createIngress(IngressInput.RTMP_INPUT, {
  name: 'obs-stream',
  roomName: 'live-room',
  participantIdentity: 'broadcaster',
  participantName: 'Live Stream',
  // Optional: video encoding options
  video: {
    source: 'SCREEN_SHARE', // or 'CAMERA'
    layers: [
      { width: 1920, height: 1080, bitrate: 3000000, quality: 90 },
      { width: 1280, height: 720, bitrate: 1500000, quality: 85 },
      { width: 640, height: 480, bitrate: 600000, quality: 80 },
    ],
  },
  audio: {
    source: 'MICROPHONE',
    preset: 'OPUS_STEREO_96KBPS',
  },
});

console.log('RTMP URL:', ingress.url);
console.log('Stream Key:', ingress.streamKey);

Python

from livekit.api import LiveKitAPI
from livekit.protocol import ingress as ingress_proto

api = LiveKitAPI()

# Create RTMP ingress
ingress = await api.ingress.create_ingress(
    ingress_proto.CreateIngressRequest(
        input_type=ingress_proto.IngressInput.RTMP_INPUT,
        name="obs-stream",
        room_name="live-room",
        participant_identity="broadcaster",
        participant_name="Live Stream",
        video=ingress_proto.IngressVideoOptions(
            source=ingress_proto.TrackSource.SCREEN_SHARE,
            layers=[
                ingress_proto.VideoLayer(width=1920, height=1080, bitrate=3000000),
                ingress_proto.VideoLayer(width=1280, height=720, bitrate=1500000),
                ingress_proto.VideoLayer(width=640, height=480, bitrate=600000),
            ],
        ),
        audio=ingress_proto.IngressAudioOptions(
            source=ingress_proto.TrackSource.MICROPHONE,
            preset=ingress_proto.IngressAudioEncodingPreset.OPUS_STEREO_96KBPS,
        ),
    )
)

print(f"RTMP URL: {ingress.url}")
print(f"Stream Key: {ingress.stream_key}")

WHIP Ingress

Low-latency WebRTC ingestion:

const ingress = await ingressClient.createIngress(IngressInput.WHIP_INPUT, {
  name: 'webrtc-stream',
  roomName: 'live-room',
  participantIdentity: 'webrtc-broadcaster',
  enableTranscoding: false, // Direct passthrough for lower latency
});

console.log('WHIP URL:', ingress.url);
// Use this URL in WHIP-compatible software

URL Ingress

Import from HTTP/HTTPS sources:

const ingress = await ingressClient.createIngress(IngressInput.URL_INPUT, {
  name: 'video-file',
  roomName: 'playback-room',
  participantIdentity: 'video-player',
  url: 'https://example.com/video.mp4',
});

OBS Studio Configuration

  1. Open OBS → Settings → Stream
  2. Service: Custom
  3. Server: rtmp://your-livekit-ingress.com/live
  4. Stream Key: <stream-key-from-api>

Recommended OBS Settings

Output Settings:
├── Video Bitrate: 3000-6000 Kbps
├── Encoder: x264 or NVENC
├── Audio Bitrate: 128-320 Kbps
├── Keyframe Interval: 2 seconds

Video Settings:
├── Base Resolution: 1920x1080
├── Output Resolution: 1920x1080
└── FPS: 30 or 60

FFmpeg Streaming

# Stream file to RTMP ingress
ffmpeg -re -i input.mp4 \
  -c:v libx264 -preset veryfast -b:v 3000k \
  -c:a aac -b:a 128k \
  -f flv "rtmp://ingress-url/live/stream-key"

# Stream webcam
ffmpeg -f avfoundation -i "0:0" \
  -c:v libx264 -preset veryfast -b:v 2000k \
  -c:a aac -b:a 128k \
  -f flv "rtmp://ingress-url/live/stream-key"

# Loop video continuously
ffmpeg -stream_loop -1 -re -i input.mp4 \
  -c:v libx264 -b:v 3000k \
  -c:a aac -b:a 128k \
  -f flv "rtmp://ingress-url/live/stream-key"

Simulcast Layers

Configure multiple quality levels:

const ingress = await ingressClient.createIngress(IngressInput.RTMP_INPUT, {
  name: 'simulcast-stream',
  roomName: 'live-room',
  participantIdentity: 'broadcaster',
  video: {
    source: 'CAMERA',
    layers: [
      // High quality
      { width: 1920, height: 1080, bitrate: 4000000, quality: 90 },
      // Medium quality
      { width: 1280, height: 720, bitrate: 1500000, quality: 85 },
      // Low quality
      { width: 640, height: 360, bitrate: 500000, quality: 80 },
    ],
  },
});

Audio Presets

// Available audio presets
const audioPresets = [
  'OPUS_STEREO_96KBPS',   // Default, good quality
  'OPUS_MONO_64KBPS',     // Low bandwidth
  'OPUS_STEREO_128KBPS',  // High quality
];

const ingress = await ingressClient.createIngress(IngressInput.RTMP_INPUT, {
  ...config,
  audio: {
    source: 'MICROPHONE',
    preset: 'OPUS_STEREO_128KBPS',
  },
});

Managing Ingress

// List all ingresses
const ingresses = await ingressClient.listIngress({
  roomName: 'live-room', // Optional filter
});

// Get ingress info
const ingress = await ingressClient.getIngress(ingressId);

// Update ingress
const updated = await ingressClient.updateIngress({
  ingressId: ingress.ingressId,
  name: 'Updated Stream Name',
  roomName: 'new-room',
});

// Delete ingress
await ingressClient.deleteIngress(ingressId);

Ingress State

import { IngressState } from 'livekit-server-sdk';

// Check ingress status
const ingress = await ingressClient.getIngress(ingressId);

switch (ingress.state) {
  case IngressState.ENDPOINT_INACTIVE:
    console.log('Waiting for stream...');
    break;
  case IngressState.ENDPOINT_BUFFERING:
    console.log('Buffering stream...');
    break;
  case IngressState.ENDPOINT_PUBLISHING:
    console.log('Stream is live!');
    break;
  case IngressState.ENDPOINT_ERROR:
    console.log('Stream error:', ingress.error);
    break;
}

Webhooks

Listen for ingress events:

// webhook handler
app.post('/livekit-webhook', (req, res) => {
  const event = req.body;

  switch (event.event) {
    case 'ingress_started':
      console.log('Ingress started:', event.ingressInfo);
      break;
    case 'ingress_ended':
      console.log('Ingress ended:', event.ingressInfo);
      break;
  }

  res.sendStatus(200);
});

CLI Commands

# Create RTMP ingress
lk ingress create \
  --room my-room \
  --identity broadcaster \
  --name "My Stream" \
  --input-type rtmp

# Create WHIP ingress
lk ingress create \
  --room my-room \
  --identity broadcaster \
  --input-type whip

# List ingresses
lk ingress list

# Get ingress info
lk ingress info <ingress-id>

# Delete ingress
lk ingress delete <ingress-id>

Complete Example

import { IngressClient, IngressInput, IngressState } from 'livekit-server-sdk';

class BroadcastService {
  private ingress: IngressClient;

  constructor() {
    this.ingress = new IngressClient(
      process.env.LIVEKIT_URL!,
      process.env.LIVEKIT_API_KEY!,
      process.env.LIVEKIT_API_SECRET!
    );
  }

  async createBroadcast(
    broadcasterId: string,
    roomName: string
  ): Promise<{ rtmpUrl: string; streamKey: string }> {
    const ingress = await this.ingress.createIngress(IngressInput.RTMP_INPUT, {
      name: `${broadcasterId}-stream`,
      roomName,
      participantIdentity: broadcasterId,
      participantName: 'Broadcaster',
      video: {
        source: 'CAMERA',
        layers: [
          { width: 1920, height: 1080, bitrate: 4000000, quality: 90 },
          { width: 1280, height: 720, bitrate: 1500000, quality: 85 },
          { width: 640, height: 360, bitrate: 500000, quality: 80 },
        ],
      },
      audio: {
        source: 'MICROPHONE',
        preset: 'OPUS_STEREO_96KBPS',
      },
    });

    return {
      rtmpUrl: ingress.url,
      streamKey: ingress.streamKey,
    };
  }

  async getBroadcastStatus(ingressId: string): Promise<{
    isLive: boolean;
    error?: string;
  }> {
    const ingress = await this.ingress.getIngress(ingressId);

    return {
      isLive: ingress.state === IngressState.ENDPOINT_PUBLISHING,
      error: ingress.error,
    };
  }

  async endBroadcast(ingressId: string): Promise<void> {
    await this.ingress.deleteIngress(ingressId);
  }

  async listActiveBroadcasts(roomName?: string) {
    const ingresses = await this.ingress.listIngress({ roomName });
    return ingresses.filter(
      (i) => i.state === IngressState.ENDPOINT_PUBLISHING
    );
  }
}

export const broadcastService = new BroadcastService();

Best Practices

  1. Use simulcast: Multiple quality layers for adaptive streaming
  2. Set keyframe interval: 2 seconds for reliable switching
  3. Monitor ingress state: Handle errors gracefully
  4. Use WHIP for low latency: When sub-second latency is needed
  5. Validate stream settings: Check OBS/encoder configuration

Deliverables

For: $ARGUMENTS

Provide:

  1. Ingress type selection
  2. Encoding configuration
  3. Simulcast layer setup
  4. Client/encoder configuration
  5. State monitoring
  6. Management endpoints
Install via CLI
npx skills add https://github.com/FutureAtoms/claude-skills-backup --skill livekit-ingress
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator