pub struct PreviewPlayer { /* private fields */ }Expand description
Drives real-time playback of a single media file.
PreviewPlayer decodes a video/audio file, synchronises video frame
presentation to an audio master clock, and delivers frames to a
registered FrameSink.
§Usage
let mut player = PreviewPlayer::open(Path::new("clip.mp4"))?;
player.set_sink(Box::new(MySink::new()));
player.play();
player.run()?;Implementations§
Source§impl PreviewPlayer
impl PreviewPlayer
Sourcepub fn open(path: &Path) -> Result<Self, PreviewError>
pub fn open(path: &Path) -> Result<Self, PreviewError>
Open a media file and prepare for playback.
Probes the file to detect audio/video streams, then opens a
DecodeBuffer for the video stream (when present). Returns
PreviewError if the file is missing, unreadable, or contains
neither a video nor an audio stream.
Audio-only files (MP3, AAC, WAV, FLAC, …) are fully supported:
run() will pace itself via the audio master clock and deliver no
video frames. Callers should drain samples via
pop_audio_samples.
§Errors
Returns PreviewError if the file cannot be probed or decoded.
Sourcepub fn set_sink(&mut self, sink: Box<dyn FrameSink>)
pub fn set_sink(&mut self, sink: Box<dyn FrameSink>)
Register the frame sink. Must be called before run.
Sourcepub fn play(&self)
pub fn play(&self)
Start (or resume) playback.
Clears the paused and stopped flags. Must be called before
run.
Sourcepub fn stop_handle(&self) -> Arc<AtomicBool>
pub fn stop_handle(&self) -> Arc<AtomicBool>
Returns a cloneable handle to the stop signal.
Storing true into the returned Arc<AtomicBool> has the same effect
as calling stop and is safe to call from any context,
including from within a FrameSink::push_frame callback.
§Example
let stop = player.stop_handle();
player.set_sink(Box::new(MySink { stop, max_frames: 10 }));
player.play();
player.run()?;Sourcepub fn pause_handle(&self) -> Arc<AtomicBool>
pub fn pause_handle(&self) -> Arc<AtomicBool>
Returns a cloneable handle to the pause flag.
Storing true pauses run; storing false resumes it.
Safe to call from any context, including from a UI thread running
concurrently with run.
§Example
let pause = player.pause_handle();
let stop = player.stop_handle();
std::thread::spawn(move || { player.play(); let _ = player.run(); });
pause.store(true, Ordering::Release); // pause from UI thread
pause.store(false, Ordering::Release); // resume
stop.store(true, Ordering::Release); // stopSourcepub fn pop_frame(&mut self) -> FrameResult
pub fn pop_frame(&mut self) -> FrameResult
Pop the next decoded video frame.
Delegates to DecodeBuffer::pop_frame. Blocks until a frame is available.
Returns FrameResult::Eof at end of file or for audio-only files.
Sourcepub fn seek(&mut self, target_pts: Duration) -> Result<(), PreviewError>
pub fn seek(&mut self, target_pts: Duration) -> Result<(), PreviewError>
Frame-accurate seek to target_pts.
Delegates to DecodeBuffer::seek. Returns Ok(()) immediately for
audio-only files (no video stream to seek).
§Errors
Returns PreviewError if the seek fails.
Sourcepub fn seek_coarse(&mut self, target_pts: Duration) -> Result<(), PreviewError>
pub fn seek_coarse(&mut self, target_pts: Duration) -> Result<(), PreviewError>
Coarse seek to the nearest I-frame at or before target_pts.
Delegates to DecodeBuffer::seek_coarse. Faster than
seek because it skips the forward-decode discard phase.
The first frame after this call will be at the nearest preceding I-frame,
which may be up to ±½ GOP from target_pts (typically ±1–2 s for H.264
at default settings).
Typical use: call repeatedly while a scrub bar is being dragged;
call seek on drag release for frame accuracy.
// Scrub-bar drag handler:
player.seek_coarse(drag_pts)?; // fast, called many times
// Drag released:
player.seek(release_pts)?; // exact, called once§Errors
Returns PreviewError if the seek fails.
Sourcepub fn use_proxy_if_available(&mut self, proxy_dir: &Path) -> bool
pub fn use_proxy_if_available(&mut self, proxy_dir: &Path) -> bool
If a proxy file for this media exists in proxy_dir, use it transparently.
Must be called before play. Returns true if a proxy was
found and activated; returns false if no proxy exists (original file
continues to be used).
Proxy lookup order: half → quarter → eighth; first match wins.
When a proxy is active, FrameSink::push_frame delivers frames at the
proxy’s native resolution. Callers should not assume a fixed resolution.
If called after play, logs a warning and returns false.
Sourcepub fn active_source(&self) -> &Path
pub fn active_source(&self) -> &Path
Returns the path currently being decoded — either the original file or the activated proxy.
Sourcepub fn set_av_offset(&self, ms: i64)
pub fn set_av_offset(&self, ms: i64)
Set the A/V offset correction in milliseconds.
- Positive value: video is delayed by
msms relative to the audio clock (video PTS is shifted down in the sync comparison). - Negative value: audio is delayed by
msms relative to video (video PTS is shifted up in the sync comparison).
Values outside ±5 000 ms are clamped and a warning is logged.
Safe to call from any thread while run is executing.
Sourcepub fn av_offset(&self) -> i64
pub fn av_offset(&self) -> i64
Returns the current A/V offset in milliseconds (default: 0).
Safe to call from any thread while run is executing.
Sourcepub fn av_offset_handle(&self) -> Arc<AtomicI64>
pub fn av_offset_handle(&self) -> Arc<AtomicI64>
Returns a cloneable handle to the A/V offset atomic.
Writing a value into the returned Arc<AtomicI64> has the same effect
as calling set_av_offset and is safe to do from
any thread while run is executing.
Note: the handle stores the raw millisecond value without clamping.
Values outside ±5 000 ms written directly to the handle will be applied
as-is by run(); prefer set_av_offset when
clamping is desired.
§Example
let av_handle = player.av_offset_handle();
let stop_handle = player.stop_handle();
std::thread::spawn(move || { player.play(); let _ = player.run(); });
// Adjust A/V sync from the UI thread without stopping playback.
av_handle.store(200, std::sync::atomic::Ordering::Relaxed);
stop_handle.store(true, std::sync::atomic::Ordering::Release);Sourcepub fn set_rate(&self, rate: f64)
pub fn set_rate(&self, rate: f64)
Set the playback rate.
Values ≤ 0.0 are silently ignored — the rate remains unchanged.
The new rate takes effect on the next frame’s sleep calculation inside
run. Safe to call from any thread while run() is
executing (same contract as set_av_offset).
1.0— real-time (default)2.0— twice real-time (sleep halved)0.5— half real-time (sleep doubled)
Sourcepub fn rate_handle(&self) -> Arc<AtomicU64>
pub fn rate_handle(&self) -> Arc<AtomicU64>
Returns a cloneable handle to the rate atomic.
Writing new_rate.to_bits() into the returned Arc<AtomicU64> has
the same effect as calling set_rate and is safe to
do from any thread while run is executing.
Note: the handle does not validate the value; storing bits that
correspond to ≤ 0.0 or NaN will produce undefined sleep behaviour.
Prefer set_rate when the validation guard is desired.
§Example
let rate = player.rate_handle();
let stop = player.stop_handle();
std::thread::spawn(move || { player.play(); let _ = player.run(); });
// Double speed from the UI thread without stopping playback.
rate.store(2.0_f64.to_bits(), std::sync::atomic::Ordering::Relaxed);
stop.store(true, std::sync::atomic::Ordering::Release);Sourcepub fn current_pts(&self) -> Duration
pub fn current_pts(&self) -> Duration
Returns the PTS of the most recently presented frame.
Returns Duration::ZERO before the first frame has been presented.
Safe to call from any thread while run is executing.
§Example
let pts_handle = Arc::new(Mutex::new(Duration::ZERO));
let pts_clone = Arc::clone(&pts_handle);
std::thread::spawn(move || {
player.play();
let _ = player.run();
});
// UI thread: poll current position to drive a seek bar.
loop {
let pos = player.current_pts();
update_seek_bar(pos);
std::thread::sleep(std::time::Duration::from_millis(16));
}Sourcepub fn duration(&self) -> Option<Duration>
pub fn duration(&self) -> Option<Duration>
Returns the container-reported duration of the media file, if known.
Returns None for live or streaming sources where the container does
not report a duration. Use the returned value to size a seek bar range:
if let Some(total) = player.duration() {
let progress = player.current_pts().as_secs_f64() / total.as_secs_f64();
seek_bar.set_fraction(progress);
}Sourcepub fn pop_audio_samples(&self, n_samples: usize) -> Vec<f32>
pub fn pop_audio_samples(&self, n_samples: usize) -> Vec<f32>
Pull up to n_samples interleaved stereo f32 PCM samples at 48 kHz.
Intended for use inside an audio output callback:
let samples = player.pop_audio_samples(buffer_size);
output_buffer[..samples.len()].copy_from_slice(&samples);
// fill remainder with silence when samples.len() < buffer_size (underrun)Advances the audio master clock by the number of stereo frames consumed
(samples.len() / 2).
Returns an empty Vec when:
- the file has no audio track,
n_samplesis0,- playback is paused or stopped, or
- the ring buffer is empty (underrun — caller should output silence).
Sourcepub fn run(&mut self) -> Result<(), PreviewError>
pub fn run(&mut self) -> Result<(), PreviewError>
A/V sync presentation loop.
Blocks until stop is called or the end of file is
reached. Must be called from the presentation thread.
Video PTS is compared against the master clock:
- Early frames (video PTS > clock + 1 frame period): sleep.
- Late frames (video PTS < clock − 1 frame period): dropped.
For video-only files the System clock (Instant) drives real-time
pacing. For files with audio the Audio clock drives sync once
pop_audio_samples has been called at least
once; before that, frames are presented immediately.
§Errors
Returns PreviewError if a frame cannot be presented to the sink.