mod controls;
mod format_time;
mod waveform;
pub use controls::MediaControls;
pub use format_time::format_time_ms;
pub use waveform::waveform;
use std::rc::Rc;
use std::sync::Arc;
use blinc_core::Color;
use blinc_media::Player;
use crate::div::{div, Div};
use crate::text::text;
pub struct AudioPlayerWidget {
player: Rc<blinc_media::AudioPlayer>,
samples: Option<Arc<Vec<f32>>>,
inner: Div,
}
pub fn audio_player(player: Rc<blinc_media::AudioPlayer>) -> AudioPlayerWidget {
AudioPlayerWidget {
player,
samples: None,
inner: div().class("blinc-audio-player"),
}
}
impl AudioPlayerWidget {
pub fn waveform_data(mut self, samples: &blinc_media::AudioSamples) -> Self {
let f32_samples = samples.as_f32();
let channels = samples.channels as usize;
let mono: Vec<f32> = if channels > 1 {
f32_samples
.chunks_exact(channels)
.map(|frame| frame.iter().sum::<f32>() / channels as f32)
.collect()
} else {
f32_samples
};
let bucket_count = 200;
let bucket_size = (mono.len() / bucket_count).max(1);
let buckets: Vec<f32> = mono
.chunks(bucket_size)
.map(|chunk| chunk.iter().map(|s| s.abs()).fold(0.0f32, f32::max))
.collect();
self.samples = Some(Arc::new(buckets));
self
}
pub fn w(mut self, v: f32) -> Self {
self.inner = self.inner.w(v);
self
}
pub fn h(mut self, v: f32) -> Self {
self.inner = self.inner.h(v);
self
}
pub fn w_full(mut self) -> Self {
self.inner = self.inner.w_full();
self
}
pub fn bg(mut self, color: Color) -> Self {
self.inner = self.inner.bg(color);
self
}
pub fn rounded(mut self, r: f32) -> Self {
self.inner = self.inner.rounded(r);
self
}
pub fn class(mut self, class: &str) -> Self {
self.inner = self.inner.class(class);
self
}
pub fn id(mut self, id: &str) -> Self {
self.inner = self.inner.id(id);
self
}
pub fn into_div(self) -> Div {
let mut container = self.inner.flex_col().gap_px(4.0);
if let Some(ref buckets) = self.samples {
container = container.child(
waveform(Arc::clone(buckets))
.w_full()
.h(60.0)
.class("blinc-audio-waveform")
.into_div(),
);
}
let controls = MediaControls::new(self.player).class("blinc-audio-controls");
container = container.child(controls.into_div());
container
}
}
pub struct VideoPlayerWidget {
player: Rc<blinc_media::VideoPlayer>,
inner: Div,
show_dimensions: bool,
}
pub fn video_player(player: Rc<blinc_media::VideoPlayer>) -> VideoPlayerWidget {
VideoPlayerWidget {
player,
inner: div().class("blinc-video-player"),
show_dimensions: false,
}
}
impl VideoPlayerWidget {
pub fn show_dimensions(mut self) -> Self {
self.show_dimensions = true;
self
}
pub fn w(mut self, v: f32) -> Self {
self.inner = self.inner.w(v);
self
}
pub fn h(mut self, v: f32) -> Self {
self.inner = self.inner.h(v);
self
}
pub fn w_full(mut self) -> Self {
self.inner = self.inner.w_full();
self
}
pub fn bg(mut self, color: Color) -> Self {
self.inner = self.inner.bg(color);
self
}
pub fn rounded(mut self, r: f32) -> Self {
self.inner = self.inner.rounded(r);
self
}
pub fn class(mut self, class: &str) -> Self {
self.inner = self.inner.class(class);
self
}
pub fn id(mut self, id: &str) -> Self {
self.inner = self.inner.id(id);
self
}
pub fn into_div(self) -> Div {
let player = Rc::clone(&self.player);
let mut container = self.inner.flex_col();
let player_for_canvas = Rc::clone(&player);
let surface = crate::canvas::canvas(
move |ctx: &mut dyn blinc_core::DrawContext, bounds: crate::canvas::CanvasBounds| {
if let Some(frame) = player_for_canvas.current_frame() {
let rgba = frame.as_rgba();
let dest = blinc_core::Rect::new(0.0, 0.0, bounds.width, bounds.height);
ctx.draw_rgba_pixels(&rgba, frame.width, frame.height, dest);
} else {
ctx.fill_rect(
blinc_core::Rect::new(0.0, 0.0, bounds.width, bounds.height),
blinc_core::CornerRadius::default(),
blinc_core::Brush::Solid(Color::rgba(0.05, 0.05, 0.07, 1.0)),
);
}
},
)
.w_full()
.flex_grow();
container = container.child(surface);
let controls = MediaControls::new(player).class("blinc-video-controls");
container = container.child(controls.into_div());
if self.show_dimensions {
if let Some(frame) = self.player.current_frame() {
container = container.child(
text(format!("{}x{}", frame.width, frame.height))
.size(10.0)
.color(Color::rgba(0.4, 0.4, 0.5, 1.0))
.class("blinc-video-dimensions"),
);
}
}
container
}
}