ogg and the granulepos
----------------------

An ogg stream contains pages with a serial number and a granulepos.
The granulepos is a 64 bit signed integer.  It is a value that in some way
represents a time since the start of the stream.
The interpretation as such is however both codec-specific and
stream-specific.

ogg has no notion of time: it only knows about bytes and granulepos values
on pages.

The granule position is just a number; the only guarantee for a valid ogg
stream is that within a logical stream, this number never decreases.

While logically a granulepos value can be constructed for every ogg packet,
the page is marked with only one granulepos value: the granulepos of the
last packet to end on that page.

theora and the granulepos
-------------------------

The granulepos in theora is an encoding of the frame number of the last
key frame ("i frame"), and the number of frames since the last key frame
("p frame").  The granulepos is constructed as the sum of the first number,
shifted to the left for granuleshift bits, and the second number:
granulepos = pframe << granuleshift + iframe

(This means that given a framenumber or a timestamp, one cannot generate
 the one and only granulepos for that page; several granulepos possibilities
 correspond to this frame number.  You also need the last keyframe, as well
 as the granuleshift.
 However, given a granulepos, the theora codec can still map that to a
 unique timestamp and frame number for that theora stream)

 Note: currently theora stores the "presentation time" as the granulepos;
       ie. a first data page with one packet contains one video frame and
       will be marked with 0/0.  Changing that to be 1/0 (so that it
       represents the number of decodable frames up to that point, like
       for Vorbis) is being discussed.

vorbis and granulepos
---------------------
In Vorbis, the granulepos represents the number of samples that can be
decoded from all packets up to that point.

in a raw vorbis stream we use the granulepos as the offset field.


What can ogg do?
----------------

An ogg demuxer can read pages and get the granulepos from them.
It can ask the decoder elements to convert a granulepos to time.

An ogg demuxer can also get the granulepos of the first and the last page of a
stream to get the start and end timestamp of that stream.
It can also get the length in bytes of the stream
(when the peer is seekable, that is).

An ogg demuxer is therefore basically able to seek to any byte position and
timestamp.

When asked to seek to a given granulepos, the ogg demuxer should always convert
the value to a timestamp using the peer decoder element conversion function. It
can then binary search the file to eventually end up on the page with the given
granule pos or a granulepos with the same timestamp.

Seeking in ogg currently
------------------------

When seeking in an ogg, the decoders can choose to forward the seek event as a 
granulepos or a timestamp to the ogg demuxer.

In the case of a granulepos, the ogg demuxer will seek back to the beginning of
the stream and skip pages until it finds one with the requested timestamp.

In the case of a timestamp, the ogg demuxer also seeks back to the beginning of
the stream. For each page it reads, it asks the decoder element to convert the
granulepos back to a timestamp. The ogg demuxer keeps on skipping pages until
the page has a timestamp bigger or equal to the requested one.

It is therefore important that the decoder elements in vorbis can convert a
granulepos into a timestamp or never seek on timestamp on the oggdemuxer.

The default format on the oggdemuxer source pads is currently defined as a the
granulepos of the packets, it is also the value of the OFFSET field in the
GstBuffer.


Oggmux
------
The ogg muxer's job is to output complete Ogg pages such that the absolute
time represented by the valid (ie, not -1) granulepos values on those pages
never decreases. This has to be true for all logical streams in the group at
the same time.

To achieve this, encoders are required to pass along the exact time that the
granulepos represents for each ogg packet that it pushes to the ogg muxer.

The ogg muxer has a packet queue per sink pad.  From this queue a page can
be flushed when:
  - total byte size of queued packets exceeds a given value
  - total time duration of queued packets exceeds a given value
  - total byte size of queued packets exceeds maximum Ogg page size
  - eos of the pad
  - encoder sent a command to flush out an ogg page after this new packet
    (in 0.8, through a flush event; in 0.9, with a GstOggBuffer)
  - muxer wants a flush to happen (so it can output pages)

The ogg muxer also has a page queue per sink pad.  This queue collects
Ogg pages from the corresponding packet queue.  Each page is also marked
with the timestamp that the granulepos in the header represents.

A page can be flushed from this collection of page queues when:
- ideally, every page queue has at least one page with a valid granulepos
  -> choose the page, from all queues, with the lowest timestamp value
- if not, muxer can wait if the following limits aren't reached:
  - total byte size of any page queue exceeds a limit
  - total time duration of any page queue exceeds a limit
- if this limit is reached, then:
  - request a page flush from packet queue to page queue for each queue
    that does not have pages
  - now take the page from all queues with the lowest timestamp value
  - make sure all later-coming data is marked as old, either to be still
    output (but producing an invalid stream, though it can be fixed later)
    or dropped (which means it's gone forever)

The oggmuxer uses the offset fields to fill in the granulepos in the pages.

NOTE:

Due to scheduling limitations in 0.8, the ogg muxer cannot be used to mux
discontinuous streams, or streams that have a low datarate, because the ogg
muxer pulls from every pad until it has at least one buffer from every pad.


TODO
----

- use the OFFSET field in the GstBuffer to store/read the granulepos as 
  opposed to the OFFSET_END field.
- Seeking should be implemented with a binary search.

PIPELINES
---------
gst-launch -v videotestsrc timestamp-offset=1000000000 ! identity name=prevr ! videorate ! video/x-raw-yuv,width=320,height=240,framerate=25.0 ! identity name=postvr ! timeoverlay ! theoraenc ! oggmux name=mux ! filesink location=test.ogg { sinesrc timestamp-offset=1000000000 ! identity name=prear ! audiorate ! identity name=postar ! audioconvert ! rawvorbisenc ! queue ! mux. } > out 2>&1
