diff --git a/ext/ogg/Makefile.am b/ext/ogg/Makefile.am
index dbb050c..a04c7ce 100644
--- a/ext/ogg/Makefile.am
+++ b/ext/ogg/Makefile.am
@@ -6,7 +6,11 @@ libgstogg_la_SOURCES = \
 	gstoggmux.c \
 	gstogmparse.c \
 	gstoggaviparse.c \
-	gstoggparse.c
+	gstoggparse.c \
+	gstoggmap.c \
+	gstoggmap.h \
+	dirac_parse.c \
+	dirac_parse.h
 
 noinst_HEADERS = \
 	gstoggdemux.h gstoggmux.h
diff --git a/ext/ogg/dirac_parse.c b/ext/ogg/dirac_parse.c
new file mode 100644
index 0000000..f7b483d
--- /dev/null
+++ b/ext/ogg/dirac_parse.c
@@ -0,0 +1,501 @@
+
+#include "dirac_parse.h"
+#include <string.h>
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+typedef struct _Unpack Unpack;
+
+struct _Unpack
+{
+  unsigned char *data;
+  int n_bits_left;
+  int index;
+  int guard_bit;
+};
+
+static void schro_unpack_init_with_data (Unpack * unpack, unsigned char *data,
+    int n_bytes, unsigned int guard_bit);
+
+static unsigned int schro_unpack_decode_bit (Unpack * unpack);
+static unsigned int schro_unpack_decode_uint (Unpack * unpack);
+
+
+void schro_video_format_set_std_video_format (DiracSequenceHeader * format,
+    int index);
+void schro_video_format_set_std_frame_rate (DiracSequenceHeader * format,
+    int index);
+void schro_video_format_set_std_aspect_ratio (DiracSequenceHeader * format,
+    int index);
+void schro_video_format_set_std_signal_range (DiracSequenceHeader * format,
+    int index);
+void schro_video_format_set_std_colour_spec (DiracSequenceHeader * format,
+    int index);
+
+
+
+
+int
+dirac_sequence_header_parse (DiracSequenceHeader * header,
+    unsigned char *data, int n_bytes)
+{
+  int bit;
+  int index;
+  Unpack _unpack;
+  Unpack *unpack = &_unpack;
+  int major_version;
+  int minor_version;
+  int profile;
+  int level;
+
+  memset (header, 0, sizeof (*header));
+
+  schro_unpack_init_with_data (unpack, data, n_bytes, 1);
+
+  /* parse parameters */
+  major_version = schro_unpack_decode_uint (unpack);
+  minor_version = schro_unpack_decode_uint (unpack);
+  profile = schro_unpack_decode_uint (unpack);
+  level = schro_unpack_decode_uint (unpack);
+
+  /* base video header */
+  index = schro_unpack_decode_uint (unpack);
+  schro_video_format_set_std_video_format (header, index);
+
+  header->major_version = major_version;
+  header->minor_version = minor_version;
+  header->profile = profile;
+  header->level = level;
+
+  /* source parameters */
+  /* frame dimensions */
+  bit = schro_unpack_decode_bit (unpack);
+  if (bit) {
+    header->width = schro_unpack_decode_uint (unpack);
+    header->height = schro_unpack_decode_uint (unpack);
+  }
+
+  /* chroma header */
+  bit = schro_unpack_decode_bit (unpack);
+  if (bit) {
+    header->chroma_format = schro_unpack_decode_uint (unpack);
+  }
+
+  /* scan header */
+  bit = schro_unpack_decode_bit (unpack);
+  if (bit) {
+    header->interlaced = schro_unpack_decode_bit (unpack);
+    if (header->interlaced) {
+      header->top_field_first = schro_unpack_decode_bit (unpack);
+    }
+  }
+
+  /* frame rate */
+  bit = schro_unpack_decode_bit (unpack);
+  if (bit) {
+    index = schro_unpack_decode_uint (unpack);
+    if (index == 0) {
+      header->frame_rate_numerator = schro_unpack_decode_uint (unpack);
+      header->frame_rate_denominator = schro_unpack_decode_uint (unpack);
+    } else {
+      schro_video_format_set_std_frame_rate (header, index);
+    }
+  }
+
+  /* aspect ratio */
+  bit = schro_unpack_decode_bit (unpack);
+  if (bit) {
+    index = schro_unpack_decode_uint (unpack);
+    if (index == 0) {
+      header->aspect_ratio_numerator = schro_unpack_decode_uint (unpack);
+      header->aspect_ratio_denominator = schro_unpack_decode_uint (unpack);
+    } else {
+      schro_video_format_set_std_aspect_ratio (header, index);
+    }
+  }
+
+  /* clean area */
+  bit = schro_unpack_decode_bit (unpack);
+  if (bit) {
+    header->clean_width = schro_unpack_decode_uint (unpack);
+    header->clean_height = schro_unpack_decode_uint (unpack);
+    header->left_offset = schro_unpack_decode_uint (unpack);
+    header->top_offset = schro_unpack_decode_uint (unpack);
+  }
+
+  /* signal range */
+  bit = schro_unpack_decode_bit (unpack);
+  if (bit) {
+    index = schro_unpack_decode_uint (unpack);
+    if (index == 0) {
+      header->luma_offset = schro_unpack_decode_uint (unpack);
+      header->luma_excursion = schro_unpack_decode_uint (unpack);
+      header->chroma_offset = schro_unpack_decode_uint (unpack);
+      header->chroma_excursion = schro_unpack_decode_uint (unpack);
+    } else {
+      schro_video_format_set_std_signal_range (header, index);
+    }
+  }
+
+  /* colour spec */
+  bit = schro_unpack_decode_bit (unpack);
+  if (bit) {
+    index = schro_unpack_decode_uint (unpack);
+    schro_video_format_set_std_colour_spec (header, index);
+    if (index == 0) {
+      /* colour primaries */
+      bit = schro_unpack_decode_bit (unpack);
+      if (bit) {
+        header->colour_primaries = schro_unpack_decode_uint (unpack);
+      }
+      /* colour matrix */
+      bit = schro_unpack_decode_bit (unpack);
+      if (bit) {
+        header->colour_matrix = schro_unpack_decode_uint (unpack);
+      }
+      /* transfer function */
+      bit = schro_unpack_decode_bit (unpack);
+      if (bit) {
+        header->transfer_function = schro_unpack_decode_uint (unpack);
+      }
+    }
+  }
+
+  header->interlaced_coding = schro_unpack_decode_uint (unpack);
+
+  return 1;
+}
+
+/* standard stuff */
+
+static DiracSequenceHeader schro_video_formats[] = {
+  {0, 0, 0, 0,
+        0,                      /* custom */
+        640, 480, SCHRO_CHROMA_420,
+        FALSE, FALSE,
+        24000, 1001, 1, 1,
+        640, 480, 0, 0,
+        0, 255, 128, 255,
+      0, 0, 0},
+  {0, 0, 0, 0,
+        1,                      /* QSIF525 */
+        176, 120, SCHRO_CHROMA_420,
+        FALSE, FALSE,
+        15000, 1001, 10, 11,
+        176, 120, 0, 0,
+        0, 255, 128, 255,
+      1, 1, 0},
+  {0, 0, 0, 0,
+        2,                      /* QCIF */
+        176, 144, SCHRO_CHROMA_420,
+        FALSE, TRUE,
+        25, 2, 12, 11,
+        176, 144, 0, 0,
+        0, 255, 128, 255,
+      2, 1, 0},
+  {0, 0, 0, 0,
+        3,                      /* SIF525 */
+        352, 240, SCHRO_CHROMA_420,
+        FALSE, FALSE,
+        15000, 1001, 10, 11,
+        352, 240, 0, 0,
+        0, 255, 128, 255,
+      1, 1, 0},
+  {0, 0, 0, 0,
+        4,                      /* CIF */
+        352, 288, SCHRO_CHROMA_420,
+        FALSE, TRUE,
+        25, 2, 12, 11,
+        352, 288, 0, 0,
+        0, 255, 128, 255,
+      2, 1, 0},
+  {0, 0, 0, 0,
+        5,                      /* 4SIF525 */
+        704, 480, SCHRO_CHROMA_420,
+        FALSE, FALSE,
+        15000, 1001, 10, 11,
+        704, 480, 0, 0,
+        0, 255, 128, 255,
+      1, 1, 0},
+  {0, 0, 0, 0,
+        6,                      /* 4CIF */
+        704, 576, SCHRO_CHROMA_420,
+        FALSE, TRUE,
+        25, 2, 12, 11,
+        704, 576, 0, 0,
+        0, 255, 128, 255,
+      2, 1, 0},
+  {0, 0, 0, 0,
+        7,                      /* SD480I-60 */
+        720, 480, SCHRO_CHROMA_422,
+        TRUE, FALSE,
+        30000, 1001, 10, 11,
+        704, 480, 8, 0,
+        64, 876, 512, 896,
+      1, 1, 0},
+  {0, 0, 0, 0,
+        8,                      /* SD576I-50 */
+        720, 576, SCHRO_CHROMA_422,
+        TRUE, TRUE,
+        25, 1, 12, 11,
+        704, 576, 8, 0,
+        64, 876, 512, 896,
+      2, 1, 0},
+  {0, 0, 0, 0,
+        9,                      /* HD720P-60 */
+        1280, 720, SCHRO_CHROMA_422,
+        FALSE, TRUE,
+        60000, 1001, 1, 1,
+        1280, 720, 0, 0,
+        64, 876, 512, 896,
+      0, 0, 0},
+  {0, 0, 0, 0,
+        10,                     /* HD720P-50 */
+        1280, 720, SCHRO_CHROMA_422,
+        FALSE, TRUE,
+        50, 1, 1, 1,
+        1280, 720, 0, 0,
+        64, 876, 512, 896,
+      0, 0, 0},
+  {0, 0, 0, 0,
+        11,                     /* HD1080I-60 */
+        1920, 1080, SCHRO_CHROMA_422,
+        TRUE, TRUE,
+        30000, 1001, 1, 1,
+        1920, 1080, 0, 0,
+        64, 876, 512, 896,
+      0, 0, 0},
+  {0, 0, 0, 0,
+        12,                     /* HD1080I-50 */
+        1920, 1080, SCHRO_CHROMA_422,
+        TRUE, TRUE,
+        25, 1, 1, 1,
+        1920, 1080, 0, 0,
+        64, 876, 512, 896,
+      0, 0, 0},
+  {0, 0, 0, 0,
+        13,                     /* HD1080P-60 */
+        1920, 1080, SCHRO_CHROMA_422,
+        FALSE, TRUE,
+        60000, 1001, 1, 1,
+        1920, 1080, 0, 0,
+        64, 876, 512, 896,
+      0, 0, 0},
+  {0, 0, 0, 0,
+        14,                     /* HD1080P-50 */
+        1920, 1080, SCHRO_CHROMA_422,
+        FALSE, TRUE,
+        50, 1, 1, 1,
+        1920, 1080, 0, 0,
+        64, 876, 512, 896,
+      0, 0, 0},
+  {0, 0, 0, 0,
+        15,                     /* DC2K */
+        2048, 1080, SCHRO_CHROMA_444,
+        FALSE, TRUE,
+        24, 1, 1, 1,
+        2048, 1080, 0, 0,
+        256, 3504, 2048, 3584,
+      3, 0, 0},
+  {0, 0, 0, 0,
+        16,                     /* DC4K */
+        4096, 2160, SCHRO_CHROMA_444,
+        FALSE, TRUE,
+        24, 1, 1, 1,
+        2048, 1536, 0, 0,
+        256, 3504, 2048, 3584,
+      3, 0, 0},
+};
+
+void
+schro_video_format_set_std_video_format (DiracSequenceHeader * format,
+    int index)
+{
+
+  if (index < 0 || index >= ARRAY_SIZE (schro_video_formats)) {
+    return;
+  }
+
+  memcpy (format, schro_video_formats + index, sizeof (DiracSequenceHeader));
+}
+
+typedef struct _SchroFrameRate SchroFrameRate;
+struct _SchroFrameRate
+{
+  int numerator;
+  int denominator;
+};
+
+static SchroFrameRate schro_frame_rates[] = {
+  {0, 0},
+  {24000, 1001},
+  {24, 1},
+  {25, 1},
+  {30000, 1001},
+  {30, 1},
+  {50, 1},
+  {60000, 1001},
+  {60, 1},
+  {15000, 1001},
+  {25, 2}
+};
+
+void
+schro_video_format_set_std_frame_rate (DiracSequenceHeader * format, int index)
+{
+  if (index < 1 || index >= ARRAY_SIZE (schro_frame_rates)) {
+    return;
+  }
+
+  format->frame_rate_numerator = schro_frame_rates[index].numerator;
+  format->frame_rate_denominator = schro_frame_rates[index].denominator;
+}
+
+typedef struct _SchroPixelAspectRatio SchroPixelAspectRatio;
+struct _SchroPixelAspectRatio
+{
+  int numerator;
+  int denominator;
+};
+
+static const SchroPixelAspectRatio schro_aspect_ratios[] = {
+  {0, 0},
+  {1, 1},
+  {10, 11},
+  {12, 11},
+  {40, 33},
+  {16, 11},
+  {4, 3}
+};
+
+void
+schro_video_format_set_std_aspect_ratio (DiracSequenceHeader * format,
+    int index)
+{
+  if (index < 1 || index >= ARRAY_SIZE (schro_aspect_ratios)) {
+    return;
+  }
+
+  format->aspect_ratio_numerator = schro_aspect_ratios[index].numerator;
+  format->aspect_ratio_denominator = schro_aspect_ratios[index].denominator;
+
+}
+
+typedef struct _SchroSignalRangeStruct SchroSignalRangeStruct;
+struct _SchroSignalRangeStruct
+{
+  int luma_offset;
+  int luma_excursion;
+  int chroma_offset;
+  int chroma_excursion;
+};
+
+static const SchroSignalRangeStruct schro_signal_ranges[] = {
+  {0, 0, 0, 0},
+  {0, 255, 128, 255},
+  {16, 219, 128, 224},
+  {64, 876, 512, 896},
+  {256, 3504, 2048, 3584}
+};
+
+void
+schro_video_format_set_std_signal_range (DiracSequenceHeader * format, int i)
+{
+  if (i < 1 || i >= ARRAY_SIZE (schro_signal_ranges)) {
+    return;
+  }
+
+  format->luma_offset = schro_signal_ranges[i].luma_offset;
+  format->luma_excursion = schro_signal_ranges[i].luma_excursion;
+  format->chroma_offset = schro_signal_ranges[i].chroma_offset;
+  format->chroma_excursion = schro_signal_ranges[i].chroma_excursion;
+}
+
+typedef struct _SchroColourSpecStruct SchroColourSpecStruct;
+struct _SchroColourSpecStruct
+{
+  int colour_primaries;
+  int colour_matrix;
+  int transfer_function;
+};
+
+static const SchroColourSpecStruct schro_colour_specs[] = {
+  {                             /* Custom */
+        SCHRO_COLOUR_PRIMARY_HDTV,
+        SCHRO_COLOUR_MATRIX_HDTV,
+      SCHRO_TRANSFER_CHAR_TV_GAMMA},
+  {                             /* SDTV 525 */
+        SCHRO_COLOUR_PRIMARY_SDTV_525,
+        SCHRO_COLOUR_MATRIX_SDTV,
+      SCHRO_TRANSFER_CHAR_TV_GAMMA},
+  {                             /* SDTV 625 */
+        SCHRO_COLOUR_PRIMARY_SDTV_625,
+        SCHRO_COLOUR_MATRIX_SDTV,
+      SCHRO_TRANSFER_CHAR_TV_GAMMA},
+  {                             /* HDTV */
+        SCHRO_COLOUR_PRIMARY_HDTV,
+        SCHRO_COLOUR_MATRIX_HDTV,
+      SCHRO_TRANSFER_CHAR_TV_GAMMA},
+  {                             /* Cinema */
+        SCHRO_COLOUR_PRIMARY_CINEMA,
+        SCHRO_COLOUR_MATRIX_HDTV,
+      SCHRO_TRANSFER_CHAR_TV_GAMMA}
+};
+
+void
+schro_video_format_set_std_colour_spec (DiracSequenceHeader * format, int i)
+{
+  if (i < 0 || i >= ARRAY_SIZE (schro_colour_specs)) {
+    return;
+  }
+
+  format->colour_primaries = schro_colour_specs[i].colour_primaries;
+  format->colour_matrix = schro_colour_specs[i].colour_matrix;
+  format->transfer_function = schro_colour_specs[i].transfer_function;
+}
+
+
+/* unpack */
+
+static void
+schro_unpack_init_with_data (Unpack * unpack, unsigned char *data,
+    int n_bytes, unsigned int guard_bit)
+{
+  memset (unpack, 0, sizeof (Unpack));
+
+  unpack->data = data;
+  unpack->n_bits_left = 8 * n_bytes;
+  unpack->guard_bit = guard_bit;
+}
+
+static unsigned int
+schro_unpack_decode_bit (Unpack * unpack)
+{
+  int bit;
+
+  if (unpack->n_bits_left < 1) {
+    return unpack->guard_bit;
+  }
+  bit = (unpack->data[unpack->index >> 3] >> (7 - (unpack->index & 7))) & 1;
+  unpack->index++;
+  unpack->n_bits_left--;
+
+  return bit;
+}
+
+static unsigned int
+schro_unpack_decode_uint (Unpack * unpack)
+{
+  int count;
+  int value;
+
+  count = 0;
+  value = 0;
+  while (!schro_unpack_decode_bit (unpack)) {
+    count++;
+    value <<= 1;
+    value |= schro_unpack_decode_bit (unpack);
+  }
+
+  return (1 << count) - 1 + value;
+}
diff --git a/ext/ogg/dirac_parse.h b/ext/ogg/dirac_parse.h
new file mode 100644
index 0000000..9dc4ffe
--- /dev/null
+++ b/ext/ogg/dirac_parse.h
@@ -0,0 +1,178 @@
+
+#ifndef __DIRAC_PARSE_H__
+#define __DIRAC_PARSE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+typedef enum _SchroParseCode {
+  SCHRO_PARSE_CODE_SEQUENCE_HEADER = 0x00,
+  SCHRO_PARSE_CODE_END_OF_SEQUENCE = 0x10,
+  SCHRO_PARSE_CODE_AUXILIARY_DATA = 0x20,
+  SCHRO_PARSE_CODE_PADDING = 0x30,
+
+  SCHRO_PARSE_CODE_INTRA_REF = 0x0c,
+  SCHRO_PARSE_CODE_INTRA_NON_REF = 0x08,
+  SCHRO_PARSE_CODE_INTRA_REF_NOARITH = 0x4c,
+  SCHRO_PARSE_CODE_INTRA_NON_REF_NOARITH = 0x48,
+
+  SCHRO_PARSE_CODE_INTER_REF_1 = 0x0d,
+  SCHRO_PARSE_CODE_INTER_REF_1_NOARITH = 0x4d,
+  SCHRO_PARSE_CODE_INTER_REF_2 = 0x0e,
+  SCHRO_PARSE_CODE_INTER_REF_2_NOARITH = 0x4e,
+
+  SCHRO_PARSE_CODE_INTER_NON_REF_1 = 0x09,
+  SCHRO_PARSE_CODE_INTER_NON_REF_1_NOARITH = 0x49,
+  SCHRO_PARSE_CODE_INTER_NON_REF_2 = 0x0a,
+  SCHRO_PARSE_CODE_INTER_NON_REF_2_NOARITH = 0x4a,
+
+  SCHRO_PARSE_CODE_LD_INTRA_REF = 0xcc,
+  SCHRO_PARSE_CODE_LD_INTRA_NON_REF = 0xc8
+} SchroParseCode;
+
+#define SCHRO_PARSE_CODE_PICTURE(is_ref,n_refs,is_lowdelay,is_noarith) \
+  (8 | ((is_ref)<<2) | (n_refs) | ((is_lowdelay)<<7) | ((is_noarith)<<6))
+
+#define SCHRO_PARSE_CODE_IS_SEQ_HEADER(x) ((x) == SCHRO_PARSE_CODE_SEQUENCE_HEADER)
+#define SCHRO_PARSE_CODE_IS_END_OF_SEQUENCE(x) ((x) == SCHRO_PARSE_CODE_END_OF_SEQUENCE)
+#define SCHRO_PARSE_CODE_IS_AUXILIARY_DATA(x) ((x) == SCHRO_PARSE_CODE_AUXILIARY_DATA)
+#define SCHRO_PARSE_CODE_IS_PADDING(x) ((x) == SCHRO_PARSE_CODE_PADDING)
+#define SCHRO_PARSE_CODE_IS_PICTURE(x) ((x) & 0x8)
+#define SCHRO_PARSE_CODE_IS_LOW_DELAY(x) (((x) & 0x88) == 0x88)
+#define SCHRO_PARSE_CODE_IS_CORE_SYNTAX(x) (((x) & 0x88) == 0x08)
+#define SCHRO_PARSE_CODE_USING_AC(x) (((x) & 0x48) == 0x08)
+#define SCHRO_PARSE_CODE_IS_REFERENCE(x) (((x) & 0xc) == 0x0c)
+#define SCHRO_PARSE_CODE_IS_NON_REFERENCE(x) (((x) & 0xc) == 0x08)
+#define SCHRO_PARSE_CODE_NUM_REFS(x) ((x) & 0x3)
+#define SCHRO_PARSE_CODE_IS_INTRA(x) (SCHRO_PARSE_CODE_IS_PICTURE(x) && SCHRO_PARSE_CODE_NUM_REFS(x) == 0)
+#define SCHRO_PARSE_CODE_IS_INTER(x) (SCHRO_PARSE_CODE_IS_PICTURE(x) && SCHRO_PARSE_CODE_NUM_REFS(x) > 0)
+
+#define SCHRO_PARSE_HEADER_SIZE (4+1+4+4)
+
+typedef enum _SchroVideoFormatEnum {
+  SCHRO_VIDEO_FORMAT_CUSTOM = 0,
+  SCHRO_VIDEO_FORMAT_QSIF,
+  SCHRO_VIDEO_FORMAT_QCIF,
+  SCHRO_VIDEO_FORMAT_SIF,
+  SCHRO_VIDEO_FORMAT_CIF,
+  SCHRO_VIDEO_FORMAT_4SIF,
+  SCHRO_VIDEO_FORMAT_4CIF,
+  SCHRO_VIDEO_FORMAT_SD480I_60,
+  SCHRO_VIDEO_FORMAT_SD576I_50,
+  SCHRO_VIDEO_FORMAT_HD720P_60,
+  SCHRO_VIDEO_FORMAT_HD720P_50,
+  SCHRO_VIDEO_FORMAT_HD1080I_60,
+  SCHRO_VIDEO_FORMAT_HD1080I_50,
+  SCHRO_VIDEO_FORMAT_HD1080P_60,
+  SCHRO_VIDEO_FORMAT_HD1080P_50,
+  SCHRO_VIDEO_FORMAT_DC2K_24,
+  SCHRO_VIDEO_FORMAT_DC4K_24
+} SchroVideoFormatEnum;
+
+typedef enum _SchroChromaFormat {
+  SCHRO_CHROMA_444 = 0,
+  SCHRO_CHROMA_422,
+  SCHRO_CHROMA_420
+} SchroChromaFormat;
+
+#define SCHRO_CHROMA_FORMAT_H_SHIFT(format) (((format) == SCHRO_CHROMA_444)?0:1)
+#define SCHRO_CHROMA_FORMAT_V_SHIFT(format) (((format) == SCHRO_CHROMA_420)?1:0)
+
+typedef enum _SchroSignalRange {
+  SCHRO_SIGNAL_RANGE_CUSTOM = 0,
+  SCHRO_SIGNAL_RANGE_8BIT_FULL = 1,
+  SCHRO_SIGNAL_RANGE_8BIT_VIDEO = 2,
+  SCHRO_SIGNAL_RANGE_10BIT_VIDEO = 3,
+  SCHRO_SIGNAL_RANGE_12BIT_VIDEO = 4
+} SchroSignalRange;
+
+typedef enum _SchroColourSpec {
+  SCHRO_COLOUR_SPEC_CUSTOM = 0,
+  SCHRO_COLOUR_SPEC_SDTV_525 = 1,
+  SCHRO_COLOUR_SPEC_SDTV_625 = 2,
+  SCHRO_COLOUR_SPEC_HDTV = 3,
+  SCHRO_COLOUR_SPEC_CINEMA = 4
+} SchroColourSpec;
+
+typedef enum _SchroColourPrimaries {
+  SCHRO_COLOUR_PRIMARY_HDTV = 0,
+  SCHRO_COLOUR_PRIMARY_SDTV_525 = 1,
+  SCHRO_COLOUR_PRIMARY_SDTV_625 = 2,
+  SCHRO_COLOUR_PRIMARY_CINEMA = 3
+} SchroColourPrimaries;
+
+typedef enum _SchroColourMatrix {
+  SCHRO_COLOUR_MATRIX_HDTV = 0,
+  SCHRO_COLOUR_MATRIX_SDTV = 1,
+  SCHRO_COLOUR_MATRIX_REVERSIBLE = 2
+}SchroColourMatrix;
+
+typedef enum _SchroTransferFunction {
+  SCHRO_TRANSFER_CHAR_TV_GAMMA = 0,
+  SCHRO_TRANSFER_CHAR_EXTENDED_GAMUT = 1,
+  SCHRO_TRANSFER_CHAR_LINEAR = 2,
+  SCHRO_TRANSFER_CHAR_DCI_GAMMA = 3
+} SchroTransferFunction;
+
+
+
+typedef struct _DiracSequenceHeader DiracSequenceHeader;
+
+struct _DiracSequenceHeader {
+  int major_version;
+  int minor_version;
+  int profile;
+  int level;
+
+  int index;
+  int width;
+  int height;
+  int chroma_format;
+  
+  int interlaced;
+  int top_field_first;
+  
+  int frame_rate_numerator;
+  int frame_rate_denominator;
+  int aspect_ratio_numerator;
+  int aspect_ratio_denominator;
+    
+  int clean_width;
+  int clean_height;
+  int left_offset;
+  int top_offset;
+    
+  int luma_offset;
+  int luma_excursion;
+  int chroma_offset;
+  int chroma_excursion;
+    
+  int colour_primaries;
+  int colour_matrix;
+  int transfer_function;
+
+  int interlaced_coding;
+
+  int unused0;
+  int unused1;
+  int unused2;
+};  
+
+
+int dirac_sequence_header_parse (DiracSequenceHeader *header,
+    unsigned char *data, int length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c
index 0f30778..7f7bbcb 100644
--- a/ext/ogg/gstoggdemux.c
+++ b/ext/ogg/gstoggdemux.c
@@ -41,7 +41,6 @@
 #endif
 #include <string.h>
 #include <gst/gst-i18n-plugin.h>
-#include <gst/base/gsttypefindhelper.h>
 
 #include "gstoggdemux.h"
 
@@ -60,8 +59,8 @@ GST_ELEMENT_DETAILS ("Ogg demuxer",
 #define GST_CHAIN_LOCK(ogg)     g_mutex_lock((ogg)->chain_lock)
 #define GST_CHAIN_UNLOCK(ogg)   g_mutex_unlock((ogg)->chain_lock)
 
-GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug);
-GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_setup_debug);
+GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
+GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
 #define GST_CAT_DEFAULT gst_ogg_demux_debug
 
 static ogg_page *
@@ -86,12 +85,6 @@ gst_ogg_page_free (ogg_page * page)
   g_free (page);
 }
 
-static GstStaticPadTemplate internaltemplate =
-GST_STATIC_PAD_TEMPLATE ("internal",
-    GST_PAD_SINK,
-    GST_PAD_ALWAYS,
-    GST_STATIC_CAPS_ANY);
-
 static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
     GstOggChain * chain);
 static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
@@ -108,10 +101,6 @@ static void gst_ogg_pad_init (GstOggPad * pad);
 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);
@@ -119,12 +108,6 @@ static GstCaps *gst_ogg_pad_getcaps (GstPad * pad);
 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 GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
     GstOggPad * pad, GstFlowReturn ret);
 
@@ -155,11 +138,9 @@ gst_ogg_pad_init (GstOggPad * pad)
 
   pad->mode = GST_OGG_PAD_MODE_INIT;
 
-  pad->first_granule = -1;
   pad->current_granule = -1;
 
   pad->start_time = GST_CLOCK_TIME_NONE;
-  pad->first_time = GST_CLOCK_TIME_NONE;
 
   pad->have_type = FALSE;
   pad->continued = NULL;
@@ -170,19 +151,6 @@ static void
 gst_ogg_pad_dispose (GObject * object)
 {
   GstOggPad *pad = GST_OGG_PAD (object);
-  GstPad **elem_pad_p;
-  GstElement **element_p;
-  GstPad **elem_out_p;
-
-  if (pad->element)
-    gst_element_set_state (pad->element, GST_STATE_NULL);
-
-  elem_pad_p = &pad->elem_pad;
-  element_p = &pad->element;
-  elem_out_p = &pad->elem_out;
-  gst_object_replace ((GstObject **) elem_pad_p, NULL);
-  gst_object_replace ((GstObject **) element_p, NULL);
-  gst_object_replace ((GstObject **) elem_out_p, NULL);
 
   pad->chain = NULL;
   pad->ogg = NULL;
@@ -211,38 +179,6 @@ gst_ogg_pad_finalize (GObject * object)
   G_OBJECT_CLASS (gst_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)
 {
@@ -417,84 +353,6 @@ gst_ogg_pad_reset (GstOggPad * pad)
   pad->last_ret = GST_FLOW_OK;
 }
 
-/* the filter function for selecting the elements we can use in
- * autoplugging */
-static gboolean
-gst_ogg_demux_factory_filter (GstPluginFeature * feature, GstCaps * caps)
-{
-  guint rank;
-  const gchar *klass;
-
-  /* we only care about element factories */
-  if (!GST_IS_ELEMENT_FACTORY (feature))
-    return FALSE;
-
-  klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature));
-  /* only demuxers and decoders can play */
-  if (strstr (klass, "Demux") == NULL &&
-      strstr (klass, "Decoder") == NULL && strstr (klass, "Parse") == NULL) {
-    return FALSE;
-  }
-
-  /* only select elements with autoplugging rank */
-  rank = gst_plugin_feature_get_rank (feature);
-  if (rank < GST_RANK_MARGINAL)
-    return FALSE;
-
-  GST_DEBUG ("checking factory %s", GST_PLUGIN_FEATURE_NAME (feature));
-  /* now see if it is compatible with the caps */
-  {
-    GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
-    const GList *templates;
-    GList *walk;
-
-    /* get the templates from the element factory */
-    templates = gst_element_factory_get_static_pad_templates (factory);
-
-    for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
-      GstStaticPadTemplate *templ = walk->data;
-
-      /* we only care about the sink templates */
-      if (templ->direction == GST_PAD_SINK) {
-        GstCaps *intersect;
-        GstCaps *scaps;
-        gboolean empty;
-
-        /* try to intersect the caps with the caps of the template */
-        scaps = gst_static_caps_get (&templ->static_caps);
-        intersect = gst_caps_intersect (caps, scaps);
-        gst_caps_unref (scaps);
-
-        empty = gst_caps_is_empty (intersect);
-        gst_caps_unref (intersect);
-
-        /* check if the intersection is empty */
-        if (!empty) {
-          /* non empty intersection, we can use this element */
-          goto found;
-        }
-      }
-    }
-  }
-  return FALSE;
-
-found:
-  return TRUE;
-}
-
-/* function used to sort element features */
-static gint
-compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
-{
-  gint diff;
-
-  diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
-  if (diff != 0)
-    return diff;
-  return strcmp (gst_plugin_feature_get_name (f2),
-      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
@@ -526,8 +384,6 @@ gst_ogg_pad_parse_skeleton_fishead (GstOggPad * pad, ogg_packet * packet)
   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 ", prestime: %" GST_TIME_FORMAT ")",
       GST_TIME_ARGS (ogg->basetime), GST_TIME_ARGS (ogg->prestime));
@@ -590,231 +446,6 @@ gst_ogg_pad_parse_skeleton_fisbone (GstOggPad * pad, ogg_packet * packet)
   }
 }
 
-/* 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 
- */
-static GstFlowReturn
-gst_ogg_pad_internal_chain (GstPad * pad, GstBuffer * buffer)
-{
-  GstOggPad *oggpad;
-  GstClockTime timestamp;
-
-  oggpad = gst_pad_get_element_private (pad);
-
-  timestamp = GST_BUFFER_TIMESTAMP (buffer);
-  GST_DEBUG_OBJECT (oggpad, "received buffer from internal pad, TS=%"
-      GST_TIME_FORMAT "=%" G_GINT64_FORMAT, GST_TIME_ARGS (timestamp),
-      timestamp);
-
-  if (oggpad->start_time == GST_CLOCK_TIME_NONE) {
-    oggpad->start_time = timestamp;
-    GST_DEBUG_OBJECT (oggpad, "new start time: %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (timestamp));
-  }
-
-  gst_buffer_unref (buffer);
-
-  return GST_FLOW_OK;
-}
-
-static void
-internal_element_pad_added_cb (GstElement * element, GstPad * pad,
-    GstOggPad * oggpad)
-{
-  if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
-    if (!(gst_pad_link (pad, oggpad->elem_out) == GST_PAD_LINK_OK)) {
-      GST_ERROR ("Really couldn't find a valid pad");
-    }
-    oggpad->dynamic = FALSE;
-    g_signal_handler_disconnect (element, oggpad->padaddedid);
-    oggpad->padaddedid = 0;
-  }
-}
-
-/* runs typefind on the packet, which is assumed to be the first
- * packet in the stream.
- * 
- * Based on the type returned from the typefind function, an element
- * is created to help in conversion between granulepos and timestamps
- * so that we can do decent seeking.
- */
-static gboolean
-gst_ogg_pad_typefind (GstOggPad * pad, ogg_packet * packet)
-{
-  GstBuffer *buf;
-  GstCaps *caps;
-  GstElement *element = NULL;
-
-#ifndef GST_DISABLE_GST_DEBUG
-  GstOggDemux *ogg = pad->ogg;
-#endif
-
-  if (GST_PAD_CAPS (pad) != NULL)
-    return TRUE;
-
-  /* The ogg spec defines that the first packet of an ogg stream must identify
-   * the stream. Therefore ogg can use a simplified approach to typefinding
-   * and only needs to check the first packet */
-  buf = gst_buffer_new ();
-  GST_BUFFER_DATA (buf) = packet->packet;
-  GST_BUFFER_SIZE (buf) = packet->bytes;
-  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,
-        "couldn't find caps for stream with serial %08x", pad->serialno);
-    caps = gst_caps_new_simple ("application/octet-stream", NULL);
-  } else {
-    /* ogg requires you to use a decoder element to define the
-     * meaning of granulepos etc so we make one. We also do this if
-     * we are in the streaming mode to calculate the first timestamp. */
-    GList *factories;
-
-    GST_LOG_OBJECT (ogg, "found caps: %" GST_PTR_FORMAT, caps);
-
-    /* first filter out the interesting element factories */
-    factories = gst_default_registry_feature_filter (
-        (GstPluginFeatureFilter) gst_ogg_demux_factory_filter, FALSE, caps);
-
-    /* sort them according to their ranks */
-    factories = g_list_sort (factories, (GCompareFunc) compare_ranks);
-
-    /* then pick the first factory to create an element */
-    if (factories) {
-      element =
-          gst_element_factory_create (GST_ELEMENT_FACTORY (factories->data),
-          NULL);
-      if (element) {
-        GstPadTemplate *template;
-
-        /* this is ours */
-        gst_object_ref (element);
-        gst_object_sink (GST_OBJECT (element));
-
-        /* FIXME, it might not be named "sink" */
-        pad->elem_pad = gst_element_get_static_pad (element, "sink");
-        gst_element_set_state (element, GST_STATE_PAUSED);
-        template = gst_static_pad_template_get (&internaltemplate);
-        pad->elem_out = gst_pad_new_from_template (template, "internal");
-        gst_pad_set_chain_function (pad->elem_out, gst_ogg_pad_internal_chain);
-        gst_pad_set_element_private (pad->elem_out, pad);
-        gst_pad_set_active (pad->elem_out, TRUE);
-        gst_object_unref (template);
-
-        /* and this pad may not be named src.. */
-        /* And it might also not exist at this time... */
-        {
-          GstPad *p;
-
-          p = gst_element_get_static_pad (element, "src");
-          if (p) {
-            gst_pad_link (p, pad->elem_out);
-            gst_object_unref (p);
-          } else {
-            pad->dynamic = TRUE;
-            pad->padaddedid = g_signal_connect (G_OBJECT (element),
-                "pad-added", G_CALLBACK (internal_element_pad_added_cb), pad);
-          }
-        }
-      }
-    }
-    gst_plugin_feature_list_free (factories);
-  }
-  pad->element = element;
-
-  gst_pad_set_caps (GST_PAD (pad), caps);
-  gst_caps_unref (caps);
-
-  return TRUE;
-}
-
-/* send packet to internal element */
-static GstFlowReturn
-gst_ogg_demux_chain_elem_pad (GstOggPad * pad, ogg_packet * packet)
-{
-  GstBuffer *buf;
-  GstFlowReturn ret;
-
-#ifndef GST_DISABLE_GST_DEBUG
-  GstOggDemux *ogg = pad->ogg;
-#endif
-
-  /* initialize our internal decoder with packets */
-  if (!pad->elem_pad)
-    goto no_decoder;
-
-  GST_DEBUG_OBJECT (ogg, "%p init decoder serial %08x", pad, pad->serialno);
-
-  buf = gst_buffer_new_and_alloc (packet->bytes);
-  memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes);
-  gst_buffer_set_caps (buf, GST_PAD_CAPS (pad));
-  GST_BUFFER_OFFSET (buf) = -1;
-  GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
-
-  ret = gst_pad_chain (pad->elem_pad, buf);
-  if (GST_FLOW_IS_FATAL (ret))
-    goto decoder_error;
-
-  return ret;
-
-no_decoder:
-  {
-    GST_WARNING_OBJECT (ogg,
-        "pad %p does not have elem_pad, no decoder ?", pad);
-    return GST_FLOW_ERROR;
-  }
-decoder_error:
-  {
-    GST_WARNING_OBJECT (ogg, "internal decoder error");
-    return ret;
-  }
-}
-
 /* queue data, basically takes the packet, puts it in a buffer and store the
  * buffer in the headers list.
  */
@@ -846,7 +477,6 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
   GstBuffer *buf;
   GstFlowReturn ret, cret;
   GstOggDemux *ogg = pad->ogg;
-  GstFormat format;
   gint64 current_time;
   GstOggChain *chain;
 
@@ -892,11 +522,8 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet)
   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))
-    goto convert_failed;
+  current_time = gst_ogg_pad_get_end_time_for_granulepos (pad,
+      packet->granulepos);
 
   /* convert to stream time */
   if ((chain = pad->chain)) {
@@ -927,11 +554,6 @@ no_buffer:
         cret, gst_flow_get_name (cret));
     goto done;
   }
-convert_failed:
-  {
-    GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
-    goto done;
-  }
 }
 
 /* submit a packet to the oggpad, this function will run the
@@ -953,8 +575,7 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
         !memcmp (packet->packet, "fishead\0", 8)) {
       gst_ogg_pad_parse_skeleton_fishead (pad, packet);
     }
-    gst_ogg_pad_typefind (pad, packet);
-    pad->have_type = TRUE;
+    pad->have_type = gst_ogg_pad_setup_map (pad, packet);
   }
 
   if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE &&
@@ -967,42 +588,32 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet)
     GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
     ogg->current_granule = granule;
     pad->current_granule = granule;
-    /* granulepos 0 and -1 are considered header packets.
-     * Note that since theora is busted, it can create non-header
-     * 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) {
-      GST_DEBUG_OBJECT (ogg, "%p found first granulepos %" G_GINT64_FORMAT, pad,
-          granule);
-      pad->first_granule = granule;
-    }
   }
 
-  if (granule != -1 && memcmp (packet->packet, "KW-DIRAC", 8) == 0) {
-    return GST_FLOW_OK;
-  }
+  if (!gst_ogg_pad_packet_is_header (pad, packet->packet, packet->bytes)) {
+    if (pad->start_time == GST_CLOCK_TIME_NONE) {
+      gint64 duration = gst_ogg_pad_get_packet_duration (pad, packet);
+      GST_ERROR ("packet duration %lld", duration);
+      if (duration != -1) {
+        GST_ERROR ("accumulating granule %lld", duration);
+        pad->accumulated_granule += duration;
+      }
 
-  /* 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) {
-    ret = gst_ogg_demux_chain_elem_pad (pad, packet);
+      pad->start_time = gst_ogg_pad_granule_to_time (pad,
+          gst_ogg_pad_granulepos_to_granule (pad, packet->granulepos) -
+          pad->accumulated_granule);
+      GST_ERROR ("pad start time %lld", pad->start_time);
+    }
+  } else {
+    pad->n_header_packets_seen++;
   }
+
   /* we know the start_time of the pad data, see if we
    * can activate the complete chain if this is a dynamic
    * chain. */
   if (pad->start_time != GST_CLOCK_TIME_NONE) {
     GstOggChain *chain = pad->chain;
 
-    /* correction for busted ogg, if the internal decoder outputed
-     * a timestamp but we did not get a granulepos, it must have
-     * been 0 and the time is therefore also 0 */
-    if (pad->first_granule == -1) {
-      GST_DEBUG_OBJECT (ogg, "%p found start time without granulepos", pad);
-      pad->first_granule = 0;
-      pad->first_time = 0;
-    }
-
     /* check if complete chain has start time */
     if (chain == ogg->building_chain) {
 
@@ -1354,8 +965,6 @@ static GstStaticPadTemplate ogg_demux_sink_template_factory =
 
 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 GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
     GstOggChain ** chain);
 static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
@@ -1974,7 +1583,6 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
         /* 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,
@@ -1989,20 +1597,13 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment,
         if (pad == NULL || pad->is_skeleton)
           continue;
 
-        format = GST_FORMAT_TIME;
-        if (!gst_ogg_pad_query_convert (pad,
-                GST_FORMAT_DEFAULT, granulepos, &format,
-                (gint64 *) & granuletime)) {
-          GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
-          granuletime = target;
-        } else {
-          if (granuletime < pad->start_time)
-            continue;
-          GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
-              GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
+        granuletime = gst_ogg_pad_get_end_time_for_granulepos (pad, granulepos);
+        if (granuletime < pad->start_time)
+          continue;
+        GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
+            GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
 
-          granuletime -= pad->start_time;
-        }
+        granuletime -= pad->start_time;
 
         GST_DEBUG_OBJECT (ogg,
             "found page with granule %" G_GINT64_FORMAT " and time %"
@@ -2527,24 +2128,10 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain)
   /* 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)
       continue;
 
-    GST_LOG_OBJECT (ogg, "convert first granule %" G_GUINT64_FORMAT " to time ",
-        pad->first_granule);
-
-    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");
-    } else {
-      GST_LOG_OBJECT (ogg, "converted to first time %" GST_TIME_FORMAT,
-          GST_TIME_ARGS (pad->first_time));
-    }
-
     pad->mode = GST_OGG_PAD_MODE_STREAMING;
   }
 
@@ -2564,7 +2151,6 @@ gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
   gint64 end = begin;
   gint64 last_granule = -1;
   GstOggPad *last_pad = NULL;
-  GstFormat target;
   GstFlowReturn ret;
   gboolean done = FALSE;
   ogg_page og;
@@ -2608,13 +2194,10 @@ gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain)
     }
   }
 
-  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)) {
-    GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
-    chain->segment_stop = GST_CLOCK_TIME_NONE;
-  }
+  chain->segment_stop = gst_ogg_pad_get_end_time_for_granulepos (last_pad,
+      last_granule);
+
+  GST_ERROR ("segment stop %lld", chain->segment_stop);
 
   return GST_FLOW_OK;
 }
@@ -2694,6 +2277,8 @@ gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain)
       && chain->segment_start != G_MAXUINT64)
     chain->total_time = chain->segment_stop - chain->segment_start;
 
+  GST_ERROR ("total time %lld", chain->total_time);
+
   GST_DEBUG_OBJECT (ogg, "return %d", res);
 
   return res;
@@ -3322,30 +2907,6 @@ gst_ogg_demux_change_state (GstElement * element, GstStateChange transition)
   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)
 {
@@ -3408,10 +2969,6 @@ gst_ogg_print (GstOggDemux * ogg)
       GST_INFO_OBJECT (ogg, "  stream %08x:", stream->serialno);
       GST_INFO_OBJECT (ogg, "   start time:       %" GST_TIME_FORMAT,
           GST_TIME_ARGS (stream->start_time));
-      GST_INFO_OBJECT (ogg, "   first granulepos: %" G_GINT64_FORMAT,
-          stream->first_granule);
-      GST_INFO_OBJECT (ogg, "   first time:       %" GST_TIME_FORMAT,
-          GST_TIME_ARGS (stream->first_time));
     }
   }
 }
diff --git a/ext/ogg/gstoggdemux.h b/ext/ogg/gstoggdemux.h
index 46f5460..11b92b7 100644
--- a/ext/ogg/gstoggdemux.h
+++ b/ext/ogg/gstoggdemux.h
@@ -43,7 +43,7 @@ typedef struct _GstOggPadClass GstOggPadClass;
 #define GST_IS_OGG_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_DEMUX))
 #define GST_IS_OGG_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_DEMUX))
 
-static GType gst_ogg_demux_get_type (void);
+GType gst_ogg_demux_get_type (void);
 
 typedef struct _GstOggDemux GstOggDemux;
 typedef struct _GstOggDemuxClass GstOggDemuxClass;
@@ -87,21 +87,29 @@ struct _GstOggPad
   gboolean have_type;
   GstOggPadMode mode;
 
+#if 0
   GstPad *elem_pad;             /* sinkpad of internal element */
   GstElement *element;          /* internal element */
   GstPad *elem_out;             /* our sinkpad to receive buffers form the internal element */
+#endif
 
   GstOggChain *chain;           /* the chain we are part of */
   GstOggDemux *ogg;             /* the ogg demuxer we are part of */
 
   GList *headers;
 
+  gint map;
   gboolean is_skeleton;
   gboolean have_fisbone;
-  gint64 granulerate_n;
-  gint64 granulerate_d;
+  gint granulerate_n;
+  gint granulerate_d;
   guint32 preroll;
   guint granuleshift;
+  gboolean is_dirac;
+  gint n_header_packets;
+  gint n_header_packets_seen;
+  gint64 accumulated_granule;
+  gint frame_size;
 
   gint serialno;
   gint64 packetno;
@@ -109,9 +117,6 @@ struct _GstOggPad
 
   GstClockTime start_time;      /* the timestamp of the first sample */
 
-  gint64 first_granule;         /* the granulepos of first page == first sample in next page */
-  GstClockTime first_time;      /* the timestamp of the second page or granuletime of first page */
-
   ogg_stream_state stream;
   GList *continued;
 
@@ -179,6 +184,23 @@ struct _GstOggDemuxClass
   GstElementClass parent_class;
 };
 
+/* in gstoggmap.c */
+
+gboolean gst_ogg_pad_setup_map (GstOggPad * pad, ogg_packet *packet);
+GstClockTime gst_ogg_pad_get_end_time_for_granulepos (GstOggPad *pad,
+    gint64 granulepos);
+GstClockTime gst_ogg_pad_granule_to_time (GstOggPad *pad, gint64 granule);
+gint64 gst_ogg_pad_granulepos_to_granule (GstOggPad * pad, gint64 granulepos);
+GstClockTime gst_ogg_pad_get_packet_start_time (GstOggPad *pad,
+    ogg_packet *packet);
+gboolean gst_ogg_pad_granulepos_is_key_frame (GstOggPad *pad,
+    gint64 granulepos);
+gboolean gst_ogg_pad_packet_is_header (GstOggPad *pad, const guint8 *data,
+    int size);
+gint64 gst_ogg_pad_get_packet_duration (GstOggPad * pad, ogg_packet *packet);
+
+
+
 G_END_DECLS
 
 #endif /* __GST_OGG_DEMUX_H__ */
diff --git a/ext/ogg/gstoggmap.c b/ext/ogg/gstoggmap.c
new file mode 100644
index 0000000..e8fb54e
--- /dev/null
+++ b/ext/ogg/gstoggmap.c
@@ -0,0 +1,1021 @@
+/* GStreamer Ogg Granulepos Mapping Utility Functions
+ * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
+ * Copyright (C) 2009 David Schleef <ds@schleef.org>
+ *
+ * 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 "gstoggdemux.h"
+#include "dirac_parse.h"
+
+#include <string.h>
+
+GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_debug);
+GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_setup_debug);
+#define GST_CAT_DEFAULT gst_ogg_demux_debug
+
+typedef struct _GstOggMap GstOggMap;
+
+typedef gboolean (*GstOggMapSetupFunc) (GstOggPad * pad, ogg_packet * packet);
+typedef GstClockTime (*GstOggMapToTimeFunc) (GstOggPad * pad,
+    gint64 granulepos);
+typedef gint64 (*GstOggMapToGranuleFunc) (GstOggPad * pad, gint64 granulepos);
+
+/* returns TRUE if the granulepos denotes a key frame */
+typedef gboolean (*GstOggMapIsKeyFrameFunc) (GstOggPad * pad,
+    gint64 granulepos);
+
+/* returns TRUE if the given packet is a stream header packet */
+typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggPad * pad,
+    const guint8 * data, guint size);
+typedef gint64 (*GstOggMapPacketDurationFunc) (GstOggPad * pad,
+    ogg_packet * packet);
+
+
+
+#define SKELETON_FISBONE_MIN_SIZE  52
+
+
+struct _GstOggMap
+{
+  const gchar *id;
+  int id_length;
+  int min_packet_size;
+  const gchar *media_type;
+  GstOggMapSetupFunc setup_func;
+  GstOggMapToGranuleFunc granulepos_to_granule_func;
+  GstOggMapIsKeyFrameFunc is_key_frame_func;
+  GstOggMapIsHeaderPacketFunc is_header_func;
+  GstOggMapPacketDurationFunc packet_duration_func;
+};
+
+static const GstOggMap mappers[];
+
+GstClockTime
+gst_ogg_pad_get_packet_start_time (GstOggPad * pad, ogg_packet * packet)
+{
+  int duration;
+
+  if (packet->granulepos == -1) {
+    return GST_CLOCK_TIME_NONE;
+  }
+
+  duration = gst_ogg_pad_get_packet_duration (pad, packet);
+  if (duration == -1) {
+    return GST_CLOCK_TIME_NONE;
+  }
+
+  return gst_ogg_pad_granule_to_time (pad,
+      gst_ogg_pad_granulepos_to_granule (pad, packet->granulepos) - duration);
+}
+
+GstClockTime
+gst_ogg_pad_get_end_time_for_granulepos (GstOggPad * pad, gint64 granulepos)
+{
+  return gst_ogg_pad_granule_to_time (pad,
+      gst_ogg_pad_granulepos_to_granule (pad, granulepos));
+}
+
+GstClockTime
+gst_ogg_pad_granule_to_time (GstOggPad * pad, gint64 granule)
+{
+  if (granule == 0 || pad->granulerate_n == 0 || pad->granulerate_d == 0)
+    return 0;
+
+  return gst_util_uint64_scale (granule, GST_SECOND * pad->granulerate_d,
+      pad->granulerate_n);
+}
+
+gint64
+gst_ogg_pad_granulepos_to_granule (GstOggPad * pad, gint64 granulepos)
+{
+  if (granulepos == -1 || granulepos == 0) {
+    return granulepos;
+  }
+
+  if (mappers[pad->map].granulepos_to_granule_func == NULL) {
+    GST_WARNING ("Failed to convert granulepos to time");
+    return GST_CLOCK_TIME_NONE;
+  }
+
+  return mappers[pad->map].granulepos_to_granule_func (pad, granulepos);
+}
+
+gboolean
+gst_ogg_pad_packet_granulepos_is_key_frame (GstOggPad * pad, gint64 granulepos)
+{
+  if (granulepos == -1) {
+    return FALSE;
+  }
+
+  if (mappers[pad->map].is_key_frame_func == NULL) {
+    GST_WARNING ("Failed to determine key frame");
+    return FALSE;
+  }
+
+  return mappers[pad->map].is_key_frame_func (pad, granulepos);
+}
+
+gboolean
+gst_ogg_pad_packet_is_header (GstOggPad * pad, const guint8 * data, int size)
+{
+  if (mappers[pad->map].is_header_func == NULL) {
+    GST_WARNING ("Failed to determine header");
+    return FALSE;
+  }
+
+  return mappers[pad->map].is_header_func (pad, data, size);
+}
+
+gint64
+gst_ogg_pad_get_packet_duration (GstOggPad * pad, ogg_packet * packet)
+{
+  if (mappers[pad->map].packet_duration_func == NULL) {
+    GST_WARNING ("Failed to determine packet duration");
+    return -1;
+  }
+
+  return mappers[pad->map].packet_duration_func (pad, packet);
+}
+
+
+
+
+/* some generic functions */
+
+static gboolean
+is_keyframe_true (GstOggPad * pad, gint64 granulepos)
+{
+  return TRUE;
+}
+
+static gint64
+granulepos_to_granule_default (GstOggPad * pad, gint64 granulepos)
+{
+  gint64 keyindex, keyoffset;
+
+  if (pad->granuleshift != 0) {
+    keyindex = granulepos >> pad->granuleshift;
+    keyoffset = granulepos - (keyindex << pad->granuleshift);
+    return keyindex + keyoffset;
+  } else {
+    return granulepos;
+  }
+}
+
+static gboolean
+is_header_unknown (GstOggPad * pad, const guint8 * data, guint size)
+{
+  GST_WARNING ("don't know how to detect header");
+  return FALSE;
+}
+
+static gboolean
+is_header_count (GstOggPad * pad, const guint8 * data, guint size)
+{
+  if (pad->n_header_packets_seen < pad->n_header_packets) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+static gint64
+packet_duration_constant (GstOggPad * pad, ogg_packet * packet)
+{
+  return pad->frame_size;
+}
+
+/* theora */
+
+static gboolean
+setup_theora_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  guint8 *data = packet->packet;
+
+  data += 1 + 6 + 1 + 1 + 1 + 2 + 2 + 3 + 3 + 1 + 1;
+
+  pad->granulerate_n = GST_READ_UINT32_BE (data);
+  pad->granulerate_d = GST_READ_UINT32_BE (data + 4);
+  GST_LOG ("fps = %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
+      pad->granulerate_n, pad->granulerate_d);
+
+  data += 4 + 4 + 3 + 3 + 1 + 3;
+
+  /* 2 bits + 3 bits = 5 bits KFGSHIFT */
+  pad->granuleshift = ((GST_READ_UINT8 (data) & 0x02) << 3) +
+      (GST_READ_UINT8 (data + 1) >> 5);
+  GST_LOG ("granuleshift: %u", pad->granuleshift);
+
+  pad->n_header_packets = 3;
+  pad->frame_size = 1;
+
+  if (pad->granuleshift == 0 || pad->granulerate_n == 0
+      || pad->granulerate_d == 0)
+    return FALSE;
+
+  gst_pad_set_caps (GST_PAD (pad), gst_caps_new_simple ("video/x-theora",
+          "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
+          pad->granulerate_d, NULL));
+
+  return TRUE;
+}
+
+static gboolean
+is_keyframe_theora (GstOggPad * pad, gint64 granulepos)
+{
+  gint64 frame_mask;
+
+  if (granulepos == (gint64) - 1)
+    return FALSE;
+
+  frame_mask = (1 << (pad->granuleshift + 1)) - 1;
+
+  return ((granulepos & frame_mask) == 0);
+}
+
+static gboolean
+is_header_theora (GstOggPad * pad, const guint8 * data, guint size)
+{
+  return (size > 0 && (data[0] & 0x80) == 0x80);
+}
+
+/* dirac */
+
+static gboolean
+setup_dirac_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  int ret;
+  DiracSequenceHeader header;
+
+  ret = dirac_sequence_header_parse (&header, packet->packet + 13,
+      packet->bytes - 13);
+  if (ret == 0) {
+    GST_DEBUG ("Failed to parse Dirac sequence header");
+    return FALSE;
+  }
+
+  pad->granulerate_n = header.frame_rate_numerator * 2;
+  pad->granulerate_d = header.frame_rate_denominator;
+  pad->granuleshift = 22;
+  pad->is_dirac = TRUE;
+  pad->n_header_packets = 1;
+  pad->frame_size = 2;
+
+  if (header.interlaced_coding != 0) {
+    GST_DEBUG ("non-progressive Dirac coding not implemented");
+    return FALSE;
+  }
+
+  gst_pad_set_caps (GST_PAD (pad), gst_caps_new_simple ("video/x-dirac",
+          "width", G_TYPE_INT, header.width,
+          "height", G_TYPE_INT, header.height,
+          "interlaced", G_TYPE_BOOLEAN, header.interlaced,
+          "pixel-aspect-ratio", GST_TYPE_FRACTION,
+          header.aspect_ratio_numerator, header.aspect_ratio_denominator,
+          "framerate", GST_TYPE_FRACTION, header.frame_rate_numerator,
+          header.frame_rate_denominator, NULL));
+
+  return TRUE;
+}
+
+#define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1)
+static gboolean
+is_keyframe_dirac (GstOggPad * pad, gint64 granulepos)
+{
+  gint64 pt;
+  int dist_h;
+  int dist_l;
+  int dist;
+  int delay;
+  gint64 dt;
+
+  if (granulepos == -1)
+    return -1;
+
+  pt = ((granulepos >> 22) + (granulepos & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
+  dist_h = (granulepos >> 22) & 0xff;
+  dist_l = granulepos & 0xff;
+  dist = (dist_h << 8) | dist_l;
+  delay = (granulepos >> 9) & 0x1fff;
+  dt = pt - delay;
+
+  return (dist == 0);
+}
+
+static gint64
+granulepos_to_granule_dirac (GstOggPad * pad, gint64 gp)
+{
+  gint64 pt;
+  int dist_h;
+  int dist_l;
+  int dist;
+  int delay;
+  gint64 dt;
+
+  pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
+  dist_h = (gp >> 22) & 0xff;
+  dist_l = gp & 0xff;
+  dist = (dist_h << 8) | dist_l;
+  delay = (gp >> 9) & 0x1fff;
+  dt = pt - delay;
+
+  GST_DEBUG ("pt %lld delay %d", pt, delay);
+
+  return dt + 4;
+}
+
+
+/* vorbis */
+
+static gboolean
+setup_vorbis_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  guint8 *data = packet->packet;
+  int long_size;
+  int short_size;
+
+  data += 1 + 6 + 4 + 1;
+  pad->granulerate_n = GST_READ_UINT32_LE (data);
+  pad->granulerate_d = 1;
+  pad->granuleshift = 0;
+  GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+
+  pad->n_header_packets = 3;
+
+  if (pad->granulerate_n == 0)
+    return FALSE;
+
+  long_size = 1 << (packet->packet[28] >> 4);
+  short_size = 1 << (packet->packet[28] & 0xF);
+
+  /* This is bogus, but at least it gets us closer to correct */
+  pad->frame_size = short_size;
+
+  gst_pad_set_caps (GST_PAD (pad), gst_caps_new_simple ("audio/x-vorbis",
+          "rate", G_TYPE_INT, pad->granulerate_n, NULL));
+
+  return TRUE;
+}
+
+static gboolean
+is_header_vorbis (GstOggPad * pad, const guint8 * data, guint size)
+{
+  if (size > 0 && (data[0] & 0x01) == 0)
+    return FALSE;
+
+  return TRUE;
+}
+
+static gint64
+packet_duration_vorbis (GstOggPad * pad, ogg_packet * packet)
+{
+  /* This is bogus, but at least it gets us closer to correct */
+  return pad->frame_size;
+}
+
+/* speex */
+
+
+static gboolean
+setup_speex_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  guint8 *data = packet->packet;
+
+  data += 8 + 20 + 4 + 4;
+  pad->granulerate_n = GST_READ_UINT32_LE (data);
+  pad->granulerate_d = 1;
+  pad->granuleshift = 0;
+  GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+
+  pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 68) + 2;
+  pad->frame_size = GST_READ_UINT32_LE (packet->packet + 64) *
+      GST_READ_UINT32_LE (packet->packet + 56);
+
+  if (pad->granulerate_n == 0)
+    return FALSE;
+
+  gst_pad_set_caps (GST_PAD (pad), gst_caps_new_simple ("audio/x-speex",
+          "rate", G_TYPE_INT, pad->granulerate_n, NULL));
+
+  return TRUE;
+}
+
+
+/* flac */
+
+static gboolean
+setup_fLaC_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  /* FIXME punt on this for now */
+  return FALSE;
+}
+
+static gboolean
+setup_flac_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  guint8 *data = packet->packet;
+
+  /* see http://flac.sourceforge.net/ogg_mapping.html */
+
+  pad->granulerate_n = (GST_READ_UINT32_BE (data + 27) & 0xFFFFF000) >> 12;
+  pad->granulerate_d = 1;
+  pad->granuleshift = 0;
+  GST_ERROR ("sample rate: %d", pad->granulerate_n);
+
+  pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
+
+  if (pad->granulerate_n == 0)
+    return FALSE;
+
+  gst_pad_set_caps (GST_PAD (pad), gst_caps_new_simple ("audio/x-flac",
+          "rate", G_TYPE_INT, pad->granulerate_n, NULL));
+
+  return TRUE;
+}
+
+static gint64
+packet_duration_flac (GstOggPad * pad, ogg_packet * packet)
+{
+  int block_size_index;
+
+  if (packet->bytes < 4)
+    return -1;
+
+  block_size_index = packet->packet[2] >> 4;
+  if (block_size_index == 1)
+    return 192;
+  if (block_size_index >= 2 && block_size_index <= 5) {
+    return 576 << (block_size_index - 2);
+  }
+  if (block_size_index >= 8) {
+    return 256 << (block_size_index - 8);
+  }
+  return -1;
+}
+
+/* fishead */
+
+static gboolean
+setup_fishead_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  guint8 *data;
+  gint64 prestime_n, prestime_d;
+  gint64 basetime_n, basetime_d;
+  gint64 basetime;
+
+  data = packet->packet;
+
+  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));
+
+  pad->n_header_packets = 1;
+
+  return TRUE;
+}
+
+gboolean
+gst_ogg_map_add_fisbone (GstOggPad * pad,
+    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 (pad->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;
+
+  pad->have_fisbone = TRUE;
+
+  /* we just overwrite whatever was set before by the format-specific setup */
+  pad->granulerate_n = GST_READ_UINT64_LE (data);
+  pad->granulerate_d = GST_READ_UINT64_LE (data + 8);
+
+  start_granule = GST_READ_UINT64_LE (data + 16);
+  preroll = GST_READ_UINT32_LE (data + 24);
+  pad->granuleshift = GST_READ_UINT8 (data + 28);
+
+  start_time = granulepos_to_granule_default (pad, 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 (GstOggPad * pad, ogg_packet * packet)
+{
+  guint8 *data = packet->packet;
+  guint size = packet->bytes;
+
+  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;
+  }
+
+  pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
+  pad->granulerate_d = 1;
+
+  GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+  if (pad->granulerate_n == 0)
+    return FALSE;
+
+  /* FIXME */
+  gst_pad_set_caps (GST_PAD (pad), gst_caps_new_simple ("audio/x-unknown",
+          "rate", G_TYPE_INT, pad->granulerate_n, NULL));
+
+  return TRUE;
+}
+
+static gboolean
+setup_ogmvideo_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  guint8 *data = packet->packet;
+  guint size = packet->bytes;
+
+  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;
+  }
+
+  pad->granulerate_n = 10000000;
+  pad->granulerate_d = GST_READ_UINT64_LE (data + 17);
+
+  GST_LOG ("fps = %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %.3f",
+      pad->granulerate_n, pad->granulerate_d,
+      (double) pad->granulerate_n / pad->granulerate_d);
+
+  if (pad->granulerate_d <= 0)
+    return FALSE;
+
+  /* FIXME */
+  gst_pad_set_caps (GST_PAD (pad), gst_caps_new_simple ("video/x-unknown",
+          "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
+          pad->granulerate_d, NULL));
+
+  return TRUE;
+}
+
+static gboolean
+setup_ogmtext_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  guint8 *data = packet->packet;
+  guint size = packet->bytes;
+
+  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;
+  }
+
+  pad->granulerate_n = 10000000;
+  pad->granulerate_d = GST_READ_UINT64_LE (data + 17);
+
+  GST_LOG ("fps = %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %.3f",
+      pad->granulerate_n, pad->granulerate_d,
+      (double) pad->granulerate_n / pad->granulerate_d);
+
+  if (pad->granulerate_d <= 0)
+    return FALSE;
+
+  /* FIXME */
+  gst_pad_set_caps (GST_PAD (pad), gst_caps_new_simple ("text/x-unknown",
+          NULL));
+
+  return TRUE;
+}
+
+/* PCM */
+
+#define OGGPCM_FMT_S8 0x00000000        /* Signed integer 8 bit */
+#define OGGPCM_FMT_U8 0x00000001        /* Unsigned integer 8 bit */
+#define OGGPCM_FMT_S16_LE 0x00000002    /* Signed integer 16 bit little endian */
+#define OGGPCM_FMT_S16_BE 0x00000003    /* Signed integer 16 bit big endian */
+#define OGGPCM_FMT_S24_LE 0x00000004    /* Signed integer 24 bit little endian */
+#define OGGPCM_FMT_S24_BE 0x00000005    /* Signed integer 24 bit big endian */
+#define OGGPCM_FMT_S32_LE 0x00000006    /* Signed integer 32 bit little endian */
+#define OGGPCM_FMT_S32_BE 0x00000007    /* Signed integer 32 bit big endian */
+
+#define OGGPCM_FMT_ULAW 0x00000010      /* G.711 u-law encoding (8 bit) */
+#define OGGPCM_FMT_ALAW 0x00000011      /* G.711 A-law encoding (8 bit) */
+
+#define OGGPCM_FMT_FLT32_LE 0x00000020  /* IEEE Float [-1,1] 32 bit little endian */
+#define OGGPCM_FMT_FLT32_BE 0x00000021  /* IEEE Float [-1,1] 32 bit big endian */
+#define OGGPCM_FMT_FLT64_LE 0x00000022  /* IEEE Float [-1,1] 64 bit little endian */
+#define OGGPCM_FMT_FLT64_BE 0x00000023  /* IEEE Float [-1,1] 64 bit big endian */
+
+
+static gboolean
+setup_pcm_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  guint8 *data = packet->packet;
+  int format;
+  int channels;
+  GstCaps *caps;
+
+  pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
+  pad->granulerate_d = 1;
+  GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+
+  format = GST_READ_UINT32_LE (data + 12);
+  channels = GST_READ_UINT8 (data + 21);
+
+  pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
+
+  if (pad->granulerate_n == 0)
+    return FALSE;
+
+  switch (format) {
+    case OGGPCM_FMT_S8:
+      caps = gst_caps_new_simple ("audio/x-raw-int",
+          "depth", G_TYPE_INT, 8,
+          "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+      break;
+    case OGGPCM_FMT_U8:
+      caps = gst_caps_new_simple ("audio/x-raw-int",
+          "depth", G_TYPE_INT, 8,
+          "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL);
+      break;
+    case OGGPCM_FMT_S16_LE:
+      caps = gst_caps_new_simple ("audio/x-raw-int",
+          "depth", G_TYPE_INT, 16,
+          "width", G_TYPE_INT, 16,
+          "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
+          "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+      break;
+    case OGGPCM_FMT_S16_BE:
+      caps = gst_caps_new_simple ("audio/x-raw-int",
+          "depth", G_TYPE_INT, 16,
+          "width", G_TYPE_INT, 16,
+          "endianness", G_TYPE_INT, G_BIG_ENDIAN,
+          "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+      break;
+    case OGGPCM_FMT_S24_LE:
+      caps = gst_caps_new_simple ("audio/x-raw-int",
+          "depth", G_TYPE_INT, 24,
+          "width", G_TYPE_INT, 24,
+          "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
+          "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+      break;
+    case OGGPCM_FMT_S24_BE:
+      caps = gst_caps_new_simple ("audio/x-raw-int",
+          "depth", G_TYPE_INT, 24,
+          "width", G_TYPE_INT, 24,
+          "endianness", G_TYPE_INT, G_BIG_ENDIAN,
+          "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+      break;
+    case OGGPCM_FMT_S32_LE:
+      caps = gst_caps_new_simple ("audio/x-raw-int",
+          "depth", G_TYPE_INT, 32,
+          "width", G_TYPE_INT, 32,
+          "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
+          "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+      break;
+    case OGGPCM_FMT_S32_BE:
+      caps = gst_caps_new_simple ("audio/x-raw-int",
+          "depth", G_TYPE_INT, 32,
+          "width", G_TYPE_INT, 32,
+          "endianness", G_TYPE_INT, G_BIG_ENDIAN,
+          "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+      break;
+    case OGGPCM_FMT_ULAW:
+      caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
+      break;
+    case OGGPCM_FMT_ALAW:
+      caps = gst_caps_new_simple ("audio/x-alaw", NULL);
+      break;
+    case OGGPCM_FMT_FLT32_LE:
+      caps = gst_caps_new_simple ("audio/x-raw-float",
+          "width", G_TYPE_INT, 32,
+          "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
+      break;
+    case OGGPCM_FMT_FLT32_BE:
+      caps = gst_caps_new_simple ("audio/x-raw-float",
+          "width", G_TYPE_INT, 32,
+          "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
+      break;
+    case OGGPCM_FMT_FLT64_LE:
+      caps = gst_caps_new_simple ("audio/x-raw-float",
+          "width", G_TYPE_INT, 64,
+          "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
+      break;
+    case OGGPCM_FMT_FLT64_BE:
+      caps = gst_caps_new_simple ("audio/x-raw-float",
+          "width", G_TYPE_INT, 64,
+          "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
+      break;
+    default:
+      return FALSE;
+  }
+
+  gst_caps_set_simple (caps, "audio/x-raw-int",
+      "rate", G_TYPE_INT, pad->granulerate_n,
+      "channels", G_TYPE_INT, channels, NULL);
+  gst_pad_set_caps (GST_PAD (pad), caps);
+
+  return TRUE;
+}
+
+/* cmml */
+
+static gboolean
+setup_cmml_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  guint8 *data = packet->packet;
+
+  pad->granulerate_n = GST_READ_UINT64_LE (data + 12);
+  pad->granulerate_d = GST_READ_UINT64_LE (data + 20);
+  pad->granuleshift = data[28];
+  GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+
+  pad->n_header_packets = 3;
+
+  if (pad->granulerate_n == 0)
+    return FALSE;
+
+  data += 4 + (4 + 4 + 4);
+  GST_ERROR ("blocksize0: %u", 1 << (data[0] >> 4));
+  GST_ERROR ("blocksize1: %u", 1 << (data[0] & 0x0F));
+
+  gst_pad_set_caps (GST_PAD (pad), gst_caps_new_simple ("text/x-cmml", NULL));
+
+  return TRUE;
+}
+
+/* celt */
+
+static gboolean
+setup_celt_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  guint8 *data = packet->packet;
+
+  pad->granulerate_n = GST_READ_UINT32_LE (data + 36);
+  pad->granulerate_d = 1;
+  pad->granuleshift = 0;
+  GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+
+  pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
+  pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
+
+  if (pad->granulerate_n == 0)
+    return FALSE;
+
+  gst_pad_set_caps (GST_PAD (pad), gst_caps_new_simple ("audio/x-celt",
+          "rate", G_TYPE_INT, pad->granulerate_n, NULL));
+
+  return TRUE;
+}
+
+/* kate */
+
+static gboolean
+setup_kate_mapper (GstOggPad * pad, ogg_packet * packet)
+{
+  guint8 *data = packet->packet;
+
+  pad->granulerate_n = GST_READ_UINT32_LE (data + 24);
+  pad->granulerate_d = GST_READ_UINT32_LE (data + 28);
+  pad->granuleshift = GST_READ_UINT8 (data + 15);
+  GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+
+  pad->n_header_packets = GST_READ_UINT8 (data + 11);
+
+  if (pad->granulerate_n == 0)
+    return FALSE;
+
+  gst_pad_set_caps (GST_PAD (pad), gst_caps_new_simple ("audio/x-kate",
+          "rate", G_TYPE_INT, pad->granulerate_n, NULL));
+
+  return TRUE;
+}
+
+
+/* *INDENT-OFF* */
+/* indent hates our freedoms */
+static const GstOggMap mappers[] = {
+  {
+    "\200theora", 7, 42,
+    "video/x-theora",
+    setup_theora_mapper,
+    granulepos_to_granule_default,
+    is_keyframe_theora,
+    is_header_theora,
+    packet_duration_constant
+  },
+  {
+    "\001vorbis", 7, 22,
+    "audio/x-vorbis",
+    setup_vorbis_mapper,
+    granulepos_to_granule_default,
+    is_keyframe_true,
+    is_header_vorbis,
+    packet_duration_vorbis
+  },
+  {
+    "Speex", 5, 80,
+    "audio/x-speex",
+    setup_speex_mapper,
+    granulepos_to_granule_default,
+    is_keyframe_true,
+    is_header_count,
+    packet_duration_constant
+  },
+  {
+    "PCM     ", 8, 0,
+    "audio/x-raw-int",
+    setup_pcm_mapper,
+    NULL,
+    NULL,
+    is_header_count,
+    NULL
+  },
+  {
+    "CMML\0\0\0\0", 8, 0,
+    "text/x-cmml",
+    setup_cmml_mapper,
+    NULL,
+    NULL,
+    is_header_count,
+    NULL
+  },
+  {
+    "Annodex", 7, 0,
+    "application/x-annodex",
+    setup_fishead_mapper,
+    granulepos_to_granule_default,
+    NULL,
+    is_header_unknown,
+    NULL
+  },
+  {
+    "fishead", 7, 64,
+    "application/octet-stream",
+    setup_fishead_mapper,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    "fLaC", 4, 0,
+    "audio/x-flac",
+    setup_fLaC_mapper,
+    granulepos_to_granule_default,
+    NULL,
+    is_header_count,
+    NULL
+  },
+  {
+    "\177FLAC", 4, 36,
+    "audio/x-flac",
+    setup_flac_mapper,
+    granulepos_to_granule_default,
+    NULL,
+    is_header_count,
+    packet_duration_flac
+  },
+  {
+    "AnxData", 7, 0,
+    "application/octet-stream",
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+  },
+  {
+    "CELT    ", 8, 0,
+    "audio/x-celt",
+    setup_celt_mapper,
+    granulepos_to_granule_default,
+    NULL,
+    is_header_count,
+    packet_duration_constant
+  },
+  {
+    "\200kate\0\0\0", 8, 0,
+    "text/x-kate",
+    setup_kate_mapper,
+    NULL,
+    NULL,
+    is_header_count,
+    NULL
+  },
+  {
+    "BBCD\0", 5, 13,
+    "video/x-dirac",
+    setup_dirac_mapper,
+    granulepos_to_granule_dirac,
+    is_keyframe_dirac,
+    is_header_count,
+    packet_duration_constant
+  },
+  {
+    "OGM audio", 100, 0,
+    "application/x-ogm-audio",
+    setup_ogmaudio_mapper,
+    granulepos_to_granule_default,
+    is_keyframe_true,
+    is_header_unknown,
+    NULL
+  },
+  {
+    "OGM video", 100, 0,
+    "application/x-ogm-video",
+    setup_ogmvideo_mapper,
+    granulepos_to_granule_default,
+    NULL,
+    is_header_unknown,
+    NULL
+  },
+  {
+    "OGM text", 100, 0,
+    "application/x-ogm-text",
+    setup_ogmtext_mapper,
+    granulepos_to_granule_default,
+    is_keyframe_true,
+    is_header_unknown,
+    NULL
+  }
+};
+/* *INDENT-ON* */
+
+gboolean
+gst_ogg_pad_setup_map (GstOggPad * pad, ogg_packet * packet)
+{
+  int i;
+  gboolean ret;
+
+  for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
+    if (packet->bytes >= mappers[i].min_packet_size &&
+        packet->bytes >= mappers[i].id_length &&
+        memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) {
+      ret = mappers[i].setup_func (pad, packet);
+      if (ret) {
+        GST_ERROR ("got stream type %" GST_PTR_FORMAT, GST_PAD_CAPS (pad));
+        pad->map = i;
+        return TRUE;
+      }
+    }
+  }
+
+  return FALSE;
+}
