Ogg Writ is a text phrase codec. While its primary purpose is to embed subtitles or captions in a [[Theora]] stream, its design makes it useful for many other purposes. It could provide lyrics to song encoded in [[Vorbis]], a transcript to a political debate or oral history recording encoded in [[Speex]], or even incorporate a live chat session as part of a continuous video stream.

−

[http://www.8cx.net/zz/zzxdqdx.htm .]

+

−

[http://www.8cx.net/zz/hxls.htm .]

+

One of the unique aspects of Writ is its discontinuous nature, that is, unlike other Ogg codecs the granules for which seperate packets effect may overlap. See the Granules and Muxing section below for how this works.

−

[http://www.8cx.net/zz/zhxls.htm .]

+

−

[http://www.8cx.net/zz/zzdnzj.htm .]

+

== SVN ==

−

[http://www.8cx.net/zz/zzlts.htm .]

+

−

[http://www.8cx.net/zz/zzwlyx.htm .]

+

Current Ogg Writ development is on Xiph CVS as package "writ". It's being developed to use libogg2, so you'll need both to work on it. The reference encoder and decoder are available as part of the py-ogg2 package which is available on Xiph SVN at http://svn.xiph.org/trunk/py-ogg2/

−

[http://www.8cx.net/zz/zzxyx.htm .]

+

−

[http://www.8cx.net/zz/zzzcm.htm .]

+

== Application Support ==

−

[http://www.8cx.net/zz/zzzq.htm .]

+

−

[http://www.8cx.net/zz/zzlw.htm .]

+

Writ is still highly speculative and incomplete and has '''not''' been endorsed by Xiph. It is used by example code, but because its implementation depends on the yet-unreleased libogg2, it is not supported by any end-user applications at this time.

−

[http://www.8cx.net/zz/zzwh.htm .]

+

−

[http://www.8cx.net/zz/zzxc.htm .]

+

== Format ==

−

[http://www.8cx.net/zz/zzen.htm .]

+

Writ has been designed so that encoders/decoders can support a bare minimum and be fully compatable with future minor versions. Each minor version adds a new feature, some building on others, adding a new header packet and likely a new field to each body packet.

−

[http://www.8cx.net/zz/zzgp.htm .]

+

−

[http://www.8cx.net/zz/zzgw.htm .]

+

Decoders should ignore header packets beyond what they were written to support and also ignore extra fields in data packets beyond their current version. This allows new features to be added without requiring that all software, or even most software, to support them.

Granulepos in Writ (as well as future discontinuous codecs) will be by start time, not end time, that the data in a given page is tagged for. This greatly simplifies this specification.

+

+

All Writ phrases will be provided at and given the granulepos of their start time, ordered by their start time within the logical bitstream.

+

+

Phrase packets with long durations should be repeated in the logical bitstream at regular intervals to ensure that a player seeking to the middle of their duration will still see them. These packet copies will be identical to their original, including the start and duration fields, the granulepos of the page they reside on will be incremented for each copy to place it forward on the logical bitstream.

+

+

No two phrases can start on the same granule. On decoding, each packet's start granule is checked against already known packets. If a match is found the new packet is ignored. This prevents phrase copies from being interpreted as new phrases.

+

+

=== Seeking Example ===

+

+

Here is a timeline (granule numbers at top, read down) of a sample stream:

Player begins decoding at beginning of stream. It reads the BOS pages for both codecs, then receives a non-BOS page. At this point it knows that it has two bitstreams to decode and has resolved that one is Writ and the other Vorbis. It'll continue processing the headers for both.

+

+

Next it's going to find two Writ packets (phrases A and B) and toss them into libwrit. Then it'll get to the first Vorbis data page. It now has data from both bitstreams, and it knows (from the granulepos on the Vorbis page) that it has enough data to run until 12. If there were any Writ packets before 12 they would have appeared first.

+

+

At around granule 9 the listener seeks forward to 24. This will cause a rapid seek through the file to find the first page with a granulepos greater than the seek position and begin decoding at that point.

+

+

It'll find a Vorbis packet containing 13-26 (and not use 13-23) and Writ phrase E. Again, having data from both bitstreams it can begin playing. D would normally appear at granule 24 but is not known about yet. The player knows that this is only enough to decode until 26 so, knowing enough to prebuffer, continues reading the file as it plays the media.

+

+

The next packet it finds is Writ phrase D, and passing it to libwrit, is found that the current granulepos is within the duration. It is thus displayed immediatly, as it's prebuffered, without waiting for granulepos 38. It'll keep reading (because the maximum decoded Vorbis is still 26) and find a Vorbis packet with a 40 granulepos.

+

+

As it nears 38 it'll read the file again and find Writ phrase F, which takes it out to 41. Vorbis only goes until 40, so it'll have to keep reading until the next Vorbis packet.

+

+

Next it'll find Writ phrase D, which will be ignored by libwrit because phrase D is already known (matches start granule of earlier D), and the EOS on that page marks this as the last of the Writ stream.

+

+

It'll continue reading for the next Vorbis data and find the packet for granule 54, followed by the Vorbis packet for granule 69. With that it's EOS, EOF, finished.

+

+

This is of course a simplistic example, Writ and Vorbis will rarely have granules which equal the same amount of time. Each bitstream has its own granule -> time mapping which is calculated when muxing concurrent bitstreams within the file. So if there are 44100 Vorbis granules per second and only 4 Writ granules per second, pages would be ordered as W25 V297892 W31 V385932 W39 W41 V463057 etc. The logic used in the above example works after this granule-time mapping is calculated.

+

+

== Past Discussion ==

+

+

=== How does this get "encoded" and "merged"? ===

+

<purple_haese> The muxing rule is pages are arranged in ascending order by the timestamp that is represented by their granulepos.

+

+

=== For what reason is the 0x00 and 0xFF byte at the beginning of header and data packet respectively? ===

+

<xiphmont> If, after a seek, I hand your codec a header packet, what does the codec do?

+

<xiphmont> It does nothing. If I haven't told it to reset, the header is not data, it must ignore the header.

+

<xiphmont> this eliminates a huge raft of special cases in Ogg seeking.

Latest revision as of 12:20, 10 November 2007

This page is inactive and is kept primarily for historical interest.

The following is a draft!

It is at best incomplete and at worst completely broken. In any case, it is not an “official” Xiph spec or codec, so use with care.

Introduction

Ogg Writ is a text phrase codec. While its primary purpose is to embed subtitles or captions in a Theora stream, its design makes it useful for many other purposes. It could provide lyrics to song encoded in Vorbis, a transcript to a political debate or oral history recording encoded in Speex, or even incorporate a live chat session as part of a continuous video stream.

One of the unique aspects of Writ is its discontinuous nature, that is, unlike other Ogg codecs the granules for which seperate packets effect may overlap. See the Granules and Muxing section below for how this works.

SVN

Current Ogg Writ development is on Xiph CVS as package "writ". It's being developed to use libogg2, so you'll need both to work on it. The reference encoder and decoder are available as part of the py-ogg2 package which is available on Xiph SVN at http://svn.xiph.org/trunk/py-ogg2/

Application Support

Writ is still highly speculative and incomplete and has not been endorsed by Xiph. It is used by example code, but because its implementation depends on the yet-unreleased libogg2, it is not supported by any end-user applications at this time.

Format

Writ has been designed so that encoders/decoders can support a bare minimum and be fully compatable with future minor versions. Each minor version adds a new feature, some building on others, adding a new header packet and likely a new field to each body packet.

Decoders should ignore header packets beyond what they were written to support and also ignore extra fields in data packets beyond their current version. This allows new features to be added without requiring that all software, or even most software, to support them.

Granules and Muxing

Granulepos in Writ (as well as future discontinuous codecs) will be by start time, not end time, that the data in a given page is tagged for. This greatly simplifies this specification.

All Writ phrases will be provided at and given the granulepos of their start time, ordered by their start time within the logical bitstream.

Phrase packets with long durations should be repeated in the logical bitstream at regular intervals to ensure that a player seeking to the middle of their duration will still see them. These packet copies will be identical to their original, including the start and duration fields, the granulepos of the page they reside on will be incremented for each copy to place it forward on the logical bitstream.

No two phrases can start on the same granule. On decoding, each packet's start granule is checked against already known packets. If a match is found the new packet is ignored. This prevents phrase copies from being interpreted as new phrases.

Seeking Example

Here is a timeline (granule numbers at top, read down) of a sample stream:

Player begins decoding at beginning of stream. It reads the BOS pages for both codecs, then receives a non-BOS page. At this point it knows that it has two bitstreams to decode and has resolved that one is Writ and the other Vorbis. It'll continue processing the headers for both.

Next it's going to find two Writ packets (phrases A and B) and toss them into libwrit. Then it'll get to the first Vorbis data page. It now has data from both bitstreams, and it knows (from the granulepos on the Vorbis page) that it has enough data to run until 12. If there were any Writ packets before 12 they would have appeared first.

At around granule 9 the listener seeks forward to 24. This will cause a rapid seek through the file to find the first page with a granulepos greater than the seek position and begin decoding at that point.

It'll find a Vorbis packet containing 13-26 (and not use 13-23) and Writ phrase E. Again, having data from both bitstreams it can begin playing. D would normally appear at granule 24 but is not known about yet. The player knows that this is only enough to decode until 26 so, knowing enough to prebuffer, continues reading the file as it plays the media.

The next packet it finds is Writ phrase D, and passing it to libwrit, is found that the current granulepos is within the duration. It is thus displayed immediatly, as it's prebuffered, without waiting for granulepos 38. It'll keep reading (because the maximum decoded Vorbis is still 26) and find a Vorbis packet with a 40 granulepos.

As it nears 38 it'll read the file again and find Writ phrase F, which takes it out to 41. Vorbis only goes until 40, so it'll have to keep reading until the next Vorbis packet.

Next it'll find Writ phrase D, which will be ignored by libwrit because phrase D is already known (matches start granule of earlier D), and the EOS on that page marks this as the last of the Writ stream.

It'll continue reading for the next Vorbis data and find the packet for granule 54, followed by the Vorbis packet for granule 69. With that it's EOS, EOF, finished.

This is of course a simplistic example, Writ and Vorbis will rarely have granules which equal the same amount of time. Each bitstream has its own granule -> time mapping which is calculated when muxing concurrent bitstreams within the file. So if there are 44100 Vorbis granules per second and only 4 Writ granules per second, pages would be ordered as W25 V297892 W31 V385932 W39 W41 V463057 etc. The logic used in the above example works after this granule-time mapping is calculated.

Past Discussion

How does this get "encoded" and "merged"?

<purple_haese> The muxing rule is pages are arranged in ascending order by the timestamp that is represented by their granulepos.

For what reason is the 0x00 and 0xFF byte at the beginning of header and data packet respectively?

<xiphmont> If, after a seek, I hand your codec a header packet, what does the codec do?
<xiphmont> It does nothing. If I haven't told it to reset, the header is not data, it must ignore the header.
<xiphmont> this eliminates a huge raft of special cases in Ogg seeking.