feat(player): add audio playback with ffplay

This commit is contained in:
accusys
2026-03-19 02:07:42 +08:00
parent a7d41b3c17
commit 342abd5aea
16 changed files with 156 additions and 72 deletions

View File

@@ -16,6 +16,7 @@ mod web;
use config::Config;
use overlay::{AsrLoader, ChunkLoader, YoloLoader};
use player::audio::AudioPlayer;
use player::ffmpeg::FFmpegDecoder;
use player::state::{PlaybackState, PlayerState};
@@ -64,6 +65,7 @@ fn run(config: &Config) -> Result<()> {
let mut asr: Option<AsrLoader> = None;
let mut yolo: Option<YoloLoader> = None;
let mut chunks: Option<ChunkLoader> = None;
let mut audio_player: Option<AudioPlayer> = None;
let mut is_fullscreen = false;
let mut is_dragging = false;
@@ -85,6 +87,10 @@ fn run(config: &Config) -> Result<()> {
texture = Some(tex);
dec.start_decoding(0)?;
decoder = Some(dec);
let player = AudioPlayer::new();
info!("Audio player initialized");
audio_player = Some(player);
}
if let Some(ref asr_path) = config.asr {
@@ -158,12 +164,21 @@ fn run(config: &Config) -> Result<()> {
match key {
sdl2::keyboard::Keycode::Escape => running = false,
sdl2::keyboard::Keycode::Space => {
player_state.playback =
let was_playing = player_state.playback == PlaybackState::Playing;
player_state.playback = if was_playing {
PlaybackState::Paused
} else {
PlaybackState::Playing
};
if let Some(ref mut audio) = audio_player {
if player_state.playback == PlaybackState::Playing {
PlaybackState::Paused
if !player_state.muted {
audio.resume();
}
} else {
PlaybackState::Playing
};
audio.pause();
}
}
}
sdl2::keyboard::Keycode::S => {
player_state.show_subtitle = !player_state.show_subtitle;
@@ -188,6 +203,14 @@ fn run(config: &Config) -> Result<()> {
}
sdl2::keyboard::Keycode::M => {
player_state.muted = !player_state.muted;
if let Some(ref mut audio) = audio_player {
if player_state.muted {
audio.pause();
} else if player_state.playback == PlaybackState::Playing {
audio.resume();
}
}
info!("Audio: {}", if player_state.muted { "MUTED" } else { "ON" });
}
sdl2::keyboard::Keycode::F => {
is_fullscreen = !is_fullscreen;
@@ -197,39 +220,62 @@ fn run(config: &Config) -> Result<()> {
dec.seek(0).ok();
player_state.current_frame = 0;
player_state.current_time_ms = 0;
sync_audio(
&mut audio_player,
&config.video,
0,
player_state.playback == PlaybackState::Playing,
);
}
}
sdl2::keyboard::Keycode::End => {
if let Some(ref mut dec) = decoder {
let last_frame = player_state.total_frames.saturating_sub(1);
dec.seek(
((last_frame as f64 / player_state.fps) * 1000.0) as u64,
)
.ok();
let time_ms =
((last_frame as f64 / player_state.fps) * 1000.0) as u64;
dec.seek(time_ms).ok();
player_state.current_frame = last_frame;
player_state.current_time_ms = player_state.duration_ms;
sync_audio(
&mut audio_player,
&config.video,
time_ms,
player_state.playback == PlaybackState::Playing,
);
}
}
sdl2::keyboard::Keycode::Left => {
let step = if shift { 60 } else { 1 };
if let Some(ref mut dec) = decoder {
let current = player_state.current_frame.saturating_sub(step);
dec.seek(((current as f64 / player_state.fps) * 1000.0) as u64)
.ok();
player_state.current_frame = current;
player_state.current_time_ms =
let time_ms =
((current as f64 / player_state.fps) * 1000.0) as u64;
dec.seek(time_ms).ok();
player_state.current_frame = current;
player_state.current_time_ms = time_ms;
sync_audio(
&mut audio_player,
&config.video,
time_ms,
player_state.playback == PlaybackState::Playing,
);
}
}
sdl2::keyboard::Keycode::Right => {
let step = if shift { 60 } else { 1 };
if let Some(ref mut dec) = decoder {
let current = player_state.current_frame + step;
dec.seek(((current as f64 / player_state.fps) * 1000.0) as u64)
.ok();
player_state.current_frame = current;
player_state.current_time_ms =
let time_ms =
((current as f64 / player_state.fps) * 1000.0) as u64;
dec.seek(time_ms).ok();
player_state.current_frame = current;
player_state.current_time_ms = time_ms;
sync_audio(
&mut audio_player,
&config.video,
time_ms,
player_state.playback == PlaybackState::Playing,
);
}
}
sdl2::keyboard::Keycode::Up => {
@@ -286,12 +332,13 @@ fn run(config: &Config) -> Result<()> {
player_state.playback = PlaybackState::Paused;
let ratio = (x - bar_x_start) as f64 / bar_width as f64;
let target_frame = (player_state.total_frames as f64 * ratio) as u64;
let time_ms =
((target_frame as f64 / player_state.fps) * 1000.0) as u64;
if let Some(ref mut dec) = decoder {
let time_ms =
((target_frame as f64 / player_state.fps) * 1000.0) as u64;
if dec.seek(time_ms).is_ok() {
player_state.current_frame = target_frame;
player_state.current_time_ms = time_ms;
sync_audio(&mut audio_player, &config.video, time_ms, false);
info!(
"Seeked to frame {} ({:.1}%)",
target_frame,
@@ -619,6 +666,22 @@ fn run(config: &Config) -> Result<()> {
Ok(())
}
fn sync_audio(
audio_player: &mut Option<AudioPlayer>,
video_path: &Option<std::path::PathBuf>,
time_ms: u64,
is_playing: bool,
) {
if let Some(ref mut audio) = audio_player {
if let Some(ref path) = video_path {
audio.play(path.as_path(), time_ms);
if !is_playing {
audio.pause();
}
}
}
}
fn format_time(ms: u64) -> String {
let total_secs = ms / 1000;
let hours = total_secs / 3600;