Index: gstogggranulemapper.c
===================================================================
RCS file: gstogggranulemapper.c
diff -N gstogggranulemapper.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gstogggranulemapper.c	14 Jun 2006 08:37:38 -0000
@@ -0,0 +1,579 @@
+/* GStreamer Ogg Granulepos Mapping Utility Functions
+ * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstogggranulemapper.h"
+
+#include <string.h>
+
+#define SKELETON_FISHEAD_SIZE      64
+#define SKELETON_FISBONE_MIN_SIZE  52
+
+static gboolean is_keyframe_theora (GstOggGranuleMapper * m, guint64 granule);
+
+static GstClockTime granulepos_to_time (GstOggGranuleMapper * m,
+    guint64 granulepos);
+
+static gboolean setup_theora_mapper (GstOggGranuleMapper * m, GstBuffer * b);
+static gboolean setup_vorbis_mapper (GstOggGranuleMapper * m, GstBuffer * b);
+static gboolean setup_speex_mapper (GstOggGranuleMapper * m, GstBuffer * b);
+static gboolean setup_flac_mapper (GstOggGranuleMapper * m, GstBuffer * b);
+static gboolean setup_fishead_mapper (GstOggGranuleMapper * m, GstBuffer * b);
+static gboolean setup_ogmaudio_mapper (GstOggGranuleMapper * m, GstBuffer * b);
+static gboolean setup_ogmvideo_mapper (GstOggGranuleMapper * m, GstBuffer * b);
+static gboolean setup_ogmtext_mapper (GstOggGranuleMapper * m, GstBuffer * b);
+
+static gboolean is_header_flac (GstOggGranuleMapper * m, const guint8 * data,
+    guint size);
+static gboolean is_header_ogm (GstOggGranuleMapper * m, const guint8 * data,
+    guint size);
+static gboolean is_header_vorbis (GstOggGranuleMapper * m, const guint8 * data,
+    guint size);
+static gboolean is_header_theora (GstOggGranuleMapper * m, const guint8 * data,
+    guint size);
+static gboolean is_header_speex (GstOggGranuleMapper * m, const guint8 * data,
+    guint size);
+static gboolean is_header_annodex (GstOggGranuleMapper * m, const guint8 * data,
+    guint size);
+
+typedef gboolean (* GstOggGranuleMapperSetupFunc) (GstOggGranuleMapper * mapper,
+    GstBuffer * buf);
+
+static const struct {
+  const gchar                          * media_type;
+  GstOggGranuleMapperSetupFunc           setup_func;
+  GstOggGranuleMapperMapToTimeFunc       to_time_func;
+  GstOggGranuleMapperIsKeyFrameFunc      keyframe_func;
+  GstOggGranuleMapperIsHeaderPacketFunc  header_func;
+} mappers[] = {
+  { "audio/x-vorbis", setup_vorbis_mapper, granulepos_to_time, NULL,
+    is_header_vorbis },
+  { "audio/x-speex", setup_speex_mapper, granulepos_to_time, NULL,
+    is_header_speex },
+  { "audio/x-flac", setup_flac_mapper, granulepos_to_time, NULL,
+    is_header_flac },
+  { "video/x-theora", setup_theora_mapper, granulepos_to_time, 
+    is_keyframe_theora, is_header_theora },
+  { "application/x-annodex", setup_fishead_mapper, granulepos_to_time, NULL,
+    is_header_annodex },
+  { "application/x-ogm-audio", setup_ogmaudio_mapper, granulepos_to_time, NULL,
+    is_header_ogm },
+  { "application/x-ogm-video", setup_ogmvideo_mapper, granulepos_to_time, NULL,
+    is_header_ogm },
+  { "application/x-ogm-text", setup_ogmtext_mapper, granulepos_to_time, NULL,
+    is_header_ogm }
+};
+
+gboolean
+gst_ogg_set_granule_mapper_for_caps (GstOggGranuleMapper * mapper,
+    GstCaps * caps, GstBuffer * firstbuf)
+{
+  GstStructure *s;
+  gboolean ret = FALSE;
+  gint i;
+
+  g_assert (caps != NULL);
+  g_assert (mapper != NULL);
+  g_assert (firstbuf != NULL);
+  g_assert (GST_CAPS_IS_SIMPLE (caps));
+  g_assert (gst_caps_is_fixed (caps));
+
+  memset (mapper, 0, sizeof (GstOggGranuleMapper));
+
+  s = gst_caps_get_structure (caps, 0);
+
+  for (i = 0; i < G_N_ELEMENTS (mappers); ++i) {
+    if (gst_structure_has_name (s, mappers[i].media_type)) {
+      if ((ret = mappers[i].setup_func (mapper, firstbuf))) {
+        mapper->to_time = mappers[i].to_time_func;
+        mapper->is_keyframe = mappers[i].keyframe_func;
+        mapper->is_header_packet = mappers[i].header_func;
+      }
+      return ret;
+    }
+  }
+
+  GST_WARNING ("Could not find OggGranuleMapper for %" GST_PTR_FORMAT, caps);
+  return FALSE;
+}
+
+static gboolean
+is_keyframe_theora (GstOggGranuleMapper * m, guint64 granulepos)
+{
+  guint64 frame_mask;
+
+  if (granulepos == (guint64) -1)
+    return FALSE;
+
+  g_return_val_if_fail (m->setup_done != FALSE, FALSE);
+
+  frame_mask = (1 << (m->granuleshift + 1)) - 1;
+
+  return ((granulepos & frame_mask) == 0);
+}
+
+static GstClockTime
+granulepos_to_time (GstOggGranuleMapper * m, guint64 granulepos)
+{
+
+  if (granulepos == (guint64) -1)
+    return GST_CLOCK_TIME_NONE;
+
+  g_return_val_if_fail (m->setup_done != FALSE, GST_CLOCK_TIME_NONE);
+
+  if (granulepos == 0 || m->rate_num == 0 || m->rate_den == 0)
+    return 0;
+
+  if (m->granuleshift != 0) {
+    gint64 keyindex, keyoffset;
+
+    keyindex = granulepos >> m->granuleshift;
+    keyoffset = granulepos - (keyindex << m->granuleshift);
+    granulepos = keyindex + keyoffset;
+  }
+
+#if 0
+  /* CHECKME: original was this - any reason for doing it in two steps?
+
+  gint64 granulerate;
+   * is there really a danger of overflowing here in practice? */
+  /* GST_SECOND / (granulerate_n / granulerate_d) */
+  granulerate = gst_util_uint64_scale (GST_SECOND,
+      m->rate_den, m->rate_num);
+  res = gst_util_uint64_scale (granulepos, granulerate, 1);
+#endif
+
+  return gst_util_uint64_scale (granulepos, GST_SECOND * m->rate_den,
+      m->rate_num);
+}
+
+static gboolean
+setup_vorbis_mapper (GstOggGranuleMapper * m, GstBuffer * buf)
+{
+  guint8 *data = GST_BUFFER_DATA (buf);
+
+  if (GST_BUFFER_SIZE (buf) < 22) {
+    GST_ERROR ("vorbis identification header too small");
+    return FALSE;
+  }
+
+  if (memcmp (data, "\001vorbis\000\000\000\000", 11) != 0) {
+    GST_ERROR ("First vorbis buffer not a valid vorbis identification header");
+    return FALSE;
+  }
+
+  data += 1 + 6 + 4 + 1;
+  m->rate_num = GST_READ_UINT32_LE (data);
+  m->rate_den = 1;
+  GST_LOG ("sample rate: %" G_GUINT64_FORMAT, m->rate_num);
+
+  if (m->rate_num == 0)
+    return FALSE;
+
+  data += 4 + (4 + 4 + 4);
+  GST_LOG ("blocksize0: %u", 1 << (data[0] >> 4));
+  GST_LOG ("blocksize1: %u", 1 << (data[0] & 0x0F));
+
+  m->is_annodex = FALSE;
+  m->is_audio = TRUE;
+  m->is_video = FALSE;
+
+  m->setup_done = TRUE;
+
+  return TRUE;
+}
+
+static gboolean
+setup_speex_mapper (GstOggGranuleMapper * m, GstBuffer * buf)
+{
+  guint8 *data = GST_BUFFER_DATA (buf);
+
+  if (GST_BUFFER_SIZE (buf) < 80) {
+    GST_ERROR ("speex identification header too small");
+    return FALSE;
+  }
+
+  if (memcmp (data, "Speex   ", 8) != 0) {
+    GST_ERROR ("First speex buffer not a valid identification header");
+    return FALSE;
+  }
+
+  data += 8 + 20 + 4 + 4;
+  m->rate_num = GST_READ_UINT32_LE (data);
+  m->rate_den = 1;
+  GST_LOG ("sample rate: %" G_GUINT64_FORMAT, m->rate_num);
+
+  if (m->rate_num == 0)
+    return FALSE;
+
+  m->is_annodex = FALSE;
+  m->is_audio = TRUE;
+  m->is_video = FALSE;
+
+  m->setup_done = TRUE;
+
+  return TRUE;
+}
+
+static gboolean
+flac_parse_streaminfo (GstOggGranuleMapper * m, const guint8 * data, guint size)
+{
+  if (size < 14)
+    return FALSE;
+
+  data += 2 + 2 + 3 + 3;
+
+  m->rate_num = (GST_READ_UINT32_BE (data) & 0xFFFFF000) >> 12;
+  m->rate_den = 1;
+  GST_LOG ("sample rate: %" G_GUINT64_FORMAT, m->rate_num);
+
+  if (m->rate_num == 0)
+    return FALSE;
+
+  m->setup_done = TRUE;
+
+  return TRUE;
+}
+
+static gboolean
+is_header_flac (GstOggGranuleMapper * m, const guint8 * data, guint size)
+{
+  g_return_val_if_fail (size >= 4, FALSE);
+
+  if (data[0] == 0xFF)
+    return FALSE;
+
+  GST_LOG ("data[0] = 0x%02x, setup_done = %d", data[0], m->setup_done);
+
+  if (memcmp (data, "fLaC", 4) == 0)
+    return TRUE;
+
+  if ((data[0] & 0x7F) <= 5) {
+    guint len;
+
+    len = GST_READ_UINT32_BE (data) & 0x00FFFFFF;
+    if (size != len + 4) {
+      GST_WARNING ("metadata block len %u != actual length %u", len, size-4);
+      return TRUE;
+    }
+    if (!m->setup_done && (data[0] & 0x7F) == 0) {
+      if (flac_parse_streaminfo (m, data + 4, size - 4))
+        m->setup_done = TRUE;
+    }
+    return TRUE;
+  }
+
+  GST_WARNING ("unexpected FLAC packet header");
+  return FALSE;
+}
+
+static gboolean
+setup_flac_mapper (GstOggGranuleMapper * m, GstBuffer * buf)
+{
+  guint8 *data = GST_BUFFER_DATA (buf);
+  guint size = GST_BUFFER_SIZE (buf);
+
+  if (size == 4 && memcmp (data, "fLaC", 4) == 0) {
+    /* sigh, the first packet might just contain these four characters
+     * for identification purposes, and the stream header info will be
+     * in the next packet. We'll parse that in the is_header_flac()
+     * function later (which the demuxer must call, otherwise it does
+     * not know which packet's granulepos to use as the first granule) */
+    GST_LOG ("Got flaC identification, waiting for second packet");
+    m->setup_done = FALSE;
+    return TRUE;
+  }
+
+  if (size >= 36 && memcmp (data, "\177FLAC\001", 6) == 0) {
+    /* see http://flac.sourceforge.net/ogg_mapping.html
+     * 18 = 6 + 2 + 6 + (1 + 3) */
+    if (flac_parse_streaminfo (m, data + 18, size - 18)) {
+      m->is_annodex = FALSE;
+      m->is_audio = TRUE;
+      m->is_video = FALSE;
+      m->setup_done = TRUE;
+      return TRUE;
+    }
+    GST_WARNING ("flac_parse_streaminfo() failed!");
+  } else {
+    GST_ERROR ("unexpected FLAC header packet of size %u", size);
+  }
+
+  return FALSE;
+}
+
+static gboolean
+setup_theora_mapper (GstOggGranuleMapper * m, GstBuffer * buf)
+{
+  guint8 *data = GST_BUFFER_DATA (buf);
+
+  if (GST_BUFFER_SIZE (buf) < 42) {
+    GST_ERROR ("theora identification header too small (%u)", buf->size);
+    return FALSE;
+  }
+
+  if (memcmp (data, "\200theora\003\002", 9) != 0) {
+    GST_ERROR ("First theora buffer not a valid identification header");
+    return FALSE;
+  }
+
+  data += 1 + 6 + 1 + 1 + 1 + 2 + 2 + 3 + 3 + 1 + 1;
+
+  m->rate_num = GST_READ_UINT32_BE (data);
+  m->rate_den = GST_READ_UINT32_BE (data + 4);
+  GST_LOG ("fps = %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %.3f",
+      m->rate_num, m->rate_den, (double) m->rate_num / m->rate_den);
+
+  data += 4 + 4 + 3 + 3 + 1 + 3;
+
+  /* 2 bits + 3 bits = 5 bits KFGSHIFT */
+  m->granuleshift = ((GST_READ_UINT8 (data) & 0x02) << 3) +
+      (GST_READ_UINT8 (data + 1) >> 5);
+  GST_LOG ("granuleshift: %u", m->granuleshift);
+
+  if (m->granuleshift == 0 || m->rate_num == 0 || m->rate_den == 0)
+    return FALSE;
+
+  m->is_annodex = FALSE;
+  m->is_audio = FALSE;
+  m->is_video = TRUE;
+
+  m->setup_done = TRUE;
+
+  return TRUE;
+}
+
+static gboolean
+setup_fishead_mapper (GstOggGranuleMapper * m, GstBuffer * buf)
+{
+  guint8 *data;
+  gint64 prestime_n, prestime_d;
+  gint64 basetime_n, basetime_d;
+  gint64 basetime;
+
+  if (GST_BUFFER_SIZE (buf) < SKELETON_FISHEAD_SIZE) {
+    GST_ERROR ("First fishead buffer too small to be a valid header");
+    return FALSE;
+  }
+
+  data = GST_BUFFER_DATA (buf);
+
+  if (memcmp (data, "fishead\0", 8) != 0) {
+    GST_ERROR ("First fishead buffer does not have a valid fishead header");
+    return FALSE;
+  }
+
+  data += 8 + 2 + 2; /* header + major/minor version */
+
+  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;
+
+  /* FIXME: we don't use basetime anywhere in the demuxer! */
+  basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
+  GST_INFO ("skeleton fishead parsed (basetime: %" GST_TIME_FORMAT ")",
+      GST_TIME_ARGS (basetime));
+
+  m->is_annodex = TRUE;
+  m->is_audio = FALSE;
+  m->is_video = FALSE;
+
+/* these are the default values anyway:
+  pad->start_time = GST_CLOCK_TIME_NONE;
+  pad->first_granule = -1;
+  pad->first_time = GST_CLOCK_TIME_NONE;
+*/
+
+  m->setup_done = TRUE;
+
+  return TRUE;
+}
+
+gboolean
+gst_ogg_granule_mapper_add_fisbone (GstOggGranuleMapper * mapper,
+    const guint8 * data, guint size, GstClockTime * p_start_time,
+    guint32 * p_preroll)
+{
+  GstClockTime start_time;
+  gint64 start_granule;
+  guint32 preroll;
+
+  if (size < SKELETON_FISBONE_MIN_SIZE || memcmp (data, "fisbone\0", 8) != 0) {
+    GST_WARNING ("invalid fisbone packet, ignoring");
+    return FALSE;
+  }
+
+  if (mapper->have_fisbone) {
+    GST_DEBUG ("already have fisbone, ignoring second one");
+    return FALSE;
+  }
+
+  /* skip "fisbone\0" + headers offset + serialno + num headers */
+  data += 8 + 4 + 4 + 4;
+
+  mapper->have_fisbone = TRUE;
+
+  /* we just overwrite whatever was set before by the format-specific setup */
+  mapper->rate_num = GST_READ_UINT64_LE (data);
+  mapper->rate_den = GST_READ_UINT64_LE (data + 8);
+
+  start_granule = GST_READ_UINT64_LE (data + 16);
+  preroll = GST_READ_UINT32_LE (data + 24);
+  mapper->granuleshift = GST_READ_UINT8 (data + 28);
+
+  start_time = granulepos_to_time (mapper, start_granule);
+
+  if (p_start_time)
+    *p_start_time = start_time;
+
+  if (p_preroll)
+    *p_preroll = preroll;
+
+  return TRUE;
+}
+
+#define STREAMHEADER_OGM_AUDIO_MIN_SIZE  53
+#define STREAMHEADER_OGM_VIDEO_MIN_SIZE  53
+#define STREAMHEADER_OGM_TEXT_MIN_SIZE    9
+
+/* Do we need these for something?
+ * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
+ * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
+ * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
+ * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
+ * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
+ * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
+ */
+
+static gboolean
+setup_ogmaudio_mapper (GstOggGranuleMapper * m, GstBuffer * buf)
+{
+  guint8 *data = GST_BUFFER_DATA (buf);
+  guint size = GST_BUFFER_SIZE (buf);
+
+  if (size < STREAMHEADER_OGM_AUDIO_MIN_SIZE ||
+      memcmp (data, "\001audio\000\000\000", 9) != 0) {
+    GST_WARNING ("not an ogm audio identification header");
+    return FALSE;
+  }
+
+  m->rate_num = GST_READ_UINT64_LE (data + 25);
+  m->rate_den = 1;
+
+  GST_LOG ("sample rate: %" G_GUINT64_FORMAT, m->rate_num);
+  if (m->rate_num == 0)
+    return FALSE;
+
+  m->setup_done = TRUE;
+
+  return TRUE;
+}
+
+static gboolean
+setup_ogmvideo_mapper (GstOggGranuleMapper * m, GstBuffer * buf)
+{
+  guint8 *data = GST_BUFFER_DATA (buf);
+  guint size = GST_BUFFER_SIZE (buf);
+
+  if (size < STREAMHEADER_OGM_VIDEO_MIN_SIZE ||
+      memcmp (data, "\001video\000\000\000", 9) != 0) {
+    GST_WARNING ("not an ogm video identification header");
+    return FALSE;
+  }
+
+  m->rate_num = 10000000;
+  m->rate_den = GST_READ_UINT64_LE (data + 17);
+
+  GST_LOG ("fps = %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %.3f",
+      m->rate_num, m->rate_den, (double) m->rate_num / m->rate_den);
+
+  if (m->rate_den <= 0)
+    return FALSE;
+
+  m->setup_done = TRUE;
+
+  return TRUE;
+}
+
+static gboolean
+setup_ogmtext_mapper (GstOggGranuleMapper * m, GstBuffer * buf)
+{
+  guint8 *data = GST_BUFFER_DATA (buf);
+  guint size = GST_BUFFER_SIZE (buf);
+
+  if (size < STREAMHEADER_OGM_AUDIO_MIN_SIZE ||
+      memcmp (data, "\001text\000\000\000\000", 9) != 0) {
+    GST_WARNING ("not an ogm text identification header");
+    return FALSE;
+  }
+
+  m->rate_num = 10000000;
+  m->rate_den = GST_READ_UINT64_LE (data + 17);
+
+  GST_LOG ("fps = %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %.3f",
+      m->rate_num, m->rate_den, (double) m->rate_num / m->rate_den);
+
+  if (m->rate_den <= 0)
+    return FALSE;
+
+  m->setup_done = TRUE;
+
+  return TRUE;
+}
+
+static gboolean
+is_header_ogm (GstOggGranuleMapper * m, const guint8 * data, guint size)
+{
+  g_return_val_if_reached (FALSE);
+}
+
+static gboolean
+is_header_vorbis (GstOggGranuleMapper * m, const guint8 * data, guint size)
+{
+  if (size > 0 && (data[0] & 0x01) == 0)
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+is_header_theora (GstOggGranuleMapper * m, const guint8 * data, guint size)
+{
+  return (size > 0 && (data[0] & 0x80) == 0x80);
+}
+
+static gboolean
+is_header_speex (GstOggGranuleMapper * m, const guint8 * data, guint size)
+{
+  g_return_val_if_reached (FALSE);
+}
+
+static gboolean
+is_header_annodex (GstOggGranuleMapper * m, const guint8 * data, guint size)
+{
+  g_return_val_if_reached (FALSE);
+}
Index: gstogggranulemapper.h
===================================================================
RCS file: gstogggranulemapper.h
diff -N gstogggranulemapper.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gstogggranulemapper.h	14 Jun 2006 08:37:38 -0000
@@ -0,0 +1,72 @@
+/* GStreamer Ogg Granulepos Mapping Utility Functions
+ * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GST_OGG_GRANULE_MAPPER_H_
+#define _GST_OGG_GRANULE_MAPPER_H_
+
+#include <gst/gst.h>
+
+typedef struct _GstOggGranuleMapper GstOggGranuleMapper;
+
+typedef GstClockTime (* GstOggGranuleMapperMapToTimeFunc) (GstOggGranuleMapper * mapper,
+                                                           guint64               granulepos);
+
+/* returns TRUE if the granulepos denotes a key frame */
+typedef gboolean     (* GstOggGranuleMapperIsKeyFrameFunc) (GstOggGranuleMapper * mapper,
+                                                            guint64               granulepos);
+
+/* returns TRUE if the given packet is a stream header packet */
+typedef gboolean     (* GstOggGranuleMapperIsHeaderPacketFunc) (GstOggGranuleMapper * mapper,
+                                                                const guint8        * data,
+                                                                guint                 size);
+
+struct _GstOggGranuleMapper {
+  GstOggGranuleMapperMapToTimeFunc      to_time;
+  GstOggGranuleMapperIsKeyFrameFunc     is_keyframe; /* NULL for non-video */
+  GstOggGranuleMapperIsHeaderPacketFunc is_header_packet;
+
+  /* format-specific details, extracted in the setup function */
+  guint      granuleshift;
+  guint64    rate_num, rate_den;
+
+  guint      setup_done : 1; /* got all headers that are needed? */
+
+  /* convenience flags */
+  guint      is_audio : 1;
+  guint      is_video : 1;
+  guint      is_annodex: 1;
+  guint      unused_flags: 3;
+
+  /* from annodex fisbone */
+  guint      have_fisbone : 1;
+  
+};
+
+gboolean  gst_ogg_set_granule_mapper_for_caps (GstOggGranuleMapper * mapper,
+                                               GstCaps             * caps,
+                                               GstBuffer           * firstbuf);
+
+gboolean  gst_ogg_granule_mapper_add_fisbone  (GstOggGranuleMapper * mapper,
+                                               const guint8        * data,
+                                               guint                 size,
+                                               GstClockTime        * start_time,
+                                               guint32             * preroll);
+
+#endif /* _GST_OGG_GRANULE_MAPPER_H_ */
+
Index: Makefile.am
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-base/ext/ogg/Makefile.am,v
retrieving revision 1.13
diff -u -p -u -r1.13 Makefile.am
--- Makefile.am	11 Jun 2006 14:08:54 -0000	1.13
+++ Makefile.am	14 Jun 2006 08:37:38 -0000
@@ -4,10 +4,12 @@ libgstogg_la_SOURCES = \
 	gstogg.c \
 	gstoggdemux.c \
 	gstoggmux.c \
+	gstogggranulemapper.c \
+	gstogggranulemapper.h \
 	gstogmparse.c \
 	gstoggparse.c
 
-libgstogg_la_CFLAGS = $(GST_CFLAGS) $(OGG_CFLAGS) 
+libgstogg_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(OGG_CFLAGS)
 libgstogg_la_LIBADD = \
 	$(top_builddir)/gst-libs/gst/riff/libgstriff-$(GST_MAJORMINOR).la \
 	$(GST_BASE_LIBS) \
Index: gstoggdemux.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-base/ext/ogg/gstoggdemux.c,v
retrieving revision 1.153
diff -u -p -u -r1.153 gstoggdemux.c
--- gstoggdemux.c	2 Jun 2006 14:07:42 -0000	1.153
+++ gstoggdemux.c	14 Jun 2006 08:37:41 -0000
@@ -26,9 +26,10 @@
 #include <ogg/ogg.h>
 #include <string.h>
 #include <gst/gst-i18n-plugin.h>
-
 #include <gst/base/gsttypefindhelper.h>
 
+#include "gstogggranulemapper.h"
+
 static const GstElementDetails gst_ogg_demux_details =
 GST_ELEMENT_DETAILS ("Ogg demuxer",
     "Codec/Demuxer",
@@ -36,7 +37,7 @@ GST_ELEMENT_DETAILS ("Ogg demuxer",
     "Wim Taymand <wim@fluendo.com>");
 
 #define CHUNKSIZE (8500)        /* this is out of vorbisfile */
-#define SKELETON_FISHEAD_SIZE 64
+
 #define SKELETON_FISBONE_MIN_SIZE 52
 
 enum
@@ -121,13 +122,6 @@ struct _GstOggPad
 
   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;
@@ -143,6 +137,8 @@ struct _GstOggPad
 
   gboolean dynamic;             /* True if the internal element had dynamic pads */
   guint padaddedid;             /* The signal id for element::pad-added */
+
+  GstOggGranuleMapper  mapper;
 };
 
 struct _GstOggPadClass
@@ -186,7 +182,6 @@ struct _GstOggDemux
 
   /* annodex stuff */
   gboolean have_fishead;
-  gint64 basetime;
 
   /* ogg stuff */
   ogg_sync_state sync;
@@ -218,10 +213,6 @@ static void gst_ogg_pad_init (GstOggPad 
 static void gst_ogg_pad_dispose (GObject * object);
 static void gst_ogg_pad_finalize (GObject * object);
 
-#if 0
-static const GstFormat *gst_ogg_pad_formats (GstPad * pad);
-static const GstEventMask *gst_ogg_pad_event_masks (GstPad * pad);
-#endif
 static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
 static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
 static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
@@ -229,13 +220,6 @@ static GstCaps *gst_ogg_pad_getcaps (Gst
 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;
 
 static GType
@@ -339,38 +323,6 @@ gst_ogg_pad_finalize (GObject * object)
   G_OBJECT_CLASS (ogg_pad_parent_class)->finalize (object);
 }
 
-#if 0
-static const GstFormat *
-gst_ogg_pad_formats (GstPad * pad)
-{
-  static GstFormat src_formats[] = {
-    GST_FORMAT_DEFAULT,         /* time */
-    GST_FORMAT_TIME,            /* granulepos */
-    0
-  };
-  static GstFormat sink_formats[] = {
-    GST_FORMAT_BYTES,
-    GST_FORMAT_DEFAULT,         /* bytes */
-    0
-  };
-
-  return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
-}
-#endif
-
-#if 0
-static const GstEventMask *
-gst_ogg_pad_event_masks (GstPad * pad)
-{
-  static const GstEventMask src_event_masks[] = {
-    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH},
-    {0,}
-  };
-
-  return src_event_masks;
-}
-#endif
-
 static const GstQueryType *
 gst_ogg_pad_query_types (GstPad * pad)
 {
@@ -597,93 +549,32 @@ compare_ranks (GstPluginFeature * f1, Gs
       gst_plugin_feature_get_name (f1));
 }
 
-/* called when the skeleton fishead is found. Caller ensures the packet is
- * precisely the correct size; we don't re-check this here. */
-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. Caller ensures that
  * the packet length is sufficient */
 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);
+  /* skip "fisbone\0" and headers offset */
+  serialno = GST_READ_UINT32_LE ((guint8 *) packet->packet + 8 + 4);
 
   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);
+    GstClockTime start_time = GST_CLOCK_TIME_NONE;
+    guint32 preroll = 0;
+
+    if (gst_ogg_granule_mapper_add_fisbone (&fisbone_pad->mapper,
+        packet->packet, packet->bytes, &start_time, &preroll)) {
+      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 (start_time),
+          fisbone_pad->mapper.rate_num, fisbone_pad->mapper.rate_den,
+          preroll, fisbone_pad->mapper.granuleshift);
+    }
   } else {
     GST_WARNING_OBJECT (pad->ogg,
         "found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT,
@@ -691,48 +582,6 @@ gst_ogg_pad_parse_skeleton_fisbone (GstO
   }
 }
 
-/* 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 
  */
@@ -804,7 +653,6 @@ gst_ogg_pad_typefind (GstOggPad * pad, o
   GST_BUFFER_OFFSET (buf) = 0;
 
   caps = gst_type_find_helper_for_buffer (GST_OBJECT (pad), buf, NULL);
-  gst_buffer_unref (buf);
 
   if (caps == NULL) {
     GST_WARNING_OBJECT (ogg,
@@ -818,6 +666,12 @@ gst_ogg_pad_typefind (GstOggPad * pad, o
 
     GST_LOG_OBJECT (ogg, "found caps: %" GST_PTR_FORMAT, caps);
 
+    if (!gst_ogg_set_granule_mapper_for_caps (&pad->mapper, caps, buf)) {
+      GST_WARNING_OBJECT (ogg, "failed to create granule mapper");
+    }
+
+if (0) { //////////////////////////////////////////////////////////////////
+
     /* first filter out the interesting element factories */
     factories = gst_default_registry_feature_filter (
         (GstPluginFeatureFilter) gst_ogg_demux_factory_filter, FALSE, caps);
@@ -833,6 +687,8 @@ gst_ogg_pad_typefind (GstOggPad * pad, o
       if (element) {
         GstPadTemplate *template;
 
+        GST_LOG ("created element %s", GST_OBJECT_NAME (element));
+
         /* this is ours */
         gst_object_ref (element);
         gst_object_sink (GST_OBJECT (element));
@@ -865,11 +721,33 @@ gst_ogg_pad_typefind (GstOggPad * pad, o
       }
     }
     g_list_free (factories);
+} /////////////////////////////////////////////////////////////
+//  pad->element = element;
+
+
   }
-  pad->element = element;
+  /* FIXME: fix up pad->element */
+/*
+  {
+    gchar *padname;
+    GstPad *newpad;
 
+    padname = g_strdup_printf ("src_%u", pad->serialno);
+    newpad = gst_pad_new_from_static_template (&ogg_demux_src_template_factory,
+        padname);
+    gst_pad_use_fixed_caps (newpad);
+    gst_pad_set_caps (newpad, caps);
+    gst_pad_set_active (newpad, TRUE);
+    gst_element_add_pad (GST_ELEMENT (avi), newpad);
+    GST_LOG_OBJECT (ogg, "Added pad %s with caps %" GST_PTR_FORMAT,
+        padname, caps);
+    g_free (padname);
+  }
+*/
   gst_pad_set_caps (GST_PAD (pad), caps);
+
   gst_caps_unref (caps);
+  gst_buffer_unref (buf);
 
   return TRUE;
 }
@@ -885,9 +763,11 @@ gst_ogg_demux_chain_elem_pad (GstOggPad 
   GstOggDemux *ogg = pad->ogg;
 #endif
 
+#if 0
   /* initialize our internal decoder with packets */
   if (!pad->elem_pad)
     goto no_decoder;
+#endif
 
   GST_DEBUG_OBJECT (ogg, "%p init decoder serial %08x", pad, pad->serialno);
 
@@ -897,18 +777,21 @@ gst_ogg_demux_chain_elem_pad (GstOggPad 
   GST_BUFFER_OFFSET (buf) = -1;
   GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
 
-  ret = gst_pad_chain (pad->elem_pad, buf);
+//  ret = gst_pad_chain (pad->elem_pad, buf);
+  ret = gst_pad_push (GST_PAD (pad), buf);
   if (GST_FLOW_IS_FATAL (ret))
     goto decoder_error;
 
   return ret;
 
+#if 0
 no_decoder:
   {
     GST_WARNING_OBJECT (ogg,
         "pad %08x does not have elem_pad, no decoder ?", pad);
     return GST_FLOW_ERROR;
   }
+#endif
 decoder_error:
   {
     GST_WARNING_OBJECT (ogg, "internal decoder error");
@@ -945,12 +828,11 @@ gst_ogg_demux_chain_peer (GstOggPad * pa
   GstBuffer *buf;
   GstFlowReturn ret;
   GstOggDemux *ogg = pad->ogg;
-  GstFormat format;
   gint64 current_time;
   GstOggChain *chain;
 
-  GST_DEBUG_OBJECT (ogg,
-      "%p streaming to peer serial %08x", pad, pad->serialno);
+  GST_DEBUG_OBJECT (ogg, "%s:%s streaming to peer serial %08x",
+      GST_DEBUG_PAD_NAME (pad), pad->serialno);
 
   ret =
       gst_pad_alloc_buffer_and_set_caps (GST_PAD (pad), GST_BUFFER_OFFSET_NONE,
@@ -976,7 +858,7 @@ gst_ogg_demux_chain_peer (GstOggPad * pa
     ret = GST_FLOW_OK;
 
   /* we're done with skeleton stuff */
-  if (pad->is_skeleton)
+  if (pad->mapper.is_annodex)
     goto done;
 
   /* check if valid granulepos, then we can calculate the current
@@ -988,10 +870,8 @@ gst_ogg_demux_chain_peer (GstOggPad * pa
   ogg->current_granule = packet->granulepos;
 
   /* convert to time */
-  format = GST_FORMAT_TIME;
-  if (!gst_ogg_pad_query_convert (pad,
-          GST_FORMAT_DEFAULT, packet->granulepos, &format,
-          (gint64 *) & current_time))
+  current_time = pad->mapper.to_time (&pad->mapper, packet->granulepos);
+  if (!GST_CLOCK_TIME_IS_VALID (current_time))
     goto convert_failed;
 
   /* convert to stream time */
@@ -1024,6 +904,14 @@ convert_failed:
   }
 }
 
+static gboolean
+gst_ogg_pad_is_header_packet (GstOggPad * pad, ogg_packet * pkt)
+{
+  g_return_val_if_fail (pad->mapper.is_header_packet != NULL, FALSE);
+
+  return pad->mapper.is_header_packet (&pad->mapper, pkt->packet, pkt->bytes);
+}
+
 /* submit a packet to the oggpad, this function will run the
  * typefind code for the pad if this is the first packet for this
  * stream 
@@ -1039,12 +927,11 @@ gst_ogg_pad_submit_packet (GstOggPad * p
   GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x", pad, pad->serialno);
 
   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 (pad->mapper.is_annodex)
+      ogg->have_fishead = TRUE;
   }
 
   if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE &&
@@ -1062,16 +949,30 @@ gst_ogg_pad_submit_packet (GstOggPad * p
      * packets with 0 granulepos. We will correct for this when our
      * internal decoder produced a frame and we don't have a
      * granulepos because in that case the granulpos must have been 0 */
-    if (pad->first_granule == -1 && granule != 0) {
+//    if (pad->first_granule == -1 &&
+//        !pad->mapper.is_header_packet (&pad->mapper, packet->packet, packet->bytes)) {
+//      GST_DEBUG_OBJECT (ogg, "%p found first granulepos %lld", pad, granule);
+//      pad->first_granule = granule;
+//    }
+  }
+
+  /* not checking granule here, as the mapper might need to see all headers */
+  if (pad->first_granule == -1 && !gst_ogg_pad_is_header_packet (pad, packet)) {
+    if (granule != -1) {
       GST_DEBUG_OBJECT (ogg, "%p found first granulepos %" G_GINT64_FORMAT, pad,
           granule);
       pad->first_granule = granule;
+      pad->start_time = pad->mapper.to_time (&pad->mapper, granule);
+    } else {
+      GST_WARNING_OBJECT (ogg, "%p first data packet has granulepos -1");
     }
   }
 
+
   /* no start time known, stream to internal plugin to
    * get time. always stream to the skeleton decoder */
-  if (pad->start_time == GST_CLOCK_TIME_NONE || pad->is_skeleton) {
+  if (pad->start_time == GST_CLOCK_TIME_NONE || pad->mapper.is_annodex) {
+    GST_DEBUG_OBJECT (ogg, "========== streaming to elem pad, packet->granulepos=%lld", packet->granulepos);
     ret = gst_ogg_demux_chain_elem_pad (pad, packet);
   }
   /* we know the start_time of the pad data, see if we
@@ -1322,19 +1223,6 @@ gst_ogg_chain_has_stream (GstOggChain * 
 
 #define CURRENT_CHAIN(ogg) (&g_array_index ((ogg)->chains, GstOggChain, (ogg)->current_chain))
 
-/* signals and args */
-enum
-{
-  /* FILL ME */
-  LAST_SIGNAL
-};
-
-enum
-{
-  ARG_0
-      /* FILL ME */
-};
-
 static GstStaticPadTemplate ogg_demux_src_template_factory =
 GST_STATIC_PAD_TEMPLATE ("src_%d",
     GST_PAD_SRC,
@@ -1350,8 +1238,6 @@ static GstStaticPadTemplate ogg_demux_si
 
 static void gst_ogg_demux_finalize (GObject * object);
 
-//static const GstEventMask *gst_ogg_demux_get_event_masks (GstPad * pad);
-//static const GstQueryType *gst_ogg_demux_get_query_types (GstPad * pad);
 static GstOggChain *gst_ogg_demux_read_chain (GstOggDemux * ogg);
 static gint gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
     GstOggChain * chain);
@@ -1846,7 +1732,6 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg
         /* found offset of next ogg page */
         gint64 granulepos;
         GstClockTime granuletime;
-        GstFormat format;
         GstOggPad *pad;
 
         GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
@@ -1858,24 +1743,24 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg
         }
 
         pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
-        if (pad == NULL || pad->is_skeleton)
+        if (pad == NULL || pad->mapper.is_annodex)
           continue;
 
-        format = GST_FORMAT_TIME;
-        if (!gst_ogg_pad_query_convert (pad,
-                GST_FORMAT_DEFAULT, granulepos, &format,
-                (gint64 *) & granuletime)) {
+        granuletime = pad->mapper.to_time (&pad->mapper, granulepos);
+        if (!GST_CLOCK_TIME_IS_VALID (granuletime)) {
           GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
           granuletime = target;
-        } else {
-          if (granuletime < pad->first_time)
-            continue;
-          GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT "maps to time %"
-              GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
-
-          granuletime -= pad->first_time;
         }
 
+        /* CHECKME: .. else ... ? (tpm) */
+        if (granuletime < pad->first_time)
+          continue;
+
+        GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT "maps to time %"
+            GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
+
+        granuletime -= pad->first_time;
+
         GST_DEBUG_OBJECT (ogg,
             "found page with granule %" G_GINT64_FORMAT " and time %"
             GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
@@ -2250,7 +2135,8 @@ gst_ogg_demux_read_chain (GstOggDemux * 
       if (pad->serialno == serial) {
         known_serial = TRUE;
 
-        if (!pad->is_skeleton && pad->start_time == -1 && ogg_page_eos (&op)) {
+        if (!pad->mapper.is_annodex && 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).
@@ -2283,17 +2169,11 @@ gst_ogg_demux_read_chain (GstOggDemux * 
   /* now we can fill in the missing info using queries */
   for (i = 0; i < chain->streams->len; i++) {
     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
-    GstFormat target;
 
-    if (pad->is_skeleton)
+    if (pad->mapper.is_annodex)
       continue;
 
-    target = GST_FORMAT_TIME;
-    if (!gst_ogg_pad_query_convert (pad,
-            GST_FORMAT_DEFAULT, pad->first_granule, &target,
-            (gint64 *) & pad->first_time)) {
-      GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
-    }
+    pad->first_time = pad->mapper.to_time (&pad->mapper, pad->first_granule);
 
     pad->mode = GST_OGG_PAD_MODE_STREAMING;
   }
@@ -2310,7 +2190,6 @@ gst_ogg_demux_read_end_chain (GstOggDemu
   gint64 end = begin;
   gint64 last_granule = -1;
   GstOggPad *last_pad = NULL;
-  GstFormat target;
   gint64 ret;
   gboolean done = FALSE;
   ogg_page og;
@@ -2336,7 +2215,7 @@ gst_ogg_demux_read_end_chain (GstOggDemu
         for (i = 0; i < chain->streams->len; i++) {
           GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
 
-          if (pad->is_skeleton)
+          if (pad->mapper.is_annodex)
             continue;
 
           if (pad->serialno == ogg_page_serialno (&og)) {
@@ -2355,12 +2234,11 @@ gst_ogg_demux_read_end_chain (GstOggDemu
     }
   }
 
-  target = GST_FORMAT_TIME;
-  if (last_granule == -1 || !gst_ogg_pad_query_convert (last_pad,
-          GST_FORMAT_DEFAULT, last_granule, &target,
-          (gint64 *) & chain->segment_stop)) {
+  chain->segment_stop = 
+      last_pad->mapper.to_time (&last_pad->mapper, last_granule);
+
+  if (!GST_CLOCK_TIME_IS_VALID (chain->segment_stop)) {
     GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
-    chain->segment_stop = GST_CLOCK_TIME_NONE;
   }
 
   return 0;
@@ -2423,7 +2301,7 @@ gst_ogg_demux_collect_chain_info (GstOgg
   for (i = 0; i < chain->streams->len; i++) {
     GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
 
-    if (pad->is_skeleton)
+    if (pad->mapper.is_annodex)
       continue;
 
     /*  can do this if the pad start time is not defined */
@@ -2870,7 +2748,6 @@ gst_ogg_demux_change_state (GstElement *
 
   switch (transition) {
     case GST_STATE_CHANGE_NULL_TO_READY:
-      ogg->basetime = 0;
       ogg->have_fishead = FALSE;
       ogg_sync_init (&ogg->sync);
       break;
@@ -2909,30 +2786,6 @@ gst_ogg_demux_change_state (GstElement *
   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;
-}
-
 gboolean
 gst_ogg_demux_plugin_init (GstPlugin * plugin)
 {
