gst/mpegstream/gstmpegdemux.*: Complete overhaul. All DVD specific functionality...
authorMartin Soto <martinsoto@users.sourceforge.net>
Sat, 27 Mar 2004 22:45:41 +0000 (22:45 +0000)
committerMartin Soto <martinsoto@users.sourceforge.net>
Sat, 27 Mar 2004 22:45:41 +0000 (22:45 +0000)
Original commit message from CVS:
* gst/mpegstream/gstmpegdemux.c:
* gst/mpegstream/gstmpegdemux.h: Complete overhaul. All DVD
specific functionality split to the new dvddemux element.
* gst/mpegstream/gstdvddemux.c:
* gst/mpegstream/gstdvddemux.h: New demultiplexer for DVD (VOB)
streams, derived from mpegdemux.
* gst/mpegstream/gstmpegparse.c: Discontinuity handling cleaned
up. SCR based timestamp rewriting can be turned off (will probably
completely disappear soon).
* ext/dvdnav/dvdnavsrc.c: Changes resulting from a few months
hacking. General cleanup. All printf statements replaced by
debugging messages. Almost complete libdvdnav support.
(dvdnavsrc_class_init): Got rid of unnecessary signals (replaced
by events. New properties for audio and subpicture languages.
(dvdnavsrc_update_highlight): Now uses events.
(dvdnavsrc_user_op): Cleaned up.
(dvdnavsrc_get): Renamed to dvdnavsrc_loop (element is now loop
based). Lots of cleanup, and propper support for most libdvdnav
events.
(dvdnavsrc_make_dvd_event): New function.
(dvdnavsrc_make_dvd_nav_packet_event): New function.
(dvdnavsrc_make_clut_change_event): New function.

ChangeLog
ext/dvdnav/dvdnavsrc.c
gst/mpegstream/Makefile.am
gst/mpegstream/gstdvddemux.c [new file with mode: 0644]
gst/mpegstream/gstdvddemux.h [new file with mode: 0644]
gst/mpegstream/gstmpegdemux.c
gst/mpegstream/gstmpegdemux.h
gst/mpegstream/gstmpegparse.c
gst/mpegstream/gstmpegparse.h
gst/mpegstream/gstmpegstream.c

index f394518..6bc2177 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2004-03-27  Martin Soto  <martinsoto@users.sourceforge.net>
+
+       * gst/mpegstream/gstmpegdemux.c: 
+       * gst/mpegstream/gstmpegdemux.h: Complete overhaul. All DVD
+       specific functionality split to the new dvddemux element.
+       * gst/mpegstream/gstdvddemux.c:
+       * gst/mpegstream/gstdvddemux.h: New demultiplexer for DVD (VOB)
+       streams, derived from mpegdemux.
+       * gst/mpegstream/gstmpegparse.c: Discontinuity handling cleaned
+       up. SCR based timestamp rewriting can be turned off (will probably
+       completely disappear soon).
+       * ext/dvdnav/dvdnavsrc.c: Changes resulting from a few months
+       hacking. General cleanup. All printf statements replaced by
+       debugging messages. Almost complete libdvdnav support.
+       (dvdnavsrc_class_init): Got rid of unnecessary signals (replaced
+       by events. New properties for audio and subpicture languages.
+       (dvdnavsrc_update_highlight): Now uses events.
+       (dvdnavsrc_user_op): Cleaned up.
+       (dvdnavsrc_get): Renamed to dvdnavsrc_loop (element is now loop
+       based). Lots of cleanup, and propper support for most libdvdnav
+       events.
+       (dvdnavsrc_make_dvd_event): New function.
+       (dvdnavsrc_make_dvd_nav_packet_event): New function.
+       (dvdnavsrc_make_clut_change_event): New function.
+
 2004-03-26  Benjamin Otte  <otte@gnome.org>
 
        * gst/typefind/gsttypefindfunctions.c: (theora_type_find):
index 95ac8af..5b508cb 100644 (file)
 #include <unistd.h>
 #include <errno.h>
 #include <assert.h>
+
+#include <gst/gst-i18n-plugin.h>
 #include <gst/gst.h>
 
 #include <dvdnav/dvdnav.h>
 #include <dvdnav/nav_print.h>
 
+
+GST_DEBUG_CATEGORY_STATIC (dvdnavsrc_debug);
+#define GST_CAT_DEFAULT (dvdnavsrc_debug)
+
+
+#define CLOCK_BASE 9LL
+#define CLOCK_FREQ CLOCK_BASE * 10000
+
+#define MPEGTIME_TO_GSTTIME(time) (((time) * (GST_MSECOND/10)) / CLOCK_BASE)
+#define GSTTIME_TO_MPEGTIME(time) (((time) * CLOCK_BASE) / (GST_MSECOND/10))
+
+/* Call a dvdnav function and, it it fails, report an error an execute
+   the code in the 'action' parameter. */
+#define DVDNAV_RAWCALL(func, params, elem, action) \
+  if (func params != DVDNAV_STATUS_OK) { \
+    GST_ELEMENT_ERROR (elem, LIBRARY, FAILED, \
+                       (_("Error invoking \"%s\": %s."), \
+                        #func, dvdnav_err_to_string ((elem)->dvdnav)), \
+                       GST_ERROR_SYSTEM); \
+    action \
+  }
+
+/* Call a dvdnav function and, it it fails, report an error and return
+   from the current procedure. */
+#define DVDNAV_CALL(func, params, elem) \
+  DVDNAV_RAWCALL (func, params, elem, return;)
+
+/* Call a dvdnav function and, it it fails, report an error and return
+   from the current procedure with the value 'retval'. */
+#define DVDNAV_CALLVAL(func, params, elem, retval) \
+  DVDNAV_RAWCALL (func, params, elem, return (retval);)
+
+
+/* The maxinum number of audio and SPU streams in a DVD. */
+#define DVDNAVSRC_MAX_AUDIO_STREAMS 8
+#define DVDNAVSRC_MAX_SPU_STREAMS 32
+
 #define GST_TYPE_DVDNAVSRC \
   (dvdnavsrc_get_type())
 #define DVDNAVSRC(obj) \
 typedef struct _DVDNavSrc DVDNavSrc;
 typedef struct _DVDNavSrcClass DVDNavSrcClass;
 
+/* The pause modes to handle still frames. */
+typedef enum
+{
+  DVDNAVSRC_PAUSE_OFF,          /* No pause active. */
+  DVDNAVSRC_PAUSE_LIMITED,      /* A time limited pause is active. */
+  DVDNAVSRC_PAUSE_UNLIMITED     /* An time unlimited pause is active. */
+}
+DVDNavSrcPauseMode;
+
+/* Interval of time to sleep during pauses. */
+#define DVDNAVSRC_PAUSE_INTERVAL (GST_SECOND / 5)
+
+/* The DVD domain types. */
+typedef enum
+{
+  DVDNAVSRC_DOMAIN_UNKNOWN,     /* Unknown domain.  */
+  DVDNAVSRC_DOMAIN_FP,          /* First Play domain. */
+  DVDNAVSRC_DOMAIN_VMGM,        /* Video Management Menu domain */
+  DVDNAVSRC_DOMAIN_VTSM,        /* Video Title Menu domain. */
+  DVDNAVSRC_DOMAIN_VTS,         /* Video Title domain. */
+}
+DVDNavSrcDomainType;
+
 struct _DVDNavSrc
 {
   GstElement element;
 
-  /* pads */
+  /* Pads */
   GstPad *srcpad;
   GstCaps *streaminfo;
 
-  /* location */
+  /* Location */
   gchar *location;
 
   gboolean did_seek;
   gboolean need_flush;
+  gboolean need_discont;
+
+  /* Timing */
+  GstClock *clock;              /* The clock for this element. */
+
+  /* Pause handling */
+  DVDNavSrcPauseMode pause_mode;        /* The current pause mode. */
+  GstClockTime pause_end;       /* The clock time for the end of the
+                                   pause. */
+
+  /* Highligh handling */
+  int button;                   /* The currently highlighted button
+                                   number (0 if no highlight). */
+  dvdnav_highlight_area_t area; /* The area corresponding to the
+                                   currently highlighted button. */
+
+  /* State handling */
+  DVDNavSrcDomainType domain;   /* The current DVD domain. */
 
   int title, chapter, angle;
-  dvdnav_t *dvdnav;
+
+  int audio_phys, audio_log;    /* The current audio streams. */
+  int subp_phys, subp_log;      /* The current subpicture streams. */
+
+  dvdnav_t *dvdnav;             /* The libdvdnav handle. */
 
   GstCaps *buttoninfo;
+
+  GstBuffer *cur_buf;           /* Current output buffer. See
+                                   dvdnavsrc_get. */
 };
 
 struct _DVDNavSrcClass
 {
   GstElementClass parent_class;
 
-  void (*button_pressed) (DVDNavSrc * src, int button);
-  void (*pointer_select) (DVDNavSrc * src, int x, int y);
-  void (*pointer_activate) (DVDNavSrc * src, int x, int y);
   void (*user_op) (DVDNavSrc * src, int op);
 };
 
@@ -86,12 +170,9 @@ GstElementDetails dvdnavsrc_details = {
 };
 
 
-/* DVDNavSrc signals and args */
+/* DVDNavSrc signals and  args */
 enum
 {
-  BUTTON_PRESSED_SIGNAL,
-  POINTER_SELECT_SIGNAL,
-  POINTER_ACTIVATE_SIGNAL,
   USER_OP_SIGNAL,
   LAST_SIGNAL
 };
@@ -105,7 +186,11 @@ enum
   ARG_TITLE_STRING,
   ARG_TITLE,
   ARG_CHAPTER,
-  ARG_ANGLE
+  ARG_ANGLE,
+  ARG_AUDIO_LANGS,
+  ARG_AUDIO_LANG,
+  ARG_SPU_LANGS,
+  ARG_SPU_LANG
 };
 
 typedef enum
@@ -121,40 +206,39 @@ GType dvdnavsrc_get_type (void);
 static void dvdnavsrc_base_init (gpointer g_class);
 static void dvdnavsrc_class_init (DVDNavSrcClass * klass);
 static void dvdnavsrc_init (DVDNavSrc * dvdnavsrc);
+static void dvdnavsrc_finalize (GObject * object);
+
+static void dvdnavsrc_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void dvdnavsrc_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
 
-static void dvdnavsrc_set_property (GObject * object, guint prop_id,
-    const GValue * value, GParamSpec * pspec);
-static void dvdnavsrc_get_property (GObject * object, guint prop_id,
-    GValue * value, GParamSpec * pspec);
+static void dvdnavsrc_set_clock (GstElement * element, GstClock * clock);
 
-static GstData *dvdnavsrc_get (GstPad * pad);
+static GstEvent *dvdnavsrc_make_dvd_event
+    (DVDNavSrc * src, const gchar * event_name, const gchar * firstfield, ...);
+static GstEvent *dvdnavsrc_make_dvd_nav_packet_event
+    (DVDNavSrc * src, const pci_t * pci);
+static GstEvent *dvdnavsrc_make_clut_change_event
+    (DVDNavSrc * src, const guint * clut);
 
-/*static GstBuffer *   dvdnavsrc_get_region    (GstPad *pad,gulong offset,gulong size); */
+static void dvdnavsrc_loop (GstElement * element);
 static gboolean dvdnavsrc_event (GstPad * pad, GstEvent * event);
 static const GstEventMask *dvdnavsrc_get_event_mask (GstPad * pad);
 static const GstFormat *dvdnavsrc_get_formats (GstPad * pad);
-
-/*static gboolean      dvdnavsrc_convert               (GstPad *pad,
-                                                        GstFormat src_format,
-                                                        gint64 src_value, 
-                                                        GstFormat *dest_format, 
-                                                        gint64 *dest_value);*/
-static gboolean dvdnavsrc_query (GstPad * pad, GstQueryType type,
-    GstFormat * format, gint64 * value);
+static gboolean dvdnavsrc_query (GstPad * pad,
+    GstQueryType type, GstFormat * format, gint64 * value);
 static const GstQueryType *dvdnavsrc_get_query_types (GstPad * pad);
 
 static gboolean dvdnavsrc_close (DVDNavSrc * src);
 static gboolean dvdnavsrc_open (DVDNavSrc * src);
 static gboolean dvdnavsrc_is_open (DVDNavSrc * src);
-static void dvdnavsrc_print_event (DVDNavSrc * src, guint8 * data, int event,
-    int len);
+static void dvdnavsrc_print_event (DVDNavSrc * src,
+    guint8 * data, int event, int len);
 static void dvdnavsrc_update_streaminfo (DVDNavSrc * src);
-static void dvdnavsrc_update_buttoninfo (DVDNavSrc * src);
-static void dvdnavsrc_button_pressed (DVDNavSrc * src, int button);
-static void dvdnavsrc_pointer_select (DVDNavSrc * src, int x, int y);
-static void dvdnavsrc_pointer_activate (DVDNavSrc * src, int x, int y);
+static void dvdnavsrc_set_domain (DVDNavSrc * src);
+static void dvdnavsrc_update_highlight (DVDNavSrc * src);
 static void dvdnavsrc_user_op (DVDNavSrc * src, int op);
-
 static GstElementStateReturn dvdnavsrc_change_state (GstElement * element);
 
 
@@ -184,14 +268,16 @@ dvdnavsrc_get_type (void)
       (GInstanceInitFunc) dvdnavsrc_init,
     };
 
-    dvdnavsrc_type =
-        g_type_register_static (GST_TYPE_ELEMENT, "DVDNavSrc", &dvdnavsrc_info,
-        0);
+    dvdnavsrc_type = g_type_register_static (GST_TYPE_ELEMENT,
+        "DVDNavSrc", &dvdnavsrc_info, 0);
 
     sector_format = gst_format_register ("sector", "DVD sector");
     title_format = gst_format_register ("title", "DVD title");
     chapter_format = gst_format_register ("chapter", "DVD chapter");
     angle_format = gst_format_register ("angle", "DVD angle");
+
+    GST_DEBUG_CATEGORY_INIT (dvdnavsrc_debug, "dvdnavsrc", 0,
+        "DVD navigation element");
   }
   return dvdnavsrc_type;
 }
@@ -215,39 +301,15 @@ dvdnavsrc_class_init (DVDNavSrcClass * klass)
 
   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
 
-  dvdnavsrc_signals[BUTTON_PRESSED_SIGNAL] =
-      g_signal_new ("button-pressed",
-      G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-      G_STRUCT_OFFSET (DVDNavSrcClass, button_pressed),
-      NULL, NULL, gst_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
-
-  dvdnavsrc_signals[POINTER_SELECT_SIGNAL] =
-      g_signal_new ("pointer-select",
-      G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-      G_STRUCT_OFFSET (DVDNavSrcClass, pointer_select),
-      NULL, NULL,
-      gst_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
-
-  dvdnavsrc_signals[POINTER_ACTIVATE_SIGNAL] =
-      g_signal_new ("pointer-activate",
-      G_TYPE_FROM_CLASS (klass),
-      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
-      G_STRUCT_OFFSET (DVDNavSrcClass, pointer_activate),
-      NULL, NULL,
-      gst_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
-
   dvdnavsrc_signals[USER_OP_SIGNAL] =
       g_signal_new ("user-op",
       G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
       G_STRUCT_OFFSET (DVDNavSrcClass, user_op),
-      NULL, NULL, gst_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+      NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+
+  gobject_class->finalize = dvdnavsrc_finalize;
 
-  klass->button_pressed = dvdnavsrc_button_pressed;
-  klass->pointer_select = dvdnavsrc_pointer_select;
-  klass->pointer_activate = dvdnavsrc_pointer_activate;
   klass->user_op = dvdnavsrc_user_op;
 
   g_object_class_install_property (gobject_class, ARG_LOCATION,
@@ -261,7 +323,7 @@ dvdnavsrc_class_init (DVDNavSrcClass * klass)
           0, 99, 1, G_PARAM_READWRITE));
   g_object_class_install_property (gobject_class, ARG_CHAPTER,
       g_param_spec_int ("chapter", "chapter", "chapter",
-          0, 99, 1, G_PARAM_READWRITE));
+          1, 99, 1, G_PARAM_READWRITE));
   g_object_class_install_property (gobject_class, ARG_ANGLE,
       g_param_spec_int ("angle", "angle", "angle", 1, 9, 1, G_PARAM_READWRITE));
   g_object_class_install_property (gobject_class, ARG_STREAMINFO,
@@ -270,11 +332,24 @@ dvdnavsrc_class_init (DVDNavSrcClass * klass)
   g_object_class_install_property (gobject_class, ARG_BUTTONINFO,
       g_param_spec_boxed ("buttoninfo", "buttoninfo", "buttoninfo",
           GST_TYPE_CAPS, G_PARAM_READABLE));
+  g_object_class_install_property (gobject_class, ARG_AUDIO_LANGS,
+      g_param_spec_string ("audio_languages", "audio_languages",
+          "Available audio languages", NULL, G_PARAM_READABLE));
+  g_object_class_install_property (gobject_class, ARG_AUDIO_LANG,
+      g_param_spec_string ("audio_language", "audio_language",
+          "Current audio language", NULL, G_PARAM_READABLE));
+  g_object_class_install_property (gobject_class, ARG_SPU_LANGS,
+      g_param_spec_string ("spu_languages", "spu_languages",
+          "Available SPU languages", NULL, G_PARAM_READABLE));
+  g_object_class_install_property (gobject_class, ARG_SPU_LANG,
+      g_param_spec_string ("spu_language", "spu_language",
+          "Current SPU language", NULL, G_PARAM_READABLE));
 
   gobject_class->set_property = GST_DEBUG_FUNCPTR (dvdnavsrc_set_property);
   gobject_class->get_property = GST_DEBUG_FUNCPTR (dvdnavsrc_get_property);
 
   gstelement_class->change_state = dvdnavsrc_change_state;
+  gstelement_class->set_clock = dvdnavsrc_set_clock;
 }
 
 static void
@@ -282,7 +357,8 @@ dvdnavsrc_init (DVDNavSrc * src)
 {
   src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
 
-  gst_pad_set_get_function (src->srcpad, dvdnavsrc_get);
+  gst_element_set_loop_function (GST_ELEMENT (src), dvdnavsrc_loop);
+
   gst_pad_set_event_function (src->srcpad, dvdnavsrc_event);
   gst_pad_set_event_mask_function (src->srcpad, dvdnavsrc_get_event_mask);
   /*gst_pad_set_convert_function (src->srcpad, dvdnavsrc_convert); */
@@ -293,13 +369,43 @@ dvdnavsrc_init (DVDNavSrc * src)
   gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
 
   src->location = g_strdup ("/dev/dvd");
+
   src->did_seek = FALSE;
   src->need_flush = FALSE;
+
+  /* Pause mode is initially inactive. */
+  src->pause_mode = DVDNAVSRC_PAUSE_OFF;
+
+  /* No highlighted button. */
+  src->button = 0;
+
+  /* Domain is unknown at the begining. */
+  src->domain = DVDNAVSRC_DOMAIN_UNKNOWN;
+
   src->title = 0;
-  src->chapter = 0;
+  src->chapter = 1;
   src->angle = 1;
   src->streaminfo = NULL;
   src->buttoninfo = NULL;
+
+  src->audio_phys = -1;
+  src->audio_log = -1;
+  src->subp_phys = -1;
+  src->subp_log = -1;
+
+  /* No current output buffer. */
+  src->cur_buf = NULL;
+}
+
+static void
+dvdnavsrc_finalize (GObject * object)
+{
+  DVDNavSrc *src = DVDNAVSRC (object);
+
+  /* If there's a current output buffer, get rid of it. */
+  if (src->cur_buf != NULL) {
+    gst_buffer_unref (src->cur_buf);
+  }
 }
 
 static gboolean
@@ -312,8 +418,8 @@ dvdnavsrc_is_open (DVDNavSrc * src)
 }
 
 static void
-dvdnavsrc_set_property (GObject * object, guint prop_id, const GValue * value,
-    GParamSpec * pspec)
+dvdnavsrc_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
 {
   DVDNavSrc *src;
 
@@ -347,16 +453,39 @@ dvdnavsrc_set_property (GObject * object, guint prop_id, const GValue * value,
     case ARG_ANGLE:
       src->angle = g_value_get_int (value);
       break;
+    case ARG_AUDIO_LANG:
+      if (dvdnavsrc_is_open (src)) {
+        const gchar *code = g_value_get_string (value);
+
+        if (code != NULL) {
+          GST_INFO_OBJECT (src, "setting language %s", code);
+          if (dvdnav_audio_language_select (src->dvdnav, (char *) code) !=
+              DVDNAV_STATUS_OK) {
+            GST_ERROR_OBJECT (src, "setting language: %s",
+                dvdnav_err_to_string (src->dvdnav));
+          }
+        }
+      }
+      break;
+    case ARG_SPU_LANG:
+      if (dvdnavsrc_is_open (src)) {
+        const gchar *code = g_value_get_string (value);
+
+        if (code != NULL) {
+          dvdnav_spu_language_select (src->dvdnav, (char *) code);
+        }
+      }
+      break;
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
-
 }
 
 static void
-dvdnavsrc_get_property (GObject * object, guint prop_id, GValue * value,
-    GParamSpec * pspec)
+dvdnavsrc_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
 {
   DVDNavSrc *src;
   const char *title_string;
@@ -395,12 +524,120 @@ dvdnavsrc_get_property (GObject * object, guint prop_id, GValue * value,
     case ARG_ANGLE:
       g_value_set_int (value, src->angle);
       break;
+    case ARG_AUDIO_LANGS:
+      if (!dvdnavsrc_is_open (src)) {
+        g_value_set_string (value, "");
+      } else {
+        uint8_t physical, logical;
+        uint16_t lang_int;
+        gchar langs[DVDNAVSRC_MAX_AUDIO_STREAMS * 3];
+        gchar *lang_ptr = langs;
+
+        for (physical = 0; physical < DVDNAVSRC_MAX_AUDIO_STREAMS; physical++) {
+          logical = dvdnav_get_audio_logical_stream (src->dvdnav, physical);
+          lang_int = dvdnav_audio_stream_to_lang (src->dvdnav, logical);
+          if (lang_int != 0xffff) {
+            lang_ptr[0] = (lang_int >> 8) & 0xff;
+            lang_ptr[1] = lang_int & 0xff;
+            lang_ptr[2] = ' ';
+            lang_ptr += 3;
+          }
+        }
+
+        if (lang_ptr > langs) {
+          /* Overwrite the space at the end. */
+          lang_ptr[-1] = '\0';
+        } else {
+          langs[0] = '\0';
+        }
+
+        g_value_set_string (value, langs);
+      }
+      break;
+    case ARG_AUDIO_LANG:
+      if (!dvdnavsrc_is_open (src)) {
+        g_value_set_string (value, "");
+      } else {
+        uint8_t logical;
+        uint16_t lang_int;
+        gchar lang[3];
+
+        logical = dvdnav_get_active_audio_stream (src->dvdnav);
+        lang_int = dvdnav_audio_stream_to_lang (src->dvdnav, logical);
+        if (lang_int != 0xffff) {
+          lang[0] = (lang_int >> 8) & 0xff;
+          lang[1] = lang_int & 0xff;
+          lang[2] = '\0';
+          g_value_set_string (value, lang);
+        } else {
+          g_value_set_string (value, "");
+        }
+      }
+      break;
+    case ARG_SPU_LANGS:
+      if (!dvdnavsrc_is_open (src)) {
+        g_value_set_string (value, "");
+      } else {
+        uint8_t physical, logical;
+        uint16_t lang_int;
+        gchar langs[DVDNAVSRC_MAX_SPU_STREAMS * 3];
+        gchar *lang_ptr = langs;
+
+        for (physical = 0; physical < DVDNAVSRC_MAX_SPU_STREAMS; physical++) {
+          logical = dvdnav_get_spu_logical_stream (src->dvdnav, physical);
+          lang_int = dvdnav_spu_stream_to_lang (src->dvdnav, logical);
+          if (lang_int != 0xffff) {
+            lang_ptr[0] = (lang_int >> 8) & 0xff;
+            lang_ptr[1] = lang_int & 0xff;
+            lang_ptr[2] = ' ';
+            lang_ptr += 3;
+          }
+        }
+
+        if (lang_ptr > langs) {
+          /* Overwrite the space at the end. */
+          lang_ptr[-1] = '\0';
+        } else {
+          langs[0] = '\0';
+        }
+
+        g_value_set_string (value, langs);
+      }
+      break;
+    case ARG_SPU_LANG:
+      if (!dvdnavsrc_is_open (src)) {
+        g_value_set_string (value, "");
+      } else {
+        uint8_t logical;
+        uint16_t lang_int;
+        gchar lang[3];
+
+        logical = dvdnav_get_active_spu_stream (src->dvdnav);
+        lang_int = dvdnav_spu_stream_to_lang (src->dvdnav, logical);
+        if (lang_int != 0xffff) {
+          lang[0] = (lang_int >> 8) & 0xff;
+          lang[1] = lang_int & 0xff;
+          lang[2] = '\0';
+          g_value_set_string (value, lang);
+        } else {
+          g_value_set_string (value, "");
+        }
+      }
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
 }
 
+static void
+dvdnavsrc_set_clock (GstElement * element, GstClock * clock)
+{
+  DVDNavSrc *src = DVDNAVSRC (element);
+
+  src->clock = clock;
+}
+
 static gboolean
 dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
 {
@@ -413,18 +650,18 @@ dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
   /* Dont try to seek to track 0 - First Play program chain */
   g_return_val_if_fail (src->title > 0, FALSE);
 
-  fprintf (stderr, "dvdnav: seeking to %d/%d/%d\n", title, chapter, angle);
+  GST_INFO_OBJECT (src, "seeking to %d/%d/%d", title, chapter, angle);
   /**
    * Make sure our title number is valid.
    */
   if (dvdnav_get_number_of_titles (src->dvdnav, &titles) != DVDNAV_STATUS_OK) {
-    fprintf (stderr, "dvdnav_get_number_of_titles error: %s\n",
+    GST_ERROR_OBJECT (src, "dvdnav_get_number_of_titles: %s",
         dvdnav_err_to_string (src->dvdnav));
     return FALSE;
   }
-  fprintf (stderr, "There are %d titles on this DVD.\n", titles);
+  GST_INFO_OBJECT (src, "there are %d titles on this DVD", titles);
   if (title < 1 || title > titles) {
-    fprintf (stderr, "Invalid title %d.\n", title);
+    GST_ERROR_OBJECT (src, "invalid title %d", title);
     dvdnavsrc_close (src);
     return FALSE;
   }
@@ -434,8 +671,9 @@ dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
    * dvdnav_title_play so that dvdnav_get_number_of_programs knows which title
    * to operate on (also needed to get the number of angles)
    */
+  /* FIXME: This is probably not necessary anymore! */
   if (dvdnav_title_play (src->dvdnav, title) != DVDNAV_STATUS_OK) {
-    fprintf (stderr, "dvdnav_title_play error: %s\n",
+    GST_ERROR_OBJECT (src, "dvdnav_title_play: %s",
         dvdnav_err_to_string (src->dvdnav));
     return FALSE;
   }
@@ -443,14 +681,15 @@ dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
   /**
    * Make sure the chapter number is valid for this title.
    */
-  if (dvdnav_get_number_of_titles (src->dvdnav, &programs) != DVDNAV_STATUS_OK) {
-    fprintf (stderr, "dvdnav_get_number_of_programs error: %s\n",
+  if (dvdnav_get_number_of_titles (src->dvdnav, &programs)
+      != DVDNAV_STATUS_OK) {
+    GST_ERROR ("dvdnav_get_number_of_programs: %s",
         dvdnav_err_to_string (src->dvdnav));
     return FALSE;
   }
-  fprintf (stderr, "There are %d chapters in this title.\n", programs);
+  GST_INFO_OBJECT (src, "there are %d chapters in this title", programs);
   if (chapter < 0 || chapter > programs) {
-    fprintf (stderr, "Invalid chapter %d\n", chapter);
+    GST_ERROR_OBJECT (src, "invalid chapter %d", chapter);
     dvdnavsrc_close (src);
     return FALSE;
   }
@@ -458,15 +697,15 @@ dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
   /**
    * Make sure the angle number is valid for this title.
    */
-  if (dvdnav_get_angle_info (src->dvdnav, &curangle,
-          &angles) != DVDNAV_STATUS_OK) {
-    fprintf (stderr, "dvdnav_get_angle_info error: %s\n",
+  if (dvdnav_get_angle_info (src->dvdnav, &curangle, &angles)
+      != DVDNAV_STATUS_OK) {
+    GST_ERROR_OBJECT (src, "dvdnav_get_angle_info: %s",
         dvdnav_err_to_string (src->dvdnav));
     return FALSE;
   }
-  fprintf (stderr, "There are %d angles in this title.\n", angles);
+  GST_INFO_OBJECT (src, "there are %d angles in this title", angles);
   if (angle < 1 || angle > angles) {
-    fprintf (stderr, "Invalid angle %d\n", angle);
+    GST_ERROR_OBJECT (src, "invalid angle %d", angle);
     dvdnavsrc_close (src);
     return FALSE;
   }
@@ -476,34 +715,23 @@ dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
    */
   if (src->chapter == 0) {
     if (dvdnav_title_play (src->dvdnav, title) != DVDNAV_STATUS_OK) {
-      fprintf (stderr, "dvdnav_title_play error: %s\n",
+      GST_ERROR_OBJECT (src, "dvdnav_title_play: %s",
           dvdnav_err_to_string (src->dvdnav));
       return FALSE;
     }
   } else {
     if (dvdnav_part_play (src->dvdnav, title, chapter) != DVDNAV_STATUS_OK) {
-      fprintf (stderr, "dvdnav_part_play error: %s\n",
+      GST_ERROR_OBJECT (src, "dvdnav_part_play: %s",
           dvdnav_err_to_string (src->dvdnav));
       return FALSE;
     }
   }
   if (dvdnav_angle_change (src->dvdnav, angle) != DVDNAV_STATUS_OK) {
-    fprintf (stderr, "dvdnav_angle_change error: %s\n",
+    GST_ERROR_OBJECT (src, "dvdnav_angle_change: %s",
         dvdnav_err_to_string (src->dvdnav));
     return FALSE;
   }
 
-  /*
-     if (dvdnav_physical_audio_stream_change (src->dvdnav, 0) != DVDNAV_STATUS_OK) {
-     fprintf (stderr, "dvdnav_physical_audio_stream_change error: %s\n", dvdnav_err_to_string(src->dvdnav));
-     return FALSE;
-     }
-     if (dvdnav_logical_audio_stream_change (src->dvdnav, 0) != DVDNAV_STATUS_OK) {
-     fprintf (stderr, "dvdnav_logical_audio_stream_change error: %s\n", dvdnav_err_to_string(src->dvdnav));
-     return FALSE;
-     }
-   */
-
   src->did_seek = TRUE;
 
   return TRUE;
@@ -530,8 +758,8 @@ dvdnavsrc_update_streaminfo (DVDNavSrc * src)
   if (dvdnavsrc_query (src->srcpad, GST_QUERY_TOTAL, &chapter_format, &value)) {
     gst_caps_set_simple (caps, "chapters", G_TYPE_INT, value, NULL);
   }
-  if (dvdnavsrc_query (src->srcpad, GST_QUERY_POSITION, &chapter_format,
-          &value)) {
+  if (dvdnavsrc_query (src->srcpad, GST_QUERY_POSITION,
+          &chapter_format, &value)) {
     gst_caps_set_simple (caps, "chapter", G_TYPE_INT, value, NULL);
   }
 
@@ -549,52 +777,87 @@ dvdnavsrc_update_streaminfo (DVDNavSrc * src)
   g_object_notify (G_OBJECT (src), "streaminfo");
 }
 
+/**
+ * Check for a new DVD domain area, and update the structure if
+ * necessary.
+ */
 static void
-dvdnavsrc_update_buttoninfo (DVDNavSrc * src)
+dvdnavsrc_set_domain (DVDNavSrc * src)
 {
-  GstCaps *caps;
+  DVDNavSrcDomainType domain;
+
+  if (dvdnav_is_domain_fp (src->dvdnav)) {
+    domain = DVDNAVSRC_DOMAIN_FP;
+  } else if (dvdnav_is_domain_vmgm (src->dvdnav)) {
+    domain = DVDNAVSRC_DOMAIN_VMGM;
+  } else if (dvdnav_is_domain_vtsm (src->dvdnav)) {
+    domain = DVDNAVSRC_DOMAIN_VTSM;
+  } else if (dvdnav_is_domain_vts (src->dvdnav)) {
+    domain = DVDNAVSRC_DOMAIN_VTS;
+  } else {
+    domain = DVDNAVSRC_DOMAIN_UNKNOWN;
+  }
+
+  /* FIXME: We may send a signal if we have a new domain. */
+  src->domain = domain;
+}
+
+/**
+ * Check for a new highlighted area, and send an spu highlight event if
+ * necessary.
+ */
+static void
+dvdnavsrc_update_highlight (DVDNavSrc * src)
+{
+  int button;
   pci_t *pci;
+  dvdnav_highlight_area_t area;
+  GstEvent *event;
+
+  DVDNAV_CALL (dvdnav_get_current_highlight, (src->dvdnav, &button), src);
 
   pci = dvdnav_get_current_nav_pci (src->dvdnav);
+  if (button > pci->hli.hl_gi.btn_ns) {
+    /* button is out of the range of possible buttons. */
+    button = 0;
+  }
 
-  if (src->buttoninfo) {
-    gint btn_ns;
+  if (button == 0) {
+    if (src->button != 0) {
+      src->button = 0;
 
-    /* Don't notify if there is no actual change */
-    if (gst_structure_get_int (gst_caps_get_structure (src->buttoninfo, 0),
-            "total", &btn_ns)
-        && (btn_ns == pci->hli.hl_gi.btn_ns)) {
-      return;
+      event = dvdnavsrc_make_dvd_event (src, "dvd-spu-reset-highlight", NULL);
+      gst_pad_push (src->srcpad, GST_DATA (event));
     }
+    return;
   }
 
-  caps = gst_caps_new_simple ("application/x-gst-dvdnavsrc-buttoninfo",
-      "total", G_TYPE_INT, (gint) pci->hli.hl_gi.btn_ns, NULL);
-
-  if (src->buttoninfo)
-    gst_caps_free (src->buttoninfo);
-
-  src->buttoninfo = caps;
-  g_object_notify (G_OBJECT (src), "buttoninfo");
-}
-
-static void
-dvdnavsrc_button_pressed (DVDNavSrc * src, int button)
-{
-}
+  DVDNAV_CALL (dvdnav_get_highlight_area, (pci, button, 0, &area), src);
+
+  /* Check if we have a new button number, or a new highlight region. */
+  if (button != src->button ||
+      memcmp (&area, &(src->area), sizeof (dvdnav_highlight_area_t)) != 0) {
+    memcpy (&(src->area), &area, sizeof (dvdnav_highlight_area_t));
+
+    event = dvdnavsrc_make_dvd_event (src,
+        "dvd-spu-highlight",
+        "button", G_TYPE_INT, (gint) button,
+        "palette", G_TYPE_INT, (gint) area.palette,
+        "sx", G_TYPE_INT, (gint) area.sx,
+        "sy", G_TYPE_INT, (gint) area.sy,
+        "ex", G_TYPE_INT, (gint) area.ex,
+        "ey", G_TYPE_INT, (gint) area.ey, NULL);
+
+    if (src->button == 0) {
+      /* When setting the button for the first time, take the
+         timestamp into account. */
+      GST_EVENT_TIMESTAMP (event) = MPEGTIME_TO_GSTTIME (area.pts);
+    }
 
-static void
-dvdnavsrc_pointer_select (DVDNavSrc * src, int x, int y)
-{
-  dvdnav_mouse_select (src->dvdnav,
-      dvdnav_get_current_nav_pci (src->dvdnav), x, y);
-}
+    src->button = button;
 
-static void
-dvdnavsrc_pointer_activate (DVDNavSrc * src, int x, int y)
-{
-  dvdnav_mouse_activate (src->dvdnav,
-      dvdnav_get_current_nav_pci (src->dvdnav), x, y);
+    gst_pad_push (src->srcpad, GST_DATA (event));
+  }
 }
 
 static void
@@ -602,7 +865,8 @@ dvdnavsrc_user_op (DVDNavSrc * src, int op)
 {
   pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav);
 
-  fprintf (stderr, "user_op %d\n", op);
+  GST_INFO_OBJECT (src, "user operation %d", op);
+
   /* Magic user_op ids */
   switch (op) {
     case 0:                    /* None */
@@ -663,8 +927,8 @@ dvdnavsrc_user_op (DVDNavSrc * src, int op)
       }
       break;
     case 12:                   /* Menu - Subpicture */
-      if (dvdnav_menu_call (src->dvdnav,
-              DVD_MENU_Subpicture) != DVDNAV_STATUS_OK) {
+      if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Subpicture)
+          != DVDNAV_STATUS_OK) {
         goto naverr;
       }
       break;
@@ -683,14 +947,31 @@ dvdnavsrc_user_op (DVDNavSrc * src, int op)
         goto naverr;
       }
       break;
+    case 50:                   /* Select button */
+    {
+      int32_t button;
+
+      dvdnav_get_current_highlight (src->dvdnav, &button);
+      if (button == 0) {
+        for (button = 1; button <= 36; button++) {
+          if (dvdnav_button_select (src->dvdnav, pci, button) ==
+              DVDNAV_STATUS_OK) {
+            break;
+          }
+        }
+        dvdnav_get_current_highlight (src->dvdnav, &button);
+      }
+      GST_INFO_OBJECT (src, "Selected button: %d", button);
+    }
+      break;
   }
   return;
 naverr:
-  GST_ELEMENT_ERROR (src, LIBRARY, TOO_LAZY, (NULL),
-      ("user op %d failure: %s", op, dvdnav_err_to_string (src->dvdnav)));
-
+  GST_WARNING_OBJECT (src, "user op %d failure: %s",
+      op, dvdnav_err_to_string (src->dvdnav));
 }
 
+#ifndef GST_DISABLE_GST_DEBUG
 static gchar *
 dvdnav_get_event_name (int event)
 {
@@ -704,6 +985,9 @@ dvdnav_get_event_name (int event)
     case DVDNAV_STILL_FRAME:
       return "DVDNAV_STILL_FRAME";
       break;
+    case DVDNAV_WAIT:
+      return "DVDNAV_WAIT";
+      break;
     case DVDNAV_SPU_STREAM_CHANGE:
       return "DVDNAV_SPU_STREAM_CHANGE";
       break;
@@ -731,12 +1015,12 @@ dvdnav_get_event_name (int event)
     case DVDNAV_HOP_CHANNEL:
       return "DVDNAV_HOP_CHANNEL";
       break;
-    case DVDNAV_WAIT:
-      return "DVDNAV_WAIT";
-      break;
   }
   return "UNKNOWN";
 }
+#else
+#define dvdnav_get_event_name(event) "<unknown>"
+#endif /* GST_DISABLE_GST_DEBUG */
 
 static gchar *
 dvdnav_get_read_domain_name (dvd_read_domain_t domain)
@@ -758,13 +1042,14 @@ dvdnav_get_read_domain_name (dvd_read_domain_t domain)
   return "UNKNOWN";
 }
 
+#ifndef GST_DISABLE_GST_DEBUG
 static void
 dvdnavsrc_print_event (DVDNavSrc * src, guint8 * data, int event, int len)
 {
   g_return_if_fail (src != NULL);
   g_return_if_fail (GST_IS_DVDNAVSRC (src));
 
-  fprintf (stderr, "dvdnavsrc (%p): event: %s\n", src,
+  GST_DEBUG_OBJECT (src, "dvdnavsrc (%p): event: %s", src,
       dvdnav_get_event_name (event));
   switch (event) {
     case DVDNAV_BLOCK_OK:
@@ -775,64 +1060,55 @@ dvdnavsrc_print_event (DVDNavSrc * src, guint8 * data, int event, int len)
     {
       dvdnav_still_event_t *event = (dvdnav_still_event_t *) data;
 
-      fprintf (stderr, "  still frame: %d seconds\n", event->length);
+      GST_DEBUG_OBJECT (src, "  still frame: %d seconds", event->length);
+    }
+      break;
+    case DVDNAV_WAIT:
+    {
     }
       break;
     case DVDNAV_SPU_STREAM_CHANGE:
     {
       dvdnav_spu_stream_change_event_t *event =
           (dvdnav_spu_stream_change_event_t *) data;
-      fprintf (stderr, "  physical_wide: %d\n", event->physical_wide);
-      fprintf (stderr, "  physical_letterbox: %d\n", event->physical_letterbox);
-      fprintf (stderr, "  physical_pan_scan: %d\n", event->physical_pan_scan);
-      fprintf (stderr, "  logical: %d\n", event->logical);
+      GST_DEBUG_OBJECT (src, "  physical_wide: %d", event->physical_wide);
+      GST_DEBUG_OBJECT (src, "  physical_letterbox: %d",
+          event->physical_letterbox);
+      GST_DEBUG_OBJECT (src, "  physical_pan_scan: %d",
+          event->physical_pan_scan);
+      GST_DEBUG_OBJECT (src, "  logical: %d", event->logical);
     }
       break;
     case DVDNAV_AUDIO_STREAM_CHANGE:
     {
       dvdnav_audio_stream_change_event_t *event =
           (dvdnav_audio_stream_change_event_t *) data;
-      fprintf (stderr, "  physical: %d\n", event->physical);
-      fprintf (stderr, "  logical: %d\n", event->logical);
+      GST_DEBUG_OBJECT (src, "  physical: %d", event->physical);
+      GST_DEBUG_OBJECT (src, "  logical: %d", event->logical);
     }
       break;
     case DVDNAV_VTS_CHANGE:
     {
       dvdnav_vts_change_event_t *event = (dvdnav_vts_change_event_t *) data;
 
-      fprintf (stderr, "  old_vtsN: %d\n", event->old_vtsN);
-      fprintf (stderr, "  old_domain: %s\n",
+      GST_DEBUG_OBJECT (src, "  old_vtsN: %d", event->old_vtsN);
+      GST_DEBUG_OBJECT (src, "  old_domain: %s",
           dvdnav_get_read_domain_name (event->old_domain));
-      fprintf (stderr, "  new_vtsN: %d\n", event->new_vtsN);
-      fprintf (stderr, "  new_domain: %s\n",
+      GST_DEBUG_OBJECT (src, "  new_vtsN: %d", event->new_vtsN);
+      GST_DEBUG_OBJECT (src, "  new_domain: %s",
           dvdnav_get_read_domain_name (event->new_domain));
     }
       break;
     case DVDNAV_CELL_CHANGE:
     {
-      /*dvdnav_cell_change_event_t *event = (dvdnav_cell_change_event_t *)data; */
-      /*fprintf (stderr, "  old_cell: %p\n", event->old_cell); */
-      /*fprintf (stderr, "  new_cell: %p\n", event->new_cell); */
+      /*dvdnav_cell_change_event_t *event =
+         (dvdnav_cell_change_event_t *)data; */
+      /* FIXME: Print something relevant here. */
     }
       break;
     case DVDNAV_NAV_PACKET:
     {
-/*
-        dvdnav_nav_packet_event_t *event = (dvdnav_nav_packet_event_t *)data;
-        pci_t *pci;
-        dsi_t *dsi;
-
-        pci = event->pci;
-        dsi = event->dsi;
-
-        pci = dvdnav_get_current_nav_pci(src->dvdnav);
-        dsi = dvdnav_get_current_nav_dsi(src->dvdnav);
-        fprintf (stderr, "  pci: %p\n", event->pci);
-        fprintf (stderr, "  dsi: %p\n", event->dsi);
-
-        navPrint_PCI(pci);
-        navPrint_DSI(dsi);
-*/
+      /* FIXME: Print something relevant here. */
     }
       break;
     case DVDNAV_STOP:
@@ -841,16 +1117,15 @@ dvdnavsrc_print_event (DVDNavSrc * src, guint8 * data, int event, int len)
     {
       dvdnav_highlight_event_t *event = (dvdnav_highlight_event_t *) data;
 
-      fprintf (stderr, "  display: %s\n",
-          event->display == 0 ? "hide" : (event->display ==
-              1 ? "show" : "unknown")
-          );
+      GST_DEBUG_OBJECT (src, "  display: %s",
+          event->display == 0 ?
+          "hide" : (event->display == 1 ? "show" : "unknown"));
       if (event->display == 1) {
-        fprintf (stderr, "  palette: %08x\n", event->palette);
-        fprintf (stderr, "  coords (%u, %u) - (%u, %u)\n", event->sx, event->sy,
-            event->ex, event->ey);
-        fprintf (stderr, "  pts: %u\n", event->pts);
-        fprintf (stderr, "  button: %u\n", event->buttonN);
+        GST_DEBUG_OBJECT (src, "  palette: %08x", event->palette);
+        GST_DEBUG_OBJECT (src, "  coords (%u, %u) - (%u, %u)",
+            event->sx, event->sy, event->ex, event->ey);
+        GST_DEBUG_OBJECT (src, "  pts: %u", event->pts);
+        GST_DEBUG_OBJECT (src, "  button: %u", event->buttonN);
       }
     }
       break;
@@ -858,131 +1133,356 @@ dvdnavsrc_print_event (DVDNavSrc * src, guint8 * data, int event, int len)
       break;
     case DVDNAV_HOP_CHANNEL:
       break;
-    case DVDNAV_WAIT:
-      break;
     default:
-      fprintf (stderr, "  event id: %d\n", event);
+      GST_DEBUG_OBJECT (src, "  event id: %d", event);
       break;
   }
 }
+#else
+#define dvdnavsrc_print_event(src, data, event, len) ((void) 0)
+#endif /* GST_DISABLE_GST_DEBUG */
 
-static GstData *
-dvdnavsrc_get (GstPad * pad)
+static GstEvent *
+dvdnavsrc_make_dvd_event (DVDNavSrc * src, const gchar * event_name,
+    const gchar * firstfield, ...)
 {
-  DVDNavSrc *src;
+  GstEvent *event;
+  GstStructure *structure;
+  va_list varargs;
+
+  g_return_val_if_fail (event_name != NULL, NULL);
+
+  /* Create a structure with the given fields. */
+  va_start (varargs, firstfield);
+  structure = gst_structure_new ("application/x-gst-dvd",
+      "event", G_TYPE_STRING, event_name, NULL);
+  gst_structure_set_valist (structure, firstfield, varargs);
+  va_end (varargs);
+
+  /* Create the DVD event and put the structure into it. */
+  event = gst_event_new (GST_EVENT_ANY);
+  event->event_data.structure.structure = structure;
+
+#ifndef GST_DISABLE_GST_DEBUG
+  {
+    gchar *text = gst_structure_to_string (structure);
+
+    GST_LOG_OBJECT (src, "creating event \"%s\"", text);
+    g_free (text);
+  }
+#endif
+
+  return event;
+}
+
+static GstEvent *
+dvdnavsrc_make_dvd_nav_packet_event (DVDNavSrc * src, const pci_t * pci)
+{
+  GstEvent *event;
+  GstStructure *structure;
+  GValue start_ptm = { 0 }, end_ptm =
+  {
+  0};
+
+  /* Store the time values in GValues. */
+  g_value_init (&start_ptm, G_TYPE_UINT64);
+  g_value_set_uint64 (&start_ptm, MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm));
+  g_value_init (&end_ptm, G_TYPE_UINT64);
+  g_value_set_uint64 (&end_ptm, MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm));
+
+  /* Add the values to a new structure. */
+  structure = gst_structure_new ("application/x-gst-dvd",
+      "event", G_TYPE_STRING, "dvd-nav-packet", NULL);
+  gst_structure_set_value (structure, "start_ptm", &start_ptm);
+  gst_structure_set_value (structure, "end_ptm", &end_ptm);
+
+  /* Create the DVD event and put the structure into it. */
+  event = gst_event_new (GST_EVENT_ANY);
+  event->event_data.structure.structure = structure;
+
+#ifndef GST_DISABLE_GST_DEBUG
+  {
+    gchar *text = gst_structure_to_string (structure);
+
+    GST_LOG_OBJECT (src, "creating event \"%s\"", text);
+    g_free (text);
+  }
+#endif
+
+  return event;
+}
+
+static GstEvent *
+dvdnavsrc_make_clut_change_event (DVDNavSrc * src, const guint * clut)
+{
+  GstEvent *event;
+  GstStructure *structure;
+  guchar name[16];
+  int i;
+
+  structure = gst_structure_new ("application/x-gst-dvd",
+      "event", G_TYPE_STRING, "dvd-spu-clut-change", NULL);
+
+  /* Create a separate field for each value in the table. */
+  for (i = 0; i < 16; i++) {
+    sprintf (name, "clut%02d", i);
+    gst_structure_set (structure, name, G_TYPE_INT, (int) clut[i], NULL);
+  }
+
+  /* Create the DVD event and put the structure into it. */
+  event = gst_event_new (GST_EVENT_ANY);
+  event->event_data.structure.structure = structure;
+
+#ifndef GST_DISABLE_GST_DEBUG
+  {
+    gchar *text = gst_structure_to_string (structure);
+
+    GST_LOG_OBJECT (src, "creating event \"%s\"", text);
+    g_free (text);
+  }
+#endif
+
+  return event;
+}
+
+static void
+dvdnavsrc_loop (GstElement * element)
+{
+  DVDNavSrc *src = DVDNAVSRC (element);
   int event, len;
-  GstBuffer *buf;
   guint8 *data;
-  gboolean have_buf;
-
-  g_return_val_if_fail (pad != NULL, NULL);
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+  GstData *send_data;
 
-  src = DVDNAVSRC (gst_pad_get_parent (pad));
-  g_return_val_if_fail (dvdnavsrc_is_open (src), NULL);
+  g_return_if_fail (dvdnavsrc_is_open (src));
 
   if (src->did_seek) {
     GstEvent *event;
 
     src->did_seek = FALSE;
-    GST_DEBUG ("dvdnavsrc sending discont");
+    GST_INFO_OBJECT (src, "sending discont");
     event = gst_event_new_discontinuous (FALSE, 0);
     src->need_flush = FALSE;
-    return GST_DATA (event);
+    gst_pad_push (src->srcpad, GST_DATA (event));
+    return;
   }
+
   if (src->need_flush) {
     src->need_flush = FALSE;
-    GST_DEBUG ("dvdnavsrc sending flush");
-    return GST_DATA (gst_event_new_flush ());
+    GST_INFO_OBJECT (src, "sending flush");
+    gst_pad_push (src->srcpad, GST_DATA (gst_event_new_flush ()));
+    return;
   }
 
-  /* loop processing blocks until data is pushed */
-  have_buf = FALSE;
-  while (!have_buf) {
-    buf = gst_buffer_new_and_alloc (DVD_VIDEO_LB_LEN);
-    if (!buf) {
-      GST_ELEMENT_ERROR (src, CORE, TOO_LAZY, (NULL),
-          ("Failed to create a new GstBuffer"));
-      return NULL;
-    }
-    data = GST_BUFFER_DATA (buf);
-
-    if (dvdnav_get_next_block (src->dvdnav, data, &event, &len) !=
-        DVDNAV_STATUS_OK) {
-      GST_ELEMENT_ERROR (src, STREAM, DECODE, (NULL),
-          ("dvdnav_get_next_block error: %s\n",
-              dvdnav_err_to_string (src->dvdnav)));
-      return NULL;
+  /* Loop processing blocks until there is data to send. */
+  send_data = NULL;
+  while (send_data == NULL) {
+    if (src->cur_buf == NULL) {
+      src->cur_buf = gst_buffer_new_and_alloc (DVD_VIDEO_LB_LEN);
+      if (src->cur_buf == NULL) {
+        GST_ELEMENT_ERROR (src, CORE, TOO_LAZY, (NULL),
+            ("Failed to create a new GstBuffer"));
+        return;
+      }
     }
+    data = GST_BUFFER_DATA (src->cur_buf);
+
+    DVDNAV_CALL (dvdnav_get_next_block, (src->dvdnav, data, &event, &len), src);
 
     switch (event) {
       case DVDNAV_NOP:
         break;
+
       case DVDNAV_BLOCK_OK:
-        g_return_val_if_fail (GST_BUFFER_DATA (buf) != NULL, NULL);
-        g_return_val_if_fail (GST_BUFFER_SIZE (buf) == DVD_VIDEO_LB_LEN, NULL);
-        have_buf = TRUE;
+        send_data = GST_DATA (src->cur_buf);
+        src->cur_buf = NULL;
         break;
+
       case DVDNAV_STILL_FRAME:
-        /* FIXME: we should pause for event->length seconds before
-         * dvdnav_still_skip */
-        dvdnavsrc_print_event (src, data, event, len);
-        if (dvdnav_still_skip (src->dvdnav) != DVDNAV_STATUS_OK) {
-          GST_ELEMENT_ERROR (src, STREAM, TOO_LAZY, (NULL),
-              ("dvdnav_still_skip error: %s\n",
-                  dvdnav_err_to_string (src->dvdnav)));
-          /* FIXME: close the stream??? */
+      {
+        dvdnav_still_event_t *info = (dvdnav_still_event_t *) data;
+        GstClockTime current_time = gst_element_get_time (GST_ELEMENT (src));
+
+        if (src->pause_mode == DVDNAVSRC_PAUSE_OFF) {
+          dvdnavsrc_print_event (src, data, event, len);
+
+          /* We just saw a still frame. Start a pause now. */
+          if (info->length == 0xff) {
+            GST_INFO_OBJECT (src, "starting unlimed pause");
+            src->pause_mode = DVDNAVSRC_PAUSE_UNLIMITED;
+          } else {
+            GST_INFO_OBJECT (src, "starting limited pause: %d seconds",
+                info->length);
+            src->pause_mode = DVDNAVSRC_PAUSE_LIMITED;
+            src->pause_end = current_time + info->length * GST_SECOND;
+          }
+
+          /* For the moment, send the first empty event to let
+             everyone know that we are displaying a still frame.
+             Subsequent calls to this function will take care of
+             the rest of the pause. */
+          GST_DEBUG_OBJECT (src, "sending still frame event");
+          send_data = GST_DATA (dvdnavsrc_make_dvd_event (src,
+                  "dvd-spu-still-frame", NULL));
+          break;
         }
+
+        if (src->pause_mode == DVDNAVSRC_PAUSE_UNLIMITED ||
+            current_time < src->pause_end) {
+          /* We are in pause mode. Make this element sleep for a
+             fraction of a second. */
+          if (current_time + DVDNAVSRC_PAUSE_INTERVAL > src->pause_end) {
+            gst_element_wait (GST_ELEMENT (src), src->pause_end);
+          } else {
+            gst_element_wait (GST_ELEMENT (src),
+                current_time + DVDNAVSRC_PAUSE_INTERVAL);
+          }
+
+          /* Send an empty event to keep the pipeline going. */
+          /* FIXME: Use an interrupt/filler event here. */
+          send_data = GST_DATA (gst_event_new (GST_EVENT_EMPTY));
+          break;
+        } else {
+          /* We reached the end of the pause. */
+          src->pause_mode = DVDNAVSRC_PAUSE_OFF;
+          DVDNAV_CALL (dvdnav_still_skip, (src->dvdnav), src);
+        }
+      }
+        break;
+
+      case DVDNAV_WAIT:
+        /* FIXME: We should really wait here until the fifos are
+           empty, but I have no idea how to do that.  In the mean time,
+           just clean the wait state. */
+        GST_INFO_OBJECT (src, "sending wait");
+        DVDNAV_CALL (dvdnav_wait_skip, (src->dvdnav), src);
         break;
+
       case DVDNAV_STOP:
-        GST_DEBUG ("dvdnavsrc sending eos");
+        GST_INFO_OBJECT (src, "sending eos");
         gst_element_set_eos (GST_ELEMENT (src));
         dvdnavsrc_close (src);
-        buf = GST_BUFFER (gst_event_new (GST_EVENT_EOS));
-        have_buf = TRUE;
+
+        send_data = GST_DATA (gst_event_new (GST_EVENT_EOS));
         break;
+
       case DVDNAV_CELL_CHANGE:
         dvdnavsrc_update_streaminfo (src);
         break;
+
       case DVDNAV_NAV_PACKET:
-        dvdnavsrc_update_buttoninfo (src);
+      {
+        pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav);
+
+        /* Check for forced buttons. */
+        if (pci->hli.hl_gi.hli_ss == 1) {
+          GST_LOG_OBJECT (src, "menu ahead");
+          if (pci->hli.hl_gi.fosl_btnn > 0) {
+            GST_DEBUG_OBJECT (src, "forced button");
+            dvdnav_button_select (src->dvdnav, pci, pci->hli.hl_gi.fosl_btnn);
+          }
+        }
+
+        dvdnavsrc_update_highlight (src);
+
+        /* Send a dvd nav packet event. */
+        send_data = GST_DATA (dvdnavsrc_make_dvd_nav_packet_event (src, pci));
+      }
         break;
-      case DVDNAV_WAIT:
-        /* FIXME: supposed to make sure all the data has made 
-         * it to the sinks before skipping the wait
-         */
+
+      case DVDNAV_SPU_CLUT_CHANGE:
+        send_data = GST_DATA (dvdnavsrc_make_clut_change_event (src,
+                (guint *) data));
+        break;
+
+      case DVDNAV_VTS_CHANGE:
+      {
+        dvdnavsrc_set_domain (src);
+
+        send_data = GST_DATA (dvdnavsrc_make_dvd_event (src,
+                "dvd-vts-change", "domain",
+                G_TYPE_INT, (gint) src->domain, NULL));
+      }
+        break;
+
+      case DVDNAV_AUDIO_STREAM_CHANGE:
+      {
+        dvdnav_audio_stream_change_event_t *info =
+            (dvdnav_audio_stream_change_event_t *) data;
+        int phys = info->physical;
+
         dvdnavsrc_print_event (src, data, event, len);
-        dvdnav_wait_skip (src->dvdnav);
+
+        if (phys < 0 || phys > DVDNAVSRC_MAX_AUDIO_STREAMS) {
+          phys = -1;
+        }
+
+        if (phys == src->audio_phys &&
+            dvdnav_get_active_audio_stream (src->dvdnav) == src->audio_log) {
+          /* Audio state hasn't changed. */
+          break;
+        }
+
+        src->audio_phys = phys;
+        src->audio_log = dvdnav_get_active_audio_stream (src->dvdnav);
+        send_data = GST_DATA (dvdnavsrc_make_dvd_event (src,
+                "dvd-audio-stream-change",
+                "physical", G_TYPE_INT, (gint) src->audio_phys,
+                "logical", G_TYPE_INT, (gint) src->audio_log, NULL));
+      }
         break;
-      case DVDNAV_HOP_CHANNEL:
-        /* Indicates a time discontinuity, and the downstream should
-         * flush
-         */
+
+      case DVDNAV_SPU_STREAM_CHANGE:
+      {
+        dvdnav_spu_stream_change_event_t *info =
+            (dvdnav_spu_stream_change_event_t *) data;
+        /* FIXME: Which type of physical stream to use here should
+           be configurable through a property. We take widescreen
+           for the moment. */
+        int phys = info->physical_wide;
+
         dvdnavsrc_print_event (src, data, event, len);
-        buf = GST_BUFFER (gst_event_new (GST_EVENT_DISCONTINUOUS));
-        have_buf = TRUE;
+
+        if (phys < 0 || phys > DVDNAVSRC_MAX_SPU_STREAMS) {
+          phys = -1;
+        }
+
+        if (phys == src->subp_phys &&
+            dvdnav_get_active_spu_stream (src->dvdnav) == src->subp_log) {
+          /* Subpicture state hasn't changed. */
+          break;
+        }
+
+        src->subp_phys = phys;
+        src->subp_log = dvdnav_get_active_spu_stream (src->dvdnav);
+        send_data = GST_DATA (dvdnavsrc_make_dvd_event (src,
+                "dvd-spu-stream-change",
+                "physical", G_TYPE_INT, (gint) phys,
+                "logical", G_TYPE_INT,
+                (gint) dvdnav_get_active_spu_stream (src->dvdnav), NULL));
+      }
         break;
+
       case DVDNAV_HIGHLIGHT:
         dvdnavsrc_print_event (src, data, event, len);
+
+        dvdnavsrc_update_highlight (src);
         break;
 
-        /* SPU_STREAM_CHANGE provides MPEG stream numbers for different
-         * formats of the video, eg letterbox/pan&scan
-         */
-      case DVDNAV_SPU_STREAM_CHANGE:
-        /* AUDIO_STREAM_CHANGE indicates that the user selected an alternate
-         * audio stream (from a menu)
-         */
-      case DVDNAV_AUDIO_STREAM_CHANGE:
-        /* VTS_CHANGE Indicates a change in VTS (Video Title Set) */
-      case DVDNAV_VTS_CHANGE:
-      case DVDNAV_SPU_CLUT_CHANGE:
-      default:
+      case DVDNAV_HOP_CHANNEL:
         dvdnavsrc_print_event (src, data, event, len);
+
+        src->button = 0;
+        src->pause_mode = DVDNAVSRC_PAUSE_OFF;
+        send_data = GST_DATA (gst_event_new_flush ());
+        break;
+
+      default:
+        g_error ("dvdnavsrc: Unknown dvdnav event %d", event);
         break;
     }
   }
-  return GST_DATA (buf);
+
+  gst_pad_push (src->srcpad, send_data);
 }
 
 /* open the file, necessary to go to RUNNING state */
@@ -994,11 +1494,8 @@ dvdnavsrc_open (DVDNavSrc * src)
   g_return_val_if_fail (!dvdnavsrc_is_open (src), FALSE);
   g_return_val_if_fail (src->location != NULL, FALSE);
 
-  if (dvdnav_open (&src->dvdnav, (char *) src->location) != DVDNAV_STATUS_OK) {
-    fprintf (stderr, "dvdnav_open error: %s location: %s\n",
-        dvdnav_err_to_string (src->dvdnav), src->location);
-    return FALSE;
-  }
+  DVDNAV_CALLVAL (dvdnav_open,
+      (&src->dvdnav, (char *) src->location), src, FALSE);
 
   GST_FLAG_SET (src, DVDNAVSRC_OPEN);
 
@@ -1022,26 +1519,13 @@ dvdnavsrc_open (DVDNavSrc * src)
     unsigned char buf[2048];
     int event, buflen = sizeof (buf);
 
-    fprintf (stderr, "+XXX\n");
-    if (dvdnav_get_next_block (src->dvdnav, buf, &event,
-            &buflen) != DVDNAV_STATUS_OK) {
-      fprintf (stderr, "pre seek dvdnav_get_next_block error: %s\n",
-          dvdnav_err_to_string (src->dvdnav));
-      return FALSE;
-    }
+    DVDNAV_CALLVAL (dvdnav_get_next_block,
+        (src->dvdnav, buf, &event, &buflen), src, FALSE);
     dvdnavsrc_print_event (src, buf, event, buflen);
-    /*
-       while (dvdnav_get_next_block(src->dvdnav, buf, &event, &buflen) == DVDNAV_STATUS_OK) {
-       if (event != DVDNAV_BLOCK_OK)
-       dvdnavsrc_print_event (src, buf, event, buflen);
-       }
-     */
-    fprintf (stderr, "pre seek dvdnav_get_next_block error: %s\n",
-        dvdnav_err_to_string (src->dvdnav));
-    fprintf (stderr, "-XXX\n");
 
-    if (!dvdnavsrc_tca_seek (src, src->title, src->chapter, src->angle))
+    if (!dvdnavsrc_tca_seek (src, src->title, src->chapter, src->angle)) {
       return FALSE;
+    }
   }
 
   return TRUE;
@@ -1056,11 +1540,7 @@ dvdnavsrc_close (DVDNavSrc * src)
   g_return_val_if_fail (dvdnavsrc_is_open (src), FALSE);
   g_return_val_if_fail (src->dvdnav != NULL, FALSE);
 
-  if (dvdnav_close (src->dvdnav) != DVDNAV_STATUS_OK) {
-    fprintf (stderr, "dvdnav_close error: %s\n",
-        dvdnav_err_to_string (src->dvdnav));
-    return FALSE;
-  }
+  DVDNAV_CALLVAL (dvdnav_close, (src->dvdnav), src, FALSE);
 
   GST_FLAG_UNSET (src, DVDNAVSRC_OPEN);
 
@@ -1140,21 +1620,23 @@ dvdnav_handle_navigation_event (DVDNavSrc * src, GstEvent * event)
     const char *key = gst_structure_get_string (structure, "key");
 
     g_assert (key != NULL);
-    g_print ("dvdnavsrc got a keypress: %s\n", key);
+    g_print ("dvdnavsrc got a keypress: %s", key);
   } else if (strcmp (event_type, "mouse-move") == 0) {
     double x, y;
 
     gst_structure_get_double (structure, "pointer_x", &x);
     gst_structure_get_double (structure, "pointer_y", &y);
 
-    dvdnavsrc_pointer_select (src, (int) x, (int) y);
+    dvdnav_mouse_select (src->dvdnav,
+        dvdnav_get_current_nav_pci (src->dvdnav), (int) x, (int) y);
   } else if (strcmp (event_type, "mouse-button-release") == 0) {
     double x, y;
 
     gst_structure_get_double (structure, "pointer_x", &x);
     gst_structure_get_double (structure, "pointer_y", &y);
 
-    dvdnavsrc_pointer_activate (src, (int) x, (int) y);
+    dvdnav_mouse_activate (src->dvdnav,
+        dvdnav_get_current_nav_pci (src->dvdnav), (int) x, (int) y);
   }
 
   return TRUE;
@@ -1423,7 +1905,8 @@ dvdnavsrc_convert (GstPad * pad,
           if (*dest_format == sector_format) {
             *dest_value = sector;
           } else if (*dest_format == track_format) {
-            /* if we go past the last sector, make sure to report the last track */
+            /* if we go past the last sector, make sure to report the
+               last track */
             if (sector > src->last_sector)
               *dest_value = cdda_sector_gettrack (src->d, src->last_sector);
             else
@@ -1471,25 +1954,26 @@ dvdnavsrc_query (GstPad * pad, GstQueryType type,
   switch (type) {
     case GST_QUERY_TOTAL:
       if (*format == sector_format) {
-        if (dvdnav_get_position (src->dvdnav, &pos, &len) != DVDNAV_STATUS_OK) {
+        if (dvdnav_get_position (src->dvdnav, &pos, &len)
+            != DVDNAV_STATUS_OK) {
           res = FALSE;
         }
         *value = len;
       } else if (*format == title_format) {
-        if (dvdnav_get_number_of_titles (src->dvdnav,
-                &titles) != DVDNAV_STATUS_OK) {
+        if (dvdnav_get_number_of_titles (src->dvdnav, &titles)
+            != DVDNAV_STATUS_OK) {
           res = FALSE;
         }
         *value = titles;
       } else if (*format == chapter_format) {
-        if (dvdnav_get_number_of_titles (src->dvdnav,
-                &parts) != DVDNAV_STATUS_OK) {
+        if (dvdnav_get_number_of_titles (src->dvdnav, &parts)
+            != DVDNAV_STATUS_OK) {
           res = FALSE;
         }
         *value = parts;
       } else if (*format == angle_format) {
-        if (dvdnav_get_angle_info (src->dvdnav, &angle,
-                &angles) != DVDNAV_STATUS_OK) {
+        if (dvdnav_get_angle_info (src->dvdnav, &angle, &angles)
+            != DVDNAV_STATUS_OK) {
           res = FALSE;
         }
         *value = angles;
@@ -1499,25 +1983,26 @@ dvdnavsrc_query (GstPad * pad, GstQueryType type,
       break;
     case GST_QUERY_POSITION:
       if (*format == sector_format) {
-        if (dvdnav_get_position (src->dvdnav, &pos, &len) != DVDNAV_STATUS_OK) {
+        if (dvdnav_get_position (src->dvdnav, &pos, &len)
+            != DVDNAV_STATUS_OK) {
           res = FALSE;
         }
         *value = pos;
       } else if (*format == title_format) {
-        if (dvdnav_current_title_info (src->dvdnav, &title,
-                &part) != DVDNAV_STATUS_OK) {
+        if (dvdnav_current_title_info (src->dvdnav, &title, &part)
+            != DVDNAV_STATUS_OK) {
           res = FALSE;
         }
         *value = title;
       } else if (*format == chapter_format) {
-        if (dvdnav_current_title_info (src->dvdnav, &title,
-                &part) != DVDNAV_STATUS_OK) {
+        if (dvdnav_current_title_info (src->dvdnav, &title, &part)
+            != DVDNAV_STATUS_OK) {
           res = FALSE;
         }
         *value = part;
       } else if (*format == angle_format) {
-        if (dvdnav_get_angle_info (src->dvdnav, &angle,
-                &angles) != DVDNAV_STATUS_OK) {
+        if (dvdnav_get_angle_info (src->dvdnav, &angle, &angles)
+            != DVDNAV_STATUS_OK) {
           res = FALSE;
         }
         *value = angle;
index 08709c7..a6eb274 100644 (file)
@@ -1,11 +1,22 @@
 
 plugin_LTLIBRARIES = libgstmpegstream.la
 
-libgstmpegstream_la_SOURCES = gstmpegstream.c gstmpegparse.c gstmpegdemux.c gstmpegpacketize.c gstrfc2250enc.c gstmpegclock.c
+libgstmpegstream_la_SOURCES = gstmpegstream.c \
+                             gstmpegparse.c \
+                             gstmpegdemux.c \
+                              gstdvddemux.c \
+                              gstmpegpacketize.c \
+                              gstrfc2250enc.c \
+                              gstmpegclock.c
 libgstmpegstream_la_CFLAGS = $(GST_CFLAGS)
 libgstmpegstream_la_LIBADD =
 libgstmpegstream_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 
-noinst_HEADERS = gstmpegparse.h gstmpegdemux.h gstmpegpacketize.h gstrfc2250enc.h gstmpegclock.h
+noinst_HEADERS = gstmpegparse.h \
+                 gstmpegdemux.h \
+                 gstdvddemux.h \
+                 gstmpegpacketize.h \
+                 gstrfc2250enc.h \
+                 gstmpegclock.h
 
 EXTRA_DIST = README notes
diff --git a/gst/mpegstream/gstdvddemux.c b/gst/mpegstream/gstdvddemux.c
new file mode 100644 (file)
index 0000000..7c5074d
--- /dev/null
@@ -0,0 +1,871 @@
+/* GStreamer
+ * Copyright (C) 2004 Martin Soto <martinsoto@users.sourceforge.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 <string.h>
+
+#include <gstdvddemux.h>
+
+
+GST_DEBUG_CATEGORY_STATIC (gstdvddemux_debug);
+#define GST_CAT_DEFAULT (gstdvddemux_debug)
+
+
+#define PARSE_CLASS(o)  GST_MPEG_PARSE_CLASS (G_OBJECT_GET_CLASS (o))
+#define DEMUX_CLASS(o)  GST_MPEG_DEMUX_CLASS (G_OBJECT_GET_CLASS (o))
+#define CLASS(o)  GST_DVD_DEMUX_CLASS (G_OBJECT_GET_CLASS (o))
+
+
+/* Element factory information */
+static GstElementDetails dvd_demux_details = {
+  "DVD Demuxer",
+  "Codec/Demuxer",
+  "Demultiplexes DVD (VOB) MPEG2 streams",
+  "Martin Soto <soto@informatik.uni-kl.de>"
+};
+
+/* DVDDemux signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  ARG_0,
+  /* FILL ME */
+};
+
+
+/* Define the capabilities separately, to be able to reuse them. */
+
+#define VIDEO_CAPS \
+  GST_STATIC_CAPS ("video/mpeg, " \
+    "mpegversion = (int) { 1, 2 }, " \
+    "systemstream = (boolean) FALSE" \
+  )
+
+#define AUDIO_CAPS \
+  GST_STATIC_CAPS ( \
+    "audio/mpeg, " \
+      "mpegversion = (int) 1;" \
+    "audio/x-raw-int, " \
+      "endianness = (int) BIG_ENDIAN, " \
+      "signed = (boolean) TRUE, " \
+      "width = (int) { 16, 20, 24 }, " \
+      "depth = (int) { 16, 20, 24 }, " \
+      "rate = (int) { 48000, 96000 }, " \
+      "channels = (int) [ 1, 8 ];" \
+    "audio/x-ac3" \
+  )
+
+#define SUBPICTURE_CAPS \
+  GST_STATIC_CAPS ("video/x-dvd-subpicture")
+
+
+static GstStaticPadTemplate cur_video_template =
+GST_STATIC_PAD_TEMPLATE ("current_video",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    VIDEO_CAPS);
+
+static GstStaticPadTemplate audio_template =
+GST_STATIC_PAD_TEMPLATE ("dvd_audio_%02d",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    AUDIO_CAPS);
+
+static GstStaticPadTemplate cur_audio_template =
+GST_STATIC_PAD_TEMPLATE ("current_audio",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    AUDIO_CAPS);
+
+static GstStaticPadTemplate subpicture_template =
+GST_STATIC_PAD_TEMPLATE ("subpicture_%d",
+    GST_PAD_SRC,
+    GST_PAD_SOMETIMES,
+    SUBPICTURE_CAPS);
+
+static GstStaticPadTemplate cur_subpicture_template =
+GST_STATIC_PAD_TEMPLATE ("current_subpicture",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    SUBPICTURE_CAPS);
+
+
+static void gst_dvd_demux_class_init (GstDVDDemuxClass * klass);
+static void gst_dvd_demux_base_init (GstDVDDemuxClass * klass);
+static void gst_dvd_demux_init (GstDVDDemux * dvd_demux);
+
+static void gst_dvd_demux_send_data (GstMPEGParse * mpeg_parse,
+    GstData * data, GstClockTime time);
+
+static void gst_dvd_demux_send_discont
+    (GstMPEGParse * mpeg_parse, GstClockTime time);
+static gboolean gst_dvd_demux_handle_dvd_event
+    (GstDVDDemux * dvd_demux, GstEvent * event);
+
+static GstMPEGStream *gst_dvd_demux_get_audio_stream
+    (GstMPEGDemux * dvd_demux,
+    guint8 stream_nr, gint type, const gpointer info);
+static GstMPEGStream *gst_dvd_demux_get_subpicture_stream
+    (GstMPEGDemux * dvd_demux,
+    guint8 stream_nr, gint type, const gpointer info);
+
+static void gst_dvd_demux_process_private
+    (GstMPEGDemux * mpeg_demux,
+    GstBuffer * buffer,
+    guint stream_nr, GstClockTime timestamp, guint headerlen, guint datalen);
+
+static void gst_dvd_demux_send_subbuffer
+    (GstMPEGDemux * mpeg_demux,
+    GstMPEGStream * outstream,
+    GstBuffer * buffer, GstClockTime timestamp, guint offset, guint size);
+
+static void gst_dvd_demux_set_cur_audio
+    (GstDVDDemux * dvd_demux, gint stream_nr);
+static void gst_dvd_demux_set_cur_subpicture
+    (GstDVDDemux * dvd_demux, gint stream_nr);
+
+
+static GstMPEGDemuxClass *parent_class = NULL;
+
+/*static guint gst_dvd_demux_signals[LAST_SIGNAL] = { 0 };*/
+
+
+GType
+gst_dvd_demux_get_type (void)
+{
+  static GType dvd_demux_type = 0;
+
+  if (!dvd_demux_type) {
+    static const GTypeInfo dvd_demux_info = {
+      sizeof (GstDVDDemuxClass),
+      (GBaseInitFunc) gst_dvd_demux_base_init,
+      NULL,
+      (GClassInitFunc) gst_dvd_demux_class_init,
+      NULL,
+      NULL,
+      sizeof (GstDVDDemux),
+      0,
+      (GInstanceInitFunc) gst_dvd_demux_init,
+    };
+
+    dvd_demux_type = g_type_register_static (GST_TYPE_MPEG_DEMUX,
+        "GstDVDDemux", &dvd_demux_info, 0);
+
+    GST_DEBUG_CATEGORY_INIT (gstdvddemux_debug, "dvddemux", 0,
+        "DVD (VOB) demultiplexer element");
+  }
+
+  return dvd_demux_type;
+}
+
+
+static void
+gst_dvd_demux_base_init (GstDVDDemuxClass * klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+  GstMPEGDemuxClass *demux_class = GST_MPEG_DEMUX_CLASS (klass);
+  GstMPEGParseClass *mpeg_parse_class = (GstMPEGParseClass *) klass;
+
+  mpeg_parse_class->send_data = gst_dvd_demux_send_data;
+
+  demux_class->audio_template = gst_static_pad_template_get (&audio_template);
+
+  klass->cur_video_template = gst_static_pad_template_get (&cur_video_template);
+  klass->cur_audio_template = gst_static_pad_template_get (&cur_audio_template);
+  klass->subpicture_template =
+      gst_static_pad_template_get (&subpicture_template);
+  klass->cur_subpicture_template =
+      gst_static_pad_template_get (&cur_subpicture_template);
+
+  gst_element_class_add_pad_template (element_class,
+      demux_class->audio_template);
+
+  gst_element_class_add_pad_template (element_class, klass->cur_video_template);
+  gst_element_class_add_pad_template (element_class, klass->cur_audio_template);
+  gst_element_class_add_pad_template (element_class,
+      klass->subpicture_template);
+  gst_element_class_add_pad_template (element_class,
+      klass->cur_subpicture_template);
+
+  gst_element_class_set_details (element_class, &dvd_demux_details);
+}
+
+
+static void
+gst_dvd_demux_class_init (GstDVDDemuxClass * klass)
+{
+  GstElementClass *gstelement_class;
+  GstMPEGParseClass *mpeg_parse_class;
+  GstMPEGDemuxClass *mpeg_demux_class;
+
+  parent_class = g_type_class_ref (GST_TYPE_MPEG_DEMUX);
+
+  gstelement_class = (GstElementClass *) klass;
+  mpeg_parse_class = (GstMPEGParseClass *) klass;
+  mpeg_demux_class = (GstMPEGDemuxClass *) klass;
+
+  mpeg_parse_class->send_discont = gst_dvd_demux_send_discont;
+
+  mpeg_demux_class->get_audio_stream = gst_dvd_demux_get_audio_stream;
+  mpeg_demux_class->send_subbuffer = gst_dvd_demux_send_subbuffer;
+  mpeg_demux_class->process_private = gst_dvd_demux_process_private;
+
+  klass->get_subpicture_stream = gst_dvd_demux_get_subpicture_stream;
+}
+
+
+static void
+gst_dvd_demux_init (GstDVDDemux * dvd_demux)
+{
+  GstMPEGDemux *mpeg_demux = GST_MPEG_DEMUX (dvd_demux);
+  GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (dvd_demux);
+  gint i;
+
+  GST_FLAG_SET (dvd_demux, GST_ELEMENT_EVENT_AWARE);
+
+  /* Create the pads for the current streams. */
+  dvd_demux->cur_video =
+      DEMUX_CLASS (dvd_demux)->new_output_pad (mpeg_demux, "current_video",
+      CLASS (dvd_demux)->cur_video_template);
+  dvd_demux->cur_audio =
+      DEMUX_CLASS (dvd_demux)->new_output_pad (mpeg_demux, "current_audio",
+      CLASS (dvd_demux)->cur_audio_template);
+  dvd_demux->cur_subpicture =
+      DEMUX_CLASS (dvd_demux)->new_output_pad (mpeg_demux, "current_subpicture",
+      CLASS (dvd_demux)->cur_subpicture_template);
+
+  dvd_demux->cur_video_nr = 0;
+  dvd_demux->cur_audio_nr = 0;
+  dvd_demux->cur_subpicture_nr = 0;
+
+  /* Start the timestamp sequence in 0. */
+  dvd_demux->last_end_ptm = 0;
+
+  /* Try to prevent the mpegparse infrastructure from doing timestamp
+     adjustment. */
+  mpeg_parse->do_adjust = FALSE;
+  mpeg_parse->adjust = 0;
+
+  dvd_demux->just_flushed = FALSE;
+  dvd_demux->discont_time = GST_CLOCK_TIME_NONE;
+
+  for (i = 0; i < GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS; i++) {
+    dvd_demux->subpicture_stream[i] = NULL;
+  }
+}
+
+
+static void
+gst_dvd_demux_send_data (GstMPEGParse * mpeg_parse, GstData * data,
+    GstClockTime time)
+{
+  GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_parse);
+
+  if (GST_IS_BUFFER (data)) {
+    gst_buffer_unref (GST_BUFFER (data));
+  } else {
+    GstEvent *event = GST_EVENT (data);
+
+    switch (GST_EVENT_TYPE (event)) {
+      case GST_EVENT_ANY:
+        gst_dvd_demux_handle_dvd_event (dvd_demux, event);
+        break;
+
+      case GST_EVENT_FLUSH:
+        GST_DEBUG_OBJECT (dvd_demux, "flush received");
+
+        dvd_demux->just_flushed = TRUE;
+
+        /* Propagate the event normally. */
+        gst_pad_event_default (mpeg_parse->sinkpad, event);
+        break;
+
+      default:
+        gst_pad_event_default (mpeg_parse->sinkpad, event);
+        break;
+    }
+  }
+}
+
+
+static gboolean
+gst_dvd_demux_handle_dvd_event (GstDVDDemux * dvd_demux, GstEvent * event)
+{
+  GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (dvd_demux);
+  GstMPEGDemux *mpeg_demux = GST_MPEG_DEMUX (dvd_demux);
+  GstStructure *structure = event->event_data.structure.structure;
+  const char *event_type = gst_structure_get_string (structure, "event");
+
+  g_return_val_if_fail (event != NULL, FALSE);
+
+#ifndef GST_DISABLE_GST_DEBUG
+  {
+    gchar *text = gst_structure_to_string (structure);
+
+    GST_LOG_OBJECT (dvd_demux, "processing event \"%s\"", text);
+    g_free (text);
+  }
+#endif
+
+  if (strcmp (event_type, "dvd-audio-stream-change") == 0) {
+    gint stream_nr;
+
+    gst_structure_get_int (structure, "physical", &stream_nr);
+    if (stream_nr < -1 || stream_nr >= GST_MPEG_DEMUX_NUM_AUDIO_STREAMS) {
+      GST_ERROR_OBJECT (dvd_demux,
+          "GstDVDDemux: Invalid audio stream %02d", stream_nr);
+      return FALSE;
+    }
+    gst_dvd_demux_set_cur_audio (dvd_demux, stream_nr);
+    gst_event_unref (event);
+  }
+
+  else if (strcmp (event_type, "dvd-spu-stream-change") == 0) {
+    gint stream_nr;
+
+    gst_structure_get_int (structure, "physical", &stream_nr);
+    if (stream_nr < -1 || stream_nr >= GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS) {
+      GST_ERROR_OBJECT (dvd_demux,
+          "GstDVDDemux: Invalid subpicture stream %02d", stream_nr);
+      return FALSE;
+    }
+    gst_dvd_demux_set_cur_subpicture (dvd_demux, stream_nr);
+    gst_event_unref (event);
+  }
+
+  else if (strcmp (event_type, "dvd-nav-packet") == 0) {
+    GstStructure *structure = event->event_data.structure.structure;
+    GstClockTime start_ptm =
+        g_value_get_uint64 (gst_structure_get_value (structure, "start_ptm"));
+    GstClockTime end_ptm =
+        g_value_get_uint64 (gst_structure_get_value (structure, "end_ptm"));
+
+    if (start_ptm != dvd_demux->last_end_ptm) {
+      /* Set the adjust value to gap the discontinuity. */
+      mpeg_demux->adjust += GST_CLOCK_DIFF (dvd_demux->last_end_ptm, start_ptm);
+
+      GST_DEBUG_OBJECT (dvd_demux,
+          "PTM sequence discontinuity: from %0.3fs to "
+          "%0.3fs, new adjust %0.3fs",
+          (double) dvd_demux->last_end_ptm / GST_SECOND,
+          (double) start_ptm / GST_SECOND,
+          (double) mpeg_demux->adjust / GST_SECOND);
+    }
+    dvd_demux->last_end_ptm = end_ptm;
+
+    if (dvd_demux->just_flushed) {
+      /* The pipeline was just flushed, schedule a discontinuity with
+         the next sequence time. We don't do it here to reduce the
+         time gap between the discontinuity and the subsequent data
+         blocks. */
+      dvd_demux->discont_time = start_ptm + mpeg_demux->adjust;
+      dvd_demux->just_flushed = FALSE;
+    }
+
+    gst_event_unref (event);
+  }
+
+  else {
+    if (GST_EVENT_TIMESTAMP (event) != GST_CLOCK_TIME_NONE) {
+      GST_EVENT_TIMESTAMP (event) += mpeg_demux->adjust;
+    }
+    gst_pad_event_default (mpeg_parse->sinkpad, event);
+  }
+
+  return TRUE;
+}
+
+
+static void
+gst_dvd_demux_send_discont (GstMPEGParse * mpeg_parse, GstClockTime time)
+{
+  GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_parse);
+  GstEvent *discont;
+  gint i;
+
+  GST_DEBUG_OBJECT (dvd_demux, "sending discontinuity: %0.3fs",
+      (double) time / GST_SECOND);
+
+  GST_MPEG_PARSE_CLASS (parent_class)->send_discont (mpeg_parse, time);
+
+  for (i = 0; i < GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS; i++) {
+    if (dvd_demux->subpicture_stream[i] &&
+        GST_PAD_IS_USABLE (dvd_demux->subpicture_stream[i]->pad)) {
+      discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME,
+          time, NULL);
+
+      gst_pad_push (dvd_demux->subpicture_stream[i]->pad, GST_DATA (discont));
+    }
+  }
+
+  /* Distribute the event to the "current" pads. */
+  if (GST_PAD_IS_USABLE (dvd_demux->cur_video)) {
+    discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, time, NULL);
+
+    gst_pad_push (dvd_demux->cur_video, GST_DATA (discont));
+  }
+
+  if (GST_PAD_IS_USABLE (dvd_demux->cur_audio)) {
+    discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, time, NULL);
+
+    gst_pad_push (dvd_demux->cur_audio, GST_DATA (discont));
+  }
+
+  if (GST_PAD_IS_USABLE (dvd_demux->cur_subpicture)) {
+    discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, time, NULL);
+
+    gst_pad_push (dvd_demux->cur_subpicture, GST_DATA (discont));
+  }
+}
+
+
+static GstMPEGStream *
+gst_dvd_demux_get_audio_stream (GstMPEGDemux * mpeg_demux,
+    guint8 stream_nr, gint type, const gpointer info)
+{
+  GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_demux);
+  guint8 sample_info = 0;
+  GstMPEGStream *str;
+  GstDVDLPCMStream *lpcm_str = NULL;
+  gchar *name;
+  GstCaps *caps;
+  gint width, rate, channels;
+
+  g_return_val_if_fail (stream_nr < GST_MPEG_DEMUX_NUM_AUDIO_STREAMS, NULL);
+  g_return_val_if_fail (type > GST_MPEG_DEMUX_AUDIO_UNKNOWN &&
+      type < GST_DVD_DEMUX_AUDIO_LAST, NULL);
+
+  if (type < GST_MPEG_DEMUX_AUDIO_LAST) {
+    return parent_class->get_audio_stream (mpeg_demux, stream_nr, type, info);
+  }
+
+  if (type == GST_DVD_DEMUX_AUDIO_LPCM) {
+    sample_info = *((guint8 *) info);
+  }
+
+  str = mpeg_demux->audio_stream[stream_nr];
+
+  if (str == NULL) {
+    if (type != GST_DVD_DEMUX_AUDIO_LPCM) {
+      str = g_new0 (GstMPEGStream, 1);
+    } else {
+      lpcm_str = g_new0 (GstDVDLPCMStream, 1);
+      str = (GstMPEGStream *) lpcm_str;
+    }
+    str->type = GST_MPEG_DEMUX_AUDIO_UNKNOWN;
+
+    name = g_strdup_printf ("audio_%02d", stream_nr);
+    DEMUX_CLASS (dvd_demux)->init_stream (mpeg_demux, type, str, stream_nr,
+        name, DEMUX_CLASS (dvd_demux)->audio_template);
+    g_free (name);
+
+    mpeg_demux->audio_stream[stream_nr] = str;
+  } else {
+    /* This stream may have been created by a derived class, reset the
+       size. */
+    if (type != GST_DVD_DEMUX_AUDIO_LPCM) {
+      str = g_renew (GstMPEGStream, str, 1);
+    } else {
+      lpcm_str = g_renew (GstDVDLPCMStream, str, 1);
+      str = (GstMPEGStream *) lpcm_str;
+    }
+  }
+
+  if (type != str->type ||
+      (type == GST_DVD_DEMUX_AUDIO_LPCM &&
+          sample_info != lpcm_str->sample_info)) {
+    /* We need to set new caps for this pad. */
+    switch (type) {
+      case GST_DVD_DEMUX_AUDIO_LPCM:
+        /* Determine the sample width. */
+        switch (sample_info & 0xC0) {
+          case 0x80:
+            width = 24;
+            break;
+          case 0x40:
+            width = 20;
+            break;
+          default:
+            width = 16;
+            break;
+        }
+
+        /* Determine the rate. */
+        if (sample_info & 0x10) {
+          rate = 96000;
+        } else {
+          rate = 48000;
+        }
+
+        /* Determine the number of channels. */
+        channels = (sample_info & 0x7) + 1;
+
+        caps = gst_caps_new_simple ("audio/x-raw-int",
+            "endianness", G_TYPE_INT, G_BIG_ENDIAN,
+            "signed", G_TYPE_BOOLEAN, TRUE,
+            "width", G_TYPE_INT, width,
+            "depth", G_TYPE_INT, width,
+            "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL);
+
+
+        lpcm_str->sample_info = sample_info;
+        break;
+
+      case GST_DVD_DEMUX_AUDIO_AC3:
+        caps = gst_caps_new_simple ("audio/x-ac3", NULL);
+        break;
+
+      case GST_DVD_DEMUX_AUDIO_DTS:
+        caps = gst_caps_new_simple ("audio/x-dts", NULL);
+        break;
+
+      default:
+        g_return_val_if_reached (NULL);
+        break;
+    }
+
+    gst_pad_set_explicit_caps (str->pad, caps);
+
+    if (str->number == dvd_demux->cur_audio_nr) {
+      /* This is the current audio stream.  Use the same caps. */
+      gst_pad_set_explicit_caps (dvd_demux->cur_audio, gst_caps_copy (caps));
+    }
+
+    str->type = type;
+  }
+
+  return str;
+}
+
+
+static GstMPEGStream *
+gst_dvd_demux_get_subpicture_stream (GstMPEGDemux * mpeg_demux,
+    guint8 stream_nr, gint type, const gpointer info)
+{
+  GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_demux);
+  GstMPEGStream *str;
+  gchar *name;
+  GstCaps *caps;
+
+  g_return_val_if_fail (stream_nr < GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS, NULL);
+  g_return_val_if_fail (type > GST_DVD_DEMUX_SUBP_UNKNOWN &&
+      type < GST_DVD_DEMUX_SUBP_LAST, NULL);
+
+  str = dvd_demux->subpicture_stream[stream_nr];
+
+  if (str == NULL) {
+    str = g_new0 (GstMPEGStream, 1);
+    str->type = GST_DVD_DEMUX_SUBP_UNKNOWN;
+
+    name = g_strdup_printf ("subpicture_%02d", stream_nr);
+    DEMUX_CLASS (dvd_demux)->init_stream (mpeg_demux, type, str, stream_nr,
+        name, CLASS (dvd_demux)->subpicture_template);
+    g_free (name);
+
+    dvd_demux->subpicture_stream[stream_nr] = str;
+  } else {
+    /* This stream may have been created by a derived class, reset the
+       size. */
+    str = g_renew (GstMPEGStream, str, 1);
+  }
+
+  if (str->type != GST_DVD_DEMUX_SUBP_DVD) {
+    /* We need to set new caps for this pad. */
+    caps = gst_caps_new_simple ("video/x-dvd-subpicture", NULL);
+    gst_pad_set_explicit_caps (str->pad, caps);
+
+    if (str->number == dvd_demux->cur_subpicture_nr) {
+      /* This is the current subpicture stream.  Use the same caps. */
+      gst_pad_set_explicit_caps (dvd_demux->cur_subpicture,
+          gst_caps_copy (caps));
+    }
+
+    str->type = GST_DVD_DEMUX_SUBP_DVD;
+  }
+
+  return str;
+}
+
+
+static void
+gst_dvd_demux_process_private (GstMPEGDemux * mpeg_demux,
+    GstBuffer * buffer,
+    guint stream_nr, GstClockTime timestamp, guint headerlen, guint datalen)
+{
+  GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_demux);
+  guint8 *basebuf;
+  guint8 ps_id_code, lpcm_sample_info;
+  GstMPEGStream *outstream = NULL;
+  guint first_access = 0;
+
+  basebuf = GST_BUFFER_DATA (buffer);
+
+  /* Determine the substream number. */
+  ps_id_code = basebuf[headerlen + 4];
+
+  /* In the following, the "first access" refers to the location in a
+     buffer the time stamp is associated to.  DVDs include this
+     information explicitely. */
+
+  switch (stream_nr) {
+    case 0:
+      /* Private stream 1. */
+
+      switch (ps_id_code) {
+        case 0x80...0x87:
+          GST_LOG_OBJECT (dvd_demux,
+              "we have an audio (AC3) packet, track %d", ps_id_code - 0x80);
+          outstream = DEMUX_CLASS (dvd_demux)->get_audio_stream (mpeg_demux,
+              ps_id_code - 0x80, GST_DVD_DEMUX_AUDIO_AC3, NULL);
+
+          /* Determine the position of the "first access".  This
+             should always be the beginning of an AC3 frame. */
+          first_access = *(basebuf + headerlen + 6) * 256 +
+              *(basebuf + headerlen + 7);
+
+          headerlen += 4;
+          datalen -= 4;
+          break;
+
+        case 0xA0...0xA7:
+          GST_LOG_OBJECT (dvd_demux,
+              "we have an audio (LPCM) packet, track %d", ps_id_code - 0xA0);
+          lpcm_sample_info = basebuf[headerlen + 9];
+          outstream = DEMUX_CLASS (dvd_demux)->get_audio_stream (mpeg_demux,
+              ps_id_code - 0xA0, GST_DVD_DEMUX_AUDIO_LPCM, &lpcm_sample_info);
+
+          /* Determine the position of the "first access". */
+          first_access = *(basebuf + headerlen + 6) * 256 +
+              *(basebuf + headerlen + 7);
+
+          /* Get rid of the LPCM header. */
+          headerlen += 7;
+          datalen -= 7;
+          break;
+
+        case 0x20...0x3f:
+          GST_LOG_OBJECT (dvd_demux,
+              "we have a subpicture packet, track %d", ps_id_code - 0x20);
+          outstream = CLASS (dvd_demux)->get_subpicture_stream (mpeg_demux,
+              ps_id_code - 0x20, GST_DVD_DEMUX_SUBP_DVD, NULL);
+
+          headerlen += 1;
+          datalen -= 1;
+          break;
+
+        default:
+          GST_ERROR_OBJECT (dvd_demux,
+              "unknown DVD (private 1) id 0x%02x", ps_id_code);
+          break;
+      }
+      break;
+
+    case 1:
+      /* Private stream 2 */
+
+      switch (ps_id_code) {
+        case 0:
+          GST_LOG_OBJECT (dvd_demux, "we have a PCI nav packet");
+
+          outstream = DEMUX_CLASS (mpeg_demux)->get_private_stream (mpeg_demux,
+              1, GST_MPEG_DEMUX_PRIVATE_UNKNOWN, NULL);
+          break;
+
+        case 1:
+          GST_LOG_OBJECT (dvd_demux, "we have a DSI nav packet");
+
+          outstream = DEMUX_CLASS (mpeg_demux)->get_private_stream (mpeg_demux,
+              1, GST_MPEG_DEMUX_PRIVATE_UNKNOWN, NULL);
+          break;
+
+        default:
+          GST_ERROR_OBJECT (dvd_demux,
+              "unknown DVD (private 1) id 0x%02x", ps_id_code);
+          break;
+      }
+      break;
+
+    default:
+      g_return_if_reached ();
+      break;
+  }
+
+  if (outstream == NULL) {
+    return;
+  }
+
+  if (timestamp != GST_CLOCK_TIME_NONE && first_access > 1) {
+    /* We have a first access location.  Since GStreamer doesn't have
+       a means to associate a timestamp to the middle of a buffer, we
+       send two separate buffers and put the timestamp in the second
+       one. */
+    DEMUX_CLASS (dvd_demux)->send_subbuffer (mpeg_demux, outstream,
+        buffer, GST_CLOCK_TIME_NONE, headerlen + 4, first_access - 1);
+    DEMUX_CLASS (dvd_demux)->send_subbuffer (mpeg_demux, outstream,
+        buffer, timestamp,
+        headerlen + 3 + first_access, datalen - (first_access - 1));
+  } else {
+    DEMUX_CLASS (dvd_demux)->send_subbuffer (mpeg_demux, outstream,
+        buffer, timestamp, headerlen + 4, datalen);
+  }
+}
+
+
+static void
+gst_dvd_demux_send_subbuffer (GstMPEGDemux * mpeg_demux,
+    GstMPEGStream * outstream, GstBuffer * buffer,
+    GstClockTime timestamp, guint offset, guint size)
+{
+  GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (mpeg_demux);
+  GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_demux);
+  GstPad *outpad;
+  gint cur_nr;
+
+  /* If there's a pending discontinuity, send it now. The idea is to
+     minimize the time interval between the discontinuity and the data
+     buffers following it. */
+  if (dvd_demux->discont_time != GST_CLOCK_TIME_NONE) {
+    PARSE_CLASS (mpeg_demux)->send_discont (mpeg_parse,
+        dvd_demux->discont_time - 200 * GST_MSECOND);
+    dvd_demux->discont_time = GST_CLOCK_TIME_NONE;
+  }
+
+  /* You never know what happens to a buffer when you send it.  Just
+     in case, we keep a reference to the buffer during the execution
+     of this function. */
+  gst_buffer_ref (buffer);
+
+  /* Send the buffer to the standard output pad. */
+  parent_class->send_subbuffer (mpeg_demux, outstream, buffer,
+      timestamp, offset, size);
+
+  /* Determine the current output pad and stream number for the given
+     type of stream. */
+  switch (GST_MPEG_DEMUX_STREAM_KIND (outstream->type)) {
+    case GST_MPEG_DEMUX_STREAM_VIDEO:
+      outpad = dvd_demux->cur_video;
+      cur_nr = dvd_demux->cur_video_nr;
+      break;
+    case GST_MPEG_DEMUX_STREAM_AUDIO:
+      outpad = dvd_demux->cur_audio;
+      cur_nr = dvd_demux->cur_audio_nr;
+      break;
+    case GST_MPEG_DEMUX_STREAM_PRIVATE:
+      outpad = NULL;
+      cur_nr = 0;
+      break;
+    case GST_DVD_DEMUX_STREAM_SUBPICTURE:
+      outpad = dvd_demux->cur_subpicture;
+      cur_nr = dvd_demux->cur_subpicture_nr;
+      break;
+    default:
+      g_return_if_reached ();
+      break;
+  }
+
+  if (outpad != NULL && cur_nr == outstream->number) {
+    GstBuffer *outbuf;
+
+    /* We have a packet of the current stream. Send it to the
+       corresponding pad as well. */
+    outbuf = gst_buffer_create_sub (buffer, offset, size);
+
+    GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
+    GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buffer) + offset;
+
+    gst_pad_push (outpad, GST_DATA (outbuf));
+  }
+
+  gst_buffer_unref (buffer);
+}
+
+
+static void
+gst_dvd_demux_set_cur_audio (GstDVDDemux * dvd_demux, gint stream_nr)
+{
+  GstMPEGDemux *mpeg_demux = GST_MPEG_DEMUX (dvd_demux);
+  GstMPEGStream *str;
+  const GstCaps *caps;
+
+  g_return_if_fail (stream_nr >= -1 &&
+      stream_nr < GST_MPEG_DEMUX_NUM_AUDIO_STREAMS);
+
+  GST_DEBUG_OBJECT (dvd_demux, "changing current audio to %02d", stream_nr);
+
+  dvd_demux->cur_audio_nr = stream_nr;
+
+  if (stream_nr == -1) {
+    return;
+  }
+
+  str = mpeg_demux->audio_stream[stream_nr];
+  if (str != NULL) {
+    /* (Re)set the caps in the "current" pad. */
+    caps = gst_pad_get_negotiated_caps (str->pad);
+    if (caps != NULL) {
+      gst_pad_set_explicit_caps (dvd_demux->cur_audio, caps);
+    }
+  }
+}
+
+
+static void
+gst_dvd_demux_set_cur_subpicture (GstDVDDemux * dvd_demux, gint stream_nr)
+{
+  GstMPEGStream *str;
+  const GstCaps *caps;
+
+  g_return_if_fail (stream_nr >= -1 &&
+      stream_nr < GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS);
+
+  GST_DEBUG_OBJECT (dvd_demux, "changing current subpicture to %02d",
+      stream_nr);
+
+  dvd_demux->cur_subpicture_nr = stream_nr;
+
+  if (stream_nr == -1) {
+    return;
+  }
+
+  str = dvd_demux->subpicture_stream[stream_nr];
+  if (str != NULL) {
+    /* (Re)set the caps in the "current" pad. */
+    caps = gst_pad_get_negotiated_caps (str->pad);
+    if (caps != NULL) {
+      gst_pad_set_explicit_caps (dvd_demux->cur_subpicture, caps);
+    }
+  }
+}
+
+
+gboolean
+gst_dvd_demux_plugin_init (GstPlugin * plugin)
+{
+  return gst_element_register (plugin, "dvddemux",
+      GST_RANK_PRIMARY, GST_TYPE_DVD_DEMUX);
+}
diff --git a/gst/mpegstream/gstdvddemux.h b/gst/mpegstream/gstdvddemux.h
new file mode 100644 (file)
index 0000000..7ce267f
--- /dev/null
@@ -0,0 +1,143 @@
+/* GStreamer
+ * Copyright (C) 2004 Martin Soto <martinsoto@users.sourceforge.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 __DVD_DEMUX_H__
+#define __DVD_DEMUX_H__
+
+
+#include <gst/gst.h>
+#include "gstmpegdemux.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GST_TYPE_DVD_DEMUX \
+  (gst_dvd_demux_get_type())
+#define GST_DVD_DEMUX(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DVD_DEMUX,GstDVDDemux))
+#define GST_DVD_DEMUX_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DVD_DEMUX,GstDVDDemuxClass))
+#define GST_IS_DVD_DEMUX(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DVD_DEMUX))
+#define GST_IS_DVD_DEMUX_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DVD_DEMUX))
+
+
+/* Supported kinds of streams in addition to what mpegdemux already
+   does. */
+enum {
+  GST_DVD_DEMUX_STREAM_SUBPICTURE = GST_MPEG_DEMUX_STREAM_LAST,
+  GST_DVD_DEMUX_STREAM_LAST,
+};
+
+/* Supported number of streams. */
+#define GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS   32
+
+
+typedef struct _GstDVDLPCMStream GstDVDLPCMStream ;
+typedef struct _GstDVDDemux GstDVDDemux;
+typedef struct _GstDVDDemuxClass GstDVDDemuxClass;
+
+
+/* Additional recognized audio types. */
+enum {
+  GST_DVD_DEMUX_AUDIO_LPCM = GST_MPEG_DEMUX_AUDIO_LAST,
+  GST_DVD_DEMUX_AUDIO_AC3,
+  GST_DVD_DEMUX_AUDIO_DTS,
+  GST_DVD_DEMUX_AUDIO_LAST,
+};
+
+
+/* The recognized subpicture types. */
+enum {
+  GST_DVD_DEMUX_SUBP_UNKNOWN =
+    GST_MPEG_DEMUX_STREAM_TYPE (GST_DVD_DEMUX_STREAM_SUBPICTURE, 1),
+  GST_DVD_DEMUX_SUBP_DVD,
+  GST_DVD_DEMUX_SUBP_LAST,
+};
+
+
+/* Extended structure to hold additional information for linear PCM
+   streams. */
+struct _GstDVDLPCMStream {
+  GstMPEGStream         parent;
+  guint8        sample_info;   /* The type of linear PCM samples
+                                   associated to this stream. The
+                                   values are bit fields with the same
+                                   format of the sample_info field in
+                                   the linear PCM header. */
+};
+
+
+struct _GstDVDDemux {
+  GstMPEGDemux  parent;
+
+  GstPad *cur_video;           /* Current video stream pad. */
+  GstPad *cur_audio;           /* Current audio stream pad. */
+  GstPad *cur_subpicture;      /* Current subpicture stream pad. */
+
+  gint cur_video_nr;           /* Current video stream number. */
+  gint cur_audio_nr;           /* Current audio stream number. */
+  gint cur_subpicture_nr;      /* Current subpicture stream number. */
+
+  GstClockTime last_end_ptm;   /* End presentation time of the las nav packet
+                                   event received. */
+
+  gboolean just_flushed;       /* The element just received a flush event. */
+  GstClockTime discont_time;   /* If different from GST_CLOCK_TIME_NONE, a
+                                   discontinuous event should be sent with the
+                                   given time, before sending the next dara
+                                   block.. */
+
+  GstMPEGStream *subpicture_stream[GST_DVD_DEMUX_NUM_SUBPICTURE_STREAMS];
+                               /* Subpicture output streams. */
+};
+
+
+struct _GstDVDDemuxClass {
+  GstMPEGDemuxClass parent_class;
+
+  GstPadTemplate *cur_video_template;
+  GstPadTemplate *cur_audio_template;
+  GstPadTemplate *subpicture_template;
+  GstPadTemplate *cur_subpicture_template;
+
+  GstMPEGStream *
+               (*get_subpicture_stream)(GstMPEGDemux *mpeg_demux,
+                                         guint8 stream_nr,
+                                         gint type,
+                                         const gpointer info);
+};
+
+
+GType          gst_dvd_demux_get_type          (void);
+
+gboolean       gst_dvd_demux_plugin_init       (GstPlugin *plugin);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __DVD_DEMUX_H__ */
index 0a5b2f1..0c612b1 100644 (file)
  */
 
 
-/*#define GST_DEBUG_ENABLED*/
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 #include <gstmpegdemux.h>
 
+
+GST_DEBUG_CATEGORY_STATIC (gstmpegdemux_debug);
+#define GST_CAT_DEFAULT (gstmpegdemux_debug)
+
 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_SEEK);
 
+
+#define CLASS(o)  GST_MPEG_DEMUX_CLASS (G_OBJECT_GET_CLASS (o))
+
 /* elementfactory information */
 static GstElementDetails mpeg_demux_details = {
   "MPEG Demuxer",
@@ -49,22 +55,14 @@ enum
   /* FILL ME */
 };
 
-static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
     GST_STATIC_CAPS ("video/mpeg, "
         "mpegversion = (int) { 1, 2 }, " "systemstream = (boolean) TRUE")
     );
 
-static GstStaticPadTemplate audio_factory =
-GST_STATIC_PAD_TEMPLATE ("audio_%02d",
-    GST_PAD_SRC,
-    GST_PAD_SOMETIMES,
-    GST_STATIC_CAPS ("audio/mpeg, "
-        "mpegversion = (int) 1, " "layer = (int) { 1, 2 }")
-    );
-
-static GstStaticPadTemplate video_src_factory =
+static GstStaticPadTemplate video_template =
 GST_STATIC_PAD_TEMPLATE ("video_%02d",
     GST_PAD_SRC,
     GST_PAD_SOMETIMES,
@@ -72,41 +70,43 @@ GST_STATIC_PAD_TEMPLATE ("video_%02d",
         "mpegversion = (int) { 1, 2 }, " "systemstream = (boolean) FALSE")
     );
 
-static GstStaticPadTemplate private1_factory =
-GST_STATIC_PAD_TEMPLATE ("private_stream_1_%02d",
-    GST_PAD_SRC,
-    GST_PAD_SOMETIMES,
-    GST_STATIC_CAPS ("audio/x-ac3")
-    );
-
-static GstStaticPadTemplate private2_factory =
-GST_STATIC_PAD_TEMPLATE ("private_stream_2",
-    GST_PAD_SRC,
-    GST_PAD_SOMETIMES,
-    GST_STATIC_CAPS_ANY);
-
-static GstStaticPadTemplate pcm_factory =
-GST_STATIC_PAD_TEMPLATE ("pcm_stream_%02d",
+static GstStaticPadTemplate audio_template =
+GST_STATIC_PAD_TEMPLATE ("audio_%02d",
     GST_PAD_SRC,
     GST_PAD_SOMETIMES,
-    GST_STATIC_CAPS ("audio/x-raw-int, "
-        "endianness = (int) BIG_ENDIAN, "
-        "signed = (boolean) TRUE, "
-        "width = (int) { 16, 20, 24 }, "
-        "depth = (int) { 16, 20, 24 }, "
-        "rate = (int) { 48000, 96000 }, " "channels = (int) [ 1, 8 ]")
+    GST_STATIC_CAPS ("audio/mpeg, " "mpegversion = (int) 1"
+        /* FIXME "layer = (int) { 1, 2 }" */
+    )
     );
 
-static GstStaticPadTemplate subtitle_factory =
-GST_STATIC_PAD_TEMPLATE ("subtitle_stream_%d",
+static GstStaticPadTemplate private_template =
+GST_STATIC_PAD_TEMPLATE ("private_%d",
     GST_PAD_SRC,
     GST_PAD_SOMETIMES,
     GST_STATIC_CAPS_ANY);
 
-static void gst_mpeg_demux_class_init (GstMPEGDemuxClass * klass);
 static void gst_mpeg_demux_base_init (GstMPEGDemuxClass * klass);
+static void gst_mpeg_demux_class_init (GstMPEGDemuxClass * klass);
 static void gst_mpeg_demux_init (GstMPEGDemux * mpeg_demux);
 
+static void gst_mpeg_demux_send_data (GstMPEGParse * mpeg_parse,
+    GstData * data, GstClockTime time);
+static void gst_mpeg_demux_send_discont (GstMPEGParse * mpeg_parse,
+    GstClockTime time);
+
+static GstPad *gst_mpeg_demux_new_output_pad (GstMPEGDemux * mpeg_demux,
+    const gchar * name, GstPadTemplate * temp);
+static void gst_mpeg_demux_init_stream (GstMPEGDemux * mpeg_demux,
+    gint type,
+    GstMPEGStream * str,
+    gint number, const gchar * name, GstPadTemplate * temp);
+static GstMPEGStream *gst_mpeg_demux_get_video_stream (GstMPEGDemux *
+    mpeg_demux, guint8 stream_nr, gint type, const gpointer info);
+static GstMPEGStream *gst_mpeg_demux_get_audio_stream (GstMPEGDemux *
+    mpeg_demux, guint8 stream_nr, gint type, const gpointer info);
+static GstMPEGStream *gst_mpeg_demux_get_private_stream (GstMPEGDemux *
+    mpeg_demux, guint8 stream_nr, gint type, const gpointer info);
+
 static gboolean gst_mpeg_demux_parse_packhead (GstMPEGParse * mpeg_parse,
     GstBuffer * buffer);
 static gboolean gst_mpeg_demux_parse_syshead (GstMPEGParse * mpeg_parse,
@@ -115,29 +115,34 @@ static gboolean gst_mpeg_demux_parse_packet (GstMPEGParse * mpeg_parse,
     GstBuffer * buffer);
 static gboolean gst_mpeg_demux_parse_pes (GstMPEGParse * mpeg_parse,
     GstBuffer * buffer);
-static void gst_mpeg_demux_send_data (GstMPEGParse * mpeg_parse, GstData * data,
-    GstClockTime time);
 
-static void gst_mpeg_demux_lpcm_set_caps (GstPad * pad, guint8 sample_info);
-static void gst_mpeg_demux_dvd_audio_clear (GstMPEGDemux * mpeg_demux,
-    int channel);
+static void gst_mpeg_demux_send_subbuffer (GstMPEGDemux * mpeg_demux,
+    GstMPEGStream * outstream, GstBuffer * buffer,
+    GstClockTime timestamp, guint offset, guint size);
+static void gst_mpeg_demux_process_private (GstMPEGDemux * mpeg_demux,
+    GstBuffer * buffer,
+    guint stream_nr, GstClockTime timestamp, guint headerlen, guint datalen);
+
+const GstFormat *gst_mpeg_demux_get_src_formats (GstPad * pad);
+
+static gboolean index_seek (GstPad * pad, GstEvent * event, gint64 * offset);
+static gboolean normal_seek (GstPad * pad, GstEvent * event, gint64 * offset);
 
-static void gst_mpeg_demux_handle_discont (GstMPEGParse * mpeg_parse);
 static gboolean gst_mpeg_demux_handle_src_event (GstPad * pad,
     GstEvent * event);
 
-const GstFormat *gst_mpeg_demux_get_src_formats (GstPad * pad);
+static GstElementStateReturn gst_mpeg_demux_change_state (GstElement * element);
+
 static void gst_mpeg_demux_set_index (GstElement * element, GstIndex * index);
 static GstIndex *gst_mpeg_demux_get_index (GstElement * element);
 
-static GstElementStateReturn gst_mpeg_demux_change_state (GstElement * element);
 
 static GstMPEGParseClass *parent_class = NULL;
 
 /*static guint gst_mpeg_demux_signals[LAST_SIGNAL] = { 0 };*/
 
 GType
-mpeg_demux_get_type (void)
+gst_mpeg_demux_get_type (void)
 {
   static GType mpeg_demux_type = 0;
 
@@ -157,7 +162,11 @@ mpeg_demux_get_type (void)
     mpeg_demux_type =
         g_type_register_static (GST_TYPE_MPEG_PARSE, "GstMPEGDemux",
         &mpeg_demux_info, 0);
+
+    GST_DEBUG_CATEGORY_INIT (gstmpegdemux_debug, "mpegdemux", 0,
+        "MPEG demultiplexer element");
   }
+
   return mpeg_demux_type;
 }
 
@@ -167,33 +176,29 @@ gst_mpeg_demux_base_init (GstMPEGDemuxClass * klass)
   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
 
   gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&sink_factory));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&video_src_factory));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&private1_factory));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&private2_factory));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&pcm_factory));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&subtitle_factory));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&audio_factory));
-  gst_element_class_set_details (element_class, &mpeg_demux_details);
+      gst_static_pad_template_get (&sink_template));
 
+  klass->video_template = gst_static_pad_template_get (&video_template);
+  klass->audio_template = gst_static_pad_template_get (&audio_template);
+  klass->private_template = gst_static_pad_template_get (&private_template);
+
+  gst_element_class_add_pad_template (element_class, klass->video_template);
+  gst_element_class_add_pad_template (element_class, klass->audio_template);
+  gst_element_class_add_pad_template (element_class, klass->private_template);
+
+  gst_element_class_set_details (element_class, &mpeg_demux_details);
 }
 
 static void
 gst_mpeg_demux_class_init (GstMPEGDemuxClass * klass)
 {
-  GstMPEGParseClass *mpeg_parse_class;
   GstElementClass *gstelement_class;
+  GstMPEGParseClass *mpeg_parse_class;
 
   parent_class = g_type_class_ref (GST_TYPE_MPEG_PARSE);
 
-  mpeg_parse_class = (GstMPEGParseClass *) klass;
   gstelement_class = (GstElementClass *) klass;
+  mpeg_parse_class = (GstMPEGParseClass *) klass;
 
   gstelement_class->change_state = gst_mpeg_demux_change_state;
   gstelement_class->set_index = gst_mpeg_demux_set_index;
@@ -204,8 +209,15 @@ gst_mpeg_demux_class_init (GstMPEGDemuxClass * klass)
   mpeg_parse_class->parse_packet = gst_mpeg_demux_parse_packet;
   mpeg_parse_class->parse_pes = gst_mpeg_demux_parse_pes;
   mpeg_parse_class->send_data = gst_mpeg_demux_send_data;
-  mpeg_parse_class->handle_discont = gst_mpeg_demux_handle_discont;
-
+  mpeg_parse_class->send_discont = gst_mpeg_demux_send_discont;
+
+  klass->new_output_pad = gst_mpeg_demux_new_output_pad;
+  klass->init_stream = gst_mpeg_demux_init_stream;
+  klass->get_video_stream = gst_mpeg_demux_get_video_stream;
+  klass->get_audio_stream = gst_mpeg_demux_get_audio_stream;
+  klass->get_private_stream = gst_mpeg_demux_get_private_stream;
+  klass->send_subbuffer = gst_mpeg_demux_send_subbuffer;
+  klass->process_private = gst_mpeg_demux_process_private;
 }
 
 static void
@@ -216,40 +228,25 @@ gst_mpeg_demux_init (GstMPEGDemux * mpeg_demux)
 
   gst_element_remove_pad (GST_ELEMENT (mpeg_parse), mpeg_parse->sinkpad);
   mpeg_parse->sinkpad =
-      gst_pad_new_from_template (gst_static_pad_template_get (&sink_factory),
+      gst_pad_new_from_template (gst_static_pad_template_get (&sink_template),
       "sink");
   gst_element_add_pad (GST_ELEMENT (mpeg_parse), mpeg_parse->sinkpad);
   gst_element_remove_pad (GST_ELEMENT (mpeg_parse), mpeg_parse->srcpad);
 
   /* i think everything is already zero'd, but oh well */
-  for (i = 0; i < NUM_PRIVATE_1_STREAMS; i++) {
-    mpeg_demux->private_1_stream[i] = NULL;
-  }
-  for (i = 0; i < NUM_PCM_STREAMS; i++) {
-    mpeg_demux->pcm_stream[i] = NULL;
-  }
-  for (i = 0; i < NUM_SUBTITLE_STREAMS; i++) {
-    mpeg_demux->subtitle_stream[i] = NULL;
-  }
-  mpeg_demux->private_2_stream = NULL;
-  for (i = 0; i < NUM_VIDEO_STREAMS; i++) {
+  for (i = 0; i < GST_MPEG_DEMUX_NUM_VIDEO_STREAMS; i++) {
     mpeg_demux->video_stream[i] = NULL;
   }
-  for (i = 0; i < NUM_AUDIO_STREAMS; i++) {
+  for (i = 0; i < GST_MPEG_DEMUX_NUM_AUDIO_STREAMS; i++) {
     mpeg_demux->audio_stream[i] = NULL;
   }
+  for (i = 0; i < GST_MPEG_DEMUX_NUM_PRIVATE_STREAMS; i++) {
+    mpeg_demux->private_stream[i] = NULL;
+  }
 
-  GST_FLAG_SET (mpeg_demux, GST_ELEMENT_EVENT_AWARE);
-}
-
-static GstMPEGStream *
-gst_mpeg_demux_new_stream (void)
-{
-  GstMPEGStream *stream;
-
-  stream = g_new0 (GstMPEGStream, 1);
+  mpeg_demux->adjust = 0;
 
-  return stream;
+  GST_FLAG_SET (mpeg_demux, GST_ELEMENT_EVENT_AWARE);
 }
 
 static void
@@ -270,50 +267,45 @@ gst_mpeg_demux_send_data (GstMPEGParse * mpeg_parse, GstData * data,
     }
   }
 }
+
 static void
-gst_mpeg_demux_handle_discont (GstMPEGParse * mpeg_parse)
+gst_mpeg_demux_send_discont (GstMPEGParse * mpeg_parse, GstClockTime time)
 {
   GstMPEGDemux *mpeg_demux = GST_MPEG_DEMUX (mpeg_parse);
-  gint64 current_time = MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr);
+  GstEvent *discont;
   gint i;
 
-  GST_DEBUG ("discont %" G_GUINT64_FORMAT "\n", current_time);
+  GST_DEBUG_OBJECT (mpeg_demux, "discont %" G_GUINT64_FORMAT, time);
 
-  for (i = 0; i < NUM_VIDEO_STREAMS; i++) {
+  for (i = 0; i < GST_MPEG_DEMUX_NUM_VIDEO_STREAMS; i++) {
     if (mpeg_demux->video_stream[i] &&
         GST_PAD_IS_USABLE (mpeg_demux->video_stream[i]->pad)) {
-      GstEvent *discont;
-
       discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME,
-          current_time, NULL);
+          time, NULL);
 
       gst_pad_push (mpeg_demux->video_stream[i]->pad, GST_DATA (discont));
     }
+  }
+
+  for (i = 0; i < GST_MPEG_DEMUX_NUM_AUDIO_STREAMS; i++) {
     if (mpeg_demux->audio_stream[i] &&
         GST_PAD_IS_USABLE (mpeg_demux->audio_stream[i]->pad)) {
-      GstEvent *discont;
-
       discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME,
-          current_time, NULL);
+          time, NULL);
 
       gst_pad_push (mpeg_demux->audio_stream[i]->pad, GST_DATA (discont));
     }
   }
-}
-
-static gboolean
-gst_mpeg_demux_parse_packhead (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
-{
-  guint8 *buf;
-
-  parent_class->parse_packhead (mpeg_parse, buffer);
-
-  GST_DEBUG ("in parse_packhead");
 
-  buf = GST_BUFFER_DATA (buffer);
-  /* do something usefull here */
+  for (i = 0; i < GST_MPEG_DEMUX_NUM_PRIVATE_STREAMS; i++) {
+    if (mpeg_demux->private_stream[i] &&
+        GST_PAD_IS_USABLE (mpeg_demux->private_stream[i]->pad)) {
+      discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME,
+          time, NULL);
 
-  return TRUE;
+      gst_pad_push (mpeg_demux->private_stream[i]->pad, GST_DATA (discont));
+    }
+  }
 }
 
 static gint
@@ -332,6 +324,171 @@ _demux_get_writer_id (GstIndex * index, GstPad * pad)
   }
 }
 
+static GstPad *
+gst_mpeg_demux_new_output_pad (GstMPEGDemux * mpeg_demux,
+    const gchar * name, GstPadTemplate * temp)
+{
+  GstPad *pad;
+
+  pad = gst_pad_new_from_template (temp, name);
+  gst_element_add_pad (GST_ELEMENT (mpeg_demux), pad);
+
+  gst_pad_set_formats_function (pad, gst_mpeg_demux_get_src_formats);
+  gst_pad_set_convert_function (pad, gst_mpeg_parse_convert_src);
+  gst_pad_set_event_mask_function (pad, gst_mpeg_parse_get_src_event_masks);
+  gst_pad_set_event_function (pad, gst_mpeg_demux_handle_src_event);
+  gst_pad_set_query_type_function (pad, gst_mpeg_parse_get_src_query_types);
+  gst_pad_set_query_function (pad, gst_mpeg_parse_handle_src_query);
+  gst_pad_use_explicit_caps (pad);
+
+  return pad;
+}
+
+static void
+gst_mpeg_demux_init_stream (GstMPEGDemux * mpeg_demux,
+    gint type,
+    GstMPEGStream * str, gint number, const gchar * name, GstPadTemplate * temp)
+{
+  str->type = type;
+  str->number = number;
+
+  str->pad = CLASS (mpeg_demux)->new_output_pad (mpeg_demux, name, temp);
+  gst_pad_set_element_private (str->pad, str);
+
+  if (mpeg_demux->index) {
+    str->index_id = _demux_get_writer_id (mpeg_demux->index, str->pad);
+  }
+}
+
+static GstMPEGStream *
+gst_mpeg_demux_get_video_stream (GstMPEGDemux * mpeg_demux,
+    guint8 stream_nr, gint type, const gpointer info)
+{
+  gint mpeg_version = *((gint *) info);
+  GstMPEGStream *str;
+  GstMPEGVideoStream *video_str;
+  gchar *name;
+  GstCaps *caps;
+
+  g_return_val_if_fail (stream_nr < GST_MPEG_DEMUX_NUM_VIDEO_STREAMS, NULL);
+  g_return_val_if_fail (type > GST_MPEG_DEMUX_VIDEO_UNKNOWN &&
+      type < GST_MPEG_DEMUX_VIDEO_LAST, NULL);
+
+  str = mpeg_demux->video_stream[stream_nr];
+
+  if (str == NULL) {
+    video_str = g_new0 (GstMPEGVideoStream, 1);
+    str = (GstMPEGStream *) video_str;
+    str->type = GST_MPEG_DEMUX_VIDEO_UNKNOWN;
+
+    name = g_strdup_printf ("video_%02d", stream_nr);
+    CLASS (mpeg_demux)->init_stream (mpeg_demux, type, str, stream_nr, name,
+        CLASS (mpeg_demux)->video_template);
+    g_free (name);
+
+    mpeg_demux->video_stream[stream_nr] = str;
+  } else {
+    /* This stream may have been created by a derived class, reset the
+       size. */
+    video_str = g_renew (GstMPEGVideoStream, str, 1);
+    str = (GstMPEGStream *) video_str;
+  }
+
+  if (str->type != GST_MPEG_DEMUX_VIDEO_MPEG ||
+      video_str->mpeg_version != mpeg_version) {
+    /* We need to set new caps for this pad. */
+    caps = gst_caps_new_simple ("video/mpeg",
+        "mpegversion", G_TYPE_INT, mpeg_version,
+        "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
+    gst_pad_set_explicit_caps (str->pad, caps);
+
+    /* Store the current values. */
+    str->type = GST_MPEG_DEMUX_VIDEO_MPEG;
+    video_str->mpeg_version = mpeg_version;
+  }
+
+  return str;
+}
+
+static GstMPEGStream *
+gst_mpeg_demux_get_audio_stream (GstMPEGDemux * mpeg_demux,
+    guint8 stream_nr, gint type, const gpointer info)
+{
+  GstMPEGStream *str;
+  gchar *name;
+  GstCaps *caps;
+
+  g_return_val_if_fail (stream_nr < GST_MPEG_DEMUX_NUM_AUDIO_STREAMS, NULL);
+  g_return_val_if_fail (type > GST_MPEG_DEMUX_AUDIO_UNKNOWN &&
+      type < GST_MPEG_DEMUX_AUDIO_LAST, NULL);
+
+  str = mpeg_demux->audio_stream[stream_nr];
+
+  if (str == NULL) {
+    str = g_new0 (GstMPEGStream, 1);
+    str->type = GST_MPEG_DEMUX_AUDIO_MPEG;
+
+    name = g_strdup_printf ("audio_%02d", stream_nr);
+    CLASS (mpeg_demux)->init_stream (mpeg_demux, type, str, stream_nr, name,
+        CLASS (mpeg_demux)->audio_template);
+    g_free (name);
+
+    mpeg_demux->audio_stream[stream_nr] = str;
+  } else {
+    /* This stream may have been created by a derived class, reset the
+       size. */
+    str = g_renew (GstMPEGStream, str, 1);
+  }
+
+  if (str->type != GST_MPEG_DEMUX_AUDIO_MPEG) {
+    /* We need to set new caps for this pad. */
+    caps = gst_caps_new_simple ("audio/mpeg",
+        "mpegversion", G_TYPE_INT, 1, NULL);
+    gst_pad_set_explicit_caps (str->pad, caps);
+
+    str->type = GST_MPEG_DEMUX_AUDIO_MPEG;
+  }
+
+  return str;
+}
+
+static GstMPEGStream *
+gst_mpeg_demux_get_private_stream (GstMPEGDemux * mpeg_demux,
+    guint8 stream_nr, gint type, const gpointer info)
+{
+  GstMPEGStream *str;
+  gchar *name;
+
+  g_return_val_if_fail (stream_nr < GST_MPEG_DEMUX_NUM_PRIVATE_STREAMS, NULL);
+
+  str = mpeg_demux->private_stream[stream_nr];
+
+  if (str == NULL) {
+    name = g_strdup_printf ("private_%d", stream_nr + 1);
+    str = g_new0 (GstMPEGStream, 1);
+    CLASS (mpeg_demux)->init_stream (mpeg_demux, type, str, stream_nr, name,
+        CLASS (mpeg_demux)->private_template);
+    g_free (name);
+
+    mpeg_demux->private_stream[stream_nr] = str;
+  }
+
+  return str;
+}
+
+static gboolean
+gst_mpeg_demux_parse_packhead (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
+{
+  guint8 *buf;
+
+  parent_class->parse_packhead (mpeg_parse, buffer);
+
+  buf = GST_BUFFER_DATA (buffer);
+  /* do something useful here */
+
+  return TRUE;
+}
+
 static gboolean
 gst_mpeg_demux_parse_syshead (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
 {
@@ -339,13 +496,11 @@ gst_mpeg_demux_parse_syshead (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
   guint16 header_length;
   guchar *buf;
 
-  GST_DEBUG ("in parse_syshead");
-
   buf = GST_BUFFER_DATA (buffer);
   buf += 4;
 
   header_length = GUINT16_FROM_BE (*(guint16 *) buf);
-  GST_DEBUG ("header_length %d", header_length);
+  GST_DEBUG_OBJECT (mpeg_demux, "header_length %d", header_length);
   buf += 2;
 
   /* marker:1==1 ! rate_bound:22 | marker:1==1 */
@@ -364,27 +519,25 @@ gst_mpeg_demux_parse_syshead (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
     gint stream_count = (header_length - 6) / 3;
     gint i, j = 0;
 
-    GST_DEBUG ("number of streams=%d ", stream_count);
+    GST_DEBUG_OBJECT (mpeg_demux, "number of streams: %d ", stream_count);
 
     for (i = 0; i < stream_count; i++) {
       guint8 stream_id;
       gboolean STD_buffer_bound_scale;
       guint16 STD_buffer_size_bound;
       guint32 buf_byte_size_bound;
-      gchar *name = NULL;
-      GstMPEGStream **outstream = NULL;
-      GstPadTemplate *newtemp = NULL;
-      GstCaps *caps = NULL;
+      GstMPEGStream *outstream = NULL;
 
       stream_id = *buf++;
       if (!(stream_id & 0x80)) {
-        GST_DEBUG ("error in system header length");
+        GST_DEBUG_OBJECT (mpeg_demux, "error in system header length");
         return FALSE;
       }
 
       /* check marker bits */
       if ((*buf & 0xC0) != 0xC0) {
-        GST_DEBUG ("expecting placeholder bit values '11' after stream id\n");
+        GST_DEBUG_OBJECT (mpeg_demux, "expecting placeholder bit values"
+            " '11' after stream id");
         return FALSE;
       }
 
@@ -398,97 +551,66 @@ gst_mpeg_demux_parse_syshead (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
         buf_byte_size_bound = STD_buffer_size_bound * 1024;
       }
 
-      if (stream_id == 0xBD) {
-        /* private_stream_1 */
-        name = NULL;
-        outstream = NULL;
-      } else if (stream_id == 0xBF) {
-        /* private_stream_2 */
-        name = g_strdup_printf ("private_stream_2");
-        outstream = &mpeg_demux->private_2_stream;
-        newtemp = gst_static_pad_template_get (&private2_factory);
-      } else if (stream_id >= 0xC0 && stream_id < 0xE0) {
-        /* Audio */
-        name = g_strdup_printf ("audio_%02d", stream_id & 0x1F);
-        outstream = &mpeg_demux->audio_stream[stream_id & 0x1F];
-        newtemp = gst_static_pad_template_get (&audio_factory);
-      } else if (stream_id >= 0xE0 && stream_id < 0xF0) {
-        /* Video */
-        name = g_strdup_printf ("video_%02d", stream_id & 0x0F);
-        outstream = &mpeg_demux->video_stream[stream_id & 0x0F];
-        newtemp = gst_static_pad_template_get (&video_src_factory);
-        if (!GST_MPEG_PARSE_IS_MPEG2 (mpeg_demux)) {
-          caps = gst_caps_new_simple ("video/mpeg",
-              "mpegversion", G_TYPE_INT, 1,
-              "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
-        } else {
-          caps = gst_caps_new_simple ("video/mpeg",
-              "mpegversion", G_TYPE_INT, 2,
-              "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
+      switch (stream_id) {
+        case 0xBD:
+          /* Private stream 1. */
+          outstream = CLASS (mpeg_demux)->get_private_stream (mpeg_demux,
+              0, GST_MPEG_DEMUX_PRIVATE_UNKNOWN, NULL);
+          break;
+
+        case 0xBF:
+          /* Private stream 2. */
+          outstream = CLASS (mpeg_demux)->get_private_stream (mpeg_demux,
+              1, GST_MPEG_DEMUX_PRIVATE_UNKNOWN, NULL);
+          break;
+
+        case 0xC0...0xDF:
+          /* Audio. */
+          outstream = CLASS (mpeg_demux)->get_audio_stream (mpeg_demux,
+              stream_id - 0xC0, GST_MPEG_DEMUX_AUDIO_MPEG, NULL);
+          break;
+
+        case 0xE0...0xEF:
+          /* Video. */
+        {
+          gint mpeg_version = !GST_MPEG_PARSE_IS_MPEG2 (mpeg_demux) ? 1 : 2;
+
+          outstream = CLASS (mpeg_demux)->get_video_stream (mpeg_demux,
+              stream_id - 0xE0, GST_MPEG_DEMUX_VIDEO_MPEG, &mpeg_version);
         }
-      } else {
-        GST_DEBUG ("unknown stream id %d", stream_id);
+          break;
+
+        default:
+          GST_WARNING ("unkown stream id 0x%02x", stream_id);
+          break;
       }
 
-      GST_DEBUG ("stream ID 0x%02X (%s)", stream_id, name);
-      GST_DEBUG ("STD_buffer_bound_scale %d", STD_buffer_bound_scale);
-      GST_DEBUG ("STD_buffer_size_bound %d or %d bytes",
+      GST_DEBUG_OBJECT (mpeg_demux, "STD_buffer_bound_scale %d",
+          STD_buffer_bound_scale);
+      GST_DEBUG_OBJECT (mpeg_demux, "STD_buffer_size_bound %d or %d bytes",
           STD_buffer_size_bound, buf_byte_size_bound);
 
-      /* create the pad and add it to self if it does not yet exist
-       * this should trigger the NEW_PAD signal, which should be caught by
-       * the app and used to attach to desired streams.
-       */
-      if (outstream && *outstream == NULL) {
-        GstPad **outpad;
-
-        *outstream = gst_mpeg_demux_new_stream ();
-        outpad = &((*outstream)->pad);
-
-        *outpad = gst_pad_new_from_template (newtemp, name);
-
-        gst_pad_set_formats_function (*outpad, gst_mpeg_demux_get_src_formats);
-        gst_pad_set_convert_function (*outpad, gst_mpeg_parse_convert_src);
-        gst_pad_set_event_mask_function (*outpad,
-            gst_mpeg_parse_get_src_event_masks);
-        gst_pad_set_event_function (*outpad, gst_mpeg_demux_handle_src_event);
-        gst_pad_set_query_type_function (*outpad,
-            gst_mpeg_parse_get_src_query_types);
-        gst_pad_set_query_function (*outpad, gst_mpeg_parse_handle_src_query);
-        if (caps && gst_caps_is_fixed (caps))
-          gst_pad_use_explicit_caps (*outpad);
-
-        if (caps && gst_caps_is_fixed (caps))
-          gst_pad_set_explicit_caps (*outpad, caps);
-        else if (caps)
-          gst_caps_free (caps);
-
-        gst_element_add_pad (GST_ELEMENT (mpeg_demux), (*outpad));
-
-        gst_pad_set_element_private (*outpad, *outstream);
-
-        (*outstream)->size_bound = buf_byte_size_bound;
+      if (outstream != NULL) {
+        outstream->size_bound = buf_byte_size_bound;
         mpeg_demux->total_size_bound += buf_byte_size_bound;
 
-        if (mpeg_demux->index)
-          (*outstream)->index_id =
-              _demux_get_writer_id (mpeg_demux->index, *outpad);
+        if (mpeg_demux->index) {
+          outstream->index_id =
+              _demux_get_writer_id (mpeg_demux->index, outstream->pad);
+        }
 
-        if (GST_PAD_IS_USABLE (*outpad)) {
+        if (GST_PAD_IS_USABLE (outstream->pad)) {
           GstEvent *event;
           gint64 time;
 
-          time = mpeg_parse->current_scr;
+          time = MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr
+              + mpeg_parse->adjust) + mpeg_demux->adjust;
 
           event = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME,
               MPEGTIME_TO_GSTTIME (time), NULL);
 
-          gst_pad_push (*outpad, GST_DATA (event));
+          gst_pad_push (outstream->pad, GST_DATA (event));
         }
-      } else {
-        /* we won't be needing this. */
-        if (name)
-          g_free (name);
       }
 
       j++;
@@ -509,19 +631,14 @@ gst_mpeg_demux_parse_packet (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
   gboolean STD_buffer_bound_scale;
   guint16 STD_buffer_size_bound;
   guint64 dts;
-  guint8 ps_id_code;
   gint64 pts = -1;
 
   guint16 datalen;
 
-  GstMPEGStream **outstream = NULL;
-  GstPad *outpad = NULL;
-  GstBuffer *outbuf;
+  GstMPEGStream *outstream = NULL;
   guint8 *buf, *basebuf;
   gint64 timestamp;
 
-  GST_DEBUG ("in parse_packet");
-
   basebuf = buf = GST_BUFFER_DATA (buffer);
   id = *(buf + 3);
   buf += 4;
@@ -529,7 +646,7 @@ gst_mpeg_demux_parse_packet (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
   /* start parsing */
   packet_length = GUINT16_FROM_BE (*((guint16 *) buf));
 
-  GST_DEBUG ("got packet_length %d", packet_length);
+  GST_DEBUG_OBJECT (mpeg_demux, "got packet_length %d", packet_length);
   headerlen = 2;
   buf += 2;
 
@@ -541,14 +658,14 @@ gst_mpeg_demux_parse_packet (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
     switch (bits & 0xC0) {
       case 0xC0:
         if (bits == 0xff) {
-          GST_DEBUG ("have stuffing byte");
+          GST_DEBUG_OBJECT (mpeg_demux, "have stuffing byte");
         } else {
-          GST_DEBUG ("expected stuffing byte");
+          GST_DEBUG_OBJECT (mpeg_demux, "expected stuffing byte");
         }
         headerlen++;
         break;
       case 0x40:
-        GST_DEBUG ("have STD");
+        GST_DEBUG_OBJECT (mpeg_demux, "have STD");
 
         STD_buffer_bound_scale = bits & 0x20;
         STD_buffer_size_bound = ((guint16) (bits & 0x1F)) << 8;
@@ -566,7 +683,7 @@ gst_mpeg_demux_parse_packet (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
             pts |= ((guint64) * buf++) << 7;
             pts |= ((guint64) (*buf++ & 0xFE)) >> 1;
 
-            GST_DEBUG ("PTS = %" G_GUINT64_FORMAT, pts);
+            GST_DEBUG_OBJECT (mpeg_demux, "PTS = %" G_GUINT64_FORMAT, pts);
             headerlen += 5;
             goto done;
           case 0x30:
@@ -584,15 +701,15 @@ gst_mpeg_demux_parse_packet (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
             dts |= ((guint64) * buf++) << 7;
             dts |= ((guint64) (*buf++ & 0xFE)) >> 1;
 
-            GST_DEBUG ("PTS = %" G_GUINT64_FORMAT ", DTS = %" G_GUINT64_FORMAT,
-                pts, dts);
+            GST_DEBUG_OBJECT (mpeg_demux, "PTS = %" G_GUINT64_FORMAT
+                ", DTS = %" G_GUINT64_FORMAT, pts, dts);
             headerlen += 10;
             goto done;
           case 0x00:
-            GST_DEBUG ("have no pts/dts");
-            GST_DEBUG ("got trailer bits %x", (bits & 0x0f));
+            GST_DEBUG_OBJECT (mpeg_demux, "have no pts/dts");
+            GST_DEBUG_OBJECT (mpeg_demux, "got trailer bits %x", (bits & 0x0f));
             if ((bits & 0x0f) != 0xf) {
-              GST_DEBUG ("not a valid packet time sequence");
+              GST_DEBUG_OBJECT (mpeg_demux, "not a valid packet time sequence");
               return FALSE;
             }
             headerlen++;
@@ -603,85 +720,62 @@ gst_mpeg_demux_parse_packet (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
         goto done;
     }
   } while (1);
-  GST_DEBUG ("done with header loop");
+  GST_DEBUG_OBJECT (mpeg_demux, "done with header loop");
 
 done:
 
   /* calculate the amount of real data in this packet */
   datalen = packet_length - headerlen + 2;
-  GST_DEBUG ("headerlen is %d, datalen is %d", headerlen, datalen);
-
-  if (id == 0xBD) {
-    /* private_stream_1 */
-    /* first find the track code */
-    ps_id_code = *(basebuf + headerlen);
-
-    if (ps_id_code >= 0x80 && ps_id_code <= 0x87) {
-      /* make sure it's valid */
-      GST_DEBUG ("0x%02X: we have a private_stream_1 (AC3) packet, track %d",
-          id, ps_id_code - 0x80);
-      outstream = &mpeg_demux->private_1_stream[ps_id_code - 0x80];
-      /* scrap first 4 bytes (so-called "mystery AC3 tag") */
-      headerlen += 4;
-      datalen -= 4;
-    }
-  } else if (id == 0xBF) {
-    /* private_stream_2 */
-    GST_DEBUG ("0x%02X: we have a private_stream_2 packet", id);
-    outstream = &mpeg_demux->private_2_stream;
-  } else if (id >= 0xC0 && id <= 0xDF) {
-    /* audio */
-    GST_DEBUG ("0x%02X: we have an audio packet", id);
-    outstream = &mpeg_demux->audio_stream[id & 0x1F];
-  } else if (id >= 0xE0 && id <= 0xEF) {
-    /* video */
-    GST_DEBUG ("0x%02X: we have a video packet", id);
-    outstream = &mpeg_demux->video_stream[id & 0x0F];
-  }
-
-  /* if we don't know what it is, bail */
-  if (outstream == NULL) {
-    GST_DEBUG ("unknown packet id 0x%02X !!", id);
-    return FALSE;
-  }
-
-  outpad = (*outstream)->pad;
+  GST_DEBUG_OBJECT (mpeg_demux, "headerlen is %d, datalen is %d",
+      headerlen, datalen);
 
-  /* the pad should have been created in parse_syshead */
-  if (outpad == NULL) {
-    GST_DEBUG ("unexpected packet id 0x%02X!!", id);
-    return FALSE;
-  }
-
-  /* attach pts, if any */
   if (pts != -1) {
     pts += mpeg_parse->adjust;
-    timestamp = MPEGTIME_TO_GSTTIME (pts);
-
-    if (mpeg_demux->index) {
-      gst_index_add_association (mpeg_demux->index,
-          (*outstream)->index_id, 0,
-          GST_FORMAT_BYTES, GST_BUFFER_OFFSET (buffer),
-          GST_FORMAT_TIME, timestamp, 0);
-    }
+    timestamp = MPEGTIME_TO_GSTTIME (pts) + mpeg_demux->adjust;
   } else {
     timestamp = GST_CLOCK_TIME_NONE;
   }
 
-  /* create the buffer and send it off to the Other Side */
-  if (GST_PAD_IS_LINKED (outpad) && datalen > 0) {
-    GST_DEBUG ("creating subbuffer len %d", datalen);
+  switch (id) {
+    case 0xBD:
+      /* Private stream 1. */
+      GST_DEBUG_OBJECT (mpeg_demux, "we have a private 1 packet");
+      CLASS (mpeg_demux)->process_private (mpeg_demux, buffer, 0, timestamp,
+          headerlen, datalen);
+      break;
 
-    /* if this is part of the buffer, create a subbuffer */
-    outbuf = gst_buffer_create_sub (buffer, headerlen + 4, datalen);
+    case 0xBF:
+      /* Private stream 2. */
+      GST_DEBUG_OBJECT (mpeg_demux, "we have a private 2 packet");
+      CLASS (mpeg_demux)->process_private (mpeg_demux, buffer, 1, timestamp,
+          headerlen, datalen);
+      break;
 
-    GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
-    GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buffer) + headerlen + 4;
+    case 0xC0...0xDF:
+      /* Audio. */
+      GST_DEBUG_OBJECT (mpeg_demux, "we have an audio packet");
+      outstream = CLASS (mpeg_demux)->get_audio_stream (mpeg_demux,
+          id - 0xC0, GST_MPEG_DEMUX_AUDIO_MPEG, NULL);
+      CLASS (mpeg_demux)->send_subbuffer (mpeg_demux, outstream, buffer,
+          timestamp, headerlen + 4, datalen);
+      break;
 
-    GST_DEBUG ("pushing buffer of len %d id %d, ts %" G_GINT64_FORMAT,
-        datalen, id, GST_BUFFER_TIMESTAMP (outbuf));
+    case 0xE0...0xEF:
+      /* Video. */
+      GST_DEBUG_OBJECT (mpeg_demux, "we have a video packet");
+      {
+        gint mpeg_version = !GST_MPEG_PARSE_IS_MPEG2 (mpeg_demux) ? 1 : 2;
 
-    gst_pad_push (outpad, GST_DATA (outbuf));
+        outstream = CLASS (mpeg_demux)->get_video_stream (mpeg_demux,
+            id - 0xE0, GST_MPEG_DEMUX_VIDEO_MPEG, &mpeg_version);
+      }
+      CLASS (mpeg_demux)->send_subbuffer (mpeg_demux, outstream, buffer,
+          timestamp, headerlen + 4, datalen);
+      break;
+
+    default:
+      GST_WARNING ("unkown stream id 0x%02x", id);
+      break;
   }
 
   return TRUE;
@@ -692,31 +786,25 @@ gst_mpeg_demux_parse_pes (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
 {
   GstMPEGDemux *mpeg_demux = GST_MPEG_DEMUX (mpeg_parse);
   guint8 id;
-  gint64 pts = -1;
 
   guint16 packet_length;
   guint8 header_data_length = 0;
 
   guint16 datalen;
   guint16 headerlen;
-  guint8 ps_id_code = 0x80;
+  GstClockTime timestamp = GST_CLOCK_TIME_NONE;
 
-  GstMPEGStream **outstream = NULL;
-  GstPad **outpad = NULL;
-  GstBuffer *outbuf;
-  GstPadTemplate *newtemp = NULL;
-  guint8 *buf, *basebuf;
-
-  GST_DEBUG ("in parse_pes");
+  GstMPEGStream *outstream = NULL;
+  guint8 *buf;
 
-  basebuf = buf = GST_BUFFER_DATA (buffer);
+  buf = GST_BUFFER_DATA (buffer);
   id = *(buf + 3);
   buf += 4;
 
   /* start parsing */
   packet_length = GUINT16_FROM_BE (*((guint16 *) buf));
 
-  GST_DEBUG ("got packet_length %d", packet_length);
+  GST_DEBUG_OBJECT (mpeg_demux, "packet_length %d", packet_length);
   buf += 2;
 
   /* we don't operate on: program_stream_map, padding_stream, */
@@ -732,304 +820,148 @@ gst_mpeg_demux_parse_pes (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
 
     header_data_length = *buf++;
 
-    GST_DEBUG ("header_data_length is %d", header_data_length);
+    GST_DEBUG_OBJECT (mpeg_demux, "header_data_length: %d", header_data_length);
 
     /* check for PTS */
     if ((flags2 & 0x80)) {
-      /*if ((flags2 & 0x80) && id == 0xe0) { */
+      gint64 pts;
+
       pts = ((guint64) (*buf++ & 0x0E)) << 29;
       pts |= ((guint64) * buf++) << 22;
       pts |= ((guint64) (*buf++ & 0xFE)) << 14;
       pts |= ((guint64) * buf++) << 7;
       pts |= ((guint64) (*buf++ & 0xFE)) >> 1;
 
-      GST_DEBUG ("%x PTS = %" G_GUINT64_FORMAT, id, MPEGTIME_TO_GSTTIME (pts));
+      GST_DEBUG_OBJECT (mpeg_demux, "0x%02x PTS = %" G_GUINT64_FORMAT,
+          id, MPEGTIME_TO_GSTTIME (pts));
 
+      pts += mpeg_parse->adjust;
+      timestamp = MPEGTIME_TO_GSTTIME (pts) + mpeg_demux->adjust;;
+    } else {
+      timestamp = GST_CLOCK_TIME_NONE;
     }
+
     if ((flags2 & 0x40)) {
-      GST_DEBUG ("%x DTS found", id);
+      GST_DEBUG_OBJECT (mpeg_demux, "%x DTS found", id);
       buf += 5;
     }
+
     if ((flags2 & 0x20)) {
-      GST_DEBUG ("%x ESCR found", id);
+      GST_DEBUG_OBJECT (mpeg_demux, "%x ESCR found", id);
       buf += 6;
     }
+
     if ((flags2 & 0x10)) {
       guint32 es_rate;
 
       es_rate = ((guint32) (*buf++ & 0x07)) << 14;
       es_rate |= ((guint32) (*buf++)) << 7;
       es_rate |= ((guint32) (*buf++ & 0xFE)) >> 1;
-      GST_DEBUG ("%x ES Rate found", id);
+      GST_DEBUG_OBJECT (mpeg_demux, "%x ES Rate found", id);
     }
     /* FIXME: lots of PES parsing missing here... */
 
-  }
-
-  /* calculate the amount of real data in this PES packet */
-  /* constant is 2 bytes packet_length, 2 bytes of bits, 1 byte header len */
-  headerlen = 5 + header_data_length;
-  /* constant is 2 bytes of bits, 1 byte header len */
-  datalen = packet_length - (3 + header_data_length);
-  GST_DEBUG ("headerlen is %d, datalen is %d", headerlen, datalen);
-
-  if (id == 0xBD) {
-    /* private_stream_1 */
-    /* first find the track code */
-    ps_id_code = *(basebuf + headerlen + 4);
-
-    if (ps_id_code >= 0x80 && ps_id_code <= 0x87) {
-      GST_DEBUG ("we have a private_stream_1 (AC3) packet, track %d",
-          ps_id_code - 0x80);
-      outstream = &mpeg_demux->private_1_stream[ps_id_code - 0x80];
-      /* scrap first 4 bytes (so-called "mystery AC3 tag") */
-      headerlen += 4;
-      datalen -= 4;
-    } else if (ps_id_code >= 0xA0 && ps_id_code <= 0xA7) {
-      GST_DEBUG ("we have a pcm_stream packet, track %d", ps_id_code - 0xA0);
-      outstream = &mpeg_demux->pcm_stream[ps_id_code - 0xA0];
-
-      /* Check for changes in the sample format. */
-      if (*outstream != NULL &&
-          basebuf[headerlen + 9] !=
-          mpeg_demux->lpcm_sample_info[ps_id_code - 0xA0]) {
-        /* Change the pad caps. */
-        gst_mpeg_demux_lpcm_set_caps ((*outstream)->pad,
-            basebuf[headerlen + 9]);
-      }
-
-      /* Store the sample info. */
-      mpeg_demux->lpcm_sample_info[ps_id_code - 0xA0] = basebuf[headerlen + 9];
-
-      /* Get rid of the LPCM header. */
-      headerlen += 7;
-      datalen -= 7;
-    } else if (ps_id_code >= 0x20 && ps_id_code <= 0x2F) {
-      GST_DEBUG ("we have a subtitle_stream packet, track %d",
-          ps_id_code - 0x20);
-      outstream = &mpeg_demux->subtitle_stream[ps_id_code - 0x20];
-      headerlen += 1;
-      datalen -= 1;
-    } else {
-      GST_DEBUG ("0x%02X: unknown id %x", id, ps_id_code);
-    }
-  } else if (id == 0xBF) {
-    /* private_stream_2 */
-    GST_DEBUG ("we have a private_stream_2 packet");
-    outstream = &mpeg_demux->private_2_stream;
-  } else if (id >= 0xC0 && id <= 0xDF) {
-    /* audio */
-    GST_DEBUG ("we have an audio packet");
-    outstream = &mpeg_demux->audio_stream[id - 0xC0];
-  } else if (id >= 0xE0 && id <= 0xEF) {
-    /* video */
-    GST_DEBUG ("we have a video packet");
-    outstream = &mpeg_demux->video_stream[id - 0xE0];
+    /* calculate the amount of real data in this PES packet */
+    /* constant is 2 bytes packet_length, 2 bytes of bits, 1 byte header len */
+    headerlen = 5 + header_data_length;
+    /* constant is 2 bytes of bits, 1 byte header len */
+    datalen = packet_length - (3 + header_data_length);
   } else {
-    GST_DEBUG ("we have a unknown packet");
+    /* Deliver the whole packet. */
+    /* constant corresponds to the 2 bytes of the packet length. */
+    headerlen = 2;
+    datalen = packet_length;
   }
 
-  /* if we don't know what it is, bail */
-  if (outstream == NULL)
-    return TRUE;
+  GST_DEBUG_OBJECT (mpeg_demux, "headerlen is %d, datalen is %d",
+      headerlen, datalen);
 
-  /* create the pad and add it if we don't already have one.            */
-  /* this should trigger the NEW_PAD signal, which should be caught by  */
-  /* the app and used to attach to desired streams.                     */
-  if ((*outstream) == NULL) {
-    gchar *name = NULL;
-    GstCaps *caps = NULL;
-
-    /* we have to name the stream approriately */
-    if (id == 0xBD) {
-      /* private_stream_1 */
-      if (ps_id_code >= 0x80 && ps_id_code <= 0x87) {
-        /* Erase any DVD audio pads. */
-        gst_mpeg_demux_dvd_audio_clear (mpeg_demux, ps_id_code - 0x80);
-
-        name = g_strdup_printf ("private_stream_1_%d", ps_id_code - 0x80);
-        newtemp = gst_static_pad_template_get (&private1_factory);
-      } else if (ps_id_code >= 0xA0 && ps_id_code <= 0xA7) {
-        /* Erase any DVD audio pads. */
-        gst_mpeg_demux_dvd_audio_clear (mpeg_demux, ps_id_code - 0xA0);
-
-        name = g_strdup_printf ("pcm_stream_%d", ps_id_code - 0xA0);
-        newtemp = gst_static_pad_template_get (&pcm_factory);
-      } else if (ps_id_code >= 0x20 && ps_id_code <= 0x2F) {
-        name = g_strdup_printf ("subtitle_stream_%d", ps_id_code - 0x20);
-        newtemp = gst_static_pad_template_get (&subtitle_factory);
-      } else {
-        name = g_strdup_printf ("unknown_stream_%d", ps_id_code);
-      }
-    } else if (id == 0xBF) {
-      /* private_stream_2 */
-      name = g_strdup ("private_stream_2");
-      newtemp = gst_static_pad_template_get (&private2_factory);
-    } else if (id >= 0xC0 && id <= 0xDF) {
-      /* audio */
-      name = g_strdup_printf ("audio_%02d", id - 0xC0);
-      newtemp = gst_static_pad_template_get (&audio_factory);
-    } else if (id >= 0xE0 && id <= 0xEF) {
-      /* video */
-      name = g_strdup_printf ("video_%02d", id - 0xE0);
-      newtemp = gst_static_pad_template_get (&video_src_factory);
-      if (!GST_MPEG_PARSE_IS_MPEG2 (mpeg_demux)) {
-        caps = gst_caps_new_simple ("video/mpeg",
-            "mpegversion", G_TYPE_INT, 1,
-            "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
-      } else {
-        caps = gst_caps_new_simple ("video/mpeg",
-            "mpegversion", G_TYPE_INT, 2,
-            "systemstream", G_TYPE_BOOLEAN, FALSE, NULL);
-      }
-    } else {
-      /* unknown */
-      name = g_strdup_printf ("unknown");
-    }
-
-    if (newtemp) {
-
-      *outstream = gst_mpeg_demux_new_stream ();
-      outpad = &((*outstream)->pad);
-
-      /* create the pad and add it to self */
-      *outpad = gst_pad_new_from_template (newtemp, name);
-      gst_element_add_pad (GST_ELEMENT (mpeg_demux), *outpad);
-
-      gst_pad_set_formats_function (*outpad, gst_mpeg_demux_get_src_formats);
-      gst_pad_set_convert_function (*outpad, gst_mpeg_parse_convert_src);
-      gst_pad_set_event_mask_function (*outpad,
-          gst_mpeg_parse_get_src_event_masks);
-      gst_pad_set_event_function (*outpad, gst_mpeg_demux_handle_src_event);
-      gst_pad_set_query_type_function (*outpad,
-          gst_mpeg_parse_get_src_query_types);
-      gst_pad_set_query_function (*outpad, gst_mpeg_parse_handle_src_query);
-      gst_pad_use_explicit_caps (*outpad);
-
-      if (ps_id_code < 0xA0 || ps_id_code > 0xA7) {
-        gst_pad_set_explicit_caps (*outpad, caps);
-      } else {
-        gst_mpeg_demux_lpcm_set_caps (*outpad,
-            mpeg_demux->lpcm_sample_info[ps_id_code - 0xA0]);
-      }
-
-      gst_pad_set_element_private (*outpad, *outstream);
-
-      if (mpeg_demux->index)
-        (*outstream)->index_id =
-            _demux_get_writer_id (mpeg_demux->index, *outpad);
-    } else {
-      g_warning ("cannot create pad %s, no template for %02x", name, id);
-    }
-    if (name)
-      g_free (name);
-  }
+  switch (id) {
+    case 0xBD:
+      /* Private stream 1. */
+      GST_DEBUG_OBJECT (mpeg_demux, "we have a private 1 packet");
+      CLASS (mpeg_demux)->process_private (mpeg_demux, buffer, 0, timestamp,
+          headerlen, datalen);
+      break;
 
-  if (*outstream) {
-    gint64 timestamp;
+    case 0xBF:
+      /* Private stream 2. */
+      GST_DEBUG_OBJECT (mpeg_demux, "we have a private 2 packet");
+      CLASS (mpeg_demux)->process_private (mpeg_demux, buffer, 1, timestamp,
+          headerlen, datalen);
+      break;
 
-    outpad = &((*outstream)->pad);
+    case 0xC0...0xDF:
+      /* Audio. */
+      GST_DEBUG_OBJECT (mpeg_demux, "we have an audio packet");
+      outstream = CLASS (mpeg_demux)->get_audio_stream (mpeg_demux,
+          id - 0xC0, GST_MPEG_DEMUX_AUDIO_MPEG, NULL);
+      CLASS (mpeg_demux)->send_subbuffer (mpeg_demux, outstream, buffer,
+          timestamp, headerlen + 4, datalen);
+      break;
 
-    /* attach pts, if any */
-    if (pts != -1) {
-      pts += mpeg_parse->adjust;
-      timestamp = MPEGTIME_TO_GSTTIME (pts);
+    case 0xE0...0xEF:
+      /* Video. */
+      GST_DEBUG_OBJECT (mpeg_demux, "we have a video packet");
+      {
+        gint mpeg_version = !GST_MPEG_PARSE_IS_MPEG2 (mpeg_demux) ? 1 : 2;
 
-      if (mpeg_demux->index) {
-        gst_index_add_association (mpeg_demux->index,
-            (*outstream)->index_id, 0,
-            GST_FORMAT_BYTES, GST_BUFFER_OFFSET (buffer),
-            GST_FORMAT_TIME, timestamp, 0);
+        outstream = CLASS (mpeg_demux)->get_video_stream (mpeg_demux,
+            id - 0xE0, GST_MPEG_DEMUX_VIDEO_MPEG, &mpeg_version);
       }
-    } else {
-      timestamp = GST_CLOCK_TIME_NONE;
-    }
-
-    /* create the buffer and send it off to the Other Side */
-    if (*outpad && GST_PAD_IS_USABLE (*outpad)) {
-      /* if this is part of the buffer, create a subbuffer */
-      GST_DEBUG ("creating subbuffer len %d", datalen);
-
-      outbuf = gst_buffer_create_sub (buffer, headerlen + 4, datalen);
-
-      GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
-      GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buffer) + headerlen + 4;
+      CLASS (mpeg_demux)->send_subbuffer (mpeg_demux, outstream, buffer,
+          timestamp, headerlen + 4, datalen);
+      break;
 
-      gst_pad_push (*outpad, GST_DATA (outbuf));
-    }
+    default:
+      GST_WARNING ("unkown stream id 0x%02x", id);
+      break;
   }
 
   return TRUE;
 }
 
-/**
- * Set the capabilities of the given pad based on the provided LPCM
- * sample information.
- */
 static void
-gst_mpeg_demux_lpcm_set_caps (GstPad * pad, guint8 sample_info)
+gst_mpeg_demux_send_subbuffer (GstMPEGDemux * mpeg_demux,
+    GstMPEGStream * outstream, GstBuffer * buffer,
+    GstClockTime timestamp, guint offset, guint size)
 {
-  gint width, rate, channels;
-  GstCaps *caps;
+  GstBuffer *outbuf;
 
-  /* Determine the sample width. */
-  switch (sample_info & 0xC0) {
-    case 0x80:
-      width = 24;
-      break;
-    case 0x40:
-      width = 20;
-      break;
-    default:
-      width = 16;
-      break;
+  if (timestamp != GST_CLOCK_TIME_NONE && mpeg_demux->index != NULL) {
+    /* Register a new index position. */
+    gst_index_add_association (mpeg_demux->index,
+        outstream->index_id, 0,
+        GST_FORMAT_BYTES,
+        GST_BUFFER_OFFSET (buffer), GST_FORMAT_TIME, timestamp, 0);
   }
 
-  /* Determine the rate. */
-  if (sample_info & 0x10) {
-    rate = 96000;
-  } else {
-    rate = 48000;
+  if (!GST_PAD_IS_USABLE (outstream->pad)) {
+    return;
   }
 
-  /* Determine the number of channels. */
-  channels = (sample_info & 0x7) + 1;
+  GST_DEBUG_OBJECT (mpeg_demux, "Creating subbuffer size %d", size);
+  outbuf = gst_buffer_create_sub (buffer, offset, size);
 
-  caps = gst_caps_new_simple ("audio/x-raw-int",
-      "endianness", G_TYPE_INT, G_BIG_ENDIAN,
-      "signed", G_TYPE_BOOLEAN, TRUE,
-      "width", G_TYPE_INT, width,
-      "depth", G_TYPE_INT, width,
-      "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL);
-  gst_pad_set_explicit_caps (pad, caps);
+  GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
+  GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buffer) + offset;
+
+  gst_pad_push (outstream->pad, GST_DATA (outbuf));
 }
 
-/**
- * Erase the DVD audio pad (if any) associated to the given channel.
- */
 static void
-gst_mpeg_demux_dvd_audio_clear (GstMPEGDemux * mpeg_demux, int channel)
+gst_mpeg_demux_process_private (GstMPEGDemux * mpeg_demux,
+    GstBuffer * buffer,
+    guint stream_nr, GstClockTime timestamp, guint headerlen, guint datalen)
 {
-  GstMPEGStream **stream = NULL;
+  GstMPEGStream *outstream;
 
-  if (mpeg_demux->private_1_stream[channel] != NULL) {
-    stream = &mpeg_demux->private_1_stream[channel];
-  } else if (mpeg_demux->pcm_stream[channel] != NULL) {
-    stream = &mpeg_demux->pcm_stream[channel];
-  }
-
-  if (stream == NULL) {
-    return;
-  }
-
-  gst_pad_unlink ((*stream)->pad, gst_pad_get_peer ((*stream)->pad));
-  gst_element_remove_pad (GST_ELEMENT (mpeg_demux), (*stream)->pad);
-
-  g_free (*stream);
-  *stream = NULL;
+  outstream = CLASS (mpeg_demux)->get_private_stream (mpeg_demux,
+      stream_nr, GST_MPEG_DEMUX_PRIVATE_UNKNOWN, NULL);
+  CLASS (mpeg_demux)->send_subbuffer (mpeg_demux, outstream, buffer,
+      timestamp, headerlen + 4, datalen);
 }
 
-
 const GstFormat *
 gst_mpeg_demux_get_src_formats (GstPad * pad)
 {
index 215c0d0..bfb4576 100644 (file)
@@ -32,28 +32,78 @@ extern "C" {
 
 
 #define GST_TYPE_MPEG_DEMUX \
-  (mpeg_demux_get_type())
+  (gst_mpeg_demux_get_type())
 #define GST_MPEG_DEMUX(obj) \
   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MPEG_DEMUX,GstMPEGDemux))
 #define GST_MPEG_DEMUX_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MPEG_DEMUX,GstMPEGDemux))
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MPEG_DEMUX,GstMPEGDemuxClass))
 #define GST_IS_MPEG_DEMUX(obj) \
   (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MPEG_DEMUX))
 #define GST_IS_MPEG_DEMUX_CLASS(obj) \
   (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MPEG_DEMUX))
 
-typedef struct _GstMPEGDemux GstMPEGDemux;
-typedef struct _GstMPEGDemuxClass GstMPEGDemuxClass;
+/* Supported kinds of streams. */
+enum {
+  GST_MPEG_DEMUX_STREAM_VIDEO = 1,
+  GST_MPEG_DEMUX_STREAM_AUDIO,
+  GST_MPEG_DEMUX_STREAM_PRIVATE,
+  GST_MPEG_DEMUX_STREAM_LAST,
+};
+
+/* Supported number of streams. */
+#define GST_MPEG_DEMUX_NUM_VIDEO_STREAMS       16
+#define GST_MPEG_DEMUX_NUM_AUDIO_STREAMS       32
+#define GST_MPEG_DEMUX_NUM_PRIVATE_STREAMS     2
+
+/* How to make stream type values. */
+#define GST_MPEG_DEMUX_STREAM_TYPE(kind, serial) \
+  (((kind) << 16) + (serial))
+
+/* How to retrieve the stream kind back from a type. */
+#define GST_MPEG_DEMUX_STREAM_KIND(type) ((type) >> 16)
+
+/* The recognized video types. */
+enum {
+  GST_MPEG_DEMUX_VIDEO_UNKNOWN =
+    GST_MPEG_DEMUX_STREAM_TYPE (GST_MPEG_DEMUX_STREAM_VIDEO, 1),
+  GST_MPEG_DEMUX_VIDEO_MPEG,
+  GST_MPEG_DEMUX_VIDEO_LAST,
+};
+
+/* The recognized audio types. */
+enum {
+  GST_MPEG_DEMUX_AUDIO_UNKNOWN =
+    GST_MPEG_DEMUX_STREAM_TYPE (GST_MPEG_DEMUX_STREAM_AUDIO, 1),
+  GST_MPEG_DEMUX_AUDIO_MPEG,
+  GST_MPEG_DEMUX_AUDIO_LAST,
+};
+
+/* The recognized private stream types. */
+enum {
+  GST_MPEG_DEMUX_PRIVATE_UNKNOWN =
+    GST_MPEG_DEMUX_STREAM_TYPE (GST_MPEG_DEMUX_STREAM_PRIVATE, 1),
+  GST_MPEG_DEMUX_PRIVATE_LAST,
+};
 
 typedef struct _GstMPEGStream GstMPEGStream;
+typedef struct _GstMPEGVideoStream GstMPEGVideoStream;
+typedef struct _GstMPEGDemux GstMPEGDemux;
+typedef struct _GstMPEGDemuxClass GstMPEGDemuxClass;
 
+/* Information associated to a single MPEG stream. */
 struct _GstMPEGStream {
-  gint8         STD_buffer_bound_scale;
-  gint16        STD_buffer_size_bound;
+  gint         type;
+  gint         number;
   GstPad       *pad;
-  guint64       pts;
-  gint          index_id;
-  gint          size_bound;
+  gint         index_id;
+  gint         size_bound;
+};
+
+/* Extended structure to hold additional information for video
+   streams. */
+struct _GstMPEGVideoStream {
+  GstMPEGStream        parent;
+  gint         mpeg_version;
 };
 
 struct _GstMPEGDemux {
@@ -74,35 +124,69 @@ struct _GstMPEGDemux {
   gboolean      packet_rate_restriction;
   gint64        total_size_bound;
 
-#define NUM_PRIVATE_1_STREAMS   8
-#define NUM_PCM_STREAMS         8
-#define NUM_SUBTITLE_STREAMS   16
-#define NUM_VIDEO_STREAMS      16
-#define NUM_AUDIO_STREAMS      32
+  GstIndex     *index;
 
   /* stream output */
-  GstMPEGStream *private_1_stream[NUM_PRIVATE_1_STREAMS];      /* up to 8 ac3 audio tracks */
-  GstMPEGStream *pcm_stream[NUM_PCM_STREAMS];
-  GstMPEGStream *subtitle_stream[NUM_SUBTITLE_STREAMS];
-  GstMPEGStream *private_2_stream;
-  GstMPEGStream *video_stream[NUM_VIDEO_STREAMS];
-  GstMPEGStream *audio_stream[NUM_AUDIO_STREAMS];
-
-  /* The type of linear PCM samples associated to each channel. The
-     values are bit fields with the same format of the sample_info
-     field in the linear PCM header. */
-  guint8        lpcm_sample_info[NUM_PCM_STREAMS];             
+  GstMPEGStream *video_stream[GST_MPEG_DEMUX_NUM_VIDEO_STREAMS];
+  GstMPEGStream *audio_stream[GST_MPEG_DEMUX_NUM_AUDIO_STREAMS];
+  GstMPEGStream *private_stream[GST_MPEG_DEMUX_NUM_PRIVATE_STREAMS];
 
-  GstIndex     *index;
+  GstClockTimeDiff adjust;      /* Added to all PTS timestamps. This element
+                                   keeps always this value in 0, but it is
+                                   there for the benefit of subclasses. */
 };
 
 struct _GstMPEGDemuxClass {
   GstMPEGParseClass parent_class;
+
+  GstPadTemplate *video_template;
+  GstPadTemplate *audio_template;
+  GstPadTemplate *private_template;
+
+  GstPad *     (*new_output_pad)       (GstMPEGDemux *mpeg_demux,
+                                         const gchar *name,
+                                         GstPadTemplate *temp);
+  void         (*init_stream)          (GstMPEGDemux *mpeg_demux,
+                                         gint type,
+                                         GstMPEGStream *str,
+                                         gint number,
+                                         const gchar *name,
+                                         GstPadTemplate *temp);
+
+  GstMPEGStream *
+               (*get_video_stream)     (GstMPEGDemux *mpeg_demux,
+                                         guint8 stream_nr,
+                                         gint type,
+                                         const gpointer info);
+  GstMPEGStream *
+               (*get_audio_stream)     (GstMPEGDemux *mpeg_demux,
+                                         guint8 stream_nr,
+                                         gint type,
+                                         const gpointer info);
+  GstMPEGStream *
+               (*get_private_stream)   (GstMPEGDemux *mpeg_demux,
+                                         guint8 stream_nr,
+                                         gint type,
+                                         const gpointer info);
+
+  void         (*send_subbuffer)        (GstMPEGDemux *mpeg_demux,
+                                          GstMPEGStream *outstream,
+                                          GstBuffer *buffer,
+                                          GstClockTime timestamp,
+                                          guint offset,
+                                          guint size);
+
+
+  void         (*process_private)      (GstMPEGDemux *mpeg_demux,
+                                         GstBuffer *buffer,
+                                         guint stream_nr,
+                                         GstClockTime timestamp,
+                                         guint headerlen, guint datalen);
 };
 
-GType gst_mpeg_demux_get_type(void);
+GType          gst_mpeg_demux_get_type         (void);
 
-gboolean gst_mpeg_demux_plugin_init    (GstPlugin *plugin);
+gboolean       gst_mpeg_demux_plugin_init      (GstPlugin *plugin);
 
 #ifdef __cplusplus
 }
index faf2c14..3f32a88 100644 (file)
 
 static GstFormat scr_format;
 
+
+GST_DEBUG_CATEGORY_STATIC (gstmpegparse_debug);
+#define GST_CAT_DEFAULT (gstmpegparse_debug)
+
 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_SEEK);
 
+
 /* elementfactory information */
 static GstElementDetails mpeg_parse_details = {
   "MPEG System Parser",
@@ -53,7 +58,7 @@ enum
   ARG_0,
   ARG_SYNC,
   ARG_MAX_DISCONT,
-  ARG_STREAMINFO,
+  ARG_DO_ADJUST,
   /* FILL ME */
 };
 
@@ -80,9 +85,14 @@ static void gst_mpeg_parse_set_clock (GstElement * element, GstClock * clock);
 
 static gboolean gst_mpeg_parse_parse_packhead (GstMPEGParse * mpeg_parse,
     GstBuffer * buffer);
+
+static void gst_mpeg_parse_handle_discont (GstMPEGParse * mpeg_parse,
+    GstEvent * event);
+
 static void gst_mpeg_parse_send_data (GstMPEGParse * mpeg_parse, GstData * data,
     GstClockTime time);
-static void gst_mpeg_parse_handle_discont (GstMPEGParse * mpeg_parse);
+static void gst_mpeg_parse_send_discont (GstMPEGParse * mpeg_parse,
+    GstClockTime time);
 
 static void gst_mpeg_parse_loop (GstElement * element);
 
@@ -120,6 +130,9 @@ gst_mpeg_parse_get_type (void)
     mpeg_parse_type =
         g_type_register_static (GST_TYPE_ELEMENT, "GstMPEGParse",
         &mpeg_parse_info, 0);
+
+    GST_DEBUG_CATEGORY_INIT (gstmpegparse_debug, "mpegparse", 0,
+        "MPEG parser element");
   }
   return mpeg_parse_type;
 }
@@ -150,6 +163,11 @@ gst_mpeg_parse_class_init (GstMPEGParseClass * klass)
       g_param_spec_int ("max_discont", "Max Discont",
           "The maximun allowed SCR discontinuity", 0, G_MAXINT,
           DEFAULT_MAX_DISCONT, G_PARAM_READWRITE));
+  /* FIXME: Default is TRUE to make the behavior backwards compatible.
+     It probably should be FALSE. */
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DO_ADJUST,
+      g_param_spec_boolean ("adjust", "adjust", "Adjust timestamps to "
+          "smooth discontinuities", TRUE, G_PARAM_READWRITE));
 
   gobject_class->get_property = gst_mpeg_parse_get_property;
   gobject_class->set_property = gst_mpeg_parse_set_property;
@@ -164,8 +182,9 @@ gst_mpeg_parse_class_init (GstMPEGParseClass * klass)
   klass->parse_syshead = NULL;
   klass->parse_packet = NULL;
   klass->parse_pes = NULL;
-  klass->send_data = gst_mpeg_parse_send_data;
   klass->handle_discont = gst_mpeg_parse_handle_discont;
+  klass->send_data = gst_mpeg_parse_send_data;
+  klass->send_discont = gst_mpeg_parse_send_discont;
 
   /* FIXME: this is a hack.  We add the pad templates here instead
    * in the base_init function, since the derived class (mpegdemux)
@@ -212,6 +231,8 @@ gst_mpeg_parse_init (GstMPEGParse * mpeg_parse)
   mpeg_parse->id = NULL;
   mpeg_parse->max_discont = DEFAULT_MAX_DISCONT;
 
+  mpeg_parse->do_adjust = TRUE;
+
   GST_FLAG_SET (mpeg_parse, GST_ELEMENT_EVENT_AWARE);
 }
 
@@ -250,6 +271,29 @@ gst_mpeg_parse_update_streaminfo (GstMPEGParse * mpeg_parse)
 #endif
 
 static void
+gst_mpeg_parse_handle_discont (GstMPEGParse * mpeg_parse, GstEvent * event)
+{
+  GstClockTime time;
+
+  g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_DISCONTINUOUS);
+
+  if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &time)) {
+    GST_DEBUG_OBJECT (mpeg_parse,
+        "forwarding discontinuity, time: %0.3fs", (double) time / GST_SECOND);
+
+    if (CLASS (mpeg_parse)->send_discont)
+      CLASS (mpeg_parse)->send_discont (mpeg_parse, time);
+  } else {
+    /* Use the next SCR to send a discontinuous event. */
+    mpeg_parse->discont_pending = TRUE;
+    mpeg_parse->scr_pending = TRUE;
+  }
+  mpeg_parse->packetize->resync = TRUE;
+
+  gst_event_unref (event);
+}
+
+static void
 gst_mpeg_parse_send_data (GstMPEGParse * mpeg_parse, GstData * data,
     GstClockTime time)
 {
@@ -283,29 +327,24 @@ gst_mpeg_parse_send_data (GstMPEGParse * mpeg_parse, GstData * data,
 }
 
 static void
-gst_mpeg_parse_handle_discont (GstMPEGParse * mpeg_parse)
+gst_mpeg_parse_send_discont (GstMPEGParse * mpeg_parse, GstClockTime time)
 {
   GstEvent *event;
 
-  event = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME,
-      MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr), NULL);
-
-  if (GST_PAD_IS_USABLE (mpeg_parse->srcpad))
+  if (GST_PAD_IS_USABLE (mpeg_parse->srcpad)) {
+    event = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, time, NULL);
     gst_pad_push (mpeg_parse->srcpad, GST_DATA (event));
-  else
-    gst_event_unref (event);
+  }
 }
 
 static gboolean
 gst_mpeg_parse_parse_packhead (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
 {
   guint8 *buf;
-  guint64 scr, scr_adj, scr_orig;
+  guint64 scr;
   guint32 scr1, scr2;
   guint32 new_rate;
 
-  GST_DEBUG ("in parse_packhead");
-
   buf = GST_BUFFER_DATA (buffer);
   buf += 4;
 
@@ -325,8 +364,9 @@ gst_mpeg_parse_parse_packhead (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
 
     scr = (scr * 300 + scr_ext % 300) / 300;
 
-    GST_DEBUG ("%" G_GINT64_FORMAT " %d, %08x %08x %" G_GINT64_FORMAT " diff: %"
-        G_GINT64_FORMAT, scr, scr_ext, scr1, scr2, mpeg_parse->bytes_since_scr,
+    GST_LOG_OBJECT (mpeg_parse, "%" G_GINT64_FORMAT " %d, %08x %08x %"
+        G_GINT64_FORMAT " diff: %" G_GINT64_FORMAT,
+        scr, scr_ext, scr1, scr2, mpeg_parse->bytes_since_scr,
         scr - mpeg_parse->current_scr);
 
     buf += 6;
@@ -345,36 +385,40 @@ gst_mpeg_parse_parse_packhead (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
     new_rate |= buf[2] >> 1;
   }
 
-  scr_orig = scr;
+  mpeg_parse->current_scr = scr;
+  mpeg_parse->scr_pending = FALSE;
   mpeg_parse->bytes_since_scr = 0;
-  scr_adj = scr + mpeg_parse->adjust;
 
   if (mpeg_parse->next_scr == -1) {
-    mpeg_parse->next_scr = scr;
+    mpeg_parse->next_scr = mpeg_parse->current_scr;
   }
 
-  GST_DEBUG ("SCR is %" G_GUINT64_FORMAT " (%" G_GUINT64_FORMAT ") next: %"
-      G_GINT64_FORMAT " (%" G_GINT64_FORMAT ") diff: %" G_GINT64_FORMAT " (%"
+  GST_LOG_OBJECT (mpeg_parse,
+      "SCR is %" G_GUINT64_FORMAT
+      " (%" G_GUINT64_FORMAT ") next: %"
+      G_GINT64_FORMAT " (%" G_GINT64_FORMAT
+      ") diff: %" G_GINT64_FORMAT " (%"
       G_GINT64_FORMAT ")",
-      scr,
-      MPEGTIME_TO_GSTTIME (scr),
+      mpeg_parse->current_scr,
+      MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr),
       mpeg_parse->next_scr,
       MPEGTIME_TO_GSTTIME (mpeg_parse->next_scr),
-      scr - mpeg_parse->next_scr,
-      MPEGTIME_TO_GSTTIME (scr) - MPEGTIME_TO_GSTTIME (mpeg_parse->next_scr));
+      mpeg_parse->current_scr - mpeg_parse->next_scr,
+      MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr) -
+      MPEGTIME_TO_GSTTIME (mpeg_parse->next_scr));
 
-  if (ABS ((gint64) mpeg_parse->next_scr - (gint64) (scr_adj)) >
+  if (ABS ((gint64) mpeg_parse->next_scr - (gint64) (scr)) >
       mpeg_parse->max_discont) {
     GST_DEBUG ("discontinuity detected; expected: %" G_GUINT64_FORMAT " got: %"
         G_GUINT64_FORMAT " real:%" G_GINT64_FORMAT " adjust:%" G_GINT64_FORMAT,
-        mpeg_parse->next_scr, scr_adj, scr, mpeg_parse->adjust);
+        mpeg_parse->next_scr, mpeg_parse->current_scr + mpeg_parse->adjust,
+        mpeg_parse->current_scr, mpeg_parse->adjust);
+    if (mpeg_parse->do_adjust) {
+      mpeg_parse->adjust +=
+          (gint64) mpeg_parse->next_scr - (gint64) mpeg_parse->current_scr;
 
-    mpeg_parse->adjust = mpeg_parse->next_scr - scr;
-    scr = mpeg_parse->next_scr;
-
-    GST_DEBUG ("new adjust: %" G_GINT64_FORMAT, mpeg_parse->adjust);
-  } else {
-    scr = scr_adj;
+      GST_DEBUG ("new adjust: %" G_GINT64_FORMAT, mpeg_parse->adjust);
+    }
   }
 
   if (mpeg_parse->index && GST_INDEX_IS_WRITABLE (mpeg_parse->index)) {
@@ -382,12 +426,9 @@ gst_mpeg_parse_parse_packhead (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
     gst_index_add_association (mpeg_parse->index, mpeg_parse->index_id,
         GST_ASSOCIATION_FLAG_KEY_UNIT,
         GST_FORMAT_BYTES, GST_BUFFER_OFFSET (buffer),
-        GST_FORMAT_TIME, MPEGTIME_TO_GSTTIME (scr), 0);
+        GST_FORMAT_TIME, MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr), 0);
   }
 
-  mpeg_parse->current_scr = scr;
-  mpeg_parse->scr_pending = FALSE;
-
   if (mpeg_parse->mux_rate != new_rate) {
     mpeg_parse->mux_rate = new_rate;
 
@@ -417,7 +458,7 @@ gst_mpeg_parse_loop (GstElement * element)
   if (GST_IS_BUFFER (data)) {
     GstBuffer *buffer = GST_BUFFER (data);
 
-    GST_DEBUG ("have chunk 0x%02X", id);
+    GST_LOG_OBJECT (mpeg_parse, "have chunk 0x%02X", id);
 
     switch (id) {
       case 0xb9:
@@ -456,11 +497,8 @@ gst_mpeg_parse_loop (GstElement * element)
 
     switch (GST_EVENT_TYPE (event)) {
       case GST_EVENT_DISCONTINUOUS:
-        GST_DEBUG ("event: %d\n", GST_EVENT_TYPE (data));
-
-        mpeg_parse->discont_pending = TRUE;
-        mpeg_parse->packetize->resync = TRUE;
-        gst_event_unref (event);
+        if (CLASS (mpeg_parse)->handle_discont)
+          CLASS (mpeg_parse)->handle_discont (mpeg_parse, event);
         return;
       default:
         break;
@@ -479,8 +517,10 @@ gst_mpeg_parse_loop (GstElement * element)
           gst_element_set_time (GST_ELEMENT (mpeg_parse),
               MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr));
         }
-        if (CLASS (mpeg_parse)->handle_discont) {
-          CLASS (mpeg_parse)->handle_discont (mpeg_parse);
+        if (CLASS (mpeg_parse)->send_discont) {
+          CLASS (mpeg_parse)->send_discont (mpeg_parse,
+              MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr +
+                  mpeg_parse->adjust));
         }
         mpeg_parse->discont_pending = FALSE;
       } else {
@@ -538,9 +578,9 @@ gst_mpeg_parse_loop (GstElement * element)
         mpeg_parse->next_scr = scr;
       }
 
-      GST_DEBUG ("size: %" G_GINT64_FORMAT ", total since SCR: %"
-          G_GINT64_FORMAT ", next SCR: %" G_GINT64_FORMAT, size, bss,
-          mpeg_parse->next_scr);
+      GST_LOG_OBJECT (mpeg_parse, "size: %" G_GINT64_FORMAT
+          ", total since SCR: %" G_GINT64_FORMAT
+          ", next SCR: %" G_GINT64_FORMAT, size, bss, mpeg_parse->next_scr);
     }
   }
 }
@@ -816,6 +856,9 @@ gst_mpeg_parse_get_property (GObject * object, guint prop_id, GValue * value,
     case ARG_MAX_DISCONT:
       g_value_set_int (value, mpeg_parse->max_discont);
       break;
+    case ARG_DO_ADJUST:
+      g_value_set_boolean (value, mpeg_parse->do_adjust);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -837,6 +880,10 @@ gst_mpeg_parse_set_property (GObject * object, guint prop_id,
     case ARG_MAX_DISCONT:
       mpeg_parse->max_discont = g_value_get_int (value);
       break;
+    case ARG_DO_ADJUST:
+      mpeg_parse->do_adjust = g_value_get_boolean (value);
+      mpeg_parse->adjust = 0;
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
index a99bb33..70db04b 100644 (file)
@@ -61,11 +61,13 @@ struct _GstMPEGParse {
 
   /* pack header values */
   guint32       mux_rate;
-  guint64       current_scr;
-  guint64       next_scr;
+  guint64       current_scr;           /* Current SCR from the stream. */
+  guint64       next_scr;              /* Expected next SCR. */
   guint64       bytes_since_scr;
 
-  gint64        adjust;
+  gboolean      do_adjust;             /* Adjust timestamps to smooth
+                                           discontinuities. */
+  gint64        adjust;                /* Current timestamp adjust value. */
 
   gboolean      discont_pending;
   gboolean      scr_pending;
@@ -88,9 +90,12 @@ struct _GstMPEGParseClass {
   gboolean     (*parse_packet)         (GstMPEGParse *parse, GstBuffer *buffer);
   gboolean     (*parse_pes)            (GstMPEGParse *parse, GstBuffer *buffer);
 
+  /* process events */
+  void         (*handle_discont)       (GstMPEGParse *parse, GstEvent *event);
+
   /* optional method to send out the data */
   void         (*send_data)            (GstMPEGParse *parse, GstData *data, GstClockTime time);
-  void         (*handle_discont)       (GstMPEGParse *parse);
+  void         (*send_discont)         (GstMPEGParse *parse, GstClockTime time);
 };
 
 GType gst_mpeg_parse_get_type(void);
index ef1e609..dec2161 100644 (file)
@@ -23,6 +23,7 @@
 #endif
 #include "gstmpegparse.h"
 #include "gstmpegdemux.h"
+#include "gstdvddemux.h"
 #include "gstrfc2250enc.h"
 
 static gboolean
@@ -38,6 +39,7 @@ plugin_init (GstPlugin * plugin)
 
   if (!gst_mpeg_parse_plugin_init (plugin) ||
       !gst_mpeg_demux_plugin_init (plugin) ||
+      !gst_dvd_demux_plugin_init (plugin) ||
       !gst_rfc2250_enc_plugin_init (plugin))
     return FALSE;