=== modified file 'ext/ogg/gstoggdemux.c'
--- ext/ogg/gstoggdemux.c	
+++ ext/ogg/gstoggdemux.c	
@@ -28,6 +28,8 @@
 #include <gst/gst-i18n-plugin.h>
 
 #define CHUNKSIZE (8500)        /* this is out of vorbisfile */
+#define SKELETON_FISHEAD_SIZE 64
+#define SKELETON_FISBONE_MIN_SIZE 52
 
 enum
 {
@@ -111,6 +113,13 @@
 
   GList *headers;
 
+  gboolean is_skeleton;
+  gboolean have_fisbone;
+  gint64 granulerate_n;
+  gint64 granulerate_d;
+  guint32 preroll;
+  guint granuleshift;
+  
   gint serialno;
   gint64 packetno;
   gint64 current_granule;
@@ -165,6 +174,10 @@
   gint64 current_granule;
   GstClockTime current_time;
 
+  /* annodex stuff */
+  gboolean have_fishead;
+  gint64 basetime;
+  
   /* ogg stuff */
   ogg_sync_state sync;
 };
@@ -205,6 +218,15 @@
 static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
 static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
 static GstCaps *gst_ogg_type_find (ogg_packet * packet);
+static GstOggPad * gst_ogg_chain_get_stream (GstOggChain * chain,
+    glong serialno);
+
+static gboolean gst_ogg_pad_query_convert (GstOggPad * pad,
+    GstFormat src_format, gint64 src_val,
+    GstFormat * dest_format, gint64 * dest_val);
+static GstClockTime gst_annodex_granule_to_time (gint64 granulepos,
+    gint64 granulerate_n, gint64 granulerate_d, guint8 granuleshift);
+
 
 static GstPadClass *ogg_pad_parent_class = NULL;
 
@@ -570,6 +592,141 @@
       gst_plugin_feature_get_name (f1));
 }
 
+/* called when the skeleton fishead is found */
+static void
+gst_ogg_pad_parse_skeleton_fishead (GstOggPad * pad, ogg_packet * packet)
+{
+  GstOggDemux *ogg = pad->ogg;
+  guint8 *data = packet->packet;
+  guint16 major, minor;
+  gint64 prestime_n, prestime_d;
+  gint64 basetime_n, basetime_d;
+  
+  /* skip "fishead\0" */
+  data += 8;
+  major = GST_READ_UINT16_LE (data);
+  data += 2;
+  minor = GST_READ_UINT16_LE (data);
+  data += 2;
+  prestime_n = (gint64)GST_READ_UINT64_LE (data);
+  data += 8;
+  prestime_d = (gint64)GST_READ_UINT64_LE (data);
+  data += 8;
+  basetime_n = (gint64)GST_READ_UINT64_LE (data);
+  data += 8;
+  basetime_d = (gint64)GST_READ_UINT64_LE (data);
+  data += 8;
+ 
+  ogg->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d); 
+  ogg->have_fishead = TRUE;
+  pad->is_skeleton = TRUE;
+  pad->start_time = GST_CLOCK_TIME_NONE;
+  pad->first_granule = -1;
+  pad->first_time = GST_CLOCK_TIME_NONE;
+  GST_INFO_OBJECT (ogg, "skeleton fishead parsed (basetime: %"
+      GST_TIME_FORMAT ")", GST_TIME_ARGS (ogg->basetime));
+}
+
+/* function called when a skeleton fisbone is found */
+static void
+gst_ogg_pad_parse_skeleton_fisbone (GstOggPad * pad, ogg_packet * packet)
+{ 
+  GstOggPad *fisbone_pad;
+  gint64 start_granule;
+  guint32 serialno;
+  guint8 *data = packet->packet;
+ 
+  /* skip "fisbone\0" */
+  data += 8;
+  /* skip headers offset */
+  data += 4;
+  serialno = GST_READ_UINT32_LE (data);
+  
+  fisbone_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
+  if (fisbone_pad) {
+    if (fisbone_pad->have_fisbone)
+      /* already parsed */
+      return;
+
+    fisbone_pad->have_fisbone = TRUE;
+    
+    data += 4;
+    /* skip number of headers */
+    data += 4;
+    fisbone_pad->granulerate_n = GST_READ_UINT64_LE (data);
+    data += 8;
+    fisbone_pad->granulerate_d = GST_READ_UINT64_LE (data);
+    data += 8;
+    start_granule = GST_READ_UINT64_LE (data);
+    data += 8;
+    fisbone_pad->preroll = GST_READ_UINT32_LE (data);
+    data += 4;
+    fisbone_pad->granuleshift = GST_READ_UINT8 (data);
+    data += 1;
+    /* padding */
+    data += 3;
+  
+    fisbone_pad->start_time = gst_annodex_granule_to_time (start_granule, 
+        fisbone_pad->granulerate_n, fisbone_pad->granulerate_d,
+        fisbone_pad->granuleshift);
+    
+    GST_INFO_OBJECT (pad->ogg, "skeleton fisbone parsed "
+        "(serialno: %" G_GUINT32_FORMAT " start time: %" GST_TIME_FORMAT
+        " granulerate_n: %" G_GINT64_FORMAT " granulerate_d: %" G_GINT64_FORMAT
+        " preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
+        serialno, GST_TIME_ARGS (fisbone_pad->start_time),
+        fisbone_pad->granulerate_n, fisbone_pad->granulerate_d,
+        fisbone_pad->preroll, fisbone_pad->granuleshift);
+  } else {
+    GST_WARNING_OBJECT (pad->ogg,
+        "found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT,
+        serialno);
+  }
+}
+
+/* function called to convert a granulepos to a timestamp */
+static gboolean
+gst_ogg_pad_query_convert (GstOggPad * pad, GstFormat src_format,
+    gint64 src_val, GstFormat * dest_format, gint64 * dest_val)
+{
+  gboolean res;
+  
+  if (src_val == -1) {
+      *dest_val = -1;
+      return TRUE;
+  }
+
+  if (!pad->have_fisbone && pad->elem_pad == NULL)
+    return FALSE;
+  
+  switch (src_format) {
+    case GST_FORMAT_DEFAULT:
+      if (pad->have_fisbone && *dest_format == GST_FORMAT_TIME) {
+        *dest_val = gst_annodex_granule_to_time (src_val,
+            pad->granulerate_n, pad->granulerate_d,
+            pad->granuleshift);
+
+        res = TRUE;
+      } else {
+        if (pad->elem_pad == NULL)
+          res = FALSE;
+        else
+          res = gst_pad_query_convert (pad->elem_pad, src_format, src_val,
+            dest_format, dest_val);
+      }
+
+      break;
+    default:
+      if (pad->elem_pad == NULL)
+        res = FALSE;
+      else
+        res = gst_pad_query_convert (pad->elem_pad, src_format, src_val,
+            dest_format, dest_val);
+  }
+
+  return res;
+}
+
 /* function called by the internal decoder elements when it outputs
  * a buffer. We use it to get the first timestamp of the stream 
  */
@@ -755,23 +912,6 @@
   return GST_FLOW_OK;
 }
 
-static gboolean
-gst_ogg_pad_query_convert (GstOggPad * ogg_pad, GstFormat from_format,
-    gint64 from_value, GstFormat * to_format, gint64 * to_value)
-{
-  if (from_value == -1) {
-    *to_value = -1;
-    return TRUE;
-  }
-
-  /* If we don't know the stream type, we won't have an internal pad to ask */
-  if (!ogg_pad->elem_pad)
-    return FALSE;
-
-  return gst_pad_query_convert (ogg_pad->elem_pad,
-      from_format, from_value, to_format, to_value);
-}
-
 /* send packet to internal element */
 static GstFlowReturn
 gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
@@ -802,13 +942,15 @@
 
       ogg->current_granule = packet->granulepos;
       format = GST_FORMAT_TIME;
-      if (!gst_ogg_pad_query_convert (pad,
-              GST_FORMAT_DEFAULT, packet->granulepos, &format,
-              (gint64 *) & ogg->current_time)) {
-        GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
-      } else {
-        GST_DEBUG ("ogg current time %" GST_TIME_FORMAT,
-            GST_TIME_ARGS (ogg->current_time));
+      if (!pad->is_skeleton) {
+        if (!gst_ogg_pad_query_convert (pad,
+                GST_FORMAT_DEFAULT, packet->granulepos, &format,
+                (gint64 *) & ogg->current_time)) {
+          GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
+        } else {
+          GST_DEBUG ("ogg current time %" GST_TIME_FORMAT,
+              GST_TIME_ARGS (ogg->current_time));
+        }
       }
     }
   } else {
@@ -835,12 +977,21 @@
 
   GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08lx", pad, pad->serialno);
 
-  /* first packet */
   if (!pad->have_type) {
+    if (!ogg->have_fishead && packet->bytes == SKELETON_FISHEAD_SIZE &&
+        !memcmp (packet->packet, "fishead\0", 8)) {
+      gst_ogg_pad_parse_skeleton_fishead (pad, packet);
+    }
+
     gst_ogg_pad_typefind (pad, packet);
     pad->have_type = TRUE;
   }
 
+  if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE &&
+      !memcmp (packet->packet, "fisbone\0", 8)) {
+    gst_ogg_pad_parse_skeleton_fisbone (pad, packet);
+  }
+  
   granule = packet->granulepos;
   if (granule != -1) {
     ogg->current_granule = granule;
@@ -851,8 +1002,8 @@
   }
 
   /* no start time known, stream to internal plugin to
-   * get time */
-  if (pad->start_time == GST_CLOCK_TIME_NONE) {
+   * get time. always stream to the skeleton decoder */
+  if (pad->start_time == GST_CLOCK_TIME_NONE || pad->is_skeleton) {
     ret = gst_ogg_demux_chain_elem_pad (pad, packet);
   }
   /* we know the start_time of the pad data, see if we
@@ -1100,7 +1251,7 @@
 GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("application/ogg")
+    GST_STATIC_CAPS ("application/ogg; application/x-annodex")
     );
 
 static void gst_ogg_demux_finalize (GObject * object);
@@ -1733,7 +1884,7 @@
           continue;
 
         pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
-        if (pad == NULL)
+        if (pad == NULL || pad->is_skeleton)
           continue;
 
         format = GST_FORMAT_TIME;
@@ -1743,6 +1894,9 @@
           GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
           granuletime = target;
         } else {
+          if (granuletime < pad->first_time)
+            continue;
+
           granuletime -= pad->first_time;
         }
 
@@ -1791,7 +1945,7 @@
     GstEvent *event;
     gint64 start_value = start + chain->segment_start;
     gint64 stop_value;
-
+    
     if (start_value == GST_CLOCK_TIME_NONE)
       start_value = 0;
     if (chain->segment_start == GST_CLOCK_TIME_NONE)
@@ -1994,7 +2148,7 @@
       if (pad->serialno == serial) {
         known_serial = TRUE;
 
-        if (pad->start_time == -1 && ogg_page_eos (&op)) {
+        if (!pad->is_skeleton && pad->start_time == -1 && ogg_page_eos (&op)) {
           /* got EOS on a pad before we could find its start_time.
            * We have no chance of finding a start_time for every pad so
            * stop searching for the other start_time(s).
@@ -2029,6 +2183,9 @@
     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
     GstFormat target;
 
+    if (pad->is_skeleton)
+      continue;
+    
     target = GST_FORMAT_TIME;
     if (!gst_ogg_pad_query_convert (pad,
             GST_FORMAT_DEFAULT, pad->first_granule, &target,
@@ -2077,6 +2234,9 @@
         for (i = 0; i < chain->streams->len; i++) {
           GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
 
+          if (pad->is_skeleton)
+            continue;
+          
           if (pad->serialno == ogg_page_serialno (&og)) {
             gint64 granulepos = ogg_page_granulepos (&og);
 
@@ -2161,6 +2321,9 @@
   for (i = 0; i < chain->streams->len; i++) {
     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
 
+    if (pad->is_skeleton)
+      continue;
+    
     /*  can do this if the pad start time is not defined */
     if (pad->start_time == GST_CLOCK_TIME_NONE)
       goto no_start_time;
@@ -2590,6 +2753,8 @@
       ogg->segment_start = GST_CLOCK_TIME_NONE;
       ogg->segment_stop = GST_CLOCK_TIME_NONE;
       ogg->total_time = GST_CLOCK_TIME_NONE;
+      ogg->basetime = 0;
+      ogg->have_fishead = FALSE;
       ogg->running = FALSE;
       ogg_sync_init (&ogg->sync);
       break;
@@ -2613,6 +2778,7 @@
       gst_ogg_demux_clear_chains (ogg);
       GST_OBJECT_LOCK (ogg);
       ogg->running = FALSE;
+      ogg->have_fishead = FALSE;
       ogg->segment_rate = 1.0;
       ogg->segment_flags = GST_SEEK_FLAG_NONE;
       ogg->segment_start = GST_CLOCK_TIME_NONE;
@@ -2628,6 +2794,30 @@
   return result;
 }
 
+static GstClockTime
+gst_annodex_granule_to_time (gint64 granulepos, gint64 granulerate_n, 
+    gint64 granulerate_d, guint8 granuleshift)
+{
+  gint64 keyindex, keyoffset;
+  gint64 granulerate;
+  GstClockTime res;
+
+  if (granulepos == 0 || granulerate_n == 0 || granulerate_d == 0)
+    return 0;
+
+  if (granuleshift != 0) {
+    keyindex = granulepos >> granuleshift;
+    keyoffset = granulepos - (keyindex << granuleshift);
+    granulepos = keyindex + keyoffset;
+  }
+
+  /* GST_SECOND / (granulerate_n / granulerate_d) */
+  granulerate = gst_util_uint64_scale (GST_SECOND,
+      granulerate_d, granulerate_n);
+  res = gst_util_uint64_scale (granulepos, granulerate, 1);
+  return res;
+}
+
 /*** typefinding **************************************************************/
 /* ogg supports its own typefinding because the ogg spec defines that the first
  * packet of an ogg stream must identify the stream. Therefore ogg can use a

=== modified file 'ext/ogg/gstoggmux.c'
--- ext/ogg/gstoggmux.c	
+++ ext/ogg/gstoggmux.c	
@@ -41,6 +41,9 @@
 
 #define GST_GP_FORMAT "[gp %8" G_GINT64_FORMAT "]"
 
+#define SKELETON_FISHEAD_SIZE 64
+#define SKELETON_FISBONE_MIN_SIZE 52
+
 typedef struct _GstOggMux GstOggMux;
 typedef struct _GstOggMuxClass GstOggMuxClass;
 
@@ -59,6 +62,11 @@
   GstBuffer *buffer;            /* the queued buffer for this pad */
 
   gint serial;
+  gint64 granulerate_n;
+  gint64 granulerate_d;
+  gint64 start_granule;
+  guint64 preroll;
+  gint granuleshift;
   ogg_stream_state stream;
   gint64 packetno;              /* number of next packet */
   gint64 pageno;                /* number of next page */
@@ -93,6 +101,18 @@
   /* the pad we are currently using to fill a page */
   GstOggPad *pulling;
 
+  /* whether or not include a skeleton bitstream */
+  gboolean encode_skeleton;
+  guint16 skeleton_major;
+  guint16 skeleton_minor;
+  guint32 skeleton_hdr_offset;
+  guint64 skeleton_prestime_n;
+  guint64 skeleton_prestime_d;
+  guint64 skeleton_basetime_n;
+  guint64 skeleton_basetime_d;
+  gchar *skeleton_utc;
+  
+ 
   /* next timestamp for the page */
   GstClockTime next_ts;
 
@@ -144,6 +164,7 @@
   ARG_0,
   ARG_MAX_DELAY,
   ARG_MAX_PAGE_DELAY,
+  ARG_ANNODEX,
 };
 
 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
@@ -152,12 +173,20 @@
     GST_STATIC_CAPS ("application/ogg")
     );
 
+#define ANX_PARAMS  " rate = (fraction) [0/1, MAX], preroll = [0, MAX],"\
+                    " granuleshift = (int) [0, 64]"
+
 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d",
     GST_PAD_SINK,
     GST_PAD_REQUEST,
-    GST_STATIC_CAPS ("video/x-theora; "
-        "audio/x-vorbis; audio/x-flac; audio/x-speex; "
-        "application/x-ogm-video; application/x-ogm-audio")
+    GST_STATIC_CAPS ("video/x-theora," ANX_PARAMS ";"
+        " audio/x-vorbis," ANX_PARAMS ";"
+        " audio/x-flac," ANX_PARAMS ";"
+        " audio/x-speex," ANX_PARAMS ";"
+        " application/x-ogm-video," ANX_PARAMS ";"
+        " application/x-ogm-audio," ANX_PARAMS ";"
+        " text/x-cmml," ANX_PARAMS
+        )
     );
 
 static void gst_ogg_mux_base_init (gpointer g_class);
@@ -170,6 +199,7 @@
 static gboolean gst_ogg_mux_handle_src_event (GstPad * pad, GstEvent * event);
 static GstPad *gst_ogg_mux_request_new_pad (GstElement * element,
     GstPadTemplate * templ, const gchar * name);
+static gboolean gst_ogg_mux_sink_setcaps (GstPad * pad, GstCaps * caps);
 static void gst_ogg_mux_set_property (GObject * object,
     guint prop_id, const GValue * value, GParamSpec * pspec);
 static void gst_ogg_mux_get_property (GObject * object,
@@ -236,6 +266,9 @@
 
   gstelement_class->request_new_pad = gst_ogg_mux_request_new_pad;
 
+  g_object_class_install_property (gobject_class, ARG_ANNODEX,
+      g_param_spec_boolean ("annodex", "Annodex",
+          "Include the annodex skeleton bitstream", FALSE, G_PARAM_READWRITE));
   g_object_class_install_property (gobject_class, ARG_MAX_DELAY,
       g_param_spec_uint64 ("max-delay", "Max delay",
           "Maximum delay in multiplexing streams", 0, G_MAXUINT64,
@@ -244,7 +277,7 @@
       g_param_spec_uint64 ("max-page-delay", "Max page delay",
           "Maximum delay for sending out a page", 0, G_MAXUINT64,
           DEFAULT_MAX_PAGE_DELAY, (GParamFlags) G_PARAM_READWRITE));
-
+  
   gstelement_class->change_state = gst_ogg_mux_change_state;
 
 }
@@ -293,6 +326,15 @@
   gst_collect_pads_set_function (ogg_mux->collect,
       (GstCollectPadsFunction) gst_ogg_mux_collected, ogg_mux);
 
+  ogg_mux->skeleton_major = 3;
+  ogg_mux->skeleton_minor = 0;
+  ogg_mux->skeleton_hdr_offset = 44;
+  ogg_mux->skeleton_prestime_n = 0;
+  ogg_mux->skeleton_prestime_d = 1;
+  ogg_mux->skeleton_basetime_n = 0;
+  ogg_mux->skeleton_basetime_d = 1;
+  ogg_mux->skeleton_utc = NULL;
+  
   gst_ogg_mux_clear (ogg_mux);
 }
 
@@ -384,6 +426,9 @@
       oggpad->new_page = TRUE;
       oggpad->first_delta = FALSE;
       oggpad->prev_delta = FALSE;
+      oggpad->start_granule = 0;
+      oggpad->granuleshift = 0;
+      oggpad->preroll = 0;
       /* TODO: delete this queue (and the things contained within) later,
        * possibly when doing gst_collect_pads_remove_pad() (which we don't seem
        * to do at all?)
@@ -394,7 +439,8 @@
 
   /* setup some pad functions */
   gst_pad_set_link_function (newpad, gst_ogg_mux_sinkconnect);
-  /* dd the pad to the element */
+  gst_pad_set_setcaps_function (newpad, gst_ogg_mux_sink_setcaps);
+  /* add the pad to the element */
   gst_element_add_pad (element, newpad);
 
   return newpad;
@@ -409,7 +455,46 @@
   {
     g_warning ("ogg_mux: this is not our template!\n");
     return NULL;
-  }
+  };
+}
+
+static gboolean
+gst_ogg_mux_sink_setcaps (GstPad * pad, GstCaps * caps)
+{
+  GstOggMux *mux;
+  GstOggPad *oggpad;
+  GstStructure *structure;
+  const GValue *value;
+  
+  mux = GST_OGG_MUX (GST_PAD_PARENT (pad));
+  oggpad = gst_pad_get_element_private (pad);
+ 
+  if (!mux->encode_skeleton)
+    /* do not parse caps if we're not annodexing */
+    return TRUE;
+  
+  structure = gst_caps_get_structure (caps, 0);
+ 
+  value = gst_structure_get_value (structure, "rate");
+  if (value == NULL) {
+    GST_DEBUG_OBJECT (mux, "no rate in caps");
+    goto bad_caps;
+  }
+  oggpad->granulerate_n = gst_value_get_fraction_numerator (value);
+  oggpad->granulerate_d = gst_value_get_fraction_denominator (value);
+
+  gst_structure_get_int (structure, "granuleshift", &oggpad->granuleshift);
+  /* the cast is safe as preroll is defined as (gint) [0, MAX] in caps.
+   * XXX: can i define a guint range?
+   */
+  gst_structure_get_int (structure, "preroll", (gint *)&oggpad->preroll);
+  
+  return TRUE;
+
+bad_caps:
+  GST_DEBUG_OBJECT (mux, "couldn't get skeleton fisbone info for stream %d",
+      oggpad->serial);
+  return FALSE;
 }
 
 /* handle events */
@@ -799,6 +884,127 @@
   return caps;
 }
 
+static GstBuffer *
+gst_ogg_mux_create_skeleton_fishead (GstOggMux * mux, ogg_stream_state * stream)
+{
+  ogg_packet packet;
+  ogg_page page;
+  guchar fishead_header[SKELETON_FISHEAD_SIZE];
+  guchar *wptr = fishead_header;
+
+  memcpy (wptr, "fishead\0", 8);
+  wptr += 8;
+  GST_WRITE_UINT16_LE (wptr, mux->skeleton_major);
+  wptr += 2;
+  GST_WRITE_UINT16_LE (wptr, mux->skeleton_minor);
+  wptr += 2;
+  GST_WRITE_UINT64_LE (wptr, mux->skeleton_prestime_n);
+  wptr += 8;
+  GST_WRITE_UINT64_LE (wptr, mux->skeleton_prestime_d);
+  wptr += 8;
+  GST_WRITE_UINT64_LE (wptr, mux->skeleton_basetime_n);
+  wptr += 8;
+  GST_WRITE_UINT64_LE (wptr, mux->skeleton_basetime_d);
+  wptr += 8;
+  if (mux->skeleton_utc)
+    memcpy (wptr, mux->skeleton_utc, 20);
+  else
+    memset (wptr, 0, 20);
+  
+  packet.packet = fishead_header;
+  packet.bytes = SKELETON_FISHEAD_SIZE;
+  packet.packetno = 0;
+  packet.b_o_s = TRUE;
+  packet.e_o_s = FALSE;
+  packet.granulepos = 0;
+  ogg_stream_packetin (stream, &packet);
+  ogg_stream_flush (stream, &page);
+  
+  return gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
+}
+
+static GstBuffer *
+gst_ogg_mux_create_skeleton_fisbone (GstOggMux * mux,
+    GstOggPad * pad, ogg_stream_state * stream)
+{
+  GstPad * peer;
+  GstCaps *caps;
+  GstStructure *structure;
+  ogg_packet packet;
+  ogg_page page;
+  guint8 *fisbone_header;
+  guint8 *wptr;
+  gchar *content_type;
+  gchar *ctype;
+  gint size;
+
+  peer = gst_pad_get_peer (pad->collect.pad);
+  caps = gst_pad_get_caps (peer);
+  structure = gst_caps_get_structure (caps, 0);
+  ctype = g_strdup (gst_structure_get_name (structure));
+  gst_caps_unref (caps);
+  gst_object_unref (peer);
+  content_type = g_strdup_printf ("Content-Type: %s", ctype);
+  
+  /* allocate space for the fisbone */
+  size = SKELETON_FISBONE_MIN_SIZE + strlen (content_type);
+  fisbone_header = g_malloc (size);
+  
+  /* fill the fisbone */
+  wptr = fisbone_header;  
+  memcpy (wptr, "fisbone\0", 8);
+  wptr += 8;
+  /* offset to fisbone's headers field */
+  GST_WRITE_UINT32_LE (wptr, mux->skeleton_hdr_offset);
+  wptr += 4;
+  /* serial number */
+  GST_WRITE_UINT32_LE (wptr, pad->serial);
+  wptr += 4;
+  /* number of header pages */
+  GST_WRITE_UINT32_LE (wptr, g_list_length (pad->headers));
+  wptr += 4;
+  /* granule rate numerator */
+  GST_WRITE_UINT64_LE (wptr, pad->granulerate_n);
+  wptr += 8;
+  /* granule rate denominator */
+  GST_WRITE_UINT64_LE (wptr, pad->granulerate_d);
+  wptr += 8;
+  /* start granule */
+  GST_WRITE_UINT64_LE (wptr, pad->start_granule);
+  wptr += 8;
+  /* preroll */
+  GST_WRITE_UINT32_LE (wptr, pad->preroll);
+  wptr += 4;
+  /* granuleshift */
+  GST_WRITE_UINT8 (wptr, pad->granuleshift);
+  wptr += 1;
+  /* padding */
+  wptr += 3;
+  /* content type */
+  memcpy (wptr, content_type, strlen (content_type));
+  
+  /* submit the header to libogg */
+  packet.packet = fisbone_header;
+  packet.bytes = size;
+  packet.granulepos = 0;
+  packet.b_o_s = 0;
+  packet.e_o_s = 0;
+  ogg_stream_packetin (stream, &packet);
+  ogg_stream_flush (stream, &page);
+
+  GST_LOG_OBJECT (mux, "encoded skeleton fisbone for stream %" G_GUINT32_FORMAT
+      " (granulerate_n: %" G_GINT64_FORMAT " granulerate_d: %" G_GINT64_FORMAT
+      " granuleshift: %d preroll: %" G_GUINT64_FORMAT " content_type: %s)",
+      pad->serial, pad->granulerate_n, pad->granulerate_d, pad->granuleshift,
+      pad->preroll, ctype);
+  
+  g_free (fisbone_header);
+  g_free (content_type);
+  g_free (ctype);
+  
+  return gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
+}
+
 /*
  * For each pad we need to write out one (small) header in one
  * page that allows decoders to identify the type of the stream.
@@ -811,15 +1017,27 @@
 gst_ogg_mux_send_headers (GstOggMux * mux)
 {
   GSList *walk;
+  ogg_stream_state skeleton_stream;
   GList *hbufs, *hwalk;
   GstCaps *caps;
   GstFlowReturn ret;
 
   hbufs = NULL;
   ret = GST_FLOW_OK;
-
+ 
+  if (mux->encode_skeleton) {
+    GstBuffer *buf;
+    gint serial;
+    
+    serial = rand ();
+    GST_LOG_OBJECT (mux, "initializing the skeleton bitstream %d", serial);
+    ogg_stream_init (&skeleton_stream, serial);
+    buf = gst_ogg_mux_create_skeleton_fishead (mux, &skeleton_stream);
+    hbufs = g_list_append (hbufs, buf);
+  } 
+  
   GST_LOG_OBJECT (mux, "collecting headers");
-
+  
   walk = mux->collect->data;
   while (walk) {
     GstOggPad *pad;
@@ -861,7 +1079,6 @@
 
     if (pad->headers) {
       buf = GST_BUFFER (pad->headers->data);
-      pad->headers = g_list_remove (pad->headers, buf);
     } else if (pad->buffer) {
       buf = pad->buffer;
       gst_buffer_ref (buf);
@@ -898,7 +1115,39 @@
       hbufs = g_list_append (hbufs, hbuf);
     }
   }
-
+ 
+  if (mux->encode_skeleton) {
+    GstOggPad *pad;
+    GstBuffer *buf;
+    
+    /* encode a fisbone for each logical bitstream */
+    walk = mux->collect->data;
+    while (walk) {
+      pad = (GstOggPad *) walk->data;
+      buf = gst_ogg_mux_create_skeleton_fisbone (mux, pad, &skeleton_stream);
+      if (buf)
+        hbufs = g_list_append (hbufs, buf);
+      walk = walk->next;
+    }
+  }
+
+  if (mux->encode_skeleton) {
+    ogg_packet eos;
+    ogg_page page;
+    GstBuffer *buf;
+
+    /* encode the EOS page for the skeleton */
+    eos.packet = NULL;
+    eos.bytes = 0;
+    eos.b_o_s = FALSE;
+    eos.e_o_s = TRUE;
+    
+    ogg_stream_packetin (&skeleton_stream, &eos);
+    ogg_stream_flush (&skeleton_stream, &page);
+    buf = gst_ogg_mux_buffer_from_page (mux, &page, FALSE);
+    hbufs = g_list_append (hbufs, buf);
+  }
+     
   GST_LOG_OBJECT (mux, "creating next headers");
   walk = mux->collect->data;
   while (walk) {
@@ -913,7 +1162,12 @@
     GST_LOG_OBJECT (mux, "looping over headers for pad %s:%s",
         GST_DEBUG_PAD_NAME (thepad));
 
-    hwalk = pad->headers;
+    if (pad->headers)
+      /* skip the first buffer (BOS) as we already encoded it */
+      hwalk = pad->headers->next;
+    else
+      hwalk = NULL;
+
     while (hwalk) {
       GstBuffer *buf = GST_BUFFER (hwalk->data);
       ogg_packet packet;
@@ -1254,6 +1508,9 @@
   ogg_mux = GST_OGG_MUX (object);
 
   switch (prop_id) {
+    case ARG_ANNODEX:
+      g_value_set_boolean (value, ogg_mux->encode_skeleton);
+      break;
     case ARG_MAX_DELAY:
       g_value_set_uint64 (value, ogg_mux->max_delay);
       break;
@@ -1275,6 +1532,9 @@
   ogg_mux = GST_OGG_MUX (object);
 
   switch (prop_id) {
+    case ARG_ANNODEX:
+      ogg_mux->encode_skeleton = g_value_get_boolean (value);
+      break;
     case ARG_MAX_DELAY:
       ogg_mux->max_delay = g_value_get_uint64 (value);
       break;

=== modified file 'ext/theora/theoraenc.c'
--- ext/theora/theoraenc.c	
+++ ext/theora/theoraenc.c	
@@ -404,6 +404,39 @@
   return caps;
 }
 
+static GstCaps *
+theora_enc_set_anx_params_on_caps (GstTheoraEnc * enc, GstCaps * caps)
+{
+  GstStructure *structure;
+  GValue value = {0};
+  guint32 frequency;
+  gint granuleshift;
+  
+  caps = gst_caps_make_writable (caps);
+  structure = gst_caps_get_structure (caps, 0);
+
+  g_value_init (&value, GST_TYPE_FRACTION);
+  gst_value_set_fraction (&value,
+      enc->info.fps_numerator, enc->info.fps_denominator);
+  gst_structure_set_value (structure, "rate", &value);
+  g_value_unset (&value);
+
+  /* granuleshift = intlog (keyframe_frequency_force-1) (taken from libtheora)
+   */
+  frequency = enc->info.keyframe_frequency_force-1;
+  while (frequency > 0) {
+    ++granuleshift;
+    frequency = frequency >> 1;
+  }
+  
+  g_value_init (&value, G_TYPE_INT);
+  g_value_set_int (&value, granuleshift);
+  gst_structure_set_value (structure, "granuleshift", &value);
+  g_value_unset (&value);
+
+  return caps;
+}
+
 static gboolean
 theora_enc_sink_event (GstPad * pad, GstEvent * event)
 {
@@ -484,6 +517,7 @@
     /* mark buffers and put on caps */
     caps = gst_pad_get_caps (enc->srcpad);
     caps = theora_set_header_on_caps (caps, buf1, buf2, buf3);
+    caps = theora_enc_set_anx_params_on_caps (enc, caps);
     GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
     gst_pad_set_caps (enc->srcpad, caps);
 

=== modified file 'ext/vorbis/vorbisenc.c'
--- ext/vorbis/vorbisenc.c	
+++ ext/vorbis/vorbisenc.c	
@@ -874,6 +874,30 @@
   return caps;
 }
 
+static GstCaps *
+gst_vorbisenc_set_anx_params_on_caps (GstVorbisEnc * vorbisenc,
+    GstCaps * caps)
+{
+  GstStructure *structure;
+  GValue value = {0};
+
+  caps = gst_caps_make_writable (caps);
+  structure = gst_caps_get_structure (caps, 0);
+
+  g_value_init (&value, GST_TYPE_FRACTION);
+  gst_value_set_fraction (&value, vorbisenc->vi.rate, 1);
+  gst_structure_set_value (structure, "rate", &value);
+  g_value_unset (&value);
+
+  g_value_init (&value, G_TYPE_UINT);
+  /* is it always 0 ? */
+  g_value_set_uint (&value, 0);
+  gst_structure_set_value (structure, "granuleshift", &value);
+  g_value_unset (&value);
+
+  return caps;
+}
+
 static gboolean
 gst_vorbisenc_sink_event (GstPad * pad, GstEvent * event)
 {
@@ -958,7 +982,8 @@
       /* mark and put on caps */
       caps = gst_pad_get_caps (vorbisenc->srcpad);
       caps = gst_vorbisenc_set_header_on_caps (caps, buf1, buf2, buf3);
-
+      caps = gst_vorbisenc_set_anx_params_on_caps (vorbisenc, caps);
+      
       /* negotiate with these caps */
       GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
       gst_pad_set_caps (vorbisenc->srcpad, caps);

