concentus.oggfile icon indicating copy to clipboard operation
concentus.oggfile copied to clipboard

The code does not support seek ?

Open StefH opened this issue 5 years ago • 4 comments

When using code like:

OpusDecoder decoder = OpusDecoder.Create(48000, 2);
OpusOggReadStream oggIn = new OpusOggReadStream(decoder, fileIn);
while (oggIn.HasNextPacket)
{
    short[] packet = oggIn.DecodeNextPacket();
}

it's not possible to just jump or seek to a position in the file?

StefH avatar Mar 02 '19 13:03 StefH

Yeah the implementation is about as bare-bones as possible. See this TODO where I punted a refactor of this whole interface.

Seeking in Ogg by itself can be a bit complicated when you have to reconcile file, stream, and granule positions, the fact that there is no global index, and the code also has to handle forwards-only streams of indeterminate length for streaming scenarios. But if you have ideas for a better implementation then I am all ears.

lostromb avatar Mar 02 '19 17:03 lostromb

Would a simple option be to:

  1. Just jump to a location in the (readable) stream, say at 50%.

  2. Start decoding from that point on, which will probably fail. However I don't know the Opus Codec at all, but is it possible to find the next valid frame from that point on?

  3. If the next valid frame is found, just start decoding as usual?

StefH avatar Mar 03 '19 10:03 StefH

That would work as a simple option. You could just reset the opus decoder, seek to an arbitrary point, and then start searching for the next ogg page. But of course the reality is more complicated.

Perhaps a quick breakdown of OggOpus would help:

  • An Ogg file is a sequence of pages. Each page is associated with an elementary stream ID - generally there's one per audio file, but not always.
  • Ogg was designed for streaming so it allows new elementary streams to come and go at any time arbitrarily, or have several streams (e.g. audio and video) playing at once
  • You can seek to any point in the stream and start looking for the OggS header to continue decoding from that point. However, there's no guarantee that the elementary stream you expect will still exist at that point, if you seek before or beyond where that elementary stream exists in the file.
  • An opus stream is an elementary stream containing opus frames (audio data). Each page might contain 2-15 seconds of audio.
  • The first two pages of it are special - OpusHead and OpusTags. These describe the encoding format and metadata of the stream before you can decode it.
  • Once you've read those special pages, you can set up your opus decoder and start decoding from any point in the stream

So, there's a few wrenches that this can throw into the design for seeking:

  • Someone might want to open a file and then immediately seek to the middle of it. But you can't do this until you've 1) established (or at least made an assumption) about which elementary stream ID the user wants to listen to and 2) read the preliminary OpusHead and OpusTags
  • The stream might not allow random access, in which case you can seek forwards in a preestablished stream but not backwards
  • The elementary stream might no longer exist at the point where you are seeking to, in which case you'd have to either lookaround for a bit and potentially fail the seek operation, or give the caller the option to switch to a different elementary stream
  • Also, if you want to seek according to the playback timestamp rather than just the file position, e.g. "start playing this song from the 3:10 mark" then you'd have to implement a heuristic search to check the granule position of various pages until you've found the one you're looking for.

lostromb avatar Mar 03 '19 17:03 lostromb

see https://github.com/lostromb/concentus.oggfile/pull/15

StefH avatar Mar 04 '19 11:03 StefH