Skip to content

Commit 918e247

Browse files
committed
Bump version to 0.9.7 with timeline seek robustness and motionless Neon Cuboids visualizer
1 parent 0efe702 commit 918e247

7 files changed

Lines changed: 779 additions & 82 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rusttracker"
3-
version = "0.9.6"
3+
version = "0.9.7"
44
edition = "2024"
55
default-run = "rusttracker"
66
description = "High-Performance Vulkan Tracker Module Visualizer"

src/audio.rs

Lines changed: 123 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustysynth::{MidiFile, MidiFileSequencer, SoundFont, Synthesizer, Synthesize
1717
use midly::{Smf, TrackEventKind, MetaMessage, MidiMessage};
1818

1919
use crate::state::AppState;
20-
use crossbeam_channel::{bounded, Sender, Receiver};
20+
use crossbeam_channel::{bounded, unbounded, Sender, Receiver};
2121

2222
#[allow(dead_code)]
2323
pub enum PlaybackHandle {
@@ -979,18 +979,14 @@ impl AudioSource for FfmpegSource {
979979
fn get_position_seconds(&mut self) -> f64 { self.current_time }
980980

981981
fn set_position_seconds(&mut self, pos: f64) {
982-
if let Some(stream) = self.ictx.stream(self.stream_index) {
983-
let tb = stream.time_base();
984-
let pts = (pos / (tb.numerator() as f64 / tb.denominator() as f64)) as i64;
985-
986-
unsafe {
987-
ffmpeg_next::ffi::av_seek_frame(
988-
self.ictx.as_mut_ptr(),
989-
self.stream_index as i32,
990-
pts,
991-
ffmpeg_next::ffi::AVSEEK_FLAG_BACKWARD
992-
);
993-
}
982+
let pts = (pos * ffmpeg_next::ffi::AV_TIME_BASE as f64) as i64;
983+
unsafe {
984+
ffmpeg_next::ffi::av_seek_frame(
985+
self.ictx.as_mut_ptr(),
986+
-1,
987+
pts,
988+
ffmpeg_next::ffi::AVSEEK_FLAG_BACKWARD
989+
);
994990
}
995991
self.decoder.flush();
996992
self.buf_pos = self.sample_buf.len();
@@ -1098,17 +1094,14 @@ impl AudioSource for VideoOnlySource {
10981094
fn get_position_seconds(&mut self) -> f64 { self.current_time }
10991095

11001096
fn set_position_seconds(&mut self, pos: f64) {
1101-
if let Some(stream) = self.ictx.stream(self.video_stream_index) {
1102-
let tb = stream.time_base();
1103-
let pts = (pos / (tb.numerator() as f64 / tb.denominator() as f64)) as i64;
1104-
unsafe {
1105-
ffmpeg_next::ffi::av_seek_frame(
1106-
self.ictx.as_mut_ptr(),
1107-
self.video_stream_index as i32,
1108-
pts,
1109-
ffmpeg_next::ffi::AVSEEK_FLAG_BACKWARD
1110-
);
1111-
}
1097+
let pts = (pos * ffmpeg_next::ffi::AV_TIME_BASE as f64) as i64;
1098+
unsafe {
1099+
ffmpeg_next::ffi::av_seek_frame(
1100+
self.ictx.as_mut_ptr(),
1101+
-1,
1102+
pts,
1103+
ffmpeg_next::ffi::AVSEEK_FLAG_BACKWARD
1104+
);
11121105
}
11131106
self.current_time = pos;
11141107
self.video_epoch += 1;
@@ -1665,15 +1658,15 @@ where
16651658
let chunk_frames = 1024;
16661659
let pool_size = 64;
16671660

1668-
let (ready_tx, ready_rx) = crossbeam_channel::bounded::<AudioChunk>(pool_size);
1669-
let (free_tx, free_rx) = crossbeam_channel::bounded::<AudioChunk>(pool_size);
1661+
let (ready_tx, ready_rx) = bounded::<AudioChunk>(pool_size);
1662+
let (free_tx, free_rx) = unbounded::<AudioChunk>();
16701663

1671-
let (video_packet_tx, video_packet_rx) = crossbeam_channel::bounded::<(u64, ffmpeg_next::Packet)>(4096);
1664+
let (video_packet_tx, video_packet_rx) = bounded::<(u64, ffmpeg_next::Packet)>(4096);
16721665
audio_source.attach_video_queue(video_packet_tx);
16731666

16741667
if let Some((params, time_base)) = audio_source.take_video_parameters() {
1675-
let (video_frame_tx, video_frame_rx) = crossbeam_channel::bounded::<crate::state::VideoFrame>(16);
1676-
let (free_video_frame_tx, free_video_frame_rx) = crossbeam_channel::bounded::<crate::state::VideoFrame>(16);
1668+
let (video_frame_tx, video_frame_rx) = bounded::<crate::state::VideoFrame>(16);
1669+
let (free_video_frame_tx, free_video_frame_rx) = unbounded::<crate::state::VideoFrame>();
16771670

16781671
for _ in 0..16 {
16791672
let _ = free_video_frame_tx.try_send(crate::state::VideoFrame {
@@ -1706,23 +1699,22 @@ where
17061699
let tb = time_base.numerator() as f64 / time_base.denominator() as f64;
17071700
let mut local_epoch = 0;
17081701
let mut fallback_pts_seconds = 0.0;
1702+
let mut is_first_frame_after_seek = false;
17091703

17101704
while let Ok((packet_epoch, packet)) = video_packet_rx_for_video.recv() {
1711-
let mut track_ended = {
1705+
{
17121706
let state = state_for_video.lock().unwrap();
17131707
if state.seek_epoch > local_epoch {
17141708
decoder.flush();
17151709
local_epoch = state.seek_epoch;
1710+
is_first_frame_after_seek = true;
17161711
}
1717-
state.track_ended
1718-
};
1712+
}
17191713

17201714
if packet_epoch < local_epoch {
17211715
continue;
17221716
}
17231717

1724-
if track_ended { return; }
1725-
17261718
if decoder.send_packet(&packet).is_ok() {
17271719
let mut decoded = ffmpeg_next::frame::Video::empty();
17281720
while decoder.receive_frame(&mut decoded).is_ok() {
@@ -1738,34 +1730,74 @@ where
17381730
}
17391731

17401732
let mut skip_push = false;
1733+
if is_first_frame_after_seek {
1734+
is_first_frame_after_seek = false;
1735+
} else {
1736+
let sync_start = std::time::Instant::now();
1737+
loop {
1738+
let (cached_seconds, current_epoch, is_paused, track_ended) = {
1739+
let state = state_for_video.lock().unwrap();
1740+
(state.current_seconds, state.seek_epoch, state.is_paused, state.track_ended)
1741+
};
1742+
1743+
if track_ended || current_epoch > local_epoch {
1744+
skip_push = true;
1745+
break;
1746+
}
1747+
1748+
if pts < cached_seconds - 0.05 {
1749+
skip_push = true;
1750+
break;
1751+
}
1752+
1753+
if pts <= cached_seconds + 0.05 {
1754+
break;
1755+
}
1756+
1757+
if sync_start.elapsed() > std::time::Duration::from_millis(500) {
1758+
skip_push = true;
1759+
break;
1760+
}
1761+
1762+
let sleep_dur = if is_paused { 50 } else { 5 };
1763+
std::thread::sleep(std::time::Duration::from_millis(sleep_dur));
1764+
}
1765+
}
1766+
1767+
if skip_push {
1768+
continue;
1769+
}
1770+
1771+
let mut frame_opt = None;
17411772
loop {
1742-
let (cached_seconds, current_epoch) = {
1773+
let (current_epoch, is_paused, track_ended) = {
17431774
let state = state_for_video.lock().unwrap();
1744-
track_ended = state.track_ended;
1745-
(state.current_seconds, state.seek_epoch)
1775+
(state.seek_epoch, state.is_paused, state.track_ended)
17461776
};
1747-
17481777
if track_ended || current_epoch > local_epoch {
17491778
skip_push = true;
17501779
break;
17511780
}
1752-
1753-
if pts < cached_seconds - 0.05 {
1754-
skip_push = true;
1755-
break;
1756-
}
1757-
1758-
if pts <= cached_seconds + 0.05 {
1759-
break;
1781+
match free_video_frame_rx.recv_timeout(std::time::Duration::from_millis(10)) {
1782+
Ok(f) => {
1783+
frame_opt = Some(f);
1784+
break;
1785+
}
1786+
Err(crossbeam_channel::RecvTimeoutError::Timeout) => {
1787+
let sleep_dur = if is_paused { 50 } else { 5 };
1788+
std::thread::sleep(std::time::Duration::from_millis(sleep_dur));
1789+
}
1790+
Err(crossbeam_channel::RecvTimeoutError::Disconnected) => {
1791+
return;
1792+
}
17601793
}
1761-
std::thread::sleep(std::time::Duration::from_millis(2));
17621794
}
17631795

17641796
if skip_push {
17651797
continue;
17661798
}
17671799

1768-
if let Ok(mut frame) = free_video_frame_rx.recv() {
1800+
if let Some(mut frame) = frame_opt {
17691801
frame.pts = pts;
17701802
frame.width = decoded.width();
17711803
frame.height = decoded.height();
@@ -1841,20 +1873,23 @@ where
18411873

18421874
std::thread::spawn(move || {
18431875
loop {
1844-
if let Ok(mut state) = state_for_decoder.try_lock() {
1845-
if let Some(pos) = state.seek_request.take() {
1846-
audio_source.set_position_seconds(pos);
1847-
state.current_seconds = pos;
1848-
state.seek_epoch += 1;
1849-
while let Ok(chunk) = ready_rx_for_decoder.try_recv() {
1850-
let _ = free_tx_for_decoder.try_send(chunk);
1876+
let mut chunk = loop {
1877+
if let Ok(mut state) = state_for_decoder.try_lock() {
1878+
if let Some(pos) = state.seek_request.take() {
1879+
audio_source.set_position_seconds(pos);
1880+
state.current_seconds = pos;
1881+
state.seek_epoch += 1;
1882+
while let Ok(c) = ready_rx_for_decoder.try_recv() {
1883+
let _ = free_tx_for_decoder.try_send(c);
1884+
}
1885+
while let Ok(_) = video_rx_for_decoder.try_recv() {}
18511886
}
18521887
}
1853-
}
1854-
1855-
let mut chunk = match free_rx.recv() {
1856-
Ok(c) => c,
1857-
Err(_) => break, // CPAL died
1888+
match free_rx.recv_timeout(std::time::Duration::from_millis(10)) {
1889+
Ok(c) => break c,
1890+
Err(crossbeam_channel::RecvTimeoutError::Timeout) => continue,
1891+
Err(crossbeam_channel::RecvTimeoutError::Disconnected) => return,
1892+
}
18581893
};
18591894

18601895
let decode_start = Instant::now();
@@ -1929,8 +1964,34 @@ where
19291964
state.stats.clipping_events += clips;
19301965
}
19311966

1932-
if ready_tx.send(chunk).is_err() {
1933-
break;
1967+
let mut chunk_to_send = chunk;
1968+
loop {
1969+
let mut seeked = false;
1970+
if let Ok(mut state) = state_for_decoder.try_lock() {
1971+
if let Some(pos) = state.seek_request.take() {
1972+
audio_source.set_position_seconds(pos);
1973+
state.current_seconds = pos;
1974+
state.seek_epoch += 1;
1975+
while let Ok(c) = ready_rx_for_decoder.try_recv() {
1976+
let _ = free_tx_for_decoder.try_send(c);
1977+
}
1978+
while let Ok(_) = video_rx_for_decoder.try_recv() {}
1979+
seeked = true;
1980+
}
1981+
}
1982+
if seeked {
1983+
let _ = free_tx_for_decoder.try_send(chunk_to_send);
1984+
break;
1985+
}
1986+
match ready_tx.send_timeout(chunk_to_send, std::time::Duration::from_millis(10)) {
1987+
Ok(_) => break,
1988+
Err(crossbeam_channel::SendTimeoutError::Timeout(c)) => {
1989+
chunk_to_send = c;
1990+
}
1991+
Err(crossbeam_channel::SendTimeoutError::Disconnected(_)) => {
1992+
return;
1993+
}
1994+
}
19341995
}
19351996

19361997
if frames_read == 0 {

0 commit comments

Comments
 (0)