Open Media Transport for ultra-low latency interactive streaming
OMT (Open Media Transport) is a next-generation streaming protocol developed by WAVE specifically for ultra-low latency interactive applications. Achieving sub-16ms end-to-end latency, OMT enables truly real-time experiences where viewer interaction and stream content are synchronized, making it ideal for gaming, live auctions, interactive shopping, and sports with live chat.
Technical architecture and protocol design
OMT Packet Structure: ┌─────────────────────────────────────────────────────────┐ │ IP Header (20 bytes) │ │ UDP Header (8 bytes) │ │ OMT Header (12 bytes) │ │ - Sequence Number (4 bytes) │ │ - Timestamp (4 bytes) - microsecond precision │ │ - Flags (2 bytes) - FEC, keyframe, etc. │ │ - Stream ID (2 bytes) │ │ Payload (MTU - 40 bytes, typically 1460 bytes) │ │ - Video/Audio data with FEC if enabled │ └─────────────────────────────────────────────────────────┘ Total Overhead: 40 bytes per packet (~2.7% for 1500 byte MTU) FEC Strategy: - Group-based FEC: Every 10 packets include 2 FEC packets - Overhead: 20% bandwidth for up to 20% packet loss recovery - Adaptive: Disabled on stable networks to reduce latency
Interactive applications that benefit from sub-16ms latency
Streamers interact with viewers in real-time. Viewers provide instant feedback, vote on gameplay decisions, or participate in tournaments. Sub-16ms latency ensures chat messages and stream content are perfectly synchronized.
Auction platforms require bidders to see current bids instantly. Live shopping shows need viewers to purchase items the moment they're showcased. Delayed streams lead to lost sales and confused bidders.
Fans watching together want to react simultaneously to game events. High latency creates spoilers when some viewers see plays seconds before others. Chat becomes disconnected from the action.
Town halls, live performances, and educational sessions where presenters respond to audience questions. Delayed streams make real-time interaction feel awkward and disjointed.
Production crews monitoring multiple camera feeds from remote locations. Directors need to make split-second decisions based on what's happening live, not what happened 10 seconds ago.
| Protocol | Latency | Primary Use Case | Quality | Adoption |
|---|---|---|---|---|
| OMT | <16ms | Interactive streaming, gaming, auctions | Excellent | Growing |
| WebRTC | <500ms | Video calls, conferencing | Good | Mature |
| SRT | 2-4s | Reliable streaming, unstable networks | Excellent | Mature |
| RTMP | 5-15s | Platform streaming, legacy systems | Good | Universal |
| HLS | 10-30s | Scalable playback, VOD | Excellent | Universal |
| Scenario | Bandwidth | Jitter Tolerance | Packet Loss | Recommended Network |
|---|---|---|---|---|
| Gaming/E-sports (720p60) | 3-4 Mbps | <5ms | <0.1% | Dedicated fiber |
| Interactive Shopping (1080p30) | 4-5 Mbps | <10ms | <0.5% | Business broadband |
| Live Sports (1080p60) | 6-8 Mbps | <10ms | <0.5% | Dedicated fiber |
| 4K Premium (4K30) | 12-15 Mbps | <15ms | <0.5% | Dedicated 10G |
OMT's ultra-low latency depends on stable network conditions. Unlike SRT or RTMP which buffer heavily to compensate for network issues, OMT prioritizes speed over deep buffering.
Recommended profiles for various OMT scenarios
| Profile | Resolution | Bitrate | Target Latency | CPU Load | Use Case |
|---|---|---|---|---|---|
| Ultra-Low Latency | 720p60 | 3,000 Kbps | <10ms | Medium | Gaming, e-sports |
| Interactive | 1080p30 | 4,000 Kbps | <16ms | Medium | Live auctions, shopping |
| Broadcast Quality | 1080p60 | 6,000 Kbps | <20ms | High | Sports, live events |
| 4K Interactive | 4K30 | 12,000 Kbps | <25ms | Very High | Premium content |
WAVE Recommended Encoder Settings for OMT: Video Encoder: Codec: AV1 (preferred) or H.265/HEVC Profile: Main (AV1) or Main (HEVC) Preset: ultrafast or veryfast (minimize encoding latency) Tune: zerolatency (critical for OMT) GOP Size: 60 frames (1 second at 60fps, 2 seconds at 30fps) B-frames: 0 (disable for lowest latency) Rate Control: VBR with max bitrate cap Lookahead: 0 (disable - adds latency) Hardware Encoding: REQUIRED (NVENC, QuickSync, VCE, Apple VT) Audio Encoder: Codec: Opus Sample Rate: 48 kHz Bitrate: 128 Kbps (stereo) or 64 Kbps (mono) Complexity: Low (minimize encoding latency) Frame Size: 20ms (lowest latency mode) Transport Settings: FEC: Enabled (20% overhead) Buffer Size: 16ms (minimal jitter buffer) MTU: 1500 bytes (standard Ethernet) Congestion Control: Enabled (GCC algorithm) Example FFmpeg Command for OMT: ffmpeg -i input.mp4 \ -c:v libsvtav1 -preset 13 -crf 30 -g 60 -bf 0 \ -c:a libopus -b:a 128k -frame_duration 20 \ -f omt omt://ingest.wave.inc/stream-key
WAVE Desktop or OBS with OMT plugin (required for OMT protocol):
OBS Settings for OMT: Stream Settings: Service: Custom Server: omt://ingest.wave.inc/ Stream Key: [your-stream-key] Output Settings: Output Mode: Advanced Encoder: NVENC H.265 (or Hardware HEVC encoder) Rate Control: VBR Bitrate: 6000 Kbps (1080p60) or 3000 Kbps (720p60) Max Bitrate: Same as bitrate (strict CBR for OMT) Keyframe Interval: 1 second (60 frames at 60fps) Preset: P5 (NVENC) - Balance of quality and speed Profile: Main Tune: Ultra Low Latency Advanced Settings: Process Priority: High Color Format: NV12 Color Space: 709 Renderer: Direct3D 11
Activate OMT-specific features for interactive experiences:
| Test Scenario | Configuration | Measured Latency | Network | Result |
|---|---|---|---|---|
| E-Sports Stream | 720p60, AV1, 3 Mbps | 14ms | Fiber 1 Gbps | ✅ Excellent |
| Live Auction | 1080p30, HEVC, 4 Mbps | 16ms | Business 100 Mbps | ✅ Perfect |
| Sports Broadcast | 1080p60, AV1, 6 Mbps | 18ms | Dedicated 10G | ✅ Excellent |
| Interactive Q&A | 720p30, HEVC, 2 Mbps | 22ms | Cable 50 Mbps | ✅ Good |
| Mobile (Cellular) | 720p30, HEVC, 1.5 Mbps | 45-120ms | 5G cellular | ⚠️ Variable |
Cause: Software encoding, network congestion, or WiFi interference
Solution: Switch to hardware encoder (NVENC/QuickSync). Use wired Ethernet. Check for background downloads. Verify encoder preset is "ultrafast" or "veryfast". Disable B-frames in encoder settings.
Cause: Network jitter, insufficient bandwidth, or packet loss
Solution: Enable FEC (Forward Error Correction) in stream settings. Reduce bitrate by 20%. Check network jitter with ping test (should be <5ms). Close bandwidth-intensive applications. Use QoS on router to prioritize streaming traffic.
Cause: Chat sync not enabled or viewer connection issues
Solution: Enable "Chat Sync" in WAVE dashboard stream settings. Ensure viewers are using WAVE player (not third-party embeds). Check viewer network latency in analytics. For viewers with >100ms latency, chat sync may be less precise.
Cause: Network instability, firewall blocking UDP, or ISP throttling
Solution: Verify firewall allows UDP 10000-20000. Check for ISP throttling (test with VPN). Enable automatic reconnection in encoder. Use OMT fallback mode (increases latency to 50-100ms but more stable). Contact WAVE support for dedicated ingress optimization.
Cause: Adaptive bitrate reducing quality due to network conditions
Solution: Check network stability with continuous ping test. Monitor bandwidth usage during stream. Disable ABR for fixed quality (not recommended - may cause buffering). Increase minimum bitrate threshold in stream settings. Upgrade network connection for more headroom.
SDK integration for OMT streaming
import { WaveClient } from '@wave/api-client';
const wave = new WaveClient({ apiKey: 'wave_live_xxxxx' });
// Create OMT stream with ultra-low latency configuration
const createOMTStream = async () => {
const stream = await wave.streams.create({
title: 'Interactive E-Sports Stream',
protocol: 'omt',
omt: {
targetLatency: 16, // Target 16ms end-to-end
quality: 'gaming', // Preset: 'gaming' | 'interactive' | 'broadcast' | '4k'
// Video encoding settings
video: {
codec: 'av1', // 'av1' | 'hevc' | 'vp9'
resolution: '720p', // '720p' | '1080p' | '4k'
fps: 60,
bitrate: {
min: 1500, // Minimum bitrate (Kbps)
target: 3000, // Target bitrate (Kbps)
max: 4500 // Maximum bitrate (Kbps)
},
keyframeInterval: 60, // Frames (1 second at 60fps)
bframes: 0, // Disable B-frames for lowest latency
tune: 'zerolatency'
},
// Audio encoding settings
audio: {
codec: 'opus',
bitrate: 128, // Kbps
sampleRate: 48000, // Hz
channels: 2, // Stereo
frameDuration: 20 // ms (lowest latency)
},
// Network optimization
network: {
fec: {
enabled: true,
overhead: 0.2 // 20% FEC overhead
},
congestionControl: 'gcc', // Google Congestion Control
bufferSize: 16, // ms (jitter buffer)
mtu: 1500 // bytes
},
// Interactive features
features: {
chatSync: true, // Synchronize chat with video timeline
polls: true, // Enable real-time polls
reactions: true, // Enable emoji reactions
latencyDisplay: true // Show latency to viewers
}
},
recording: { enabled: true }
});
console.log('OMT Stream Created:', stream.id);
console.log('Ingest URL:', stream.ingest.omt.url);
console.log('Stream Key:', stream.ingest.omt.streamKey);
console.log('Playback URL:', stream.playback.url);
return stream;
};
// Monitor OMT stream health with real-time metrics
const monitorOMTStream = async (streamId: string) => {
const ws = wave.streams.subscribe(streamId, {
metrics: ['latency', 'bitrate', 'fps', 'packetLoss', 'jitter', 'viewers']
});
ws.on('stats', (stats) => {
console.log('OMT Stream Stats:', {
latency: {
encoder: stats.latency.encoder, // Encoding latency (ms)
network: stats.latency.network, // Network transit (ms)
playback: stats.latency.playback, // Playback buffer (ms)
total: stats.latency.total // End-to-end latency (ms)
},
quality: {
resolution: stats.video.resolution,
fps: stats.video.fps,
bitrate: stats.video.bitrate, // Current bitrate (Kbps)
codec: stats.video.codec
},
network: {
packetLoss: stats.network.packetLoss, // Percentage
jitter: stats.network.jitter, // ms
rtt: stats.network.rtt // Round-trip time (ms)
},
viewers: {
total: stats.viewers.total,
peak: stats.viewers.peak,
avgLatency: stats.viewers.avgLatency // Average viewer latency
}
});
// Alert on high latency
if (stats.latency.total > 25) {
console.warn('WARNING: Latency exceeds 25ms:', stats.latency.total);
}
// Alert on packet loss
if (stats.network.packetLoss > 1) {
console.warn('WARNING: High packet loss:', stats.network.packetLoss + '%');
}
});
ws.on('warning', (warning) => {
console.warn('Stream Warning:', warning.message);
// Send alert to monitoring system
});
return ws;
};
// Synchronized chat with OMT stream
const setupSynchronizedChat = async (streamId: string) => {
const chat = wave.chat.connect(streamId, {
sync: true, // Enable timestamp-based sync
syncMode: 'video' // Sync to video timeline
});
chat.on('message', (message) => {
console.log(`[${message.timestamp}] ${message.user}: ${message.text}`);
// Display message at exact video timestamp
});
// Send message
await chat.send({
text: 'This message is synced to video timestamp',
metadata: {
videoTimestamp: Date.now() // Automatically synced by SDK
}
});
return chat;
};
// Interactive poll synchronized with stream
const createSynchronizedPoll = async (streamId: string) => {
const poll = await wave.interactions.createPoll({
streamId,
question: 'Which weapon should I use?',
options: ['Sniper Rifle', 'Shotgun', 'SMG'],
duration: 30000, // 30 seconds
showResults: 'realtime', // Show results as votes come in
syncToVideo: true // Display at specific video timestamp
});
// Listen for votes
poll.on('vote', (vote) => {
console.log(`Vote for ${vote.option}: Total ${vote.count} votes`);
});
// Close poll
setTimeout(() => poll.close(), 30000);
return poll;
};
// Complete OMT streaming workflow
(async () => {
try {
// Create stream
const stream = await createOMTStream();
// Start monitoring
const monitor = await monitorOMTStream(stream.id);
// Setup chat
const chat = await setupSynchronizedChat(stream.id);
// Create poll
const poll = await createSynchronizedPoll(stream.id);
console.log('OMT Stream Ready!');
console.log('Target Latency: <16ms');
console.log('Share URL:', stream.playback.url);
// Keep running
process.on('SIGINT', async () => {
await wave.streams.end(stream.id);
monitor.close();
chat.disconnect();
console.log('Stream ended');
process.exit(0);
});
} catch (error) {
console.error('OMT Stream Error:', error);
}
})();