Fixes for seek
This commit is contained in:
parent
d39c10b946
commit
e11c9dee55
1 changed files with 67 additions and 54 deletions
121
src/player.rs
121
src/player.rs
|
|
@ -259,6 +259,7 @@ fn ffmpeg_thread<P: AsRef<Path>>(
|
||||||
.best(Type::Audio)
|
.best(Type::Audio)
|
||||||
.ok_or(ffmpeg::Error::StreamNotFound)?;
|
.ok_or(ffmpeg::Error::StreamNotFound)?;
|
||||||
let audio_stream_index = audio_stream.index();
|
let audio_stream_index = audio_stream.index();
|
||||||
|
let audio_time_base = f64::from(audio_stream.time_base());
|
||||||
|
|
||||||
let audio_context_decoder =
|
let audio_context_decoder =
|
||||||
ffmpeg::codec::context::Context::from_parameters(audio_stream.parameters())?;
|
ffmpeg::codec::context::Context::from_parameters(audio_stream.parameters())?;
|
||||||
|
|
@ -281,72 +282,77 @@ fn ffmpeg_thread<P: AsRef<Path>>(
|
||||||
audio_config.sample_rate().0,
|
audio_config.sample_rate().0,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut start_time_opt = None;
|
let mut sync_sample = 0;
|
||||||
let mut start_sample = 0;
|
let mut current_sample = 0;
|
||||||
let mut end_sample = 0;
|
|
||||||
let min_sleep = Duration::from_millis(1);
|
let min_sleep = Duration::from_millis(1);
|
||||||
let min_skip = Duration::from_millis(1);
|
let min_skip = Duration::from_millis(1);
|
||||||
let mut receive_and_process_decoded_audio_frames =
|
let mut receive_and_process_decoded_audio_frames = |decoder: &mut ffmpeg::decoder::Audio,
|
||||||
|decoder: &mut ffmpeg::decoder::Audio| -> Result<(), ffmpeg::Error> {
|
sync_time_opt: &mut Option<Instant>|
|
||||||
let mut decoded = Audio::empty();
|
-> Result<(), ffmpeg::Error> {
|
||||||
let mut resampled = Audio::empty();
|
let mut decoded = Audio::empty();
|
||||||
while decoder.receive_frame(&mut decoded).is_ok() {
|
let mut resampled = Audio::empty();
|
||||||
audio_resampler.run(&decoded, &mut resampled)?;
|
while decoder.receive_frame(&mut decoded).is_ok() {
|
||||||
|
audio_resampler.run(&decoded, &mut resampled)?;
|
||||||
|
{
|
||||||
|
// plane method doesn't work with packed samples, so do it manually
|
||||||
|
let plane = unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
(*resampled.as_ptr()).data[0] as *const f32,
|
||||||
|
resampled.samples() * resampled.channels() as usize,
|
||||||
|
)
|
||||||
|
};
|
||||||
{
|
{
|
||||||
// plane method doesn't work with packed samples, so do it manually
|
let mut audio_queue = audio_queue_lock.lock().unwrap();
|
||||||
let plane = unsafe {
|
audio_queue.extend(plane);
|
||||||
slice::from_raw_parts(
|
|
||||||
(*resampled.as_ptr()).data[0] as *const f32,
|
|
||||||
resampled.samples() * resampled.channels() as usize,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
{
|
|
||||||
let mut audio_queue = audio_queue_lock.lock().unwrap();
|
|
||||||
audio_queue.extend(plane);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
end_sample += decoded.samples();
|
|
||||||
if start_time_opt.is_none() {
|
|
||||||
start_time_opt = Some(Instant::now());
|
|
||||||
start_sample = end_sample;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync with audio
|
current_sample += decoded.samples();
|
||||||
if let Some(start_time) = &start_time_opt {
|
if sync_time_opt.is_none() {
|
||||||
let samples = end_sample - start_sample;
|
*sync_time_opt = Some(Instant::now());
|
||||||
let expected_float = (samples as f64) / f64::from(decoder.rate());
|
sync_sample = current_sample;
|
||||||
let expected = Duration::from_secs_f64(expected_float);
|
}
|
||||||
let actual = start_time.elapsed();
|
}
|
||||||
if expected > actual {
|
|
||||||
let sleep = expected - actual;
|
// Sync with audio
|
||||||
if sleep > min_sleep {
|
if let Some(sync_time) = &sync_time_opt {
|
||||||
// We leave min_sleep of buffer room
|
let samples = current_sample - sync_sample;
|
||||||
thread::sleep(sleep - min_sleep);
|
let expected_float = (samples as f64) / f64::from(decoder.rate());
|
||||||
}
|
let expected = Duration::from_secs_f64(expected_float);
|
||||||
} else {
|
let actual = sync_time.elapsed();
|
||||||
let skip = actual - expected;
|
if expected > actual {
|
||||||
if skip > min_skip {
|
let sleep = expected - actual;
|
||||||
//TODO: handle frame skipping
|
if sleep > min_sleep {
|
||||||
}
|
// We leave min_sleep of buffer room
|
||||||
|
thread::sleep(sleep - min_sleep);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let skip = actual - expected;
|
||||||
|
if skip > min_skip {
|
||||||
|
//TODO: handle frame skipping
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
Ok(())
|
let mut sync_time_opt = None;
|
||||||
};
|
let mut seconds_opt = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut pts_opt = None;
|
|
||||||
let mut packet = Packet::empty();
|
let mut packet = Packet::empty();
|
||||||
match packet.read(&mut ictx) {
|
match packet.read(&mut ictx) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
pts_opt = packet.pts();
|
|
||||||
if packet.stream() == video_stream_index {
|
if packet.stream() == video_stream_index {
|
||||||
video_packet_tx.send(packet).unwrap();
|
video_packet_tx.send(packet).unwrap();
|
||||||
} else if packet.stream() == audio_stream_index {
|
} else if packet.stream() == audio_stream_index {
|
||||||
audio_decoder.send_packet(&packet)?;
|
audio_decoder.send_packet(&packet)?;
|
||||||
receive_and_process_decoded_audio_frames(&mut audio_decoder)?;
|
receive_and_process_decoded_audio_frames(
|
||||||
|
&mut audio_decoder,
|
||||||
|
&mut sync_time_opt,
|
||||||
|
)?;
|
||||||
|
if let Some(pts) = packet.pts() {
|
||||||
|
seconds_opt = Some(pts as f64 * audio_time_base);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(error::Error::Eof) => break,
|
Err(error::Error::Eof) => break,
|
||||||
|
|
@ -356,16 +362,23 @@ fn ffmpeg_thread<P: AsRef<Path>>(
|
||||||
while let Ok(message) = player_rx.try_recv() {
|
while let Ok(message) = player_rx.try_recv() {
|
||||||
match message {
|
match message {
|
||||||
PlayerMessage::SeekRelative(seek_seconds) => {
|
PlayerMessage::SeekRelative(seek_seconds) => {
|
||||||
if let Some(pts) = pts_opt {
|
if let Some(seconds) = seconds_opt {
|
||||||
//TODO: use time base instead of hardcoded values
|
//TODO: use time base instead of hardcoded values
|
||||||
let timestamp = (pts + (seek_seconds * 1000.0) as i64) * 1000;
|
let timestamp = ((seconds + seek_seconds) * 1000000.0) as i64;
|
||||||
if seek_seconds.is_sign_negative() {
|
if seek_seconds.is_sign_negative() {
|
||||||
println!("backwards {} = {}", seek_seconds, timestamp);
|
println!(
|
||||||
|
"backwards {} from {} = {}",
|
||||||
|
seek_seconds, seconds, timestamp
|
||||||
|
);
|
||||||
ictx.seek(timestamp, ..timestamp)?;
|
ictx.seek(timestamp, ..timestamp)?;
|
||||||
} else {
|
} else {
|
||||||
println!("forwards {} = {}", seek_seconds, timestamp);
|
println!("forwards {} from {} = {}", seek_seconds, seconds, timestamp);
|
||||||
ictx.seek(timestamp, timestamp..)?;
|
ictx.seek(timestamp, timestamp..)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: improve sync when seeking
|
||||||
|
// Clear audio sync time
|
||||||
|
sync_time_opt = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -373,7 +386,7 @@ fn ffmpeg_thread<P: AsRef<Path>>(
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_decoder.send_eof()?;
|
audio_decoder.send_eof()?;
|
||||||
receive_and_process_decoded_audio_frames(&mut audio_decoder)?;
|
receive_and_process_decoded_audio_frames(&mut audio_decoder, &mut sync_time_opt)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue