Crate egui_video
source ·Expand description
egui-video, a video playing library for egui
https://github.com/n00kii/egui-video/assets/57325298/c618ff0a-9ad2-4cf0-b14a-dda65dc54b23
plays videos in egui from file path or from bytes
dependancies:
- requires ffmpeg 6. follow the build instructions here
- requires sdl2. by default, a feature is enabled to automatically compile it for you, but you are free to disable it and follow these instructions
usage:
/* called once (top level initialization) */
{ // if using audio...
let audio_device = egui_video::AudioDevice::new()?;
// don't let audio_device drop out of memory! (or else you lose audio)
add_audio_device_to_state_somewhere(audio_device);
}
/* called once (creating a player) */
let mut player = egui_video::Player::new(ctx, my_media_path)?;
{ // if using audio...
player = player.with_audio(&mut my_state.audio_device)
}
/* called every frame (showing the player) */
player.ui(ui, player.size);
contributions
are welcome :)
current caveats
- need to compile in
release
oropt-level=3
otherwise limited playback performance
Simple video player example
use eframe::NativeOptions;
use egui::{CentralPanel, DragValue, Grid, Sense, Slider, TextEdit, Window};
use egui_video::{AudioDevice, Player};
fn main() {
let _ = eframe::run_native(
"app",
NativeOptions::default(),
Box::new(|_| Box::new(App::default())),
);
}
struct App {
audio_device: AudioDevice,
player: Option<Player>,
media_path: String,
stream_size_scale: f32,
seek_frac: f32,
}
impl Default for App {
fn default() -> Self {
Self {
audio_device: AudioDevice::new().unwrap(),
media_path: String::new(),
stream_size_scale: 1.,
seek_frac: 0.,
player: None,
}
}
}
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
ctx.request_repaint();
CentralPanel::default().show(ctx, |ui| {
ui.horizontal(|ui| {
ui.add_enabled_ui(!self.media_path.is_empty(), |ui| {
if ui.button("load").clicked() {
match Player::new(ctx, &self.media_path.replace("\"", "")).and_then(|p| {
p.with_audio(&mut self.audio_device)
.and_then(|p| p.with_subtitles())
}) {
Ok(player) => {
self.player = Some(player);
}
Err(e) => println!("failed to make stream: {e}"),
}
}
});
ui.add_enabled_ui(!self.media_path.is_empty(), |ui| {
if ui.button("clear").clicked() {
self.player = None;
}
});
let tedit_resp = ui.add_sized(
[ui.available_width(), ui.available_height()],
TextEdit::singleline(&mut self.media_path)
.hint_text("click to set path")
.interactive(false),
);
if ui
.interact(
tedit_resp.rect,
tedit_resp.id.with("click_sense"),
Sense::click(),
)
.clicked()
{
if let Some(path_buf) = rfd::FileDialog::new()
.add_filter("videos", &["mp4", "gif", "webm", "mkv", "ogg"])
.pick_file()
{
self.media_path = path_buf.as_path().to_string_lossy().to_string();
}
}
});
ui.separator();
if let Some(player) = self.player.as_mut() {
Window::new("info").show(ctx, |ui| {
Grid::new("info_grid").show(ui, |ui| {
ui.label("frame rate");
ui.label(player.framerate.to_string());
ui.end_row();
ui.label("size");
ui.label(format!("{}x{}", player.size.x, player.size.y));
ui.end_row();
ui.label("elapsed / duration");
ui.label(player.duration_text());
ui.end_row();
ui.label("state");
ui.label(format!("{:?}", player.player_state.get()));
ui.end_row();
ui.label("has audio?");
ui.label(player.audio_streamer.is_some().to_string());
ui.end_row();
ui.label("has subtitles?");
ui.label(player.subtitle_streamer.is_some().to_string());
ui.end_row();
});
});
Window::new("controls").show(ctx, |ui| {
ui.horizontal(|ui| {
if ui.button("seek to:").clicked() {
player.seek(self.seek_frac);
}
ui.add(
DragValue::new(&mut self.seek_frac)
.speed(0.05)
.clamp_range(0.0..=1.0),
);
ui.checkbox(&mut player.looping, "loop");
});
ui.horizontal(|ui| {
ui.label("size scale");
ui.add(Slider::new(&mut self.stream_size_scale, 0.0..=2.));
});
ui.separator();
ui.horizontal(|ui| {
if ui.button("play").clicked() {
player.start()
}
if ui.button("unpause").clicked() {
player.resume();
}
if ui.button("pause").clicked() {
player.pause();
}
if ui.button("stop").clicked() {
player.stop();
}
});
ui.horizontal(|ui| {
ui.label("volume");
let mut volume = player.audio_volume.get();
if ui
.add(Slider::new(&mut volume, 0.0..=player.max_audio_volume))
.changed()
{
player.audio_volume.set(volume);
};
});
});
player.ui(ui, player.size * self.stream_size_scale);
}
});
}
}
Structs
- The playback device. Needs to be initialized (and kept alive!) for use by a
Player
. - Pipes audio samples to SDL2.
- Streams audio.
- The
Player
processes and controls streams of video/audio. This is what you use to show a video file. Initialize once, and use thePlayer::ui
orPlayer::ui_at()
functions to show the playback. - Simple concurrecy of primitive values.
- Streams subtitles.
- Streams video.
Enums
- The possible states of a
Player
.
Traits
- Streams data.