Files
clipbin-desktop/crates/clipbin-media/src/lib.rs
T
harry dd016f7727 Merge feat/playback: session playback, synced audio and thumbnails
# Conflicts:
#	README.md
#	crates/clipbin-desktop/Cargo.toml
#	crates/clipbin-desktop/src/main.rs
#	crates/clipbin-media/src/lib.rs
#	docs/validation.md
2026-10-08 19:26:04 +02:00

81 lines
2.2 KiB
Rust

//! Reading recorded segments with FFmpeg: clip export (remuxing without
//! re-encoding) and decoding for playback.
//!
//! FFmpeg is linked dynamically: the system's libraries on Linux (the OBS
//! runtime already depends on them) and, on Windows, the FFmpeg DLLs shipped
//! in the official OBS release that scripts/obs/assemble-windows.sh bundles.
use std::{path::Path, sync::Once, time::Duration};
pub use ffmpeg_next as ffmpeg;
/// Initialises FFmpeg once per process.
pub fn init() {
static INIT: Once = Once::new();
INIT.call_once(|| {
let _ = ffmpeg::init();
ffmpeg::log::set_level(ffmpeg::log::Level::Error);
});
}
/// Basic facts about a segment file.
#[derive(Debug, Clone, PartialEq)]
pub struct Probe {
pub duration: Duration,
pub video_codec: String,
pub width: u32,
pub height: u32,
pub audio_tracks: usize,
}
pub fn probe(path: &Path) -> Result<Probe, ffmpeg::Error> {
init();
let input = ffmpeg::format::input(path)?;
let video = input
.streams()
.best(ffmpeg::media::Type::Video)
.ok_or(ffmpeg::Error::StreamNotFound)?;
let decoder = ffmpeg::codec::context::Context::from_parameters(video.parameters())?
.decoder()
.video()?;
let micros = input.duration().max(0) as u64;
Ok(Probe {
duration: Duration::from_micros(micros),
video_codec: decoder.id().name().to_owned(),
width: decoder.width(),
height: decoder.height(),
audio_tracks: input
.streams()
.filter(|stream| stream.parameters().medium() == ffmpeg::media::Type::Audio)
.count(),
})
}
#[cfg(test)]
mod tests {
use super::*;
/// Set CLIPBIN_TEST_SEGMENT to a recorded segment to run this.
#[test]
fn probes_a_recorded_segment() {
let Some(path) = std::env::var_os("CLIPBIN_TEST_SEGMENT") else {
return;
};
let probe = probe(Path::new(&path)).unwrap();
assert!(probe.duration > Duration::ZERO);
assert!(probe.width > 0 && probe.height > 0);
println!("{probe:?}");
}
}
pub mod audio;
pub mod decode;
pub mod export;
pub mod player;
pub mod thumbnail;
mod priority;
#[cfg(windows)]
#[allow(unsafe_code)]
mod windows_priority;