Crate egui_video

source ·
Expand description

egui-video, a video playing library for egui

crates.io docs license

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 or opt-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

Enums

Traits