Skip to content

Multiple tracks within a container #713

Open
@roderickvd

Description

@roderickvd

Containers like Ogg and MP4 can contain multiple tracks. Somewhat similarly, Ogg streams can be "chained" into a single file, so beyond multiple tracks in one container, you then have multiple containers, each one with one or more tracks. Before continuing with more decoder work, I would like to define how we will deal with that.

Current behavior seems emergent - which is a nice word for saying "not by design" 😄
Though I did not test any of it yet, from a code review I am fairly confident the following is true today:

Symphonia

Track selection: For containers with multiple tracks, the Symphonia decoder will load the default track. This will usually be the first track in the container, but does not have to be. After finishing this default track, it will proceed onto the next track in the container, if any. This will succeed if and only if the decoder specification (codec, channels, sample rate) is equal to the default track, in which case the iterator will continue. If the specification is different, no attempt is made to reset the decoder.

total_duration: For the default track only. So in the case of multiple tracks with the same spec, the actual playback time may exceed the reported total_duration.

Seeking: Hard-coded to work with the default track. Calling try_seek with a track playing beyond the default track, will make the demuxer reload the default track and seek in it.

Saturating seeks: Rodio's logic for saturating seeks will saturate on the duration of the default track. So if you've got three 5 minute tracks and you seek to 7'03'' then in effect you will seek to the end of the default track == the start of the second track.

Chained streams: As with tracks, but now for the demuxer instead of the decoder: will succeed if and only if the specification is equal. If the specification is different, no attempt is made to recreate the decoder.

lewton

Track selection: lewton (and ogg underneath it) do not provide high-level functions for determining track numbering, sequencing, or defaults. It will play the first track it encounters. After that, when there are more tracks, it is not certain but likely to stop (depending on whether the packet that comes other only contains headers or also samples).

total_duration: Not available. May be possible by parsing Ogg headers manually, but I don't intend to go there (just use Symphonia).

Seeking: Currently not implemented. Could be re-implemented probably with coarse seeking, in which case it would be relative to the entire container. As the number of tracks increases, so will the headers in between, and the seeking error will increase because you have to seek a number of pages and you don't know how many pages of headers you have. So if you seek to 7'03'' (with the same three 5 minute tracks) you will land somewhere before 7'03'' (so 2'03'' into the second track).

Saturating seeks: Not available, because no total_duration.

Chained streams: As with tracks.

Proposal

First:

  1. Implement DecoderBuilder::with_track_id to optionally specify a track to load
  2. Change the decoders to by default play a single track only

Then:

  1. Implement DecoderBuilder::with_multi_track to:
  • enable multi-track processing mode
  • report total_duration (if available) for all tracks
  • seek relative to the entire container
  • saturate seeks relative to the entire duration

This last step will not be perfect for chained streams, because you cannot determine total_duration until you have read each stream. That is not desirable nor possible, given that chained streams are literally designed for streaming. Such streams should not allow seeking anyway, so best to have the user call with_seekable(false) on them and forget about total_duration.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions