dvdnavsrc ported to 0.10, for the most part at least. Not quite ready for prime time...
authorTim-Philipp Müller <tim@centricular.net>
Sun, 26 Feb 2006 17:55:05 +0000 (17:55 +0000)
committerTim-Philipp Müller <tim@centricular.net>
Sun, 26 Feb 2006 17:55:05 +0000 (17:55 +0000)
Original commit message from CVS:
* configure.ac:
* ext/Makefile.am:
* ext/dvdnav/Makefile.am:
* ext/dvdnav/dvdnavsrc.c: (gst_dvd_nav_src_base_init),
(gst_dvd_nav_src_class_init), (gst_dvd_nav_src_check_get_range),
(gst_dvd_nav_src_init), (gst_dvd_nav_src_finalize),
(gst_dvd_nav_src_is_open), (gst_dvd_nav_src_set_property),
(gst_dvd_nav_src_get_property), (gst_dvd_nav_src_set_clock),
(gst_dvd_nav_src_tca_seek), (gst_dvd_nav_src_update_streaminfo),
(gst_dvd_nav_src_set_domain), (gst_dvd_nav_src_update_highlight),
(gst_dvd_nav_src_user_op), (dvdnav_get_event_name),
(dvdnav_get_read_domain_name), (gst_dvd_nav_src_print_event),
(gst_dvd_nav_src_make_dvd_event),
(gst_dvd_nav_src_structure_set_uint64),
(gst_dvd_nav_src_push_dvd_nav_packet_event),
(gst_dvd_nav_src_push_clut_change_event), (read_vts_info),
(gst_dvd_nav_src_push_titlelang_event),
(gst_dvd_nav_src_process_next_block), (gst_dvd_nav_src_create),
(gst_dvd_nav_src_start), (gst_dvd_nav_src_stop),
(gst_dvd_nav_src_handle_navigation_event),
(gst_dvd_nav_src_handle_seek_event), (gst_dvd_nav_src_src_event),
(gst_dvd_nav_src_query_position), (gst_dvd_nav_src_query_duration),
(gst_dvd_nav_src_query), (gst_dvd_nav_src_uri_get_type),
(gst_dvd_nav_src_uri_get_protocols), (gst_dvd_nav_src_uri_get_uri),
(gst_dvd_nav_src_uri_set_uri), (gst_dvd_nav_src_uri_handler_init),
(gst_dvd_nav_src_do_init), (plugin_init):
dvdnavsrc ported to 0.10, for the most part at least. Not quite
ready for prime time yet though.

ChangeLog
configure.ac
ext/Makefile.am
ext/dvdnav/Makefile.am
ext/dvdnav/dvdnavsrc.c

index 968eef6..2bd19f4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2006-02-26  Tim-Philipp Müller  <tim at centricular dot net>
+
+       * configure.ac:
+       * ext/Makefile.am:
+       * ext/dvdnav/Makefile.am:
+       * ext/dvdnav/dvdnavsrc.c: (gst_dvd_nav_src_base_init),
+       (gst_dvd_nav_src_class_init), (gst_dvd_nav_src_check_get_range),
+       (gst_dvd_nav_src_init), (gst_dvd_nav_src_finalize),
+       (gst_dvd_nav_src_is_open), (gst_dvd_nav_src_set_property),
+       (gst_dvd_nav_src_get_property), (gst_dvd_nav_src_set_clock),
+       (gst_dvd_nav_src_tca_seek), (gst_dvd_nav_src_update_streaminfo),
+       (gst_dvd_nav_src_set_domain), (gst_dvd_nav_src_update_highlight),
+       (gst_dvd_nav_src_user_op), (dvdnav_get_event_name),
+       (dvdnav_get_read_domain_name), (gst_dvd_nav_src_print_event),
+       (gst_dvd_nav_src_make_dvd_event),
+       (gst_dvd_nav_src_structure_set_uint64),
+       (gst_dvd_nav_src_push_dvd_nav_packet_event),
+       (gst_dvd_nav_src_push_clut_change_event), (read_vts_info),
+       (gst_dvd_nav_src_push_titlelang_event),
+       (gst_dvd_nav_src_process_next_block), (gst_dvd_nav_src_create),
+       (gst_dvd_nav_src_start), (gst_dvd_nav_src_stop),
+       (gst_dvd_nav_src_handle_navigation_event),
+       (gst_dvd_nav_src_handle_seek_event), (gst_dvd_nav_src_src_event),
+       (gst_dvd_nav_src_query_position), (gst_dvd_nav_src_query_duration),
+       (gst_dvd_nav_src_query), (gst_dvd_nav_src_uri_get_type),
+       (gst_dvd_nav_src_uri_get_protocols), (gst_dvd_nav_src_uri_get_uri),
+       (gst_dvd_nav_src_uri_set_uri), (gst_dvd_nav_src_uri_handler_init),
+       (gst_dvd_nav_src_do_init), (plugin_init):
+         dvdnavsrc ported to 0.10, for the most part at least. Not quite
+         ready for prime time yet though.
+
 2006-02-22  Tim-Philipp Müller  <tim at centricular dot net>
 
        * configure.ac:
index 2f2b243..c829243 100644 (file)
@@ -269,6 +269,37 @@ GST_CHECK_FEATURE(DVDREAD, [dvdread library], dvdreadsrc, [
   AC_SUBST(DVDREAD_LIBS)
 ])
 
+dnl *** dvdnav ***
+translit(dnm, m, l) AM_CONDITIONAL(USE_DVDNAV, true)
+GST_CHECK_FEATURE(DVDNAV, [dvdnav library], dvdnavsrc, [
+  translit(dnm, m, l) AC_SUBST(DVDNAV_LIBS)
+  translit(dnm, m, l) AC_SUBST(DVDNAV_CFLAGS)
+  GST_CHECK_CONFIGPROG(DVDNAV, dvdnav-config)
+  if test x"$HAVE_DVDNAV" = x"yes"; then
+    dnl check version
+    DVDNAV_VERSION=`dvdnav-config --version|head -n 1|sed 's/^.*) //'|sed 's/ (.*)//'`
+    DVDNAV_MAJOR=`echo $DVDNAV_VERSION | cut -d. -f1 | sed s/[a-zA-Z\-].*//g`
+    DVDNAV_MINOR=`echo $DVDNAV_VERSION | cut -d. -f2 | sed s/[a-zA-Z\-].*//g`
+    DVDNAV_MICRO=`echo $DVDNAV_VERSION | cut -d. -f3 | sed s/[a-zA-Z\-].*//g`
+    if [[ "$DVDNAV_MAJOR" -eq "0" ]] && \
+       [[ "$DVDNAV_MINOR" -lt "1" ]]; then
+      AC_MSG_WARN([libdvdnav >= 0.1.7 is required, you have $DVDNAV_VERSION])
+      HAVE_DVDNAV="no"
+    elif [[ "$DVDNAV_MAJOR" -eq "0" ]] && \
+         [[ "$DVDNAV_MINOR" -eq "1" ]] && \
+         [[ "$DVDNAV_MICRO" -lt "7" ]]; then
+      AC_MSG_WARN([libdvdnav >= 0.1.7 is required, you have $DVDNAV_VERSION])
+      HAVE_DVDNAV="no"
+    fi
+  fi
+  dnl now check for dvdread/nav_print.h - see #133002
+  AC_CHECK_HEADER(dvdread/nav_print.h, , [
+      AC_MSG_WARN([header dvdread/nav_print.h from dvdread missing])
+      HAVE_DVDNAV="no"
+  ])
+  AS_SCRUB_INCLUDE(DVDNAV_CFLAGS)
+])
+
 dnl *** lame ***
 translit(dnm, m, l) AM_CONDITIONAL(USE_LAME, true)
 GST_CHECK_FEATURE(LAME, [lame mp3 encoder library], lame, [
@@ -402,6 +433,7 @@ gst/realmedia/Makefile
 ext/Makefile
 ext/a52dec/Makefile
 ext/amrnb/Makefile
+ext/dvdnav/Makefile
 ext/dvdread/Makefile
 ext/lame/Makefile
 ext/mad/Makefile
index 5b89c18..2820391 100644 (file)
@@ -16,11 +16,11 @@ else
  DVDREAD_DIR =
 endif
 
-if USE_DVDNAV
-# DVDNAV_DIR = dvdnav
-else
-DVDNAV_DIR =
-endif
+if USE_DVDNAV
+ DVDNAV_DIR = dvdnav
+else
+ DVDNAV_DIR =
+endif
 
 if USE_LAME
 LAME_DIR = lame
index 6f94c95..3b87870 100644 (file)
@@ -1,9 +1,17 @@
 
-plugin_LTLIBRARIES = libgstdvdnavsrc.la
+plugin_LTLIBRARIES = libgstdvdnav.la
 
-libgstdvdnavsrc_la_SOURCES = dvdnavsrc.c
-libgstdvdnavsrc_la_CFLAGS = $(GST_CFLAGS) $(DVDNAV_CFLAGS)
-libgstdvdnavsrc_la_LIBADD = $(DVDNAV_LIBS)
-libgstdvdnavsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstdvdnav_la_SOURCES = dvdnavsrc.c
+libgstdvdnav_la_CFLAGS = \
+       $(GST_CFLAGS)       \
+       $(GST_BASE_CFLAGS)  \
+       $(DVDNAV_CFLAGS)
+libgstdvdnav_la_LIBADD = \
+       $(GST_BASE_LIBS)    \
+       $(DVDNAV_LIBS)
+libgstdvdnav_la_LDFLAGS = \
+       $(GST_PLUGIN_LDFLAGS)
+
+noinst_HEADERS = dvdnavsrc.h
 
 EXTRA_DIST = README
index e9accb9..ea0e5de 100644 (file)
@@ -21,6 +21,7 @@
 #include "config.h"
 #endif
 
+#include "_stdint.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
 
-#include <gst/gst-i18n-plugin.h>
-#include <gst/gst.h>
+//#include <gst/gst-i18n-plugin.h>
+#define _(s) s                  /* FIXME */
 
-#include <dvdnav/dvdnav.h>
-#include <dvdnav/nav_print.h>
+#include "dvdnavsrc.h"
 
-
-GST_DEBUG_CATEGORY_STATIC (dvdnavsrc_debug);
-#define GST_CAT_DEFAULT (dvdnavsrc_debug)
+GST_DEBUG_CATEGORY_STATIC (gst_dvd_nav_src_debug);
+#define GST_CAT_DEFAULT (gst_dvd_nav_src_debug)
 
 /* Size of a DVD sector, used for sector-byte format conversions */
 #define DVD_SECTOR_SIZE 2048
@@ -48,130 +47,23 @@ GST_DEBUG_CATEGORY_STATIC (dvdnavsrc_debug);
 #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, \
-                       (_("Library call failed.")), \
-                       ("Error invoking \"%s\": %s.", \
-                        #func, dvdnav_err_to_string ((elem)->dvdnav)));
-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) \
-  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DVDNAVSRC,DVDNavSrc))
-#define DVDNAVSRC_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DVDNAVSRC,DVDNavSrcClass))
-#define GST_IS_DVDNAVSRC(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DVDNAVSRC))
-#define GST_IS_DVDNAVSRC_CLASS(obj) \
-  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DVDNAVSRC))
-
-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 / 20)
-
-/* 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 */
-  GstPad *srcpad;
-  GstCaps *streaminfo;
-
-  /* Location */
-  gchar *location;
-
-  gboolean did_seek;
-  gboolean need_flush;
-  gboolean need_newmedia;
+#define GST_DVD_NAV_SRC_MAX_AUDIO_STREAMS 8
+#define GST_DVD_NAV_SRC_MAX_SPU_STREAMS 32
 
-  /* 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;
-
-  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;
+/* Interval of time to sleep during pauses. */
+#define GST_DVD_NAV_SRC_PAUSE_INTERVAL (GST_SECOND / 30)
 
-  void (*user_op) (DVDNavSrc * src, int op);
-};
 
-/* elementfactory information */
-GstElementDetails dvdnavsrc_details = {
+static const GstElementDetails gst_dvd_nav_src_details = {
   "DVD Source",
   "Source/File/DVD",
   "Access a DVD with navigation features using libdvdnav",
   "David I. Lehn <dlehn@users.sourceforge.net>",
 };
 
-
-/* DVDNavSrc signals and  args */
 enum
 {
   USER_OP_SIGNAL,
@@ -181,11 +73,10 @@ enum
 enum
 {
   ARG_0,
-  ARG_LOCATION,
-  ARG_DEVICE,
-  ARG_STREAMINFO,
+  ARG_DEVICE
+#if 0
+      ARG_STREAMINFO,
   ARG_BUTTONINFO,
-  ARG_TITLE_STRING,
   ARG_TITLE,
   ARG_CHAPTER,
   ARG_ANGLE,
@@ -193,154 +84,114 @@ enum
   ARG_AUDIO_LANG,
   ARG_SPU_LANGS,
   ARG_SPU_LANG
+#endif
 };
 
-typedef enum
-{
-  DVDNAVSRC_OPEN = GST_ELEMENT_FLAG_LAST,
-
-  DVDNAVSRC_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 2
-}
-DVDNavSrcFlags;
-
+static void gst_dvd_nav_src_do_init (GType dvdnavsrc_type);
 
-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,
+static gboolean gst_dvd_nav_src_start (GstBaseSrc * basesrc);
+static gboolean gst_dvd_nav_src_stop (GstBaseSrc * basesrc);
+static GstEvent *
+gst_dvd_nav_src_make_dvd_event (GstDvdNavSrc * src,
+    const gchar * event_name, const gchar * firstfield, ...)
+    G_GNUC_NULL_TERMINATED;
+     static void gst_dvd_nav_src_finalize (GObject * object);
+     static void gst_dvd_nav_src_set_property (GObject * object,
     guint prop_id, const GValue * value, GParamSpec * pspec);
-static void dvdnavsrc_get_property (GObject * object,
+     static void gst_dvd_nav_src_get_property (GObject * object,
     guint prop_id, GValue * value, GParamSpec * pspec);
 
-static void dvdnavsrc_set_clock (GstElement * element, GstClock * clock);
-
-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 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_query (GstPad * pad,
-    GstQueryType type, GstFormat * format, gint64 * value);
-static const GstQueryType *dvdnavsrc_get_query_types (GstPad * pad);
-static gboolean dvdnavsrc_convert (GstPad * pad,
-    GstFormat src_format, gint64 src_value,
-    GstFormat * dest_format, gint64 * dest_value);
-
-static gboolean dvdnavsrc_close (DVDNavSrc * src);
-static gboolean dvdnavsrc_open (DVDNavSrc * src);
-static gboolean dvdnavsrc_is_open (DVDNavSrc * src);
+     static void gst_dvd_nav_src_push_dvd_nav_packet_event (GstDvdNavSrc * src,
+    const pci_t * pci);
+     static void gst_dvd_nav_src_push_clut_change_event (GstDvdNavSrc * src,
+    const guint * clut);
+     static GstFlowReturn gst_dvd_nav_src_create (GstPushSrc * pushsrc,
+    GstBuffer ** p_buf);
+     static gboolean gst_dvd_nav_src_src_event (GstBaseSrc * basesrc,
+    GstEvent * event);
+     static gboolean gst_dvd_nav_src_query (GstBaseSrc * basesrc,
+    GstQuery * query);
+     static gboolean gst_dvd_nav_src_is_open (GstDvdNavSrc * src);
+
+#if 0
+     static void gst_dvd_nav_src_set_clock (GstElement * element,
+    GstClock * clock);
+#endif
 
 #ifndef GST_DISABLE_GST_DEBUG
-static void dvdnavsrc_print_event (DVDNavSrc * src,
+     static void gst_dvd_nav_src_print_event (GstDvdNavSrc * src,
     guint8 * data, int event, int len);
 #else
-#define dvdnavsrc_print_event(src, data, event, len) ((void) 0)
+#define gst_dvd_nav_src_print_event(src, data, event, len) ((void) 0)
 #endif /* GST_DISABLE_GST_DEBUG */
-static void dvdnavsrc_update_streaminfo (DVDNavSrc * src);
-static void dvdnavsrc_set_domain (DVDNavSrc * src);
-static void dvdnavsrc_update_highlight (DVDNavSrc * src);
-static void dvdnavsrc_user_op (DVDNavSrc * src, int op);
-static GstStateChangeReturn dvdnavsrc_change_state (GstElement * element,
-    GstStateChange transition);
+     static void gst_dvd_nav_src_update_streaminfo (GstDvdNavSrc * src);
+     static void gst_dvd_nav_src_set_domain (GstDvdNavSrc * src);
+     static void gst_dvd_nav_src_update_highlight (GstDvdNavSrc * src,
+    gboolean force);
+     static void gst_dvd_nav_src_user_op (GstDvdNavSrc * src, gint op);
 
-static void dvdnavsrc_uri_handler_init (gpointer g_iface, gpointer iface_data);
+     static void gst_dvd_nav_src_uri_handler_init (gpointer g_iface,
+    gpointer iface_data);
 
-static GstElementClass *parent_class = NULL;
-static guint dvdnavsrc_signals[LAST_SIGNAL] = { 0 };
+     static guint gst_dvd_nav_src_signals[LAST_SIGNAL];
 
-static GstFormat sector_format;
-static GstFormat title_format;
-static GstFormat chapter_format;
-static GstFormat angle_format;
+     static GstFormat sector_format;
+     static GstFormat title_format;
+     static GstFormat chapter_format;
+     static GstFormat angle_format;
 
-GType
-dvdnavsrc_get_type (void)
-{
-  static GType dvdnavsrc_type = 0;
-
-  if (!dvdnavsrc_type) {
-    static const GTypeInfo dvdnavsrc_info = {
-      sizeof (DVDNavSrcClass),
-      dvdnavsrc_base_init,
-      NULL,
-      (GClassInitFunc) dvdnavsrc_class_init,
-      NULL,
-      NULL,
-      sizeof (DVDNavSrc),
-      0,
-      (GInstanceInitFunc) dvdnavsrc_init,
-    };
-    static const GInterfaceInfo urihandler_info = {
-      dvdnavsrc_uri_handler_init,
-      NULL,
-      NULL
-    };
-
-    dvdnavsrc_type = g_type_register_static (GST_TYPE_ELEMENT,
-        "DVDNavSrc", &dvdnavsrc_info, 0);
-    g_type_add_interface_static (dvdnavsrc_type,
-        GST_TYPE_URI_HANDLER, &urihandler_info);
-
-    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;
-}
+#define DVD_NAV_SRC_CAPS \
+  "video/mpeg, mpegversion=(int)1, systemstream=(boolean)true"
 
-static void
-dvdnavsrc_base_init (gpointer g_class)
+     static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (DVD_NAV_SRC_CAPS));
+
+
+GST_BOILERPLATE_FULL (GstDvdNavSrc, gst_dvd_nav_src, GstPushSrc,
+    GST_TYPE_PUSH_SRC, gst_dvd_nav_src_do_init);
+
+     static void gst_dvd_nav_src_base_init (gpointer g_class)
 {
   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
-  gst_element_class_set_details (element_class, &dvdnavsrc_details);
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&srctemplate));
+
+  gst_element_class_set_details (element_class, &gst_dvd_nav_src_details);
 }
 
 static void
-dvdnavsrc_class_init (DVDNavSrcClass * klass)
+gst_dvd_nav_src_class_init (GstDvdNavSrcClass * klass)
 {
   GObjectClass *gobject_class;
-  GstElementClass *gstelement_class;
+  GstBaseSrcClass *gstbasesrc_class;
+  GstPushSrcClass *gstpushsrc_class;
 
   gobject_class = (GObjectClass *) klass;
-  gstelement_class = (GstElementClass *) klass;
-
-  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+  gstbasesrc_class = (GstBaseSrcClass *) klass;
+  gstpushsrc_class = (GstPushSrcClass *) klass;
 
-  dvdnavsrc_signals[USER_OP_SIGNAL] =
+  gst_dvd_nav_src_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),
+      G_STRUCT_OFFSET (GstDvdNavSrcClass, user_op),
       NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
 
-  gobject_class->finalize = dvdnavsrc_finalize;
+  klass->user_op = gst_dvd_nav_src_user_op;
 
-  klass->user_op = dvdnavsrc_user_op;
+  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_dvd_nav_src_finalize);
+  gobject_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_dvd_nav_src_set_property);
+  gobject_class->get_property =
+      GST_DEBUG_FUNCPTR (gst_dvd_nav_src_get_property);
 
-  g_object_class_install_property (gobject_class, ARG_LOCATION,
-      g_param_spec_string ("location", "Location",
-          "DVD device location (deprecated; use device)",
-          NULL, G_PARAM_READWRITE));
   g_object_class_install_property (gobject_class, ARG_DEVICE,
       g_param_spec_string ("device", "Device",
           "DVD device location", NULL, G_PARAM_READWRITE));
-  g_object_class_install_property (gobject_class, ARG_TITLE_STRING,
-      g_param_spec_string ("title_string", "title string", "DVD title string",
-          NULL, G_PARAM_READABLE));
+#if 0
   g_object_class_install_property (gobject_class, ARG_TITLE,
       g_param_spec_int ("title", "title", "title",
           0, 99, 1, G_PARAM_READWRITE));
@@ -349,9 +200,12 @@ dvdnavsrc_class_init (DVDNavSrcClass * klass)
           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));
+  /* FIXME: use tags instead of this? */
+/*
   g_object_class_install_property (gobject_class, ARG_STREAMINFO,
       g_param_spec_boxed ("streaminfo", "streaminfo", "streaminfo",
           GST_TYPE_CAPS, G_PARAM_READABLE));
+*/
   g_object_class_install_property (gobject_class, ARG_BUTTONINFO,
       g_param_spec_boxed ("buttoninfo", "buttoninfo", "buttoninfo",
           GST_TYPE_CAPS, G_PARAM_READABLE));
@@ -367,48 +221,50 @@ dvdnavsrc_class_init (DVDNavSrcClass * klass)
   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));
+#endif
 
-  gobject_class->set_property = GST_DEBUG_FUNCPTR (dvdnavsrc_set_property);
-  gobject_class->get_property = GST_DEBUG_FUNCPTR (dvdnavsrc_get_property);
+  gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_dvd_nav_src_start);
+  gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_dvd_nav_src_stop);
+  gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_dvd_nav_src_src_event);
+  gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_dvd_nav_src_query);
 
-  gstelement_class->change_state = dvdnavsrc_change_state;
-  gstelement_class->set_clock = dvdnavsrc_set_clock;
+  gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_dvd_nav_src_create);
+
+#if 0
+  gstelement_class->set_clock = gst_dvd_nav_src_set_clock;
+#endif
 }
 
-static void
-dvdnavsrc_init (DVDNavSrc * src)
+static gboolean
+gst_dvd_nav_src_check_get_range (GstPad * pad)
 {
-  src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
-
-  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);
-  gst_pad_set_query_function (src->srcpad, dvdnavsrc_query);
-  gst_pad_set_query_type_function (src->srcpad, dvdnavsrc_get_query_types);
-  gst_pad_set_formats_function (src->srcpad, dvdnavsrc_get_formats);
-
-  gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
+  return FALSE;
+}
 
-  src->location = g_strdup ("/dev/dvd");
+static void
+gst_dvd_nav_src_init (GstDvdNavSrc * src, GstDvdNavSrcClass * klass)
+{
+  src->device = g_strdup ("/dev/dvd");
+  src->last_uri = NULL;
 
+  src->pending_offset = -1;
   src->did_seek = FALSE;
+  src->new_seek = FALSE;
+  src->seek_pending = FALSE;
   src->need_flush = FALSE;
-  src->need_newmedia = TRUE;
 
   /* Pause mode is initially inactive. */
-  src->pause_mode = DVDNAVSRC_PAUSE_OFF;
+  src->pause_mode = GST_DVD_NAV_SRC_PAUSE_OFF;
 
   /* No highlighted button. */
   src->button = 0;
 
   /* Domain is unknown at the begining. */
-  src->domain = DVDNAVSRC_DOMAIN_UNKNOWN;
+  src->domain = GST_DVD_NAV_SRC_DOMAIN_UNKNOWN;
 
-  src->title = 0;
-  src->chapter = 1;
-  src->angle = 1;
+  src->uri_title = 0;
+  src->uri_chapter = 1;
+  src->uri_angle = 1;
   src->streaminfo = NULL;
   src->buttoninfo = NULL;
 
@@ -419,65 +275,76 @@ dvdnavsrc_init (DVDNavSrc * src)
 
   /* No current output buffer. */
   src->cur_buf = NULL;
+
+  src->pgc_length = GST_CLOCK_TIME_NONE;
+  src->cell_start = 0;
+  src->pg_start = 0;
+
+  src->vts_attrs = NULL;
+  src->cur_vts = 0;
+
+  /* avoid unnecessary start/stop in gst_base_src_check_get_range() */
+  gst_pad_set_checkgetrange_function (GST_BASE_SRC_PAD (src),
+      GST_DEBUG_FUNCPTR (gst_dvd_nav_src_check_get_range));
 }
 
 static void
-dvdnavsrc_finalize (GObject * object)
+gst_dvd_nav_src_finalize (GObject * object)
 {
-  DVDNavSrc *src = DVDNAVSRC (object);
+  GstDvdNavSrc *src = GST_DVD_NAV_SRC (object);
 
   /* If there's a current output buffer, get rid of it. */
   if (src->cur_buf != NULL) {
     gst_buffer_unref (src->cur_buf);
   }
+
+  g_free (src->last_uri);
+
+  if (src->vts_attrs)
+    g_array_free (src->vts_attrs, TRUE);
 }
 
 static gboolean
-dvdnavsrc_is_open (DVDNavSrc * src)
+gst_dvd_nav_src_is_open (GstDvdNavSrc * src)
 {
-  g_return_val_if_fail (src != NULL, FALSE);
-  g_return_val_if_fail (GST_IS_DVDNAVSRC (src), FALSE);
-
-  return GST_OBJECT_FLAG_IS_SET (src, DVDNAVSRC_OPEN);
+  return GST_OBJECT_FLAG_IS_SET (GST_OBJECT (src), GST_BASE_SRC_STARTED);
 }
 
 static void
-dvdnavsrc_set_property (GObject * object, guint prop_id,
+gst_dvd_nav_src_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec)
 {
-  DVDNavSrc *src;
-
-  g_return_if_fail (GST_IS_DVDNAVSRC (object));
-
-  src = DVDNAVSRC (object);
+  GstDvdNavSrc *src = GST_DVD_NAV_SRC (object);
 
   switch (prop_id) {
-    case ARG_LOCATION:
     case ARG_DEVICE:
       /* the element must be stopped in order to do this */
-      /*g_return_if_fail(!GST_OBJECT_FLAG_IS_SET(src,GST_STATE_RUNNING)); */
-
-      g_free (src->location);
-      /* clear the filename if we get a NULL (is that possible?) */
-      if (g_value_get_string (value) == NULL)
-        src->location = g_strdup ("/dev/dvd");
-      /* otherwise set the new filename */
-      else
-        src->location = g_strdup (g_value_get_string (value));
+      GST_OBJECT_LOCK (src);
+
+      if (!gst_dvd_nav_src_is_open (src)) {
+        g_free (src->device);
+        if (g_value_get_string (value) == NULL)
+          src->device = g_strdup ("/dev/dvd");
+        else
+          src->device = g_value_dup_string (value);
+      } else {
+        g_warning ("dvdnavsrc: cannot change device while running");
+      }
       break;
+#if 0
     case ARG_TITLE:
-      src->title = g_value_get_int (value);
+      src->uri_title = g_value_get_int (value);
       src->did_seek = TRUE;
       break;
     case ARG_CHAPTER:
-      src->chapter = g_value_get_int (value);
+      src->uri_chapter = g_value_get_int (value);
       src->did_seek = TRUE;
       break;
     case ARG_ANGLE:
-      src->angle = g_value_get_int (value);
+      src->uri_angle = g_value_get_int (value);
       break;
     case ARG_AUDIO_LANG:
-      if (dvdnavsrc_is_open (src)) {
+      if (gst_dvd_nav_src_is_open (src)) {
         const gchar *code = g_value_get_string (value);
 
         if (code != NULL) {
@@ -491,7 +358,7 @@ dvdnavsrc_set_property (GObject * object, guint prop_id,
       }
       break;
     case ARG_SPU_LANG:
-      if (dvdnavsrc_is_open (src)) {
+      if (gst_dvd_nav_src_is_open (src)) {
         const gchar *code = g_value_get_string (value);
 
         if (code != NULL) {
@@ -499,7 +366,7 @@ dvdnavsrc_set_property (GObject * object, guint prop_id,
         }
       }
       break;
-      break;
+#endif
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -507,56 +374,41 @@ dvdnavsrc_set_property (GObject * object, guint prop_id,
 }
 
 static void
-dvdnavsrc_get_property (GObject * object, guint prop_id,
+gst_dvd_nav_src_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec)
 {
-  DVDNavSrc *src;
-  const char *title_string;
-
-  g_return_if_fail (GST_IS_DVDNAVSRC (object));
-
-  src = DVDNAVSRC (object);
+  GstDvdNavSrc *src = GST_DVD_NAV_SRC (object);
 
   switch (prop_id) {
-    case ARG_LOCATION:
     case ARG_DEVICE:
-      g_value_set_string (value, src->location);
+      g_value_set_string (value, src->device);
       break;
+#if 0
     case ARG_STREAMINFO:
       g_value_set_boxed (value, src->streaminfo);
       break;
     case ARG_BUTTONINFO:
       g_value_set_boxed (value, src->buttoninfo);
       break;
-    case ARG_TITLE_STRING:
-      if (!dvdnavsrc_is_open (src)) {
-        g_value_set_string (value, "");
-      } else if (dvdnav_get_title_string (src->dvdnav, &title_string) !=
-          DVDNAV_STATUS_OK) {
-        g_value_set_string (value, "UNKNOWN");
-      } else {
-        g_value_set_string (value, title_string);
-      }
-      break;
     case ARG_TITLE:
-      g_value_set_int (value, src->title);
+      g_value_set_int (value, src->uri_title);
       break;
     case ARG_CHAPTER:
-      g_value_set_int (value, src->chapter);
+      g_value_set_int (value, src->uri_chapter);
       break;
     case ARG_ANGLE:
-      g_value_set_int (value, src->angle);
+      g_value_set_int (value, src->uri_angle);
       break;
     case ARG_AUDIO_LANGS:
-      if (!dvdnavsrc_is_open (src)) {
+      if (!gst_dvd_nav_src_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 langs[DVD_NAV_SRC_MAX_AUDIO_STREAMS * 3];
         gchar *lang_ptr = langs;
 
-        for (physical = 0; physical < DVDNAVSRC_MAX_AUDIO_STREAMS; physical++) {
+        for (physical = 0; physical < DVD_NAV_SRC_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) {
@@ -578,7 +430,7 @@ dvdnavsrc_get_property (GObject * object, guint prop_id,
       }
       break;
     case ARG_AUDIO_LANG:
-      if (!dvdnavsrc_is_open (src)) {
+      if (!gst_dvd_nav_src_is_open (src)) {
         g_value_set_string (value, "");
       } else {
         uint8_t logical;
@@ -598,15 +450,15 @@ dvdnavsrc_get_property (GObject * object, guint prop_id,
       }
       break;
     case ARG_SPU_LANGS:
-      if (!dvdnavsrc_is_open (src)) {
+      if (!gst_dvd_nav_src_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 langs[DVD_NAV_SRC_MAX_SPU_STREAMS * 3];
         gchar *lang_ptr = langs;
 
-        for (physical = 0; physical < DVDNAVSRC_MAX_SPU_STREAMS; physical++) {
+        for (physical = 0; physical < DVD_NAV_SRC_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) {
@@ -628,7 +480,7 @@ dvdnavsrc_get_property (GObject * object, guint prop_id,
       }
       break;
     case ARG_SPU_LANG:
-      if (!dvdnavsrc_is_open (src)) {
+      if (!gst_dvd_nav_src_is_open (src)) {
         g_value_set_string (value, "");
       } else {
         uint8_t logical;
@@ -647,36 +499,38 @@ dvdnavsrc_get_property (GObject * object, guint prop_id,
         }
       }
       break;
+#endif
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
   }
 }
 
+#if 0
 static void
-dvdnavsrc_set_clock (GstElement * element, GstClock * clock)
+gst_dvd_nav_src_set_clock (GstElement * element, GstClock * clock)
 {
-  DVDNavSrc *src = DVDNAVSRC (element);
+  GstDvdNavSrc *src = GST_DVD_NAV_SRC (element);
 
   src->clock = clock;
 }
+#endif
 
 static gboolean
-dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
+gst_dvd_nav_src_tca_seek (GstDvdNavSrc * src, gint title, gint chapter,
+    gint angle)
 {
   int titles, programs, curangle, angles;
 
   g_return_val_if_fail (src != NULL, FALSE);
   g_return_val_if_fail (src->dvdnav != NULL, FALSE);
-  g_return_val_if_fail (dvdnavsrc_is_open (src), FALSE);
+  g_return_val_if_fail (gst_dvd_nav_src_is_open (src), FALSE);
 
   /* Dont try to seek to track 0 - First Play program chain */
   g_return_val_if_fail (src->title > 0, FALSE);
 
   GST_INFO_OBJECT (src, "seeking to %d/%d/%d", title, chapter, angle);
-  /*
-   * Make sure our title number is valid.
-   */
+  /* Make sure our title number is valid */
   if (dvdnav_get_number_of_titles (src->dvdnav, &titles) != DVDNAV_STATUS_OK) {
     GST_ERROR_OBJECT (src, "dvdnav_get_number_of_titles: %s",
         dvdnav_err_to_string (src->dvdnav));
@@ -685,15 +539,12 @@ dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
   GST_INFO_OBJECT (src, "there are %d titles on this DVD", titles);
   if (title < 1 || title > titles) {
     GST_ERROR_OBJECT (src, "invalid title %d", title);
-    dvdnavsrc_close (src);
     return FALSE;
   }
 
-  /*
-   * Before we can get the number of chapters (programs) we need to call
+  /* Before we can get the number of chapters (programs) we need to call
    * dvdnav_title_play so that dvdnav_get_number_of_programs knows which title
-   * to operate on (also needed to get the number of angles)
-   */
+   * 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) {
     GST_ERROR_OBJECT (src, "dvdnav_title_play: %s",
@@ -701,9 +552,7 @@ dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
     return FALSE;
   }
 
-  /*
-   * Make sure the chapter number is valid for this title.
-   */
+  /* Make sure the chapter number is valid for this title */
   if (dvdnav_get_number_of_titles (src->dvdnav, &programs)
       != DVDNAV_STATUS_OK) {
     GST_ERROR ("dvdnav_get_number_of_programs: %s",
@@ -713,13 +562,10 @@ dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
   GST_INFO_OBJECT (src, "there are %d chapters in this title", programs);
   if (chapter < 0 || chapter > programs) {
     GST_ERROR_OBJECT (src, "invalid chapter %d", chapter);
-    dvdnavsrc_close (src);
     return FALSE;
   }
 
-  /*
-   * Make sure the angle number is valid for this title.
-   */
+  /* Make sure the angle number is valid for this title */
   if (dvdnav_get_angle_info (src->dvdnav, &curangle, &angles)
       != DVDNAV_STATUS_OK) {
     GST_ERROR_OBJECT (src, "dvdnav_get_angle_info: %s",
@@ -729,13 +575,10 @@ dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
   GST_INFO_OBJECT (src, "there are %d angles in this title", angles);
   if (angle < 1 || angle > angles) {
     GST_ERROR_OBJECT (src, "invalid angle %d", angle);
-    dvdnavsrc_close (src);
     return FALSE;
   }
 
-  /*
-   * We've got enough info, time to open the title set data.
-   */
+  /* We've got enough info, time to open the title set data */
   if (src->chapter == 0) {
     if (dvdnav_title_play (src->dvdnav, title) != DVDNAV_STATUS_OK) {
       GST_ERROR_OBJECT (src, "dvdnav_title_play: %s",
@@ -761,8 +604,11 @@ dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
 }
 
 static void
-dvdnavsrc_update_streaminfo (DVDNavSrc * src)
+gst_dvd_nav_src_update_streaminfo (GstDvdNavSrc * src)
 {
+/* FIXME: we should really use TAGS for this stuff, shouldn't we? */
+#if 0
+  GstBaseSrc *bsrc = GST_BASE_SRC (src);
   GstCaps *caps;
   GstStructure *structure;
   gint64 value;
@@ -771,73 +617,77 @@ dvdnavsrc_update_streaminfo (DVDNavSrc * src)
   structure = gst_structure_empty_new ("application/x-gst-streaminfo");
   gst_caps_append_structure (caps, structure);
 
-  if (dvdnavsrc_query (src->srcpad, GST_QUERY_TOTAL, &title_format, &value)) {
+  if (gst_dvd_nav_src_query_duration (bsrc, title_format, &value)) {
     gst_caps_set_simple (caps, "titles", G_TYPE_INT, value, NULL);
   }
-  if (dvdnavsrc_query (src->srcpad, GST_QUERY_POSITION, &title_format, &value)) {
+  if (gst_dvd_nav_src_query_position (bsrc, title_format, &value)) {
     gst_caps_set_simple (caps, "title", G_TYPE_INT, value, NULL);
   }
 
-  if (dvdnavsrc_query (src->srcpad, GST_QUERY_TOTAL, &chapter_format, &value)) {
+  if (gst_dvd_nav_src_query_duration (bsrc, 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 (gst_dvd_nav_src_query_position (bsrc, chapter_format, &value)) {
     gst_caps_set_simple (caps, "chapter", G_TYPE_INT, value, NULL);
   }
 
-  if (dvdnavsrc_query (src->srcpad, GST_QUERY_TOTAL, &angle_format, &value)) {
+  if (gst_dvd_nav_src_query_duration (bsrc, angle_format, &value)) {
     gst_caps_set_simple (caps, "angles", G_TYPE_INT, value, NULL);
   }
-  if (dvdnavsrc_query (src->srcpad, GST_QUERY_POSITION, &angle_format, &value)) {
+  if (gst_dvd_nav_src_query_position (bsrc, angle_format, &value)) {
     gst_caps_set_simple (caps, "angle", G_TYPE_INT, value, NULL);
   }
 
-  if (src->streaminfo) {
-    gst_caps_free (src->streaminfo);
-  }
-  src->streaminfo = caps;
+  gst_caps_replace (&src->streaminfo, caps);
+  gst_caps_unref (caps);
+
   g_object_notify (G_OBJECT (src), "streaminfo");
+#endif
 }
 
-/*
+/**
  * Check for a new DVD domain area, and update the structure if
  * necessary.
  */
 static void
-dvdnavsrc_set_domain (DVDNavSrc * src)
+gst_dvd_nav_src_set_domain (GstDvdNavSrc * src)
 {
-  DVDNavSrcDomainType domain;
+  GstDvdNavSrcDomainType domain;
 
   if (dvdnav_is_domain_fp (src->dvdnav)) {
-    domain = DVDNAVSRC_DOMAIN_FP;
+    domain = GST_DVD_NAV_SRC_DOMAIN_FP;
   } else if (dvdnav_is_domain_vmgm (src->dvdnav)) {
-    domain = DVDNAVSRC_DOMAIN_VMGM;
+    domain = GST_DVD_NAV_SRC_DOMAIN_VMGM;
   } else if (dvdnav_is_domain_vtsm (src->dvdnav)) {
-    domain = DVDNAVSRC_DOMAIN_VTSM;
+    domain = GST_DVD_NAV_SRC_DOMAIN_VTSM;
   } else if (dvdnav_is_domain_vts (src->dvdnav)) {
-    domain = DVDNAVSRC_DOMAIN_VTS;
+    domain = GST_DVD_NAV_SRC_DOMAIN_VTS;
   } else {
-    domain = DVDNAVSRC_DOMAIN_UNKNOWN;
+    domain = GST_DVD_NAV_SRC_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)
+gst_dvd_nav_src_update_highlight (GstDvdNavSrc * src, gboolean force)
 {
   int button = 0;
   pci_t *pci;
   dvdnav_highlight_area_t area;
   GstEvent *event;
 
-  DVDNAV_CALL (dvdnav_get_current_highlight, (src->dvdnav, &button), src);
+  if (dvdnav_get_current_highlight (src->dvdnav, &button) != DVDNAV_STATUS_OK) {
+    GST_ELEMENT_ERROR (src, LIBRARY, FAILED, (NULL),
+        ("dvdnav_get_current_highlight: %s",
+            dvdnav_err_to_string (src->dvdnav)));
+    return;
+  }
 
   pci = dvdnav_get_current_nav_pci (src->dvdnav);
   if ((button > pci->hli.hl_gi.btn_ns) || (button < 0)) {
@@ -854,20 +704,25 @@ dvdnavsrc_update_highlight (DVDNavSrc * src)
     if (src->button != 0) {
       src->button = 0;
 
-      event = dvdnavsrc_make_dvd_event (src, "dvd-spu-reset-highlight", NULL);
-      gst_pad_push (src->srcpad, GST_DATA (event));
+      gst_pad_push_event (GST_BASE_SRC_PAD (src),
+          gst_dvd_nav_src_make_dvd_event (src, "dvd-spu-reset-highlight",
+              NULL, NULL));
     }
     return;
   }
 
-  DVDNAV_CALL (dvdnav_get_highlight_area, (pci, button, 0, &area), src);
+  if (dvdnav_get_highlight_area (pci, button, 0, &area) != DVDNAV_STATUS_OK) {
+    GST_ELEMENT_ERROR (src, LIBRARY, FAILED, (NULL),
+        ("dvdnav_get_highlight_area: %s", dvdnav_err_to_string (src->dvdnav)));
+    return;
+  }
 
   /* Check if we have a new button number, or a new highlight region. */
-  if (button != src->button ||
+  if (button != src->button || force ||
       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,
+    event = gst_dvd_nav_src_make_dvd_event (src,
         "dvd-spu-highlight",
         "button", G_TYPE_INT, (gint) button,
         "palette", G_TYPE_INT, (gint) area.palette,
@@ -885,12 +740,12 @@ dvdnavsrc_update_highlight (DVDNavSrc * src)
     src->button = button;
 
     GST_DEBUG ("Sending dvd-spu-highlight for button %d", button);
-    gst_pad_push (src->srcpad, GST_DATA (event));
+    gst_pad_push_event (GST_BASE_SRC_PAD (src), event);
   }
 }
 
 static void
-dvdnavsrc_user_op (DVDNavSrc * src, int op)
+gst_dvd_nav_src_user_op (GstDvdNavSrc * src, gint op)
 {
   pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav);
 
@@ -995,84 +850,73 @@ dvdnavsrc_user_op (DVDNavSrc * src, int op)
       break;
   }
   return;
+
 naverr:
   GST_WARNING_OBJECT (src, "user op %d failure: %s",
       op, dvdnav_err_to_string (src->dvdnav));
 }
 
 #ifndef GST_DISABLE_GST_DEBUG
-static gchar *
+static const gchar *
 dvdnav_get_event_name (int event)
 {
   switch (event) {
     case DVDNAV_BLOCK_OK:
       return "DVDNAV_BLOCK_OK";
-      break;
     case DVDNAV_NOP:
       return "DVDNAV_NOP";
-      break;
     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;
     case DVDNAV_AUDIO_STREAM_CHANGE:
       return "DVDNAV_AUDIO_STREAM_CHANGE";
-      break;
     case DVDNAV_VTS_CHANGE:
       return "DVDNAV_VTS_CHANGE";
-      break;
     case DVDNAV_CELL_CHANGE:
       return "DVDNAV_CELL_CHANGE";
-      break;
     case DVDNAV_NAV_PACKET:
       return "DVDNAV_NAV_PACKET";
-      break;
     case DVDNAV_STOP:
       return "DVDNAV_STOP";
-      break;
     case DVDNAV_HIGHLIGHT:
       return "DVDNAV_HIGHLIGHT";
-      break;
     case DVDNAV_SPU_CLUT_CHANGE:
       return "DVDNAV_SPU_CLUT_CHANGE";
-      break;
     case DVDNAV_HOP_CHANNEL:
       return "DVDNAV_HOP_CHANNEL";
+    default:
       break;
   }
   return "UNKNOWN";
 }
 
-static gchar *
+static const gchar *
 dvdnav_get_read_domain_name (dvd_read_domain_t domain)
 {
   switch (domain) {
     case DVD_READ_INFO_FILE:
       return "DVD_READ_INFO_FILE";
-      break;
     case DVD_READ_INFO_BACKUP_FILE:
       return "DVD_READ_INFO_BACKUP_FILE";
-      break;
     case DVD_READ_MENU_VOBS:
       return "DVD_READ_MENU_VOBS";
-      break;
     case DVD_READ_TITLE_VOBS:
       return "DVD_READ_TITLE_VOBS";
+    default:
       break;
   }
   return "UNKNOWN";
 }
 
 static void
-dvdnavsrc_print_event (DVDNavSrc * src, guint8 * data, int event, int len)
+gst_dvd_nav_src_print_event (GstDvdNavSrc * src, guint8 * data, int event,
+    int len)
 {
   g_return_if_fail (src != NULL);
-  g_return_if_fail (GST_IS_DVDNAVSRC (src));
+  g_return_if_fail (GST_IS_DVD_NAV_SRC (src));
 
   GST_DEBUG_OBJECT (src, "dvdnavsrc (%p): event: %s", src,
       dvdnav_get_event_name (event));
@@ -1081,39 +925,34 @@ dvdnavsrc_print_event (DVDNavSrc * src, guint8 * data, int event, int len)
       break;
     case DVDNAV_NOP:
       break;
-    case DVDNAV_STILL_FRAME:
-    {
+    case DVDNAV_STILL_FRAME:{
       dvdnav_still_event_t *event = (dvdnav_still_event_t *) data;
 
       GST_DEBUG_OBJECT (src, "  still frame: %d seconds", event->length);
-    }
       break;
-    case DVDNAV_WAIT:
-    {
     }
+    case DVDNAV_WAIT:
       break;
-    case DVDNAV_SPU_STREAM_CHANGE:
-    {
-      dvdnav_spu_stream_change_event_t *event =
-          (dvdnav_spu_stream_change_event_t *) data;
+    case DVDNAV_SPU_STREAM_CHANGE:{
+      dvdnav_spu_stream_change_event_t *event;
+
+      event = (dvdnav_spu_stream_change_event_t *) data;
       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:
-    {
+    }
+    case DVDNAV_AUDIO_STREAM_CHANGE:{
       dvdnav_audio_stream_change_event_t *event =
           (dvdnav_audio_stream_change_event_t *) data;
       GST_DEBUG_OBJECT (src, "  physical: %d", event->physical);
       GST_DEBUG_OBJECT (src, "  logical: %d", event->logical);
-    }
       break;
-    case DVDNAV_VTS_CHANGE:
-    {
+    }
+    case DVDNAV_VTS_CHANGE:{
       dvdnav_vts_change_event_t *event = (dvdnav_vts_change_event_t *) data;
 
       GST_DEBUG_OBJECT (src, "  old_vtsN: %d", event->old_vtsN);
@@ -1122,24 +961,17 @@ dvdnavsrc_print_event (DVDNavSrc * src, guint8 * data, int event, int len)
       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; */
-      /* FIXME: Print something relevant here. */
     }
+    case DVDNAV_CELL_CHANGE:
       break;
-    case DVDNAV_NAV_PACKET:
-    {
+    case DVDNAV_NAV_PACKET:{
       /* FIXME: Print something relevant here. */
-    }
       break;
+    }
     case DVDNAV_STOP:
       break;
-    case DVDNAV_HIGHLIGHT:
-    {
+    case DVDNAV_HIGHLIGHT:{
       dvdnav_highlight_event_t *event = (dvdnav_highlight_event_t *) data;
 
       GST_DEBUG_OBJECT (src, "  display: %s",
@@ -1152,21 +984,22 @@ dvdnavsrc_print_event (DVDNavSrc * src, guint8 * data, int event, int len)
         GST_DEBUG_OBJECT (src, "  pts: %u", event->pts);
         GST_DEBUG_OBJECT (src, "  button: %u", event->buttonN);
       }
-    }
       break;
+    }
     case DVDNAV_SPU_CLUT_CHANGE:
       break;
     case DVDNAV_HOP_CHANNEL:
       break;
-    default:
+    default:{
       GST_DEBUG_OBJECT (src, "  event id: %d", event);
       break;
+    }
   }
 }
 #endif /* GST_DISABLE_GST_DEBUG */
 
 static GstEvent *
-dvdnavsrc_make_dvd_event (DVDNavSrc * src, const gchar * event_name,
+gst_dvd_nav_src_make_dvd_event (GstDvdNavSrc * src, const gchar * event_name,
     const gchar * firstfield, ...)
 {
   GstEvent *event;
@@ -1183,63 +1016,57 @@ dvdnavsrc_make_dvd_event (DVDNavSrc * src, const gchar * event_name,
   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;
+  event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, 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
+  GST_LOG_OBJECT (src, "created event %" GST_PTR_FORMAT, event);
 
   return event;
 }
 
-static GstEvent *
-dvdnavsrc_make_dvd_nav_packet_event (DVDNavSrc * src, const pci_t * pci)
+static void
+gst_dvd_nav_src_structure_set_uint64 (GstStructure * structure,
+    const gchar * name, guint64 val)
+{
+  GValue gvalue = { 0, };
+
+  g_value_init (&gvalue, G_TYPE_UINT64);
+  g_value_set_uint64 (&gvalue, val);
+  gst_structure_set_value (structure, name, &gvalue);
+}
+
+static void
+gst_dvd_nav_src_push_dvd_nav_packet_event (GstDvdNavSrc * 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. */
+  /* Build the event 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;
+  gst_dvd_nav_src_structure_set_uint64 (structure, "start_ptm",
+      MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm));
+  gst_dvd_nav_src_structure_set_uint64 (structure, "end_ptm",
+      MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm));
+  gst_dvd_nav_src_structure_set_uint64 (structure, "cell_start",
+      src->cell_start);
+  gst_dvd_nav_src_structure_set_uint64 (structure, "pg_start", src->pg_start);
 
-#ifndef GST_DISABLE_GST_DEBUG
-  {
-    gchar *text = gst_structure_to_string (structure);
+  /* Create the DVD event and put the structure into it. */
+  event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
 
-    GST_LOG_OBJECT (src, "creating event \"%s\"", text);
-    g_free (text);
-  }
-#endif
+  GST_LOG_OBJECT (src, "pushing nav packet event %" GST_PTR_FORMAT, event);
 
-  return event;
+  gst_pad_push_event (GST_BASE_SRC_PAD (src), event);
 }
 
-static GstEvent *
-dvdnavsrc_make_clut_change_event (DVDNavSrc * src, const guint * clut)
+static void
+gst_dvd_nav_src_push_clut_change_event (GstDvdNavSrc * src, const guint * clut)
 {
   GstEvent *event;
   GstStructure *structure;
-  guchar name[16];
+  gchar name[16];
   int i;
 
   structure = gst_structure_new ("application/x-gst-dvd",
@@ -1252,287 +1079,536 @@ dvdnavsrc_make_clut_change_event (DVDNavSrc * src, const guint * clut)
   }
 
   /* 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);
+  event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
 
-    GST_LOG_OBJECT (src, "creating event \"%s\"", text);
-    g_free (text);
-  }
-#endif
+  GST_LOG_OBJECT (src, "pushing clut change event %" GST_PTR_FORMAT, event);
 
-  return event;
+  gst_pad_push_event (GST_BASE_SRC_PAD (src), event);
 }
 
-static void
-dvdnavsrc_loop (GstElement * element)
+/* Use libdvdread to read and cache info from the IFO file about
+ * streams in each VTS
+ */
+static gboolean
+read_vts_info (GstDvdNavSrc * src)
 {
-  DVDNavSrc *src = DVDNAVSRC (element);
-  int event, len;
-  guint8 *data;
-  GstData *send_data;
+  dvd_reader_t *dvdi;
+  ifo_handle_t *ifo;
+  gint i;
+  gint n_vts;
+
+  if (src->vts_attrs) {
+    g_array_free (src->vts_attrs, TRUE);
+    src->vts_attrs = NULL;
+  }
 
-  g_return_if_fail (dvdnavsrc_is_open (src));
+  dvdi = DVDOpen (src->device);
+  if (!dvdi)
+    return FALSE;
 
-  if (src->did_seek || src->need_newmedia) {
-    GstEvent *event;
+  ifo = ifoOpen (dvdi, 0);
+  if (!ifo) {
+    GST_ERROR ("Can't open VMG info");
+    return FALSE;
+  }
+  n_vts = ifo->vts_atrt->nr_of_vtss;
+  memcpy (&src->vmgm_attr, ifo->vmgi_mat, sizeof (vmgi_mat_t));
+  ifoClose (ifo);
+
+  GST_DEBUG ("Reading IFO info for %d VTSs", n_vts);
+  src->vts_attrs = g_array_sized_new (FALSE, TRUE, sizeof (vtsi_mat_t),
+      n_vts + 1);
+  if (!src->vts_attrs)
+    return FALSE;
+  g_array_set_size (src->vts_attrs, n_vts + 1);
 
-    src->did_seek = FALSE;
-    GST_INFO_OBJECT (src, "sending discont");
+  for (i = 1; i <= n_vts; i++) {
+    ifo = ifoOpen (dvdi, i);
+    if (!ifo) {
+      GST_ERROR ("Can't open VTS %d", i);
+      return FALSE;
+    }
 
-    event = gst_event_new_discontinuous (src->need_newmedia, 0);
+    GST_DEBUG ("VTS %d, Menu has %d audio %d subpictures. "
+        "Title has %d and %d", i,
+        ifo->vtsi_mat->nr_of_vtsm_audio_streams,
+        ifo->vtsi_mat->nr_of_vtsm_subp_streams,
+        ifo->vtsi_mat->nr_of_vts_audio_streams,
+        ifo->vtsi_mat->nr_of_vts_subp_streams);
 
-    src->need_flush = FALSE;
-    src->need_newmedia = FALSE;
-    gst_pad_push (src->srcpad, GST_DATA (event));
-    return;
+    memcpy (&g_array_index (src->vts_attrs, vtsi_mat_t, i),
+        ifo->vtsi_mat, sizeof (vtsi_mat_t));
+
+    ifoClose (ifo);
   }
+  DVDClose (dvdi);
+  return TRUE;
+}
 
-  if (src->need_flush) {
-    src->need_flush = FALSE;
-    GST_INFO_OBJECT (src, "sending flush");
-    gst_pad_push (src->srcpad, GST_DATA (gst_event_new_flush ()));
-    return;
+static gboolean
+gst_dvd_nav_src_push_titlelang_event (GstDvdNavSrc * src)
+{
+  vtsi_mat_t *vts_attr;
+  audio_attr_t *a_attrs;
+  subp_attr_t *s_attrs;
+  gint n_audio, n_subp;
+  GstStructure *s;
+  GstEvent *e;
+  gint i;
+  gchar lang_code[3] = { '\0', '\0', '\0' };
+  gchar *t;
+
+  if (src->vts_attrs == NULL || src->cur_vts >= src->vts_attrs->len) {
+    if (src->vts_attrs)
+      GST_ERROR ("No stream info for VTS %d (have %d)", src->cur_vts,
+          src->vts_attrs->len);
+    else
+      GST_ERROR ("No stream info");
+    return FALSE;
   }
 
-  /* 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);
+  if (src->domain == GST_DVD_NAV_SRC_DOMAIN_VMGM) {
+    vts_attr = NULL;
+    a_attrs = &src->vmgm_attr.vmgm_audio_attr;
+    n_audio = MIN (1, src->vmgm_attr.nr_of_vmgm_audio_streams);
+    s_attrs = &src->vmgm_attr.vmgm_subp_attr;
+    n_subp = MIN (1, src->vmgm_attr.nr_of_vmgm_subp_streams);
+  } else {
+    vts_attr = &g_array_index (src->vts_attrs, vtsi_mat_t, src->cur_vts);
+    a_attrs = vts_attr->vts_audio_attr;
+    n_audio = vts_attr->nr_of_vts_audio_streams;
+    s_attrs = vts_attr->vts_subp_attr;
+    n_subp = vts_attr->nr_of_vts_subp_streams;
+  }
 
-    DVDNAV_CALL (dvdnav_get_next_block, (src->dvdnav, data, &event, &len), src);
+  /* build event */
 
-    switch (event) {
-      case DVDNAV_NOP:
-        break;
+  s = gst_structure_new ("application/x-gst-event",
+      "event", G_TYPE_STRING, "dvd-lang-codes", NULL);
+  e = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
 
-      case DVDNAV_BLOCK_OK:
-        send_data = GST_DATA (src->cur_buf);
-        src->cur_buf = NULL;
-        break;
+  /* audio */
+  for (i = 0; i < n_audio; i++) {
+    const audio_attr_t *a = a_attrs + i;
 
-      case DVDNAV_STILL_FRAME:
-      {
-        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 unlimited pause");
-            src->pause_mode = DVDNAVSRC_PAUSE_UNLIMITED;
-          } else {
-            src->pause_mode = DVDNAVSRC_PAUSE_LIMITED;
-            src->pause_end = current_time + info->length * GST_SECOND;
-            GST_INFO_OBJECT (src,
-                "starting limited pause: %d seconds at %llu until %llu",
-                info->length, current_time, src->pause_end);
-          }
+    t = g_strdup_printf ("audio-%d-format", i);
+    gst_structure_set (s, t, G_TYPE_INT, (int) a->audio_format, NULL);
+    g_free (t);
 
-          /* 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;
-        }
+    GST_DEBUG ("Audio stream %d is format %d", i, (int) a->audio_format);
 
-        if (src->pause_mode == DVDNAVSRC_PAUSE_UNLIMITED ||
-            current_time < src->pause_end) {
-          GstEvent *event;
-
-          /* 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. */
-          event = gst_event_new (GST_EVENT_EMPTY);
-          send_data = GST_DATA (event);
-          GST_EVENT_TIMESTAMP (event) =
-              gst_element_get_time (GST_ELEMENT (src));
+    if (a->lang_type) {
+      t = g_strdup_printf ("audio-%d-language", i);
+      lang_code[0] = (a->lang_code >> 8) & 0xff;
+      lang_code[1] = a->lang_code & 0xff;
+      gst_structure_set (s, t, G_TYPE_STRING, lang_code, NULL);
+      g_free (t);
 
-          break;
-        } else {
-          /* We reached the end of the pause. */
-          src->pause_mode = DVDNAVSRC_PAUSE_OFF;
-          DVDNAV_CALL (dvdnav_still_skip, (src->dvdnav), src);
-        }
-      }
-        break;
+      GST_DEBUG ("Audio stream %d is language %s", i, lang_code);
+    } else
+      GST_DEBUG ("Audio stream %d - no language", i, lang_code);
+  }
 
-      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;
+  /* subtitle */
+  for (i = 0; i < n_subp; i++) {
+    const subp_attr_t *u = s_attrs + i;
+
+    t = g_strdup_printf ("subtitle-%d-language", i);
+    if (u->type) {
+      lang_code[0] = (u->lang_code >> 8) & 0xff;
+      lang_code[1] = u->lang_code & 0xff;
+      gst_structure_set (s, t, G_TYPE_STRING, lang_code, NULL);
+    } else {
+      gst_structure_set (s, t, G_TYPE_STRING, "MENU", NULL);
+    }
+    g_free (t);
 
-      case DVDNAV_STOP:
-        GST_INFO_OBJECT (src, "sending eos");
-        gst_element_set_eos (GST_ELEMENT (src));
-        dvdnavsrc_close (src);
+    GST_DEBUG ("Subtitle stream %d is language %s", i,
+        lang_code[0] ? lang_code : "NONE");
+  }
 
-        send_data = GST_DATA (gst_event_new (GST_EVENT_EOS));
-        break;
+  gst_pad_push_event (GST_BASE_SRC_PAD (src), e);
+  return TRUE;
+}
 
-      case DVDNAV_CELL_CHANGE:
-        dvdnavsrc_update_streaminfo (src);
-        break;
+static GstFlowReturn
+gst_dvd_nav_src_process_next_block (GstDvdNavSrc * src, GstBuffer ** p_buf)
+{
+  dvdnav_status_t navret;
+  guint8 *data;
+  gint event, len;
 
-      case DVDNAV_NAV_PACKET:
-      {
-        pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav);
+  if (src->cur_buf == NULL)
+    src->cur_buf = gst_buffer_new_and_alloc (DVD_VIDEO_LB_LEN);
 
-        /* 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);
-          }
-        }
+  data = GST_BUFFER_DATA (src->cur_buf);
+  len = GST_BUFFER_SIZE (src->cur_buf);
 
-        dvdnavsrc_update_highlight (src);
+  navret = dvdnav_get_next_block (src->dvdnav, data, &event, &len);
+  if (navret != DVDNAV_STATUS_OK) {
+    GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
+        ("dvdnav_get_next_block: %s", dvdnav_err_to_string (src->dvdnav)));
+    return GST_FLOW_ERROR;
+  }
 
-        /* Send a dvd nav packet event. */
-        send_data = GST_DATA (dvdnavsrc_make_dvd_nav_packet_event (src, pci));
-      }
-        break;
+  switch (event) {
+    case DVDNAV_NOP:
+      break;
+    case DVDNAV_BLOCK_OK:{
+      *p_buf = src->cur_buf;
+      src->cur_buf = NULL;
+      break;
+    }
+    case DVDNAV_STILL_FRAME:{
+      dvdnav_still_event_t *info = (dvdnav_still_event_t *) data;
 
-      case DVDNAV_SPU_CLUT_CHANGE:
-        send_data = GST_DATA (dvdnavsrc_make_clut_change_event (src,
-                (guint *) data));
-        break;
+      if (src->pause_mode == GST_DVD_NAV_SRC_PAUSE_OFF) {
+        gst_dvd_nav_src_print_event (src, data, event, len);
 
-      case DVDNAV_VTS_CHANGE:
-      {
-        dvdnavsrc_set_domain (src);
+        /* We just saw a still frame. Start a pause now. */
+        if (info->length == 0xff) {
+          GST_INFO_OBJECT (src, "starting unlimited pause");
+          src->pause_mode = GST_DVD_NAV_SRC_PAUSE_UNLIMITED;
+          src->pause_remain = -1;
+        } else {
+          src->pause_mode = GST_DVD_NAV_SRC_PAUSE_LIMITED;
+          src->pause_remain = info->length * GST_SECOND;
+/* FIXME */
+#if 0
+          GST_INFO_OBJECT (src,
+              "starting limited pause: %d seconds at %llu",
+              info->length, gst_element_get_time (GST_ELEMENT (src)));
+#endif
+        }
 
-        send_data = GST_DATA (dvdnavsrc_make_dvd_event (src,
-                "dvd-vts-change", "domain",
-                G_TYPE_INT, (gint) src->domain, NULL));
-      }
+        /* 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");
+        gst_pad_push_event (GST_BASE_SRC_PAD (src),
+            gst_dvd_nav_src_make_dvd_event (src, "dvd-spu-still-frame",
+                NULL, 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);
-
-        if (phys < 0 || phys > DVDNAVSRC_MAX_AUDIO_STREAMS) {
-          phys = -1;
+      if (src->pause_mode == GST_DVD_NAV_SRC_PAUSE_UNLIMITED ||
+          src->pause_remain > 0) {
+/* FIXME */
+#if 0
+        GstEvent *event;
+
+        /* Send a filler event to keep the pipeline going */
+        event = gst_event_new_filler_stamped (GST_CLOCK_TIME_NONE,
+            MIN (src->pause_remain, DVD_NAV_SRC_PAUSE_INTERVAL));
+        send_data = GST_DATA (event);
+
+        GST_DEBUG_OBJECT (src,
+            "Pause mode %d, Sending filler at %" G_GUINT64_FORMAT
+            ", dur %" G_GINT64_FORMAT ", remain %" G_GINT64_FORMAT,
+            src->pause_mode, gst_element_get_time (GST_ELEMENT (src)),
+            MIN (src->pause_remain, DVD_NAV_SRC_PAUSE_INTERVAL),
+            src->pause_remain);
+#endif
+        if (src->pause_mode == GST_DVD_NAV_SRC_PAUSE_LIMITED) {
+          if (src->pause_remain < GST_DVD_NAV_SRC_PAUSE_INTERVAL)
+            src->pause_remain = 0;
+          else
+            src->pause_remain -= GST_DVD_NAV_SRC_PAUSE_INTERVAL;
         }
 
-        if (phys == src->audio_phys &&
-            dvdnav_get_active_audio_stream (src->dvdnav) == src->audio_log) {
-          /* Audio state hasn't changed. */
-          break;
-        }
+        /* If pause isn't finished, discont because time isn't actually
+         * advancing */
+        if (src->pause_mode == GST_DVD_NAV_SRC_PAUSE_UNLIMITED ||
+            src->pause_remain > 0)
+          src->did_seek = TRUE;
 
-        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;
+      } else {
+        /* We reached the end of the pause. */
+        src->pause_mode = GST_DVD_NAV_SRC_PAUSE_OFF;
+        if (dvdnav_still_skip (src->dvdnav) != DVDNAV_STATUS_OK) {
+          GST_WARNING_OBJECT (src, "dvdnav_still_skip failed: %s",
+              dvdnav_err_to_string (src->dvdnav));
+        }
+        /* Schedule a discont to reset the time */
+        src->did_seek = TRUE;
+      }
+      break;
+    }
 
-      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;
+    case DVDNAV_WAIT:{
+/* was commented out already: */
+#if 0
+      GstEvent *event;
 
-        dvdnavsrc_print_event (src, data, event, len);
+      /* 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");
 
-        if (phys < 0 || phys > DVDNAVSRC_MAX_SPU_STREAMS) {
-          phys = -1;
-        }
+      gst_element_wait (GST_ELEMENT (src),
+          gst_element_get_time (GST_ELEMENT (src)) + 1.5 * GST_SECOND);
+#endif
+      if (dvdnav_wait_skip (src->dvdnav) != DVDNAV_STATUS_OK) {
+        GST_WARNING_OBJECT (src, "dvdnav_wait_skip failed: %s",
+            dvdnav_err_to_string (src->dvdnav));
+      }
 
-        if (phys == src->subp_phys &&
-            dvdnav_get_active_spu_stream (src->dvdnav) == src->subp_log) {
-          /* Subpicture state hasn't changed. */
-          break;
+/* was commented out already: */
+#if 0
+      event = gst_event_new_filler ();
+      send_data = GST_DATA (event);
+#endif
+      break;
+    }
+    case DVDNAV_STOP:{
+      GST_INFO_OBJECT (src, "stop - EOS");
+      return GST_FLOW_UNEXPECTED;
+    }
+    case DVDNAV_CELL_CHANGE:{
+      dvdnav_cell_change_event_t *event = (dvdnav_cell_change_event_t *) data;
+
+      src->pgc_length = MPEGTIME_TO_GSTTIME (event->pgc_length);
+      src->cell_start = MPEGTIME_TO_GSTTIME (event->cell_start);
+      src->pg_start = MPEGTIME_TO_GSTTIME (event->pg_start);
+
+      GST_LOG_OBJECT (src, "New cell. PGC Len %" GST_TIME_FORMAT
+          " cell_start %" GST_TIME_FORMAT ", pg_start %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (src->pgc_length),
+          GST_TIME_ARGS (src->cell_start), GST_TIME_ARGS (src->pg_start));
+      gst_dvd_nav_src_update_streaminfo (src);
+      break;
+    }
+    case DVDNAV_NAV_PACKET:{
+      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);
         }
+      }
 
-        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));
+      gst_dvd_nav_src_update_highlight (src, FALSE);
+
+      /* Send a dvd nav packet event. */
+      gst_dvd_nav_src_push_dvd_nav_packet_event (src, pci);
+      break;
+    }
+    case DVDNAV_SPU_CLUT_CHANGE:{
+      /* FIXME: does this work on 64-bit/big-endian machines? (tpm) */
+      gst_dvd_nav_src_push_clut_change_event (src, (guint *) data);
+      break;
+    }
+    case DVDNAV_VTS_CHANGE:{
+      dvdnav_vts_change_event_t *event = (dvdnav_vts_change_event_t *) data;
+
+      gst_dvd_nav_src_set_domain (src);
+
+      if (src->domain == GST_DVD_NAV_SRC_DOMAIN_VMGM)
+        src->cur_vts = 0;
+      else
+        src->cur_vts = event->new_vtsN;
+
+/* FIXME */
+#if 0
+      gst_pad_push_event (GST_BASE_SRC_PAD (src),
+          gst_event_new_discontinuous (FALSE, GST_FORMAT_UNDEFINED));
+#endif
+
+      if (src->domain == GST_DVD_NAV_SRC_DOMAIN_VTSM ||
+          src->domain == GST_DVD_NAV_SRC_DOMAIN_VTS ||
+          src->domain == GST_DVD_NAV_SRC_DOMAIN_VMGM) {
+        if (!gst_dvd_nav_src_push_titlelang_event (src)) {
+          GST_ELEMENT_ERROR (src, LIBRARY, FAILED,
+              (_("Invalid title information on DVD.")), GST_ERROR_SYSTEM);
+        }
       }
-        break;
 
-      case DVDNAV_HIGHLIGHT:
-        dvdnavsrc_print_event (src, data, event, len);
+      gst_pad_push_event (GST_BASE_SRC_PAD (src),
+          gst_dvd_nav_src_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;
+      gint phys;
 
-        dvdnavsrc_update_highlight (src);
-        break;
+      info = (dvdnav_audio_stream_change_event_t *) data;
+      phys = info->physical;
 
-      case DVDNAV_HOP_CHANNEL:
-        dvdnavsrc_print_event (src, data, event, len);
+      gst_dvd_nav_src_print_event (src, data, event, len);
 
-        src->button = 0;
-        src->pause_mode = DVDNAVSRC_PAUSE_OFF;
-        send_data = GST_DATA (gst_event_new_flush ());
+      if (phys < 0 || phys > GST_DVD_NAV_SRC_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);
+      gst_pad_push_event (GST_BASE_SRC_PAD (src),
+          gst_dvd_nav_src_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_SPU_STREAM_CHANGE:{
+      dvdnav_spu_stream_change_event_t *info;
+      gint phys;
+
+      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. */
+      phys = info->physical_wide;
+
+      gst_dvd_nav_src_print_event (src, data, event, len);
 
-      default:
-        g_error ("dvdnavsrc: Unknown dvdnav event %d", event);
+      if (phys < 0 || phys > GST_DVD_NAV_SRC_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);
+
+      gst_pad_push_event (GST_BASE_SRC_PAD (src),
+          gst_dvd_nav_src_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:{
+      gst_dvd_nav_src_print_event (src, data, event, len);
+
+      gst_dvd_nav_src_update_highlight (src, FALSE);
+      break;
+    }
+    case DVDNAV_HOP_CHANNEL:{
+      gst_dvd_nav_src_print_event (src, data, event, len);
+
+      src->button = 0;
+      src->pause_mode = GST_DVD_NAV_SRC_PAUSE_OFF;
+      src->need_flush = TRUE;
+      /* was commented out already: */
+      /* send_data = GST_DATA (gst_event_new_flush ()); */
+      break;
+    }
+    default:
+      g_error ("dvdnavsrc: Unknown dvdnav event %d", event);
+      break;
+  }
+
+  return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_dvd_nav_src_create (GstPushSrc * pushsrc, GstBuffer ** p_buf)
+{
+  GstFlowReturn ret;
+  GstDvdNavSrc *src;
+
+  src = GST_DVD_NAV_SRC (pushsrc);
+
+  if (src->new_seek) {
+    gst_dvd_nav_src_tca_seek (src, src->title, src->chapter, src->angle);
+    src->new_seek = FALSE;
+  }
+
+  *p_buf = NULL;
+
+  /* Loop processing blocks until there is data to send. */
+  do {
+    if (src->need_flush) {
+      if (src->pause_mode != GST_DVD_NAV_SRC_PAUSE_OFF) {
+        if (dvdnav_still_skip (src->dvdnav) != DVDNAV_STATUS_OK) {
+          GST_ELEMENT_ERROR (src, LIBRARY, FAILED, (NULL),
+              ("dvdnav_still_skip: %s", dvdnav_err_to_string (src->dvdnav)));
+          return GST_FLOW_ERROR;
+        }
+        src->pause_mode = GST_DVD_NAV_SRC_PAUSE_OFF;
+      }
+
+      src->need_flush = FALSE;
+      GST_INFO_OBJECT (src, "sending flush");
+      gst_pad_push_event (GST_BASE_SRC_PAD (src), gst_event_new_flush_start ());
+      gst_pad_push_event (GST_BASE_SRC_PAD (src), gst_event_new_flush_stop ());
+      gst_dvd_nav_src_update_highlight (src, TRUE);
+    }
+
+    if (src->pause_mode == GST_DVD_NAV_SRC_PAUSE_OFF) {
+/* FIXME */
+      if (src->did_seek) {
+        GstEvent *event;
+
+        src->did_seek = FALSE;
+        GST_INFO_OBJECT (src, "sending newsegment event with offset %"
+            G_GINT64_FORMAT, src->pending_offset);
+
+        if (src->pending_offset != -1) {
+          event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
+              src->pending_offset, -1, src->pending_offset);
+          src->pending_offset = -1;
+        } else {
+          /* g_warning ("dvdnav: FIXME - what newsegment to send here?"); */
+          event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
+              0, -1, 0);
+          /* was: gst_event_new_discontinuous (FALSE, GST_FORMAT_UNDEFINED); */
+        }
+
+        gst_pad_push_event (GST_BASE_SRC_PAD (src), event);
+
+        /* Sent a discont, make sure to enable highlight */
+        src->button = 0;
+        gst_dvd_nav_src_update_highlight (src, TRUE);
+      }
     }
+    ret = gst_dvd_nav_src_process_next_block (src, p_buf);
   }
+  while (ret == GST_FLOW_OK && *p_buf == NULL);
 
-  gst_pad_push (src->srcpad, send_data);
+  src->seek_pending = FALSE;
+  return ret;
 }
 
-/* open the file, necessary to go to RUNNING state */
 static gboolean
-dvdnavsrc_open (DVDNavSrc * src)
+gst_dvd_nav_src_start (GstBaseSrc * basesrc)
 {
-  g_return_val_if_fail (src != NULL, FALSE);
-  g_return_val_if_fail (GST_IS_DVDNAVSRC (src), FALSE);
-  g_return_val_if_fail (!dvdnavsrc_is_open (src), FALSE);
-  g_return_val_if_fail (src->location != NULL, FALSE);
+  GstDvdNavSrc *src = GST_DVD_NAV_SRC (basesrc);
+  GstTagList *tags;
+  const char *title_str;
 
-  if (dvdnav_open (&src->dvdnav, (char *) src->location) != DVDNAV_STATUS_OK) {
+  if (!read_vts_info (src)) {
     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
-        (_("Failed to open DVD device '%s'."), src->location),
-        GST_ERROR_SYSTEM);
+        (_("Could not read title information for DVD.")), GST_ERROR_SYSTEM);
+    return FALSE;
+  }
+
+  if (dvdnav_open (&src->dvdnav, src->device) != DVDNAV_STATUS_OK) {
+    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
+        (_("Failed to open DVD device '%s'."), src->device));
     return FALSE;
   }
 
-  GST_OBJECT_FLAG_SET (src, DVDNAVSRC_OPEN);
+  if (dvdnav_set_PGC_positioning_flag (src->dvdnav, 1) != DVDNAV_STATUS_OK) {
+    GST_ELEMENT_ERROR (src, LIBRARY, FAILED,
+        (_("Failed to set PGC based seeking.")), GST_ERROR_SYSTEM);
+    return FALSE;
+  }
 
   /* Read the first block before seeking to force a libdvdnav internal
    * call to vm_start, otherwise it ignores our seek position.
@@ -1550,110 +1626,118 @@ dvdnavsrc_open (DVDNavSrc * src)
    * title==0 thing.
    */
 
+  src->title = src->uri_title;
+  src->chapter = src->uri_chapter;
+  src->angle = src->uri_angle;
+
   if (src->title > 0) {
-    unsigned char buf[2048];
-    int event, buflen = sizeof (buf);
+    dvdnav_status_t ret;
+    guint8 buf[DVD_SECTOR_SIZE];
+    gint event, buflen = sizeof (buf);
+
+    ret = dvdnav_get_next_block (src->dvdnav, buf, &event, &buflen);
+    if (ret != DVDNAV_STATUS_OK) {
+      GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
+          ("dvdnav_get_next_block: %s", 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);
 
-    if (!dvdnavsrc_tca_seek (src, src->title, src->chapter, src->angle)) {
+    gst_dvd_nav_src_print_event (src, buf, event, buflen);
+
+    if (!gst_dvd_nav_src_tca_seek (src, src->title, src->chapter, src->angle)) {
       return FALSE;
     }
   }
 
-  return TRUE;
-}
-
-/* close the file */
-static gboolean
-dvdnavsrc_close (DVDNavSrc * src)
-{
-  g_return_val_if_fail (src != NULL, FALSE);
-  g_return_val_if_fail (GST_IS_DVDNAVSRC (src), FALSE);
-  g_return_val_if_fail (dvdnavsrc_is_open (src), FALSE);
-  g_return_val_if_fail (src->dvdnav != NULL, FALSE);
+  tags = gst_tag_list_new ();
+  if (dvdnav_get_title_string (src->dvdnav, &title_str) == DVDNAV_STATUS_OK) {
+    gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
+        GST_TAG_TITLE, title_str, NULL);
+  }
 
-  DVDNAV_CALLVAL (dvdnav_close, (src->dvdnav), src, FALSE);
+  if (tags && gst_structure_n_fields ((GstStructure *) tags) > 0) {
+    gst_element_found_tags (GST_ELEMENT (src), tags);
+  }
 
-  GST_OBJECT_FLAG_UNSET (src, DVDNAVSRC_OPEN);
+  src->streaminfo = NULL;
+  src->did_seek = TRUE;
 
   return TRUE;
 }
 
-static GstStateChangeReturn
-dvdnavsrc_change_state (GstElement * element, GstStateChange transition)
+static gboolean
+gst_dvd_nav_src_stop (GstBaseSrc * basesrc)
 {
-  DVDNavSrc *src;
-
-  g_return_val_if_fail (GST_IS_DVDNAVSRC (element), GST_STATE_CHANGE_FAILURE);
-
-  src = DVDNAVSRC (element);
+  GstDvdNavSrc *src = GST_DVD_NAV_SRC (basesrc);
 
-  switch (transition) {
-    case GST_STATE_CHANGE_NULL_TO_READY:
-      break;
-    case GST_STATE_CHANGE_READY_TO_PAUSED:
-      if (!dvdnavsrc_is_open (src)) {
-        if (!dvdnavsrc_open (src)) {
-          return GST_STATE_CHANGE_FAILURE;
-        }
-      }
-      src->streaminfo = NULL;
-      src->need_newmedia = TRUE;
-      break;
-    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
-      break;
-    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
-      break;
-    case GST_STATE_CHANGE_PAUSED_TO_READY:
-      if (dvdnavsrc_is_open (src)) {
-        if (!dvdnavsrc_close (src)) {
-          return GST_STATE_CHANGE_FAILURE;
-        }
-      }
-      break;
-    case GST_STATE_CHANGE_READY_TO_NULL:
-      break;
+  if (src->dvdnav && dvdnav_close (src->dvdnav) != DVDNAV_STATUS_OK) {
+    GST_ELEMENT_ERROR (src, RESOURCE, CLOSE, (NULL),
+        ("dvdnav_close failed: %s", dvdnav_err_to_string (src->dvdnav)));
+    return FALSE;
   }
 
-  /* if we haven't failed already, give the parent class a chance to ;-) */
-  if (GST_ELEMENT_CLASS (parent_class)->change_state)
-    return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
-  return GST_STATE_CHANGE_SUCCESS;
-}
-
-static const GstEventMask *
-dvdnavsrc_get_event_mask (GstPad * pad)
-{
-  static const GstEventMask masks[] = {
-    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET |
-          GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
-    {GST_EVENT_FLUSH, 0},
-    {GST_EVENT_NAVIGATION, GST_EVENT_FLAG_NONE},
-    {0,}
-  };
-
-  return masks;
+  return TRUE;
 }
 
 static gboolean
-dvdnav_handle_navigation_event (DVDNavSrc * src, GstEvent * event)
+gst_dvd_nav_src_handle_navigation_event (GstDvdNavSrc * src, GstEvent * event)
 {
-  GstStructure *structure = event->event_data.structure.structure;
-  const char *event_type = gst_structure_get_string (structure, "event");
+  const GstStructure *structure;
+  const gchar *event_type;
 
-  g_return_val_if_fail (event != NULL, FALSE);
+  structure = gst_event_get_structure (event);
+  event_type = gst_structure_get_string (structure, "event");
 
   if (strcmp (event_type, "key-press") == 0) {
-    const char *key = gst_structure_get_string (structure, "key");
+    const gchar *key = gst_structure_get_string (structure, "key");
+
+    if (key == NULL)
+      return TRUE;
+
+    if (g_str_equal (key, "Return")) {
+      dvdnav_button_activate (src->dvdnav,
+          dvdnav_get_current_nav_pci (src->dvdnav));
+    } else if (g_str_equal (key, "Left")) {
+      dvdnav_left_button_select (src->dvdnav,
+          dvdnav_get_current_nav_pci (src->dvdnav));
+    } else if (g_str_equal (key, "Right")) {
+      dvdnav_right_button_select (src->dvdnav,
+          dvdnav_get_current_nav_pci (src->dvdnav));
+    } else if (g_str_equal (key, "Up")) {
+      dvdnav_upper_button_select (src->dvdnav,
+          dvdnav_get_current_nav_pci (src->dvdnav));
+    } else if (g_str_equal (key, "Down")) {
+      dvdnav_lower_button_select (src->dvdnav,
+          dvdnav_get_current_nav_pci (src->dvdnav));
+    } else if (g_str_equal (key, "m")) {
+      dvdnav_menu_call (src->dvdnav, DVD_MENU_Escape);
+    } else if (g_str_equal (key, "t")) {
+      dvdnav_menu_call (src->dvdnav, DVD_MENU_Title);
+    } else if (g_str_equal (key, "r")) {
+      dvdnav_menu_call (src->dvdnav, DVD_MENU_Root);
+    } else if (g_str_equal (key, "comma")) {
+      gint title = 0;
+      gint part = 0;
+
+      if (dvdnav_current_title_info (src->dvdnav, &title, &part) && title > 0
+          && part > 1) {
+        dvdnav_part_play (src->dvdnav, title, part - 1);
+        src->did_seek = TRUE;
+      }
+    } else if (g_str_equal (key, "period")) {
+      gint title = 0;
+      gint part = 0;
+
+      if (dvdnav_current_title_info (src->dvdnav, &title, &part) && title > 0) {
+        dvdnav_part_play (src->dvdnav, title, part + 1);
+        src->did_seek = TRUE;
+      }
+    }
 
-    g_assert (key != NULL);
     GST_DEBUG ("dvdnavsrc got a keypress: %s", key);
   } else if (strcmp (event_type, "mouse-move") == 0) {
-    double x, y;
+    gdouble x, y;
 
     gst_structure_get_double (structure, "pointer_x", &x);
     gst_structure_get_double (structure, "pointer_y", &y);
@@ -1661,12 +1745,13 @@ dvdnav_handle_navigation_event (DVDNavSrc * src, GstEvent * event)
     dvdnav_mouse_select (src->dvdnav,
         dvdnav_get_current_nav_pci (src->dvdnav), (int) x, (int) y);
 
-    dvdnavsrc_update_highlight (src);
+    gst_dvd_nav_src_update_highlight (src, FALSE);
   } else if (strcmp (event_type, "mouse-button-release") == 0) {
-    double x, y;
+    gdouble x, y;
 
     gst_structure_get_double (structure, "pointer_x", &x);
     gst_structure_get_double (structure, "pointer_y", &y);
+    GST_DEBUG_OBJECT (src, "Got click at %g, %g", x, y);
 
     dvdnav_mouse_activate (src->dvdnav,
         dvdnav_get_current_nav_pci (src->dvdnav), (int) x, (int) y);
@@ -1676,385 +1761,431 @@ dvdnav_handle_navigation_event (DVDNavSrc * src, GstEvent * event)
 }
 
 static gboolean
-dvdnavsrc_event (GstPad * pad, GstEvent * event)
+gst_dvd_nav_src_handle_seek_event (GstDvdNavSrc * src, GstEvent * event)
 {
-  DVDNavSrc *src;
-  gboolean res = TRUE;
-
-  src = DVDNAVSRC (gst_pad_get_parent (pad));
-
-  if (!GST_OBJECT_FLAG_IS_SET (src, DVDNAVSRC_OPEN))
+/* FIXME: */
+#if 0
+  gint64 offset;
+  gint format;
+  int titles, title, new_title;
+  int parts, part, new_part;
+  int angles, angle, new_angle;
+  int origin;
+  guint32 curoff, len;
+
+  format = GST_EVENT_SEEK_FORMAT (event);
+  offset = GST_EVENT_SEEK_OFFSET (event);
+
+  /* Disallow seek before the DVD VM has started or if we haven't finished a 
+   * previous seek yet */
+  if (dvdnav_get_position (src->dvdnav, &curoff, &len) != DVDNAV_STATUS_OK
+      || src->seek_pending)
     goto error;
 
-  switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_SEEK:
-    {
-      gint64 offset;
-      gint format;
-      int titles, title, new_title;
-      int parts, part, new_part;
-      int angles, angle, new_angle;
-      int origin;
-
-      format = GST_EVENT_SEEK_FORMAT (event);
-      offset = GST_EVENT_SEEK_OFFSET (event);
-
-      switch (format) {
-        case GST_FORMAT_BYTES:
-          switch (GST_EVENT_SEEK_METHOD (event)) {
-            case GST_SEEK_METHOD_SET:
-              origin = SEEK_SET;
-              break;
-            case GST_SEEK_METHOD_CUR:
-              origin = SEEK_CUR;
-              break;
-            case GST_SEEK_METHOD_END:
-              origin = SEEK_END;
-              break;
-            default:
-              goto error;
-          }
-          if (dvdnav_sector_search (src->dvdnav, (offset / DVD_SECTOR_SIZE),
-                  origin) != DVDNAV_STATUS_OK) {
-            goto error;
-          }
+  GST_DEBUG_OBJECT (src, "Seeking to %lld, format %d, method %d\n", offset,
+      format, GST_EVENT_SEEK_METHOD (event));
+
+  switch (format) {
+    case GST_FORMAT_BYTES:
+      switch (GST_EVENT_SEEK_METHOD (event)) {
+        case GST_SEEK_METHOD_SET:
+          origin = SEEK_SET;
+          src->pending_offset = offset;
+          break;
+        case GST_SEEK_METHOD_CUR:
+          origin = SEEK_CUR;
+          src->pending_offset = curoff + offset;
+          break;
+        case GST_SEEK_METHOD_END:
+          origin = SEEK_END;
+          src->pending_offset = len + offset;
+          break;
         default:
-          if (format == sector_format) {
-            switch (GST_EVENT_SEEK_METHOD (event)) {
-              case GST_SEEK_METHOD_SET:
-                origin = SEEK_SET;
-                break;
-              case GST_SEEK_METHOD_CUR:
-                origin = SEEK_CUR;
-                break;
-              case GST_SEEK_METHOD_END:
-                origin = SEEK_END;
-                break;
-              default:
-                goto error;
-            }
-            if (dvdnav_sector_search (src->dvdnav, offset, origin) !=
-                DVDNAV_STATUS_OK) {
-              goto error;
-            }
-          } else if (format == title_format) {
-            if (dvdnav_current_title_info (src->dvdnav, &title, &part) !=
-                DVDNAV_STATUS_OK) {
-              goto error;
-            }
-            switch (GST_EVENT_SEEK_METHOD (event)) {
-              case GST_SEEK_METHOD_SET:
-                new_title = offset;
-                break;
-              case GST_SEEK_METHOD_CUR:
-                new_title = title + offset;
-                break;
-              case GST_SEEK_METHOD_END:
-                if (dvdnav_get_number_of_titles (src->dvdnav, &titles) !=
-                    DVDNAV_STATUS_OK) {
-                  goto error;
-                }
-                new_title = titles + offset;
-                break;
-              default:
-                goto error;
-            }
-            if (dvdnav_title_play (src->dvdnav, new_title) != DVDNAV_STATUS_OK) {
-              goto error;
-            }
-          } else if (format == chapter_format) {
-            if (dvdnav_current_title_info (src->dvdnav, &title, &part) !=
-                DVDNAV_STATUS_OK) {
-              goto error;
-            }
-            switch (GST_EVENT_SEEK_METHOD (event)) {
-              case GST_SEEK_METHOD_SET:
-                new_part = offset;
-                break;
-              case GST_SEEK_METHOD_CUR:
-                new_part = part + offset;
-                break;
-              case GST_SEEK_METHOD_END:
-                if (dvdnav_get_number_of_titles (src->dvdnav, &parts) !=
-                    DVDNAV_STATUS_OK) {
-                  goto error;
-                }
-                new_part = parts + offset;
-                break;
-              default:
-                goto error;
-            }
-            /*if (dvdnav_part_search(src->dvdnav, new_part) != */
-            if (dvdnav_part_play (src->dvdnav, title, new_part) !=
-                DVDNAV_STATUS_OK) {
-              goto error;
-            }
-          } else if (format == angle_format) {
-            if (dvdnav_get_angle_info (src->dvdnav, &angle, &angles) !=
+          goto error;
+      }
+      if (dvdnav_sector_search (src->dvdnav, (offset / DVD_SECTOR_SIZE),
+              origin) != DVDNAV_STATUS_OK) {
+        goto error;
+      }
+      break;
+    default:
+      if (format == sector_format) {
+        switch (GST_EVENT_SEEK_METHOD (event)) {
+          case GST_SEEK_METHOD_SET:
+            origin = SEEK_SET;
+            break;
+          case GST_SEEK_METHOD_CUR:
+            origin = SEEK_CUR;
+            break;
+          case GST_SEEK_METHOD_END:
+            origin = SEEK_END;
+            break;
+          default:
+            goto error;
+        }
+        if (dvdnav_sector_search (src->dvdnav, offset, origin) !=
+            DVDNAV_STATUS_OK) {
+          goto error;
+        }
+      } else if (format == title_format) {
+        if (dvdnav_current_title_info (src->dvdnav, &title, &part) !=
+            DVDNAV_STATUS_OK) {
+          goto error;
+        }
+        switch (GST_EVENT_SEEK_METHOD (event)) {
+          case GST_SEEK_METHOD_SET:
+            new_title = offset;
+            break;
+          case GST_SEEK_METHOD_CUR:
+            new_title = title + offset;
+            break;
+          case GST_SEEK_METHOD_END:
+            if (dvdnav_get_number_of_titles (src->dvdnav, &titles) !=
                 DVDNAV_STATUS_OK) {
               goto error;
             }
-            switch (GST_EVENT_SEEK_METHOD (event)) {
-              case GST_SEEK_METHOD_SET:
-                new_angle = offset;
-                break;
-              case GST_SEEK_METHOD_CUR:
-                new_angle = angle + offset;
-                break;
-              case GST_SEEK_METHOD_END:
-                new_angle = angles + offset;
-                break;
-              default:
-                goto error;
-            }
-            if (dvdnav_angle_change (src->dvdnav, new_angle) !=
+            new_title = titles + offset;
+            break;
+          default:
+            goto error;
+        }
+        if (dvdnav_title_play (src->dvdnav, new_title) != DVDNAV_STATUS_OK) {
+          goto error;
+        }
+      } else if (format == chapter_format) {
+        if (dvdnav_current_title_info (src->dvdnav, &title, &part) !=
+            DVDNAV_STATUS_OK) {
+          goto error;
+        }
+        switch (GST_EVENT_SEEK_METHOD (event)) {
+          case GST_SEEK_METHOD_SET:
+            new_part = offset;
+            break;
+          case GST_SEEK_METHOD_CUR:
+            new_part = part + offset;
+            break;
+          case GST_SEEK_METHOD_END:
+            if (dvdnav_get_number_of_titles (src->dvdnav, &parts) !=
                 DVDNAV_STATUS_OK) {
               goto error;
             }
-          } else {
+            new_part = parts + offset;
+            break;
+          default:
             goto error;
-          }
+        }
+        /*if (dvdnav_part_search(src->dvdnav, new_part) != */
+        if (dvdnav_part_play (src->dvdnav, title, new_part) != DVDNAV_STATUS_OK) {
+          goto error;
+        }
+      } else if (format == angle_format) {
+        if (dvdnav_get_angle_info (src->dvdnav, &angle, &angles) !=
+            DVDNAV_STATUS_OK) {
+          goto error;
+        }
+        switch (GST_EVENT_SEEK_METHOD (event)) {
+          case GST_SEEK_METHOD_SET:
+            new_angle = offset;
+            break;
+          case GST_SEEK_METHOD_CUR:
+            new_angle = angle + offset;
+            break;
+          case GST_SEEK_METHOD_END:
+            new_angle = angles + offset;
+            break;
+          default:
+            goto error;
+        }
+        if (dvdnav_angle_change (src->dvdnav, new_angle) != DVDNAV_STATUS_OK) {
+          goto error;
+        }
+      } else {
+        goto error;
       }
-      src->did_seek = TRUE;
-      src->need_flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH;
-      break;
-    }
-    case GST_EVENT_NAVIGATION:
-      res = dvdnav_handle_navigation_event (src, event);
-      break;
-    case GST_EVENT_FLUSH:
-      src->need_flush = TRUE;
-      break;
-    default:
-      goto error;
   }
+  src->did_seek = TRUE;
+  src->seek_pending = TRUE;
+  src->need_flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH;
+  return TRUE;
 
-  if (FALSE) {
-  error:
-    res = FALSE;
-  }
-  gst_event_unref (event);
-
-  return res;
-}
-
-static const GstFormat *
-dvdnavsrc_get_formats (GstPad * pad)
-{
-  int i;
-  static GstFormat formats[] = {
-    GST_FORMAT_BYTES,
-    /*
-       GST_FORMAT_TIME,
-       GST_FORMAT_DEFAULT,
-     */
-    0,                          /* filled later */
-    0,                          /* filled later */
-    0,                          /* filled later */
-    0,                          /* filled later */
-    0
-  };
-  static gboolean format_initialized = FALSE;
-
-  if (!format_initialized) {
-    for (i = 0; formats[i] != 0; i++) {
-    }
-    formats[i++] = sector_format;
-    formats[i++] = title_format;
-    formats[i++] = chapter_format;
-    formats[i++] = angle_format;
-    format_initialized = TRUE;
-  }
+error:
 
-  return formats;
+  GST_DEBUG_OBJECT (src, "seek failed");
+#endif
+  return FALSE;
 }
 
 static gboolean
-dvdnavsrc_convert (GstPad * pad,
-    GstFormat src_format, gint64 src_value,
-    GstFormat * dest_format, gint64 * dest_value)
+gst_dvd_nav_src_src_event (GstBaseSrc * basesrc, GstEvent * event)
 {
-  DVDNavSrc *src;
+  GstDvdNavSrc *src = GST_DVD_NAV_SRC (basesrc);
+  gboolean res;
 
-  src = DVDNAVSRC (gst_pad_get_parent (pad));
+  GST_LOG_OBJECT (src, "handling %s event", GST_EVENT_TYPE_NAME (event));
 
-  if (!GST_OBJECT_FLAG_IS_SET (src, DVDNAVSRC_OPEN))
+  if (!gst_dvd_nav_src_is_open (src)) {
+    GST_DEBUG_OBJECT (src, "device not open yet");
     return FALSE;
+  }
 
-  switch (src_format) {
-    case GST_FORMAT_BYTES:
-      if (*dest_format == sector_format) {
-        *dest_value = src_value / DVD_SECTOR_SIZE;
-      } else
-        return FALSE;
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEEK:
+      res = gst_dvd_nav_src_handle_seek_event (src, event);
+      break;
+    case GST_EVENT_NAVIGATION:
+      res = gst_dvd_nav_src_handle_navigation_event (src, event);
+      break;
+    case GST_EVENT_FLUSH_START:        /* FIXME: hmm? */
+      src->need_flush = TRUE;
+      /* fall through */
     default:
-      if ((src_format == sector_format) && (*dest_format == GST_FORMAT_BYTES)) {
-        *dest_value = src_value * DVD_SECTOR_SIZE;
-      } else
-        return FALSE;
+      res = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
+      break;
   }
 
-  return TRUE;
+  return res;
 }
 
-static const GstQueryType *
-dvdnavsrc_get_query_types (GstPad * pad)
+static gboolean
+gst_dvd_nav_src_query_position (GstDvdNavSrc * src, GstFormat format,
+    gint64 * p_val)
 {
-  static const GstQueryType src_query_types[] = {
-    GST_QUERY_TOTAL,
-    GST_QUERY_POSITION,
-    0
-  };
+  guint32 pos, len;
+  gint32 title, angle, ch, x;
+
+  *p_val = -1;
+
+  if (format == sector_format) {
+    if (dvdnav_get_position (src->dvdnav, &pos, &len) == DVDNAV_STATUS_OK)
+      *p_val = pos;
+  } else if (format == GST_FORMAT_BYTES) {
+    if (dvdnav_get_position (src->dvdnav, &pos, &len) == DVDNAV_STATUS_OK)
+      *p_val = (gint64) pos *DVD_SECTOR_SIZE;
+  } else if (format == title_format) {
+    if (dvdnav_current_title_info (src->dvdnav, &title, &x) == DVDNAV_STATUS_OK)
+      *p_val = title;
+  } else if (format == chapter_format) {
+    if (dvdnav_current_title_info (src->dvdnav, &x, &ch) == DVDNAV_STATUS_OK)
+      *p_val = ch;
+  } else if (format == angle_format) {
+    if (dvdnav_get_angle_info (src->dvdnav, &angle, &x) == DVDNAV_STATUS_OK)
+      *p_val = angle;
+  }
 
-  return src_query_types;
+  return (*p_val != -1);
 }
 
 static gboolean
-dvdnavsrc_query (GstPad * pad, GstQueryType type,
-    GstFormat * format, gint64 * value)
+gst_dvd_nav_src_query_duration (GstDvdNavSrc * src, GstFormat format,
+    gint64 * p_val)
 {
-  gboolean res = TRUE;
-  DVDNavSrc *src;
-  int titles, title;
-  int parts, part;
-  int angles, angle;
-  unsigned int pos, len;
+  guint32 pos, len;
+  gint32 titles, t, angles, parts, x;
+
+  *p_val = -1;
+
+  if (format == GST_FORMAT_TIME) {
+    if (src->pgc_length != GST_CLOCK_TIME_NONE)
+      *p_val = src->pgc_length;
+  } else if (format == sector_format) {
+    if (dvdnav_get_position (src->dvdnav, &pos, &len) == DVDNAV_STATUS_OK)
+      *p_val = len;
+  } else if (format == GST_FORMAT_BYTES) {
+    if (dvdnav_get_position (src->dvdnav, &pos, &len) == DVDNAV_STATUS_OK)
+      *p_val = (gint64) len *DVD_SECTOR_SIZE;
+  } else if (format == title_format) {
+    if (dvdnav_get_number_of_titles (src->dvdnav, &titles) == DVDNAV_STATUS_OK)
+      *p_val = titles;
+  } else if (format == chapter_format) {
+    if (dvdnav_current_title_info (src->dvdnav, &t, &x) == DVDNAV_STATUS_OK &&
+        dvdnav_get_number_of_parts (src->dvdnav, t, &parts) == DVDNAV_STATUS_OK)
+      *p_val = parts;
+  } else if (format == angle_format) {
+    if (dvdnav_get_angle_info (src->dvdnav, &x, &angles) == DVDNAV_STATUS_OK)
+      *p_val = angles;
+  }
 
-  src = DVDNAVSRC (gst_pad_get_parent (pad));
+  return (*p_val != -1);
+}
+
+static gboolean
+gst_dvd_nav_src_query (GstBaseSrc * basesrc, GstQuery * query)
+{
+  GstDvdNavSrc *src = GST_DVD_NAV_SRC (basesrc);
+  GstFormat format;
+  gboolean res;
+  gint64 val;
 
-  if (!GST_OBJECT_FLAG_IS_SET (src, DVDNAVSRC_OPEN))
+  if (!gst_dvd_nav_src_is_open (src)) {
+    GST_DEBUG_OBJECT (src, "query failed: device not open yet");
     return FALSE;
+  }
 
-  switch (type) {
-    case GST_QUERY_TOTAL:
-      if (*format == sector_format) {
-        if (dvdnav_get_position (src->dvdnav, &pos, &len)
-            != DVDNAV_STATUS_OK) {
-          res = FALSE;
-        }
-        *value = len;
-      } else if (*format == GST_FORMAT_BYTES) {
-        if (dvdnav_get_position (src->dvdnav, &pos, &len) != DVDNAV_STATUS_OK) {
-          res = FALSE;
-        }
-        *value = len * DVD_SECTOR_SIZE;
-      } else if (*format == title_format) {
-        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) {
-          res = FALSE;
-        }
-        *value = parts;
-      } else if (*format == angle_format) {
-        if (dvdnav_get_angle_info (src->dvdnav, &angle, &angles)
-            != DVDNAV_STATUS_OK) {
-          res = FALSE;
-        }
-        *value = angles;
-      } else {
-        res = FALSE;
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_DURATION:{
+      gst_query_parse_duration (query, &format, NULL);
+      res = gst_dvd_nav_src_query_duration (src, format, &val);
+      if (res) {
+        gst_query_set_duration (query, format, val);
       }
       break;
-    case GST_QUERY_POSITION:
-      if (*format == sector_format) {
-        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) {
-          res = FALSE;
-        }
-        *value = title;
-      } else if (*format == chapter_format) {
-        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) {
-          res = FALSE;
-        }
-        *value = angle;
-      } else {
-        res = FALSE;
+    }
+    case GST_QUERY_POSITION:{
+      gst_query_parse_position (query, &format, NULL);
+      res = gst_dvd_nav_src_query_position (src, format, &val);
+      if (res) {
+        gst_query_set_position (query, format, val);
       }
       break;
-    default:
-      res = FALSE;
+    }
+    default:{
+      res = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
       break;
+    }
   }
+
   return res;
 }
 
-/*
- * URI interface.
- */
+/* URI interface */
 
 static guint
-dvdnavsrc_uri_get_type (void)
+gst_dvd_nav_src_uri_get_type (void)
 {
   return GST_URI_SRC;
 }
 
 static gchar **
-dvdnavsrc_uri_get_protocols (void)
+gst_dvd_nav_src_uri_get_protocols (void)
 {
-  static gchar *protocols[] = { "dvdnav", NULL };
+  static gchar *protocols[] = { "dvd", "dvdnav", NULL };
 
   return protocols;
 }
 
 static const gchar *
-dvdnavsrc_uri_get_uri (GstURIHandler * handler)
+gst_dvd_nav_src_uri_get_uri (GstURIHandler * handler)
 {
-  return "dvdnav://";
+  GstDvdNavSrc *src = GST_DVD_NAV_SRC (handler);
+
+  g_free (src->last_uri);
+  src->last_uri = g_strdup_printf ("dvd://%d,%d,%d", src->uri_title,
+      src->uri_chapter, src->uri_angle);
+
+  return src->last_uri;
 }
 
 static gboolean
-dvdnavsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)
+gst_dvd_nav_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
 {
+  GstDvdNavSrc *src = GST_DVD_NAV_SRC (handler);
   gboolean ret;
   gchar *protocol = gst_uri_get_protocol (uri);
 
-  ret = (protocol && !strcmp (protocol, "dvdnav")) ? TRUE : FALSE;
+  ret = (protocol &&
+      (!strcmp (protocol, "dvdnav") ||
+          !strcmp (protocol, "dvd"))) ? TRUE : FALSE;
   g_free (protocol);
+  protocol = NULL;
+
+  if (!ret)
+    return ret;
+
+  /*
+   * Parse out the new t/c/a and seek to them
+   */
+  {
+    gchar *location = NULL;
+    gchar **strs;
+    gchar **strcur;
+    gint pos = 0;
+
+    location = gst_uri_get_location (uri);
+
+    if (!location)
+      return ret;
+
+    strcur = strs = g_strsplit (location, ",", 0);
+    while (strcur && *strcur) {
+      gint val;
+
+      if (!sscanf (*strcur, "%d", &val))
+        break;
+
+      switch (pos) {
+        case 0:
+          if (val != src->uri_title) {
+            src->uri_title = val;
+            src->new_seek = TRUE;
+          }
+          break;
+        case 1:
+          if (val != src->uri_chapter) {
+            src->uri_chapter = val;
+            src->new_seek = TRUE;
+          }
+          break;
+        case 2:
+          src->uri_angle = val;
+          break;
+      }
+
+      strcur++;
+      pos++;
+    }
+
+    g_strfreev (strs);
+    g_free (location);
+  }
 
   return ret;
 }
 
 static void
-dvdnavsrc_uri_handler_init (gpointer g_iface, gpointer iface_data)
+gst_dvd_nav_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
 {
   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
 
-  iface->get_type = dvdnavsrc_uri_get_type;
-  iface->get_protocols = dvdnavsrc_uri_get_protocols;
-  iface->get_uri = dvdnavsrc_uri_get_uri;
-  iface->set_uri = dvdnavsrc_uri_set_uri;
+  iface->get_type = gst_dvd_nav_src_uri_get_type;
+  iface->get_protocols = gst_dvd_nav_src_uri_get_protocols;
+  iface->get_uri = gst_dvd_nav_src_uri_get_uri;
+  iface->set_uri = gst_dvd_nav_src_uri_set_uri;
+}
+
+static void
+gst_dvd_nav_src_do_init (GType dvdnavsrc_type)
+{
+  static const GInterfaceInfo urihandler_info = {
+    gst_dvd_nav_src_uri_handler_init,
+    NULL,
+    NULL
+  };
+
+  g_type_add_interface_static (dvdnavsrc_type, GST_TYPE_URI_HANDLER,
+      &urihandler_info);
+
+  title_format = gst_format_register ("title", "DVD title");
+  angle_format = gst_format_register ("angle", "DVD angle");
+  sector_format = gst_format_register ("sector", "DVD sector");
+  chapter_format = gst_format_register ("chapter", "DVD chapter");
 }
 
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
   if (!gst_element_register (plugin, "dvdnavsrc", GST_RANK_NONE,
-          GST_TYPE_DVDNAVSRC))
+          /* GST_RANK_PRIMARY + 1, */ GST_TYPE_DVD_NAV_SRC)) {
     return FALSE;
+  }
+
+  GST_DEBUG_CATEGORY_INIT (gst_dvd_nav_src_debug, "dvdnavsrc", 0,
+      "DVD navigation element based on libdvdnav");
 
   return TRUE;
 }
 
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
     GST_VERSION_MINOR,
-    "dvdnavsrc",
+    "dvdnav",
     "Access a DVD with navigation features using libdvdnav",
     plugin_init, VERSION, "GPL", GST_PACKAGE, GST_ORIGIN)