Getting Started
đ Getting Started
Section titled âđ Getting StartedâThis guide walks you through connecting to a Cast device, playing media, and reacting to events.
Installation
Section titled âInstallationâ[dependencies]oxicast = "0.0.1"tokio = { version = "1", features = ["full"] }Connecting
Section titled âConnectingâBy IP (simplest)
Section titled âBy IP (simplest)âIf you know the deviceâs IP address:
use oxicast::CastClient;
let client = CastClient::connect("192.168.1.100", 8009).await?;With custom settings
Section titled âWith custom settingsâuse std::time::Duration;
let client = CastClient::builder("192.168.1.100", 8009) .heartbeat_interval(Duration::from_secs(5)) .heartbeat_timeout(Duration::from_secs(15)) .request_timeout(Duration::from_secs(10)) .auto_reconnect(true) .max_reconnect_attempts(5) .reconnect_delay(Duration::from_secs(2)) .verify_tls(false) // Cast devices use self-signed certs .connect() .await?;Via mDNS discovery
Section titled âVia mDNS discoveryâScan the local network for Cast devices:
use std::time::Duration;
let devices = oxicast::discovery::discover_devices(Duration::from_secs(3)).await?;for device in &devices { println!("{} at {}:{}", device.name, device.ip, device.port);}
// Connect to the first oneif let Some(device) = devices.first() { let client = device.connect().await?;}Or stream devices as theyâre found (useful for real-time UI updates):
let mut stream = oxicast::discovery::discover(Duration::from_secs(5))?;while let Some(device) = stream.recv().await { println!("Found: {} at {}", device.name, device.ip);}Playing media
Section titled âPlaying mediaâCast devices need a running application to play media. The Default Media Receiver handles most formats (MP4, HLS, WebM).
use oxicast::{CastApp, MediaInfo};
// 1. Launch the media receiverclient.launch_app(&CastApp::DefaultMediaReceiver).await?;
// 2. Load medialet status = client.load_media( &MediaInfo::new("https://example.com/video.mp4", "video/mp4"), true, // autoplay 0.0, // start position in seconds).await?;
println!("State: {:?}", status.player_state);With metadata
Section titled âWith metadataâuse oxicast::{MediaInfo, MediaMetadata, Image};
let media = MediaInfo::new("https://example.com/movie.mp4", "video/mp4") .duration(7200.0) .metadata(MediaMetadata::Movie { title: Some("My Movie".into()), subtitle: Some("A great film".into()), studio: None, images: vec![Image { url: "https://example.com/poster.jpg".into(), width: Some(800), height: Some(1200), }], });HLS streams
Section titled âHLS streamsâlet media = MediaInfo::new( "https://example.com/stream.m3u8", "application/x-mpegURL",);Live content
Section titled âLive contentâuse oxicast::StreamType;
let media = MediaInfo::live("https://example.com/live.m3u8", "application/x-mpegURL");// orlet media = MediaInfo::new(url, content_type).stream_type(StreamType::Live);Controlling playback
Section titled âControlling playbackâclient.pause().await?;client.play().await?;client.seek(120.0).await?; // seek to 2 minutesclient.stop_media().await?; // end the media sessionclient.set_volume(0.5).await?; // 0.0 to 1.0, clampedclient.set_muted(true).await?;Reacting to events
Section titled âReacting to eventsâOption A: Event stream
Section titled âOption A: Event streamâUse next_event() in a tokio::select! loop. This is the most common pattern:
use oxicast::CastEvent;
loop { tokio::select! { Some(event) = client.next_event() => match event { CastEvent::MediaStatusChanged(status) => { println!("{:?} at {:.1}s", status.player_state, status.current_time); } CastEvent::MediaSessionEnded { idle_reason, .. } => { println!("Ended: {idle_reason}"); } CastEvent::Disconnected(reason) => { println!("Lost connection: {reason:?}"); break; } CastEvent::Reconnected => { println!("Connection restored"); } _ => {} }, _ = tokio::signal::ctrl_c() => { client.disconnect().await?; break; } }}Note:
next_event()is single-consumer. Only one task should call it at a time. The event channel is bounded â if you donât drain it, events are dropped (not blocked). The reader task is never stalled.
Option B: Watch channels
Section titled âOption B: Watch channelsâIf you only care about the latest state and donât need every event:
let mut media_rx = client.watch_media_status();let mut receiver_rx = client.watch_receiver_status();
// Always has the latest value, no draining neededloop { media_rx.changed().await?; if let Some(status) = media_rx.borrow_and_update().as_ref() { println!("Position: {:.1}s", status.current_time); }}Watch channels work regardless of whether anyone calls next_event().
Casting local files
Section titled âCasting local filesâWith the serve feature, you can cast files from your local filesystem:
oxicast = { version = "0.0.1", features = ["serve"] }One-call convenience
Section titled âOne-call convenienceâlet (server, status) = client.serve_and_cast( "/path/to/video.mp4", "video/mp4", true, 0.0,).await?;// Keep `server` alive for the duration of playback!Manual setup
Section titled âManual setupâuse oxicast::serve::FileServer;
let server = FileServer::start("0.0.0.0:0").await?;let url = server.serve_file("/path/to/video.mp4", "video/mp4")?;println!("Serving at: {url}");
client.load_media(&MediaInfo::new(&url, "video/mp4"), true, 0.0).await?;Disconnecting
Section titled âDisconnectingâ// Graceful shutdown â stops all background tasks, next_event() returns Noneclient.disconnect().await?;
// Check connection stateclient.is_connected(); // false after disconnectclient.connection_state(); // ConnectionState::DisconnectedAfter disconnect(), you can call reconnect() to re-establish the connection.