The way that other classes interact with IncrementallyPopulatedStream
is now through a virtual interface MediaStream and MediaStreamCursor.
This way, we can have simpler implementations of reading media data
that will not require an RB tree and synchronization.
...and abstract away the stream/cursor blocking/aborting functionality
so that demuxers can implement or ignore those methods as they see fit.
This is a step towards implementing a wrapper demuxer for MSE streams.
This ensures that we're using the reader for the particular thread that
the block was read from, avoiding any race conditions between seeks and
reads across threads.
We only need to get the frames from a block when requested by the
demuxer, so factor that out into a function that it can call when it is
outputting frames.
Refactor the FFmpeg and Matroska demuxers to consume data through
`IncrementallyPopulatedStream::Cursor` instead of a pointer to fully
buffered.
This change establishes a new rule: each track must be initialized with
its own cursor. Data providers now explicitly create a per-track context
via `Demuxer::create_context_for_track(track, cursor)`, and own pointer
to that cursor. In the upcoming changes, holding the cursor in the
provider would allow to signal "cancel blocking reads" so an
in-flight seek can fail immediately when a newer seek request arrives.
In the upcoming changes we will explicitly have to ask demuxer for track
context creation from provided stream consumer, so it won't be possilble
to drop track context as an optimization when seeking to 0.
Otherwise, if the sample iterator resides in a block with multiple
frames before the seek, the demuxer will output all the remaining
frames from that block before moving on to the block at the seeked
position.
By default, MatroskaDemuxer chooses not to seek if the current frame
is closer to the seek target than the keyframe that precedes the seek
target. However, it can be desirable to seek to a keyframe anyway, so
let's allow that.
Reader::seek_to_random_access_point() isn't actually guaranteed to
return a sample iterator that has already gotten a block timestamp.
This verify passes in almost every case, but if we happen to seek to a
timestamp before the second keyframe, we'd crash.
There was a warning for the Optional initializer having no effect, but
removing the initializer caused the call to add a track to the HashMap
to complain. A constructor looks a little nicer here anyway.
Most users will only care about the total file duration, and shouldn't
be required to determine the file duration from multiple track
durations. To facilitate that, add a total_duration() function that
returns the demuxer's duration not associated to any particular track.