Audio & Video

Enable the media feature on your blinc dependency for the playback and capture APIs in this chapter.

The blinc_media crate provides cross-platform audio/video with royalty-free codecs. Media widgets in blinc_layout (behind the media feature) provide player UIs.

Audio Playback

#![allow(unused)]
fn main() {
use blinc::media::{AudioPlayer, AudioSource};

let player = AudioPlayer::new();
player.play(AudioSource::file("music.ogg"));
player.set_volume(0.8);
player.pause();
player.seek(30_000); // seek to 30s
player.resume();

println!("Position: {}ms", player.position_ms());
}

Desktop: Vorbis, WAV, FLAC via rodio. Mobile: platform codecs via native bridge.

Video Playback

#![allow(unused)]
fn main() {
use blinc::media::{VideoPlayer, VideoDecoder};

let mut decoder = VideoDecoder::new();
let player = VideoPlayer::new();

// Decode H.264 NAL units → RGBA frames
if let Some(frame) = decoder.decode_nal(h264_packet) {
    player.push_frame(frame);
}

player.play();
player.seek(10_000);
}

Desktop: OpenH264 (royalty-free). Mobile: platform decoders via native bridge.

Player Trait

Both players implement the shared Player trait:

#![allow(unused)]
fn main() {
use blinc::media::Player;

fn show_status(p: &dyn Player) {
    println!("{} / {} | vol: {}", p.position_ms(), p.duration_ms(), p.volume());
}
}
MethodDescription
play() / pause() / stop()Playback controls
seek(ms)Seek to position
position_ms() / duration_ms()Time tracking
volume() / set_volume(f32)Volume (0.0–1.0)
is_playing() / is_live()State queries

Audio Widget

#![allow(unused)]
fn main() {
use std::rc::Rc;
use blinc::layout::widgets::media::audio_player;

let player = Rc::new(AudioPlayer::new());

// Basic controls
audio_player(Rc::clone(&player)).w_full().into_div()

// With waveform
audio_player(Rc::clone(&player))
    .waveform_data(&samples)
    .w_full()
    .into_div()
}

The waveform follows the player's position and duration: its played region advances during playback, holds when paused, and follows seeks. The waveform asks for animation frames only while its player is playing.

Video Widget

#![allow(unused)]
fn main() {
use std::rc::Rc;
use blinc::layout::widgets::media::video_player;

let player = Rc::new(VideoPlayer::new());

video_player(Rc::clone(&player))
    .show_dimensions()
    .w_full()
    .h(400.0)
    .into_div()
}

Waveform

Standalone amplitude visualization:

#![allow(unused)]
fn main() {
use blinc::layout::widgets::media::waveform;

waveform(buckets)
    .progress(0.5)
    .played_color(Color::BLUE)
    .unplayed_color(Color::GRAY)
    .w_full().h(60.0)
    .into_div()
}

Shared Controls

MediaControls is generic over Player:

#![allow(unused)]
fn main() {
use blinc::layout::widgets::media::MediaControls;

MediaControls::new(player_rc).class("my-controls").into_div()
}

Layout: [ ▶ ] [ 1:23 / 3:45 ] [ ══seek══ ] [ 80% ]

Live streams: [ ▶ ] [ LIVE ] [ ════════════ ]

Camera & Recording

#![allow(unused)]
fn main() {
use blinc::media::rtc::{CameraStream, CameraConfig, AudioRecorder};

let camera = CameraStream::open(CameraConfig::default());
let frame = camera.latest_frame(); // RGBA Frame

let recorder = AudioRecorder::open(Default::default());
let samples = recorder.latest_samples(); // AudioSamples

drop(camera);   // stops capture
drop(recorder);  // stops recording
}

Frame Utilities

#![allow(unused)]
fn main() {
use blinc::media::{Frame, AudioSamples};

// Video
let small = Frame::from_rgba(data, 640, 480).scale(320, 240);
let gray = small.to_gray();

// Audio
let mono = AudioSamples::from_f32(&pcm, 2, 44100).to_mono();
let resampled = mono.resample(48000);
}

CSS Classes

The controls take their colours from the default stylesheet's rules, so they follow the theme and a stylesheet restyles them:

ClassElementDefault
.blinc-audio-playerAudio container
.blinc-video-playerVideo container
.blinc-audio-waveform, .blinc-waveformWaveformsee below
.blinc-media-controlsControls row
.blinc-media-play-btnPlay/pause--surface-elevated fill, --text-primary icon
.blinc-media-timeTime display--text-tertiary
.blinc-media-live-badgeLIVE indicator--error fill, white text
.blinc-media-seek, .blinc-media-seek-trackSeek barsee below
.blinc-media-seek-railSeek bar track--surface-elevated
.blinc-media-seek-fillSeek progress--primary
.blinc-media-volumeVolume--text-tertiary
.blinc-media-mute-btnMute--text-secondary

The video seek bar, the volume level and the waveform are drawn, in colours the stylesheet's variables give:

VariableDrawsDefault
--blinc-media-trackthe seek bar's track--surface-elevated
--blinc-media-bufferedwhat is buffered ahead--border
--blinc-media-progresswhat has played--primary
--blinc-media-level, --blinc-media-level-restthe volume columns at and above the level--surface-elevated, --text-secondary
--blinc-waveform-played, --blinc-waveform-unplayedthe waveform's bars--primary, --border-secondary
:root { --blinc-media-progress: #22c55e; }
.blinc-media-play-btn { background: var(--primary); color: var(--text-inverse); }

waveform(..).played_color(..) and .unplayed_color(..) set the waveform's colours over the variables.

Licensing

Desktop uses royalty-free codecs only — no ffmpeg, no patent fees:

CodecLicense
Vorbis, WAV, FLACBSD / Public domain
OpenH264BSD, Cisco covers patents

Mobile uses OS-provided codecs (licensing handled by the OS).

The component gallery has separate Audio Player, Video Player and Waveform pages. Their native recordings use ashui’s original Night Drive soundtrack and outdoor handpan session. The waveform uses amplitude peaks from the same Night Drive excerpt. Source assets and conversion details live in examples/blinc_component_demos/assets/README.md.

Native recordings start with audio paused and muted. Browser playback starts only after Play; the video sample is silent. Native playback needs the blinc_layout media feature.

The gallery connects browser media decoding to the dedicated worker. Controls, time, volume, seek and decoded video frames use the real Blinc players. Hiding a Live demo pauses playback; disposing it releases its media elements and frame transport.