Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the path to ffmpeg customisable #8100

Merged
merged 13 commits into from
Nov 13, 2024
2 changes: 1 addition & 1 deletion crates/store/re_video/examples/frames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ fn main() {
let mut decoder = re_video::decode::new_decoder(
&video_path.to_string(),
&video,
re_video::decode::DecodeHardwareAcceleration::Auto,
&re_video::decode::DecodeSettings::default(),
on_output,
)
.expect("Failed to create decoder");
Expand Down
30 changes: 24 additions & 6 deletions crates/store/re_video/src/decode/ffmpeg_h264/ffmpeg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ impl FFmpegProcessAndListener {
debug_name: &str,
on_output: Arc<OutputCallback>,
avcc: re_mp4::Avc1Box,
ffmpeg_path: Option<&std::path::Path>,
) -> Result<Self, Error> {
re_tracing::profile_function!();

Expand Down Expand Up @@ -204,7 +205,13 @@ impl FFmpegProcessAndListener {
}
};

let mut ffmpeg = FfmpegCommand::new()
let mut ffmpeg_command = if let Some(ffmpeg_path) = ffmpeg_path {
FfmpegCommand::new_with_path(ffmpeg_path)
} else {
FfmpegCommand::new()
};

let mut ffmpeg = ffmpeg_command
// Keep banner enabled so we can check on the version more easily.
//.hide_banner()
// "Reduce the latency introduced by buffering during initial input streams analysis."
Expand Down Expand Up @@ -647,25 +654,29 @@ pub struct FFmpegCliH264Decoder {
ffmpeg: FFmpegProcessAndListener,
avcc: re_mp4::Avc1Box,
on_output: Arc<OutputCallback>,
ffmpeg_path: Option<std::path::PathBuf>,
}

impl FFmpegCliH264Decoder {
pub fn new(
debug_name: String,
avcc: re_mp4::Avc1Box,
on_output: impl Fn(crate::decode::Result<Frame>) + Send + Sync + 'static,
ffmpeg_path: Option<std::path::PathBuf>,
) -> Result<Self, Error> {
re_tracing::profile_function!();

// TODO(ab): Pass executable path here.
if !ffmpeg_sidecar::command::ffmpeg_is_installed() {
if let Some(ffmpeg_path) = &ffmpeg_path {
if !ffmpeg_path.is_file() {
return Err(Error::FFmpegNotInstalled);
}
} else if !ffmpeg_sidecar::command::ffmpeg_is_installed() {
return Err(Error::FFmpegNotInstalled);
}

// Check the version once ahead of running FFmpeg.
// The error is still handled if it happens while running FFmpeg, but it's a bit unclear if we can get it to start in the first place then.
// TODO(ab): Pass executable path here.
match FFmpegVersion::for_executable(None) {
match FFmpegVersion::for_executable(ffmpeg_path.as_deref()) {
Ok(version) => {
if !version.is_compatible() {
return Err(Error::UnsupportedFFmpegVersion {
Expand All @@ -686,13 +697,19 @@ impl FFmpegCliH264Decoder {
}

let on_output = Arc::new(on_output);
let ffmpeg = FFmpegProcessAndListener::new(&debug_name, on_output.clone(), avcc.clone())?;
let ffmpeg = FFmpegProcessAndListener::new(
&debug_name,
on_output.clone(),
avcc.clone(),
ffmpeg_path.as_deref(),
)?;

Ok(Self {
debug_name,
ffmpeg,
avcc,
on_output,
ffmpeg_path,
})
}
}
Expand Down Expand Up @@ -736,6 +753,7 @@ impl AsyncDecoder for FFmpegCliH264Decoder {
&self.debug_name,
self.on_output.clone(),
self.avcc.clone(),
self.ffmpeg_path.as_deref(),
)?;
Ok(())
}
Expand Down
22 changes: 19 additions & 3 deletions crates/store/re_video/src/decode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ mod av1;
mod ffmpeg_h264;

#[cfg(with_ffmpeg)]
pub use ffmpeg_h264::{ffmpeg_download_url, Error as FFmpegError, FFmpegVersionParseError};
pub use ffmpeg_h264::{
ffmpeg_download_url, Error as FFmpegError, FFmpegVersion, FFmpegVersionParseError,
};

#[cfg(target_arch = "wasm32")]
mod webcodecs;
Expand Down Expand Up @@ -149,7 +151,7 @@ pub trait AsyncDecoder: Send + Sync {
pub fn new_decoder(
debug_name: &str,
video: &crate::VideoData,
hw_acceleration: DecodeHardwareAcceleration,
decode_settings: &DecodeSettings,
on_output: impl Fn(Result<Frame>) + Send + Sync + 'static,
) -> Result<Box<dyn AsyncDecoder>> {
#![allow(unused_variables, clippy::needless_return)] // With some feature flags
Expand All @@ -162,7 +164,7 @@ pub fn new_decoder(
#[cfg(target_arch = "wasm32")]
return Ok(Box::new(webcodecs::WebVideoDecoder::new(
video,
hw_acceleration,
decode_settings.hw_acceleration,
on_output,
)?));

Expand Down Expand Up @@ -197,6 +199,7 @@ pub fn new_decoder(
debug_name.to_owned(),
avc1_box.clone(),
on_output,
decode_settings.ffmpeg_path.clone(),
)?))
}

Expand Down Expand Up @@ -370,6 +373,19 @@ pub enum DecodeHardwareAcceleration {
PreferHardware,
}

/// Settings for video decoding.
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct DecodeSettings {
/// How the video should be decoded.
pub hw_acceleration: DecodeHardwareAcceleration,

/// Custom path for the ffmpeg binary.
///
/// If not provided, we use the path automatically determined by `ffmpeg_sidecar`.
pub ffmpeg_path: Option<std::path::PathBuf>,
}

impl std::fmt::Display for DecodeHardwareAcceleration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expand Down
2 changes: 1 addition & 1 deletion crates/viewer/re_data_ui/src/blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ pub fn blob_preview_and_save_ui(
blob_row_id,
blob,
media_type,
ctx.app_options.video_decoder_hw_acceleration,
ctx.app_options.video_decoder_settings(),
)
});
video_result_ui(ui, ui_layout, &video_result);
Expand Down
15 changes: 5 additions & 10 deletions crates/viewer/re_renderer/src/video/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ use std::{collections::hash_map::Entry, sync::Arc};
use ahash::HashMap;
use parking_lot::Mutex;

use re_video::{decode::DecodeHardwareAcceleration, VideoData};

use crate::{
resource_managers::{GpuTexture2D, SourceImageDataFormat},
RenderContext,
};
use re_video::{decode::DecodeSettings, VideoData};

/// Error that can occur during playing videos.
#[derive(thiserror::Error, Debug, Clone)]
Expand Down Expand Up @@ -87,26 +86,22 @@ pub struct Video {
debug_name: String,
data: Arc<re_video::VideoData>,
players: Mutex<HashMap<VideoPlayerStreamId, PlayerEntry>>,
decode_hw_acceleration: DecodeHardwareAcceleration,
decode_settings: DecodeSettings,
}

impl Video {
/// Loads a video from the given data.
///
/// Currently supports the following media types:
/// - `video/mp4`
pub fn load(
debug_name: String,
data: Arc<VideoData>,
decode_hw_acceleration: DecodeHardwareAcceleration,
) -> Self {
pub fn load(debug_name: String, data: Arc<VideoData>, decode_settings: DecodeSettings) -> Self {
let players = Mutex::new(HashMap::default());

Self {
debug_name,
data,
players,
decode_hw_acceleration,
decode_settings,
}
}

Expand Down Expand Up @@ -163,7 +158,7 @@ impl Video {
&self.debug_name,
render_context,
self.data.clone(),
self.decode_hw_acceleration,
&self.decode_settings,
)?;
vacant_entry.insert(PlayerEntry {
player: new_player,
Expand Down
6 changes: 3 additions & 3 deletions crates/viewer/re_renderer/src/video/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{sync::Arc, time::Duration};
use web_time::Instant;

use re_video::{
decode::{DecodeHardwareAcceleration, FrameInfo},
decode::{DecodeSettings, FrameInfo},
Time,
};

Expand Down Expand Up @@ -68,7 +68,7 @@ impl VideoPlayer {
debug_name: &str,
render_ctx: &RenderContext,
data: Arc<re_video::VideoData>,
hw_acceleration: DecodeHardwareAcceleration,
decode_settings: &DecodeSettings,
) -> Result<Self, VideoPlayerError> {
let debug_name = format!(
"{debug_name}, codec: {}",
Expand All @@ -85,7 +85,7 @@ impl VideoPlayer {
}

let chunk_decoder = VideoChunkDecoder::new(debug_name.clone(), |on_output| {
re_video::decode::new_decoder(&debug_name, &data, hw_acceleration, on_output)
re_video::decode::new_decoder(&debug_name, &data, decode_settings, on_output)
})?;

let texture = alloc_video_frame_texture(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ fn latest_at_query_video_from_datastore(
blob_row_id,
&blob,
media_type.as_ref(),
ctx.app_options.video_decoder_hw_acceleration,
ctx.app_options.video_decoder_settings(),
)
});
Some((video, blob))
Expand Down
6 changes: 6 additions & 0 deletions crates/viewer/re_ui/src/context_ext.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use egui::{emath::Float, pos2, Align2, Color32, Mesh, Rect, Shape, Vec2};

use crate::toasts::SUCCESS_COLOR;
use crate::{DesignTokens, TopBarStyle};

/// Extension trait for [`egui::Context`].
Expand Down Expand Up @@ -58,6 +59,11 @@ pub trait ContextExt {
// egui::Stroke::new(stroke_width, color)
}

#[must_use]
fn success_text(&self, text: impl Into<String>) -> egui::RichText {
egui::RichText::new(text).color(SUCCESS_COLOR)
}

#[must_use]
fn warning_text(&self, text: impl Into<String>) -> egui::RichText {
let style = self.ctx().style();
Expand Down
6 changes: 6 additions & 0 deletions crates/viewer/re_ui/src/ui_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ pub trait UiExt {
fn ui(&self) -> &egui::Ui;
fn ui_mut(&mut self) -> &mut egui::Ui;

/// Show the label with a success color.
fn success_label(&mut self, text: &str) -> egui::Response {
let label = egui::Label::new(self.ui().ctx().success_text(text));
self.ui_mut().add(label)
}

/// Shows a warning label.
fn warning_label(&mut self, warning_text: &str) -> egui::Response {
let label = egui::Label::new(self.ui().ctx().warning_text(warning_text));
Expand Down
Loading
Loading