-
Notifications
You must be signed in to change notification settings - Fork 144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mkv/Webm seeking error #278
Comments
Hey! I'm not super familiar with MKV but i studied the code for a bit to get a grasp at what is going on and i understand the problem you are running into, this seems valid yes. I also think this is the correct place to add the fix. And as for the unwrap, i think if you use a switch statement (or is_some()) to prevent the panic. Something like
I didn't reproduce the problem though, if you have any file/stream that allows me to replicate it and there is no copyright issues with sharing it, would you mind sharing it? Thanks |
Hi @dedobbin! So to begin with:
That's what the conditional is supposed to check. If
So my main concerns with this solution are:
But I think adding the while loop and use std::io;
fn seek_track_by_ts(&mut self, track_id: u32, ts: u64) -> Result<SeekedTo> {
if self.clusters.is_empty() {
self.seek_track_by_ts_forward(track_id, ts)
} else {
while self.frames.is_empty() {
match self.next_element() {
Err(Error::IoError(error)) => {
if matches!(error.kind(), io::ErrorKind::UnexpectedEof) {
// break so user can seek backward
break;
}
return Err(error);
}
Err(error) => return Err(error),
_ => {},
}
}
/*
Code for getting cluster, block and pos
*/
let seek_ts = match target_block {
Some(block) => block.timestamp,
None => cluster.timestamp,
};
match self.frames.front() {
Some(frame) => {
// skip internal seeking if the target timestamp is in the current block/cluster
if seek_ts > frame.timestamp || ts < frame.timestamp {
self.iter.seek(pos)?;
}
}
// Should be end of stream, maybe user trying to seek backward
None => self.iter.seek(pos)?,
}
/*
The rest
*/
}
}
Maybe we could somehow make use of
I think the problem is a source not being seekable (read-only) as it didn't work for all streams I used (temporary links, limited to ip address, so can't share it). I managed to recreate the issue with the following code (file in attachments): use std::fs::File;
use symphonia::{
core::{
formats::{SeekMode, SeekTo},
io::{MediaSourceStream, ReadOnlySource},
},
default::formats::MkvReader,
};
use symphonia_core::formats::FormatReader;
#[tokio::main]
async fn main() {
let file = File::open("nocopyright.webm").unwrap();
let source = ReadOnlySource::new(file);
let mss = MediaSourceStream::new(Box::new(source), Default::default());
let mut mkv = MkvReader::try_new(mss, &Default::default()).unwrap();
for _ in 0..5 {
let packet = mkv.next_packet().unwrap();
println!("{} - {}", packet.ts, packet.dur);
}
let result = mkv.seek(
SeekMode::Accurate,
SeekTo::TimeStamp {
ts: 500,
track_id: 1,
},
);
println!("{:?}", result);
} nocopyright.webm |
Ah super. Thanks for the file and way to reproduce it, appreciated.
Yes i figured it out, no worries, just feels like stating it in a conditional that involves explicit comparing clusters is more intuitive.
Ah, i implied in previous post tat this would be difficult but when playing around with your provided code i think it's clearly viable yes. I think it's worth trying to use it! It would make it more clear. As for the issues you raise with my provided code snippet; You seem very right, i was clearly thinking too naive. The solution you provide here does seem to make sense but i haven't tested it yet. I've been spending some time trying to recreate these 2 "edge"-cases (if you can even call them that haha) and to get more input data so we can test for any regressions with your provided solution. If that all behaves as expected we can just focusing on the code structure itself. I didn't have enough time to get that all done in a proper way though and it i'll be bit busy upcoming weeks, so not sure when i can give it the attention it deserves, so i felt it was good to give you this update for now. You clearly spent some nice time with this though, so if you have any input or ideas on this don't hesitate to share it. |
Thanks for the response. I managed to recreate these 2 "edge"-cases pretty easily with the audio file I provided recently. Here is the code with comments for respective case: use std::fs::File;
use symphonia::{
core::{
formats::{SeekMode, SeekTo},
io::MediaSourceStream,
},
default::formats::MkvReader,
};
use symphonia_core::formats::FormatReader;
fn main() {
let file = File::open("nocopyright.webm").unwrap();
let source = file;
let mss = MediaSourceStream::new(Box::new(source), Default::default());
let mut mkv = MkvReader::try_new(mss, &Default::default()).unwrap();
// edge case number 1
for _ in 0..500 {
let packet = mkv.next_packet().unwrap();
}
// edge case number 2
// while let Ok(_) = mkv.next_packet() {}
let result = mkv.seek(
SeekMode::Accurate,
SeekTo::TimeStamp {
ts: 500,
track_id: 1,
},
);
println!("{:?}", result);
} But before running it, I put your code snippet in the symphonia's code: let seek_ts = match target_block {
Some(block) => block.timestamp,
None => cluster.timestamp,
};
match self.frames.front() {
Some(frame) => {
if seek_ts > frame.timestamp || ts < frame.timestamp {
self.iter.seek(pos)?;
}
}
None => {}
} So in the first scenario I read 500 packets because first cluster's timestamp is In the second scenario we read all packets from track and then we try to seek backward, we can't. When playing with these "edge"-cases I've come to the conclusion that the while loop from my previous solution can be deleted, meaning that this match self.frames.front() {
Some(frame) => {
// skip internal seeking if the target timestamp is in the current block/cluster
if seek_ts > frame.timestamp || ts < frame.timestamp {
self.iter.seek(pos)?;
}
}
// Should be end of stream, maybe user trying to seek backward
None => self.iter.seek(pos)?,
} Because as far as I know there can be these cases:
I also tried to simplify the conditional with the
I think I shared all my knowledge about this problem. I feel that the only possible things left are testing, which I unfortunately don't know how to do properly, maybe better solution and analyzing whole code for perhaps new nuances, but I think we have a pretty solid understanding on this one.
No problem, I will be a bit more busy too, so might take longer to reply. |
Alright, played around with a bit more. I think this is just a proper solution !
I say go for it. One thing i saw in the prototype for the fix, since both pos and seek_ts are taken from the block or cluster, they can just be combined using |
When trying to seek forward in a streamed source only a little bit (like 1-5 seconds, seeking more than about 10 seconds works fine) I get an error that the source is only forward seekable.
I searched for the cause of it in the code and what I think is the problem is that since clusters don't have blocks and we are trying to seek forward in the current cluster, it tries to seek to the start of it, meaning it would go backwards and because the source itself is not seekable it returns the error.
Prototype solution, put here:
But because I'm not that familiar with the codebase, I don't know how to get the current timestamp if not from the frames which might be empty (eg. the end of track), meaning the unwrap would panic.
If it's not intended behavior and I diagnosed the issue correctly I am willing to try implementing solution for that.
The text was updated successfully, but these errors were encountered: