hang
A media library built on top of moq-lite for streaming audio and video.
Overview
hang provides media-specific functionality on top of the generic moq-lite transport:
- Broadcast - Discoverable collection of tracks with catalog
- Catalog - Metadata describing available tracks, codec info, etc. (updated live)
- Track - Audio/video streams and other data types
- Group - Group of pictures (video) or collection of samples (audio)
- Frame - Timestamp + codec payload pair
Installation
Add to your Cargo.toml:
[dependencies]
hang = "0.1"Supported Codecs
hang implements most of the WebCodecs specification.
Video:
- H.264 (AVC)
- H.265 (HEVC)
- VP8
- VP9
- AV1
Audio:
- AAC
- Opus
Quick Start
Publishing Video
use hang::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect to relay
let connection = moq_lite::Connection::connect(
"https://relay.example.com/demo"
).await?;
// Create a hang broadcast
let mut broadcast = Broadcast::new("my-stream");
// Create video track
let video_track = broadcast.create_video_track(VideoConfig {
codec: "avc1.64002a".to_string(),
width: 1920,
height: 1080,
framerate: 30.0,
bitrate: 5_000_000,
})?;
// Create audio track
let audio_track = broadcast.create_audio_track(AudioConfig {
codec: "opus".to_string(),
sample_rate: 48000,
channels: 2,
bitrate: 128_000,
})?;
// Publish encoded frames
video_track.append_frame(Frame {
timestamp: 0,
data: h264_keyframe_data,
is_keyframe: true,
})?;
audio_track.append_frame(Frame {
timestamp: 0,
data: opus_packet_data,
is_keyframe: false,
})?;
// Publish to relay
connection.publish_broadcast(broadcast).await?;
Ok(())
}Subscribing to Video
use hang::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect to relay
let connection = moq_lite::Connection::connect(
"https://relay.example.com/demo"
).await?;
// Subscribe to broadcast
let broadcast = connection.consume_broadcast("my-stream").await?;
// Read catalog to discover tracks
let catalog = broadcast.catalog().await?;
for track_info in catalog.tracks {
println!("Track: {} ({})", track_info.name, track_info.codec);
if track_info.kind == "video" {
// Subscribe to video track
let track = broadcast.subscribe(&track_info.name).await?;
// Read frames
while let Some(frame) = track.next_frame().await? {
println!("Video frame: {}µs, {} bytes",
frame.timestamp, frame.data.len());
// Decode with your video decoder
}
}
}
Ok(())
}Catalog
The catalog is a special track containing JSON metadata about available tracks:
{
"version": 1,
"tracks": [
{
"name": "video",
"kind": "video",
"codec": "avc1.64002a",
"width": 1920,
"height": 1080,
"framerate": 30,
"bitrate": 5000000
},
{
"name": "audio",
"kind": "audio",
"codec": "opus",
"sampleRate": 48000,
"channelConfig": "2",
"bitrate": 128000
}
]
}The catalog is updated live as tracks are added, removed, or changed.
Frame Container
Each frame in hang consists of:
pub struct Frame {
pub timestamp: u64, // Microseconds
pub data: Vec<u8>, // Codec bitstream
pub is_keyframe: bool, // Keyframe flag
}This simple container:
- Works with WebCodecs
- Minimal overhead
- Codec-agnostic
- Any timestamp base
CMAF Import
hang includes a cmaf module to import fMP4/CMAF files:
use hang::cmaf::*;
// Import CMAF file
let cmaf_data = std::fs::read("video.mp4")?;
let broadcast = Broadcast::from_cmaf(&cmaf_data)?;
// Publish to relay
connection.publish_broadcast(broadcast).await?;This is useful for:
- Ingesting existing content
- Converting VOD to live
- Testing with sample files
Note: The CMAF importer is basic and doesn't support all features.
Grouping
Groups are aligned with natural boundaries:
Video:
- Start with keyframe (I-frame)
- Include dependent frames (P/B-frames)
- Enable joining at group boundaries
Audio:
- Collection of audio packets
- Usually 1 second of audio
- Independent decoding
let mut group = track.new_group();
group.append(keyframe)?;
group.append(p_frame_1)?;
group.append(p_frame_2)?;
group.finalize()?;Prioritization
hang automatically prioritizes:
- Keyframes - Highest priority (can't decode without them)
- Recent frames - Higher priority than old frames
- Audio - Often prioritized over video
This is handled automatically based on frame metadata.
Examples
The repository includes examples:
- Video track - Publishing video frames
CLI Tool
The hang-cli package provides a command-line tool (binary name: hang):
# Install
cargo install hang-cli
# Publish a video file
hang publish video.mp4
# Publish from FFmpeg
ffmpeg -i input.mp4 -f mpegts - | hang publish -
# Custom encoding settings
hang publish --codec h264 --bitrate 2000000 video.mp4See hang --help for all options.
API Reference
Full API documentation: docs.rs/hang
Key types:
Broadcast- Media broadcast with catalogCatalog- Track metadataVideoConfig/AudioConfig- Track configurationFrame- Timestamp + codec bitstreamcmafmodule - CMAF/fMP4 import
Protocol Specification
See the hang specification for protocol details.
Next Steps
- Use the moq-lite transport layer
- Deploy a relay server
- Read the Architecture guide
- View code examples