Half-baked port to 0.10. Needs some love in the seeking department, but at least...
authorTim-Philipp Müller <tim@centricular.net>
Thu, 9 Feb 2006 17:27:57 +0000 (17:27 +0000)
committerTim-Philipp Müller <tim@centricular.net>
Thu, 9 Feb 2006 17:27:57 +0000 (17:27 +0000)
Original commit message from CVS:
* configure.ac:
* ext/Makefile.am:
* ext/dvdread/Makefile.am:
* ext/dvdread/dvdreadsrc.c:
* ext/dvdread/dvdreadsrc.h:
Half-baked port to 0.10. Needs some love
in the seeking department, but at least
it does something.
* ext/dvdread/stream_labels.c:
* ext/dvdread/stream_labels.h:
Remove these (we use ISO-639 language codes internally; applications
that want to translate those into language names for display to the
user should rely on the iso-codes package for that).

ChangeLog
configure.ac
ext/Makefile.am
ext/dvdread/Makefile.am
ext/dvdread/dvdreadsrc.c
ext/dvdread/dvdreadsrc.h
ext/dvdread/stream_labels.c [deleted file]
ext/dvdread/stream_labels.h [deleted file]

index c73086b..e31a650 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2006-02-09  Tim-Philipp Müller  <tim at centricular dot net>
+
+       * configure.ac:
+       * ext/Makefile.am:
+       * ext/dvdread/Makefile.am:
+       * ext/dvdread/dvdreadsrc.c:
+       * ext/dvdread/dvdreadsrc.h:
+         Half-baked port to 0.10. Needs some love
+         in the seeking department, but at least
+         it does something.
+
+       * ext/dvdread/stream_labels.c:
+       * ext/dvdread/stream_labels.h:
+         Remove these (we use ISO-639 language codes internally; applications
+         that want to translate those into language names for display to the
+         user should rely on the iso-codes package for that).
+
 2006-02-06  Wim Taymans  <wim@fluendo.com>
 
        * ext/amrnb/amrnbdec.c: (gst_amrnbdec_init),
index 1c9421c..f11e3e3 100644 (file)
@@ -260,6 +260,12 @@ GST_CHECK_FEATURE(AMRNB, [AMR-NB], amrnbdec amrnbenc, [
                      AC_SUBST(AMRNB_LIBS))
 ])
 
+dnl *** dvdread ***
+translit(dnm, m, l) AM_CONDITIONAL(USE_DVDREAD, true)
+GST_CHECK_FEATURE(DVDREAD, [dvdread library], dvdreadsrc, [
+  GST_CHECK_LIBHEADER(DVDREAD, dvdread, DVDOpen, , dvdread/dvd_reader.h, DVDREAD_LIBS="-ldvdread")
+  AC_SUBST(DVDREAD_LIBS)
+])
 
 dnl *** lame ***
 translit(dnm, m, l) AM_CONDITIONAL(USE_LAME, true)
@@ -392,6 +398,7 @@ gst/realmedia/Makefile
 ext/Makefile
 ext/a52dec/Makefile
 ext/amrnb/Makefile
+ext/dvdread/Makefile
 ext/lame/Makefile
 ext/mad/Makefile
 ext/mpeg2dec/Makefile
index 686f476..5b89c18 100644 (file)
@@ -10,11 +10,11 @@ else
  AMRNB_DIR =
 endif
 
-if USE_DVDREAD
-# DVDREAD_DIR = dvdread
-else
-DVDREAD_DIR =
-endif
+if USE_DVDREAD
+ DVDREAD_DIR = dvdread
+else
+ DVDREAD_DIR =
+endif
 
 # if USE_DVDNAV
 # DVDNAV_DIR = dvdnav
@@ -49,6 +49,7 @@ endif
 SUBDIRS = \
        $(A52DEC_DIR) \
        $(AMRNB_DIR) \
+       $(DVDREAD_DIR) \
        $(DVDNAV_DIR) \
        $(LAME_DIR) \
        $(MAD_DIR) \
@@ -58,6 +59,7 @@ SUBDIRS = \
 DIST_SUBDIRS = \
        a52dec \
        amrnb \
+       dvdread \
        lame \
        mad \
        mpeg2dec \
index 3dba019..e3b33b2 100644 (file)
@@ -1,16 +1,17 @@
 
-plugin_LTLIBRARIES = libgstdvdreadsrc.la
+plugin_LTLIBRARIES = libgstdvdread.la
 
-## FIXME: the extra option, what do we do with it ?
-## AM_CFLAGS = -D_LARGEFILE64_SOURCE $(LIBDVDREADCFLAGS)
+libgstdvdread_la_SOURCES = dvdreadsrc.c
+libgstdvdread_la_CFLAGS = \
+       $(GST_CFLAGS) \
+       $(GST_BASE_CFLAGS) \
+       $(DVDREAD_CFLAGS)
+libgstdvdread_la_LIBADD = \
+       $(GST_BASE_LIBS) \
+       $(DVDREAD_LIBS)
+libgstdvdread_la_LDFLAGS = \
+       $(GST_PLUGIN_LDFLAGS)
 
-libgstdvdreadsrc_la_SOURCES = dvdreadsrc.c stream_labels.c
-libgstdvdreadsrc_la_CFLAGS = $(GST_CFLAGS)
-libgstdvdreadsrc_la_LIBADD = $(DVDREAD_LIBS)
-libgstdvdreadsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+noinst_HEADERS = dvdreadsrc.h
 
-noinst_HEADERS = dvdreadsrc.h stream_labels.h
-
-EXTRA_DIST = \
-       README \
-       demo-play
+EXTRA_DIST = README demo-play
index 16c68cd..cb5e6b8 100644 (file)
@@ -1,5 +1,5 @@
 /* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
  * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>.
  *
  * This library is free software; you can redistribute it and/or
 #include "config.h"
 #endif
 
+#include "_stdint.h"
+
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <time.h>
 #include <unistd.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <dirent.h>
+#include <string.h>
 #include <errno.h>
-//#include <linux/cdrom.h>
-#include <assert.h>
 
 #include "dvdreadsrc.h"
-#include "stream_labels.h"
-
-#include <dvdread/dvd_reader.h>
-#include <dvdread/ifo_types.h>
-#include <dvdread/ifo_read.h>
-#include <dvdread/nav_read.h>
-#include <dvdread/nav_print.h>
-
-struct _DVDReadSrcPrivate
-{
-  /* pads */
-  GstPad *srcpad;
 
-  /* location */
-  gchar *location;
-  gchar *last_uri;
-
-  gboolean new_seek;
-
-  gboolean new_cell;
-
-  int title, chapter, angle;
-  int pgc_id, start_cell, cur_cell, cur_pack;
-  int last_cell;
-  int ttn, pgn, next_cell;
-  dvd_reader_t *dvd;
-  dvd_file_t *dvd_title;
-  ifo_handle_t *vmg_file;
-  tt_srpt_t *tt_srpt;
-  ifo_handle_t *vts_file;
-  vts_ptt_srpt_t *vts_ptt_srpt;
-  pgc_t *cur_pgc;
-
-  /* where we are */
-  gboolean seek_pend, flush_pend;
-  GstFormat seek_pend_fmt;
-};
-
-GST_DEBUG_CATEGORY_STATIC (gstdvdreadsrc_debug);
-#define GST_CAT_DEFAULT (gstdvdreadsrc_debug)
+/* #include <gst/gst-i18n-plugin.h> */
+/* FIXME: remove once GETTEXT_PACKAGE etc. is set */
+#define _(s) s
 
-GstElementDetails dvdreadsrc_details = {
-  "DVD Source",
-  "Source/File/DVD",
-  "Access a DVD title/chapter/angle using libdvdread",
-  "Erik Walthinsen <omega@cse.ogi.edu>",
-};
+GST_DEBUG_CATEGORY_STATIC (gstgst_dvd_read_src_debug);
+#define GST_CAT_DEFAULT (gstgst_dvd_read_src_debug)
 
-
-/* DVDReadSrc signals and args */
-enum
-{
-  /* FILL ME */
-  LAST_SIGNAL
-};
+static void gst_dvd_read_src_do_init (GType dvdreadsrc_type);
 
 enum
 {
   ARG_0,
-  ARG_LOCATION,
   ARG_DEVICE,
   ARG_TITLE,
   ARG_CHAPTER,
   ARG_ANGLE
 };
 
-static void dvdreadsrc_base_init (gpointer g_class);
-static void dvdreadsrc_class_init (DVDReadSrcClass * klass);
-static void dvdreadsrc_init (DVDReadSrc * dvdreadsrc);
-static void dvdreadsrc_finalize (GObject * object);
+static GstElementDetails gst_dvd_read_src_details = {
+  "DVD Source",
+  "Source/File/DVD",
+  "Access a DVD title/chapter/angle using libdvdread",
+  "Erik Walthinsen <omega@cse.ogi.edu>",
+};
 
-static void dvdreadsrc_set_property (GObject * object, guint prop_id,
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/mpeg, mpegversion=2, systemstream=(boolean)true"));
+
+static GstFormat title_format;
+static GstFormat angle_format;
+static GstFormat sector_format;
+static GstFormat chapter_format;
+
+static gboolean gst_dvd_read_src_start (GstBaseSrc * basesrc);
+static gboolean gst_dvd_read_src_stop (GstBaseSrc * basesrc);
+static GstFlowReturn gst_dvd_read_src_create (GstPushSrc * pushsrc,
+    GstBuffer ** buf);
+static gboolean gst_dvd_read_src_src_query (GstBaseSrc * basesrc,
+    GstQuery * query);
+static gboolean gst_dvd_read_src_src_event (GstBaseSrc * basesrc,
+    GstEvent * event);
+static gboolean gst_dvd_read_src_goto_title (GstDvdReadSrc * src, gint title,
+    gint angle);
+static gboolean gst_dvd_read_src_goto_chapter (GstDvdReadSrc * src,
+    gint chapter);
+static gboolean gst_dvd_read_src_goto_sector (GstDvdReadSrc * src, gint angle);
+static void gst_dvd_read_src_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec);
-static void dvdreadsrc_get_property (GObject * object, guint prop_id,
+static void gst_dvd_read_src_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
+static GstEvent *gst_dvd_read_src_make_clut_change_event (GstDvdReadSrc * src,
+    const guint * clut);
 
-static const GstEventMask *dvdreadsrc_get_event_mask (GstPad * pad);
-static const GstQueryType *dvdreadsrc_get_query_types (GstPad * pad);
-static const GstFormat *dvdreadsrc_get_formats (GstPad * pad);
-static gboolean dvdreadsrc_srcpad_event (GstPad * pad, GstEvent * event);
-static gboolean dvdreadsrc_srcpad_query (GstPad * pad, GstQueryType type,
-    GstFormat * format, gint64 * value);
-
-static GstData *dvdreadsrc_get (GstPad * pad);
-static GstStateChangeReturn dvdreadsrc_change_state (GstElement * element,
-    GstStateChange transition);
+GST_BOILERPLATE_FULL (GstDvdReadSrc, gst_dvd_read_src, GstPushSrc,
+    GST_TYPE_PUSH_SRC, gst_dvd_read_src_do_init)
 
-static void dvdreadsrc_uri_handler_init (gpointer g_iface, gpointer iface_data);
-
-
-static GstElementClass *parent_class = NULL;
+     static void gst_dvd_read_src_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
 
-static GstFormat sector_format, angle_format, title_format, chapter_format;
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&srctemplate));
 
-/*static guint dvdreadsrc_signals[LAST_SIGNAL] = { 0 }; */
+  gst_element_class_set_details (element_class, &gst_dvd_read_src_details);
+}
 
-GType
-dvdreadsrc_get_type (void)
+static void
+gst_dvd_read_src_finalize (GObject * object)
 {
-  static GType dvdreadsrc_type = 0;
-
-  if (!dvdreadsrc_type) {
-    static const GTypeInfo dvdreadsrc_info = {
-      sizeof (DVDReadSrcClass),
-      dvdreadsrc_base_init,
-      NULL,
-      (GClassInitFunc) dvdreadsrc_class_init,
-      NULL,
-      NULL,
-      sizeof (DVDReadSrc),
-      0,
-      (GInstanceInitFunc) dvdreadsrc_init,
-    };
-    static const GInterfaceInfo urihandler_info = {
-      dvdreadsrc_uri_handler_init,
-      NULL,
-      NULL
-    };
-
-    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");
-
-    dvdreadsrc_type =
-        g_type_register_static (GST_TYPE_ELEMENT, "DVDReadSrc",
-        &dvdreadsrc_info, 0);
-    g_type_add_interface_static (dvdreadsrc_type,
-        GST_TYPE_URI_HANDLER, &urihandler_info);
-  }
+  GstDvdReadSrc *src = GST_DVD_READ_SRC (object);
 
-  return dvdreadsrc_type;
+  g_free (src->location);
+  g_free (src->last_uri);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
 static void
-dvdreadsrc_base_init (gpointer g_class)
+gst_dvd_read_src_init (GstDvdReadSrc * src, GstDvdReadSrcClass * klass)
 {
-  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
-  gst_element_class_set_details (element_class, &dvdreadsrc_details);
+  src->dvd = NULL;
+  src->vts_file = NULL;
+  src->vmg_file = NULL;
+  src->dvd_title = NULL;
+
+  src->location = g_strdup ("/dev/dvd");
+  src->last_uri = NULL;
+  src->new_seek = TRUE;
+  src->new_cell = TRUE;
+  src->change_cell = FALSE;
+  src->title = 0;
+  src->chapter = 0;
+  src->angle = 0;
+
+  src->seek_pend = FALSE;
+  src->flush_pend = FALSE;
+  src->seek_pend_fmt = GST_FORMAT_UNDEFINED;
+  src->title_lang_event_pending = NULL;
+  src->pending_clut_event = NULL;
 }
 
 static void
-dvdreadsrc_class_init (DVDReadSrcClass * klass)
+gst_dvd_read_src_class_init (GstDvdReadSrcClass * klass)
 {
-  GObjectClass *gobject_class;
-  GstElementClass *gstelement_class;
-
-  gobject_class = (GObjectClass *) klass;
-  gstelement_class = (GstElementClass *) klass;
+  GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
+  GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
-  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+  gobject_class->finalize = gst_dvd_read_src_finalize;
+  gobject_class->set_property = gst_dvd_read_src_set_property;
+  gobject_class->get_property = gst_dvd_read_src_get_property;
 
-  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
-      g_param_spec_string ("location", "Location",
-          "DVD device location (deprecated; use device)",
-          NULL, G_PARAM_READWRITE));
   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE,
       g_param_spec_string ("device", "Device",
           "DVD device location", NULL, G_PARAM_READWRITE));
   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TITLE,
       g_param_spec_int ("title", "title", "title",
-          0, G_MAXINT, 0, G_PARAM_READWRITE));
+          0, 999, 0, G_PARAM_READWRITE));
   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CHAPTER,
       g_param_spec_int ("chapter", "chapter", "chapter",
-          0, G_MAXINT, 0, G_PARAM_READWRITE));
+          0, 999, 0, G_PARAM_READWRITE));
   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ANGLE,
       g_param_spec_int ("angle", "angle", "angle",
-          0, G_MAXINT, 0, G_PARAM_READWRITE));
-
-  gobject_class->set_property = GST_DEBUG_FUNCPTR (dvdreadsrc_set_property);
-  gobject_class->get_property = GST_DEBUG_FUNCPTR (dvdreadsrc_get_property);
-
-  gobject_class->finalize = dvdreadsrc_finalize;
+          0, 999, 0, G_PARAM_READWRITE));
 
-  gstelement_class->change_state = dvdreadsrc_change_state;
+  gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_dvd_read_src_start);
+  gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_dvd_read_src_stop);
+  gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_dvd_read_src_src_query);
+  gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_dvd_read_src_src_event);
 
-  GST_DEBUG_CATEGORY_INIT (gstdvdreadsrc_debug, "dvdreadsrc", 0,
-      "DVD reader element based on dvdreadsrc");
+  gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_dvd_read_src_create);
 }
 
-static void
-dvdreadsrc_init (DVDReadSrc * dvdreadsrc)
+static gboolean
+gst_dvd_read_src_start (GstBaseSrc * basesrc)
 {
-  dvdreadsrc->priv = g_new (DVDReadSrcPrivate, 1);
-  dvdreadsrc->priv->srcpad = gst_pad_new ("src", GST_PAD_SRC);
-  gst_pad_set_get_function (dvdreadsrc->priv->srcpad, dvdreadsrc_get);
-  gst_pad_set_event_function (dvdreadsrc->priv->srcpad,
-      dvdreadsrc_srcpad_event);
-  gst_pad_set_event_mask_function (dvdreadsrc->priv->srcpad,
-      dvdreadsrc_get_event_mask);
-  gst_pad_set_query_function (dvdreadsrc->priv->srcpad,
-      dvdreadsrc_srcpad_query);
-  gst_pad_set_query_type_function (dvdreadsrc->priv->srcpad,
-      dvdreadsrc_get_query_types);
-  gst_pad_set_formats_function (dvdreadsrc->priv->srcpad,
-      dvdreadsrc_get_formats);
-  gst_element_add_pad (GST_ELEMENT (dvdreadsrc), dvdreadsrc->priv->srcpad);
-
-  dvdreadsrc->priv->dvd = NULL;
-  dvdreadsrc->priv->vts_file = NULL;
-  dvdreadsrc->priv->vmg_file = NULL;
-  dvdreadsrc->priv->dvd_title = NULL;
-
-  dvdreadsrc->priv->location = g_strdup ("/dev/dvd");
-  dvdreadsrc->priv->last_uri = NULL;
-  dvdreadsrc->priv->new_seek = TRUE;
-  dvdreadsrc->priv->new_cell = TRUE;
-  dvdreadsrc->priv->title = 0;
-  dvdreadsrc->priv->chapter = 0;
-  dvdreadsrc->priv->angle = 0;
-
-  dvdreadsrc->priv->seek_pend = FALSE;
-  dvdreadsrc->priv->flush_pend = FALSE;
-  dvdreadsrc->priv->seek_pend_fmt = GST_FORMAT_UNDEFINED;
-}
+  GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
 
-static void
-dvdreadsrc_finalize (GObject * object)
-{
-  DVDReadSrc *dvdreadsrc = DVDREADSRC (object);
+  g_return_val_if_fail (src->location != NULL, FALSE);
+
+  GST_DEBUG_OBJECT (src, "Opening DVD '%s'", src->location);
 
-  if (dvdreadsrc->priv) {
-    g_free (dvdreadsrc->priv->location);
-    g_free (dvdreadsrc->priv->last_uri);
-    g_free (dvdreadsrc->priv);
-    dvdreadsrc->priv = NULL;
+  src->dvd = DVDOpen (src->location);
+  if (src->dvd == NULL) {
+    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+        (_("Could not open DVD")),
+        ("DVDOpen(%s) failed: %s", src->location, g_strerror (errno)));
+    return FALSE;
   }
-  G_OBJECT_CLASS (parent_class)->finalize (object);
-}
 
-static void
-dvdreadsrc_set_property (GObject * object, guint prop_id, const GValue * value,
-    GParamSpec * pspec)
-{
-  DVDReadSrc *src;
-  DVDReadSrcPrivate *priv;
+  /* Load the video manager to find out the information about the titles */
+  GST_DEBUG_OBJECT (src, "Loading VMG info");
 
-  g_return_if_fail (GST_IS_DVDREADSRC (object));
+  src->vmg_file = ifoOpen (src->dvd, 0);
+  if (!src->vmg_file) {
+    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+        (_("Could not open DVD")),
+        ("ifoOpen() failed: %s", g_strerror (errno)));
+    return FALSE;
+  }
 
-  src = DVDREADSRC (object);
-  priv = src->priv;
+  src->tt_srpt = src->vmg_file->tt_srpt;
 
-  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)); */
+  src->seek_pend_fmt = title_format;
+  src->seek_pend = TRUE;
 
-      g_free (priv->location);
-      /* clear the filename if we get a NULL (is that possible?) */
-      if (g_value_get_string (value) == NULL)
-        priv->location = g_strdup ("/dev/dvd");
-      /* otherwise set the new filename */
-      else
-        priv->location = g_strdup (g_value_get_string (value));
-      break;
-    case ARG_TITLE:
-      priv->title = g_value_get_int (value);
-      priv->new_seek = TRUE;
-      break;
-    case ARG_CHAPTER:
-      priv->chapter = g_value_get_int (value);
-      priv->new_seek = TRUE;
-      break;
-    case ARG_ANGLE:
-      priv->angle = g_value_get_int (value);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
+  return TRUE;
+}
+
+static gboolean
+gst_dvd_read_src_stop (GstBaseSrc * basesrc)
+{
+  GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
+
+  if (src->vts_file) {
+    ifoClose (src->vts_file);
+    src->vts_file = NULL;
+  }
+  if (src->vmg_file) {
+    ifoClose (src->vmg_file);
+    src->vmg_file = NULL;
+  }
+  if (src->dvd_title) {
+    DVDCloseFile (src->dvd_title);
+    src->dvd_title = NULL;
+  }
+  if (src->dvd) {
+    DVDClose (src->dvd);
+    src->dvd = NULL;
+  }
+  src->new_cell = TRUE;
+  src->new_seek = TRUE;
+  src->change_cell = FALSE;
+  src->chapter = 0;
+  src->title = 0;
+  src->flush_pend = FALSE;
+  src->seek_pend = FALSE;
+  src->seek_pend_fmt = GST_FORMAT_UNDEFINED;
+  if (src->title_lang_event_pending) {
+    gst_event_unref (src->title_lang_event_pending);
+    src->title_lang_event_pending = NULL;
+  }
+  if (src->pending_clut_event) {
+    gst_event_unref (src->pending_clut_event);
+    src->pending_clut_event = NULL;
   }
 
+  return TRUE;
 }
 
 static void
-dvdreadsrc_get_property (GObject * object, guint prop_id, GValue * value,
-    GParamSpec * pspec)
+cur_title_get_chapter_pgc (GstDvdReadSrc * src, gint chapter, gint * p_pgn,
+    gint * p_pgc_id, pgc_t ** p_pgc)
 {
-  DVDReadSrc *src;
-  DVDReadSrcPrivate *priv;
+  pgc_t *pgc;
+  gint pgn, pgc_id;
 
-  g_return_if_fail (GST_IS_DVDREADSRC (object));
+  g_assert (chapter >= 0 && chapter < src->num_chapters);
 
-  src = DVDREADSRC (object);
-  priv = src->priv;
+  pgc_id = src->vts_ptt_srpt->title[src->ttn - 1].ptt[chapter].pgcn;
+  pgn = src->vts_ptt_srpt->title[src->ttn - 1].ptt[chapter].pgn;
+  pgc = src->vts_file->vts_pgcit->pgci_srp[pgc_id - 1].pgc;
 
-  switch (prop_id) {
-    case ARG_DEVICE:
-    case ARG_LOCATION:
-      g_value_set_string (value, priv->location);
-      break;
-    case ARG_TITLE:
-      g_value_set_int (value, priv->title);
-      break;
-    case ARG_CHAPTER:
-      g_value_set_int (value, priv->chapter);
-      break;
-    case ARG_ANGLE:
-      g_value_set_int (value, priv->angle);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
+  *p_pgn = pgn;
+  *p_pgc_id = pgc_id;
+  *p_pgc = pgc;
 }
 
-/*
- * Querying and seeking.
- */
-
-static const GstEventMask *
-dvdreadsrc_get_event_mask (GstPad * pad)
+static void
+cur_title_get_chapter_bounds (GstDvdReadSrc * src, gint chapter,
+    gint * p_first_cell, gint * p_last_cell)
 {
-  static const GstEventMask masks[] = {
-    {GST_EVENT_SEEK, GST_SEEK_METHOD_CUR |
-          GST_SEEK_METHOD_SET | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
-    {0, 0}
-  };
+  pgc_t *pgc;
+  gint pgn, pgc_id, pgn_next_ch;
 
-  return masks;
-}
+  g_assert (chapter >= 0 && chapter < src->num_chapters);
 
-static const GstQueryType *
-dvdreadsrc_get_query_types (GstPad * pad)
-{
-  static const GstQueryType types[] = {
-    GST_QUERY_TOTAL,
-    GST_QUERY_POSITION,
-    0
-  };
+  cur_title_get_chapter_pgc (src, chapter, &pgn, &pgc_id, &pgc);
 
-  return types;
-}
+  *p_first_cell = pgc->program_map[pgn - 1] - 1;
 
-static const GstFormat *
-dvdreadsrc_get_formats (GstPad * pad)
-{
-  static GstFormat formats[] = {
-    GST_FORMAT_BYTES,
-    0, 0, 0, 0,                 /* init later */
-    0,
-  };
-  if (formats[1] == 0) {
-    formats[1] = sector_format;
-    formats[2] = angle_format;
-    formats[3] = title_format;
-    formats[4] = chapter_format;
+  if (chapter == (src->num_chapters - 1)) {
+    *p_last_cell = pgc->nr_of_cells;
+  } else {
+    pgn_next_ch = src->vts_ptt_srpt->title[src->ttn - 1].ptt[chapter + 1].pgn;
+    *p_last_cell = pgc->program_map[pgn_next_ch - 1] - 1;
   }
-
-  return formats;
 }
 
 static gboolean
-dvdreadsrc_srcpad_event (GstPad * pad, GstEvent * event)
+gst_dvd_read_src_goto_chapter (GstDvdReadSrc * src, gint chapter)
 {
-  DVDReadSrc *dvdreadsrc = DVDREADSRC (gst_pad_get_parent (pad));
-  gboolean res = TRUE;
+  gint i;
 
-  switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_SEEK:{
-      gint64 new_off, total, cur;
-      GstFormat fmt;
-
-      /* get requested offset */
-      new_off = GST_EVENT_SEEK_OFFSET (event);
-      switch (GST_EVENT_SEEK_FORMAT (event)) {
-        case GST_FORMAT_BYTES:
-          new_off /= DVD_VIDEO_LB_LEN;
-          fmt = sector_format;
-          break;
-        default:
-          fmt = GST_EVENT_SEEK_FORMAT (event);
-          if (fmt == sector_format ||
-              fmt == angle_format ||
-              fmt == title_format || fmt == chapter_format)
-            break;
-          GST_LOG ("Unsupported seek format");
-          return FALSE;
-      }
+  /* make sure the chapter number is valid for this title */
+  if (chapter < 0 || chapter >= src->num_chapters) {
+    GST_WARNING_OBJECT (src, "invalid chapter %d (only %d available)",
+        chapter, src->num_chapters);
+    chapter = CLAMP (chapter, 0, src->num_chapters - 1);
+  }
 
-      /* get current offset and length */
-      gst_pad_query (pad, GST_QUERY_TOTAL, &fmt, &total);
-      gst_pad_query (pad, GST_QUERY_POSITION, &fmt, &cur);
-      if (cur == new_off) {
-        GST_LOG ("We're already at that position!");
-        return TRUE;
-      }
+  /* determine which program chain we want to watch. This is
+   * based on the chapter number */
+  cur_title_get_chapter_pgc (src, chapter, &src->pgn, &src->pgc_id,
+      &src->cur_pgc);
+  cur_title_get_chapter_bounds (src, chapter, &src->start_cell,
+      &src->last_cell);
 
-      /* get absolute */
-      switch (GST_EVENT_SEEK_METHOD (event)) {
-        case GST_SEEK_METHOD_SET:
-          /* no-op */
-          break;
-        case GST_SEEK_METHOD_CUR:
-          new_off += cur;
-          break;
-        case GST_SEEK_METHOD_END:
-          new_off = total - new_off;
-          break;
-        default:
-          GST_LOG ("Unsupported seek method");
-          return FALSE;
-      }
-      if (new_off < 0 || new_off >= total) {
-        GST_LOG ("Invalid seek position");
-        return FALSE;
-      }
+  GST_LOG_OBJECT (src, "Opened chapter %d - cell %d-%d", chapter,
+      src->start_cell, src->last_cell);
 
-      GST_LOG ("Seeking to unit %d in format %d", new_off, fmt);
-
-      if (fmt == sector_format || fmt == chapter_format || fmt == title_format) {
-        if (fmt == sector_format) {
-          dvdreadsrc->priv->cur_pack = new_off;
-        } else if (fmt == chapter_format) {
-          dvdreadsrc->priv->cur_pack = 0;
-          dvdreadsrc->priv->chapter = new_off;
-          dvdreadsrc->priv->seek_pend_fmt = fmt;
-        } else if (fmt == title_format) {
-          dvdreadsrc->priv->cur_pack = 0;
-          dvdreadsrc->priv->title = new_off;
-          dvdreadsrc->priv->chapter = 0;
-          dvdreadsrc->priv->seek_pend_fmt = fmt;
-        }
+  /* retrieve position */
+  src->cur_pack = 0;
+  for (i = 0; i < chapter; i++) {
+    gint c1, c2;
 
-        /* leave for events */
-        dvdreadsrc->priv->seek_pend = TRUE;
-        if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH)
-          dvdreadsrc->priv->flush_pend = TRUE;
-      } else if (fmt == angle_format) {
-        dvdreadsrc->priv->angle = new_off;
-      }
+    cur_title_get_chapter_bounds (src, i, &c1, &c2);
 
-      break;
+    while (c1 < c2) {
+      src->cur_pack +=
+          src->cur_pgc->cell_playback[c1].last_sector -
+          src->cur_pgc->cell_playback[c1].first_sector;
+      ++c1;
     }
-    default:
-      res = FALSE;
-      break;
   }
 
-  gst_event_unref (event);
+  /* prepare reading for new cell */
+  src->new_cell = TRUE;
+  src->next_cell = src->start_cell;
 
-  return res;
+  src->chapter = chapter;
+
+  if (src->pending_clut_event)
+    gst_event_unref (src->pending_clut_event);
+
+  src->pending_clut_event =
+      gst_dvd_read_src_make_clut_change_event (src, src->cur_pgc->palette);
+
+  return TRUE;
 }
 
 static gboolean
-dvdreadsrc_srcpad_query (GstPad * pad, GstQueryType type,
-    GstFormat * format, gint64 * value)
+gst_dvd_read_src_goto_title (GstDvdReadSrc * src, gint title, gint angle)
 {
-  DVDReadSrc *dvdreadsrc = DVDREADSRC (gst_pad_get_parent (pad));
-  DVDReadSrcPrivate *priv = dvdreadsrc->priv;
-  gboolean res = TRUE;
+  GstStructure *s;
+  gchar lang_code[3] = { '\0', '\0', '\0' }, *t;
+  gint title_set_nr;
+  gint num_titles;
+  gint num_angles;
+  gint i;
+
+  /* make sure our title number is valid */
+  num_titles = src->tt_srpt->nr_of_srpts;
+  GST_INFO_OBJECT (src, "There are %d titles on this DVD", num_titles);
+  if (title < 0 || title >= num_titles) {
+    GST_WARNING_OBJECT (src, "Invalid title %d (only %d available)",
+        title, num_titles);
+    title = CLAMP (title, 0, num_titles - 1);
+  }
+
+  src->num_chapters = src->tt_srpt->title[title].nr_of_ptts;
+  GST_INFO_OBJECT (src, "Title %d has %d chapters", title, src->num_chapters);
+
+  /* make sure the angle number is valid for this title */
+  num_angles = src->tt_srpt->title[title].nr_of_angles;
+  GST_LOG_OBJECT (src, "Title %d has %d angles", title, num_angles);
+  if (angle < 0 || angle >= num_angles) {
+    GST_WARNING_OBJECT (src, "Invalid angle %d (only %d available)",
+        angle, num_angles);
+    angle = CLAMP (angle, 0, num_angles - 1);
+  }
 
-  if (!GST_OBJECT_FLAG_IS_SET (dvdreadsrc, DVDREADSRC_OPEN))
+  /* load the VTS information for the title set our title is in */
+  title_set_nr = src->tt_srpt->title[title].title_set_nr;
+  src->vts_file = ifoOpen (src->dvd, title_set_nr);
+  if (src->vts_file == NULL) {
+    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+        (_("Could not open DVD title %d"), title_set_nr),
+        ("ifoOpen(%d) failed: %s", title_set_nr, g_strerror (errno)));
     return FALSE;
+  }
 
-  switch (type) {
-    case GST_QUERY_TOTAL:
-      switch (*format) {
-        case GST_FORMAT_BYTES:
-          *value = DVDFileSize (priv->dvd_title) * DVD_VIDEO_LB_LEN;
-          break;
-        default:
-          if (*format == sector_format) {
-            *value = DVDFileSize (priv->dvd_title);
-          } else if (*format == title_format) {
-            *value = priv->tt_srpt->nr_of_srpts;
-          } else if (*format == chapter_format) {
-            *value = priv->tt_srpt->title[priv->title].nr_of_ptts;
-          } else if (*format == angle_format) {
-            *value = priv->tt_srpt->title[priv->title].nr_of_angles;
-          } else {
-            GST_LOG ("Unknown format");
-            res = FALSE;
-          }
-          break;
-      }
-      break;
-    case GST_QUERY_POSITION:
-      switch (*format) {
-        case GST_FORMAT_BYTES:
-          *value = priv->cur_pack * DVD_VIDEO_LB_LEN;
-          break;
-        default:
-          if (*format == sector_format) {
-            *value = priv->cur_pack;
-          } else if (*format == title_format) {
-            *value = priv->title;
-          } else if (*format == chapter_format) {
-            *value = priv->chapter;
-          } else if (*format == angle_format) {
-            *value = priv->angle;
-          } else {
-            GST_LOG ("Unknown format");
-            res = FALSE;
-          }
-          break;
-      }
-      break;
-    default:
-      res = FALSE;
-      break;
+  src->ttn = src->tt_srpt->title[title].vts_ttn;
+  src->vts_ptt_srpt = src->vts_file->vts_ptt_srpt;
+
+  /* we've got enough info, time to open the title set data */
+  src->dvd_title = DVDOpenFile (src->dvd, title_set_nr, DVD_READ_TITLE_VOBS);
+  if (src->dvd_title == NULL) {
+    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+        (_("Could not open DVD title %d"), title_set_nr),
+        ("Can't open title VOBS (VTS_%02d_1.VOB)", title_set_nr));
+    return FALSE;
   }
 
-  return res;
-}
+  GST_INFO_OBJECT (src, "Opened title %d, angle %d", title, angle);
+  src->title = title;
+  src->angle = angle;
 
-/*
- * Returns true if the pack is a NAV pack.  This check is clearly insufficient,
- * and sometimes we incorrectly think that valid other packs are NAV packs.  I
- * need to make this stronger.
- */
-static int
-is_nav_pack (unsigned char *buffer)
-{
-  return (buffer[41] == 0xbf && buffer[1027] == 0xbf);
-}
+  /* build event */
 
-static int
-_close (DVDReadSrcPrivate * priv)
-{
-  ifoClose (priv->vts_file);
-  priv->vts_file = NULL;
+  if (src->title_lang_event_pending) {
+    gst_event_unref (src->title_lang_event_pending);
+    src->title_lang_event_pending = NULL;
+  }
 
-  ifoClose (priv->vmg_file);
-  priv->vmg_file = NULL;
+  s = gst_structure_new ("application/x-gst-event",
+      "event", G_TYPE_STRING, "dvd-lang-codes", NULL);
 
-  DVDCloseFile (priv->dvd_title);
-  priv->dvd_title = NULL;
+  /* audio */
+  for (i = 0; i < src->vts_file->vtsi_mat->nr_of_vts_audio_streams; i++) {
+    const audio_attr_t *a = &src->vts_file->vtsi_mat->vts_audio_attr[i];
 
-  DVDClose (priv->dvd);
-  priv->dvd = NULL;
+    t = g_strdup_printf ("audio-%d-format", i);
+    gst_structure_set (s, t, G_TYPE_INT, (int) a->audio_format, NULL);
+    g_free (t);
 
-  return 0;
-}
+    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);
+    } else {
+      lang_code[0] = '\0';
+    }
 
-static int
-_open (DVDReadSrcPrivate * priv, const gchar * location)
-{
-  g_return_val_if_fail (priv != NULL, -1);
-  g_return_val_if_fail (location != NULL, -1);
-
-  /*
-   * Open the disc.
-   */
-  priv->dvd = DVDOpen (location);
-  if (!priv->dvd) {
-    GST_ERROR ("Couldn't open DVD: %s", location);
-    return -1;
+    GST_INFO_OBJECT (src, "[%02d] Audio    %02d: lang='%s', format=%d",
+        src->title, i, lang_code, (gint) a->audio_format);
   }
 
+  /* subtitle */
+  for (i = 0; i < src->vts_file->vtsi_mat->nr_of_vts_subp_streams; i++) {
+    const subp_attr_t *u = &src->vts_file->vtsi_mat->vts_subp_attr[i];
 
-  /*
-   * Load the video manager to find out the information about the titles on
-   * this disc.
-   */
-  priv->vmg_file = ifoOpen (priv->dvd, 0);
-  if (!priv->vmg_file) {
-    GST_ERROR ("Can't open VMG info");
-    return -1;
+    if (u->type) {
+      t = g_strdup_printf ("subtitle-%d-language", i);
+      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);
+      g_free (t);
+    } else {
+      lang_code[0] = '\0';
+    }
+
+    GST_INFO_OBJECT (src, "[%02d] Subtitle %02d: lang='%s', format=%d",
+        src->title, i, lang_code);
   }
-  priv->tt_srpt = priv->vmg_file->tt_srpt;
+
+  src->title_lang_event_pending =
+      gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
 
   return 0;
 }
 
-static int
-_seek_title (DVDReadSrcPrivate * priv, int title, int angle)
+/* FIXME: double-check this function, compare against original */
+static gint
+gst_dvd_read_src_get_next_cell_for (GstDvdReadSrc * src, gint cell)
 {
-  GHashTable *languagelist = NULL;
-
-  /*
-   * Make sure our title number is valid.
-   */
-  GST_LOG ("There are %d titles on this DVD", priv->tt_srpt->nr_of_srpts);
-  if (title < 0 || title >= priv->tt_srpt->nr_of_srpts) {
-    GST_WARNING ("Invalid title %d (only %d available)",
-        title, priv->tt_srpt->nr_of_srpts);
-
-    if (title < 0)
-      title = 0;
-    else
-      title = priv->tt_srpt->nr_of_srpts - 1;
-  }
+  /* Check if we're entering an angle block. */
+  if (src->cur_pgc->cell_playback[cell].block_type != BLOCK_TYPE_ANGLE_BLOCK)
+    return (cell + 1);
 
-  GST_LOG ("There are %d chapters in this title",
-      priv->tt_srpt->title[title].nr_of_ptts);
-
-  /*
-   * Make sure the angle number is valid for this title.
-   */
-  GST_LOG ("There are %d angles available in this title",
-      priv->tt_srpt->title[title].nr_of_angles);
-
-  if (angle < 0 || angle >= priv->tt_srpt->title[title].nr_of_angles) {
-    GST_WARNING ("Invalid angle %d (only %d available)",
-        angle, priv->tt_srpt->title[title].nr_of_angles);
-    if (angle < 0)
-      angle = 0;
-    else
-      angle = priv->tt_srpt->title[title].nr_of_angles - 1;
-  }
+  while (src->cur_pgc->cell_playback[cell].block_mode == BLOCK_MODE_LAST_CELL)
+    ++cell;
 
-  /*
-   * Load the VTS information for the title set our title is in.
-   */
-  priv->vts_file =
-      ifoOpen (priv->dvd, priv->tt_srpt->title[title].title_set_nr);
-  if (!priv->vts_file) {
-    GST_ERROR ("Can't open the info file of title %d",
-        priv->tt_srpt->title[title].title_set_nr);
-    _close (priv);
-    return -1;
-  }
+  return cell + 1;              /* really +1? (tpm) */
+}
 
-  priv->ttn = priv->tt_srpt->title[title].vts_ttn;
-  priv->vts_ptt_srpt = priv->vts_file->vts_ptt_srpt;
-
-  /*
-   * We've got enough info, time to open the title set data.
-   */
-  priv->dvd_title =
-      DVDOpenFile (priv->dvd, priv->tt_srpt->title[title].title_set_nr,
-      DVD_READ_TITLE_VOBS);
-  if (!priv->dvd_title) {
-    GST_ERROR ("Can't open title VOBS (VTS_%02d_1.VOB)",
-        priv->tt_srpt->title[title].title_set_nr);
-    _close (priv);
-    return -1;
-  }
+/* Returns true if the pack is a NAV pack.  This check is clearly insufficient,
+ * and sometimes we incorrectly think that valid other packs are NAV packs.  I
+ * need to make this stronger. */
+static gboolean
+gst_dvd_read_src_is_nav_pack (const guint8 * buffer)
+{
+  return (buffer[41] == 0xbf && buffer[1027] == 0xbf);
+}
 
-  /* Get stream labels for all audio and subtitle streams */
-  languagelist = dvdreadsrc_init_languagelist ();
+typedef enum
+{
+  GST_DVD_READ_OK = 0,
+  GST_DVD_READ_ERROR = -1,
+  GST_DVD_READ_EOS = -2,
+  GST_DVD_READ_AGAIN = -3
+} GstDvdReadReturn;
+
+static GstDvdReadReturn
+gst_dvd_read_src_read (GstDvdReadSrc * src, gint angle, gint new_seek,
+    GstBuffer * buf)
+{
+  guint8 *data;
+  dsi_t dsi_pack;
+  guint next_vobu, next_ilvu_start, cur_output_size;
+  gint len;
 
-  dvdreadsrc_get_audio_stream_labels (priv->vts_file, languagelist);
-  dvdreadsrc_get_subtitle_stream_labels (priv->vts_file, languagelist);
+  /* playback by cell in this pgc, starting at the cell for our chapter */
+  if (new_seek)
+    src->cur_cell = src->start_cell;
 
-  g_hash_table_destroy (languagelist);
+again:
 
-  GST_LOG ("Opened title %d, angle %d", title, angle);
-  priv->title = title;
-  priv->angle = angle;
+  if (src->cur_cell >= src->last_cell) {
+    /* advance to next chapter */
+    if (src->chapter == (src->num_chapters - 1)) {
+      GST_INFO_OBJECT (src, "last chapter done - EOS");
+      return GST_DVD_READ_EOS;
+    }
 
-  return 0;
-}
+    GST_INFO_OBJECT (src, "end of chapter %d, switch to next", src->chapter);
 
-static int
-_seek_chapter (DVDReadSrcPrivate * priv, int chapter)
-{
-  int i;
-
-  /*
-   * Make sure the chapter number is valid for this title.
-   */
-  if (chapter < 0 || chapter >= priv->tt_srpt->title[priv->title].nr_of_ptts) {
-    GST_WARNING ("Invalid chapter %d (only %d available)",
-        chapter, priv->tt_srpt->title[priv->title].nr_of_ptts);
-    if (chapter < 0)
-      chapter = 0;
-    chapter = priv->tt_srpt->title[priv->title].nr_of_ptts - 1;
-  }
+    ++src->chapter;
+    gst_dvd_read_src_goto_chapter (src, src->chapter);
 
-  /*
-   * Determine which program chain we want to watch.  This is based on the
-   * chapter number.
-   */
-  priv->pgc_id = priv->vts_ptt_srpt->title[priv->ttn - 1].ptt[chapter].pgcn;
-  priv->pgn = priv->vts_ptt_srpt->title[priv->ttn - 1].ptt[chapter].pgn;
-  priv->cur_pgc = priv->vts_file->vts_pgcit->pgci_srp[priv->pgc_id - 1].pgc;
-  priv->start_cell = priv->cur_pgc->program_map[priv->pgn - 1] - 1;
-
-  if (chapter + 1 == priv->tt_srpt->title[priv->title].nr_of_ptts) {
-    priv->last_cell = priv->cur_pgc->nr_of_cells;
-  } else {
-    priv->last_cell =
-        priv->cur_pgc->program_map[(priv->vts_ptt_srpt->title[priv->ttn -
-                1].ptt[chapter + 1].pgn) - 1] - 1;
+    return GST_DVD_READ_AGAIN;
   }
 
-  GST_LOG ("Opened chapter %d - cell %d-%d",
-      chapter, priv->start_cell, priv->last_cell);
+  if (src->new_cell || new_seek) {
+    if (!new_seek) {
+      src->cur_cell = src->next_cell;
+      if (src->cur_cell >= src->last_cell) {
+        GST_LOG_OBJECT (src, "last cell in chapter");
+        goto again;
+      }
+    }
 
-  /* retrieve position */
-  priv->cur_pack = 0;
-  for (i = 0; i < chapter; i++) {
-    gint c1, c2;
+    /* take angle into account */
+    if (src->cur_pgc->cell_playback[src->cur_cell].block_type
+        == BLOCK_TYPE_ANGLE_BLOCK)
+      src->cur_cell += angle;
 
-    c1 = priv->cur_pgc->program_map[(priv->vts_ptt_srpt->title[priv->ttn -
-                1].ptt[i].pgn) - 1] - 1;
-    if (i + 1 == priv->tt_srpt->title[priv->title].nr_of_ptts) {
-      c2 = priv->cur_pgc->nr_of_cells;
-    } else {
-      c2 = priv->cur_pgc->program_map[(priv->vts_ptt_srpt->title[priv->ttn -
-                  1].ptt[i + 1].pgn) - 1] - 1;
-    }
+    /* calculate next cell */
+    src->next_cell = gst_dvd_read_src_get_next_cell_for (src, src->cur_cell);
 
-    for (; c1 < c2; c1++) {
-      priv->cur_pack +=
-          priv->cur_pgc->cell_playback[c1].last_sector -
-          priv->cur_pgc->cell_playback[c1].first_sector;
-    }
+    /* we loop until we're out of this cell */
+    src->cur_pack = src->cur_pgc->cell_playback[src->cur_cell].first_sector;
+    src->new_cell = FALSE;
   }
 
-  /* prepare reading for new cell */
-  priv->new_cell = TRUE;
-  priv->next_cell = priv->start_cell;
-
-  priv->chapter = chapter;
-  return 0;
-}
+  if (src->cur_pack >= src->cur_pgc->cell_playback[src->cur_cell].last_sector) {
+    src->new_cell = TRUE;
+    GST_LOG_OBJECT (src, "Beyond last sector, go to next cell");
+    return GST_DVD_READ_AGAIN;
+  }
 
-static int
-get_next_cell_for (DVDReadSrcPrivate * priv, int cell)
-{
-  /* Check if we're entering an angle block. */
-  if (priv->cur_pgc->cell_playback[cell].block_type == BLOCK_TYPE_ANGLE_BLOCK) {
-    int i;
+  /* read NAV packet */
+  data = GST_BUFFER_DATA (buf);
 
-    for (i = 0;; ++i) {
-      if (priv->cur_pgc->cell_playback[cell + i].block_mode
-          == BLOCK_MODE_LAST_CELL) {
-        return cell + i + 1;
-      }
-    }
+nav_retry:
 
-    /* not reached */
+  len = DVDReadBlocks (src->dvd_title, src->cur_pack, 1, data);
+  if (len == 0) {
+    GST_ERROR_OBJECT (src, "Read failed for block %d", src->cur_pack);
+    return GST_DVD_READ_ERROR;
   }
 
-  return cell + 1;
-}
+  if (!gst_dvd_read_src_is_nav_pack (data)) {
+    src->cur_pack++;
+    goto nav_retry;
+  }
 
-/*
- * Read function.
- * -1: error, -2: eos, -3: try again, 0: ok.
- */
+  /* parse the contained dsi packet */
+  navRead_DSI (&dsi_pack, &data[DSI_START_BYTE]);
+  g_assert (src->cur_pack == dsi_pack.dsi_gi.nv_pck_lbn);
+
+  /* determine where we go next. These values are the ones we
+   * mostly care about */
+  next_ilvu_start = src->cur_pack + dsi_pack.sml_agli.data[angle].address;
+  cur_output_size = dsi_pack.dsi_gi.vobu_ea;
+
+  /* If we're not at the end of this cell, we can determine the next
+   * VOBU to display using the VOBU_SRI information section of the
+   * DSI.  Using this value correctly follows the current angle,
+   * avoiding the doubled scenes in The Matrix, and makes our life
+   * really happy.
+   *
+   * Otherwise, we set our next address past the end of this cell to
+   * force the code above to go to the next cell in the program. */
+  if (dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL) {
+    next_vobu = src->cur_pack + (dsi_pack.vobu_sri.next_vobu & 0x7fffffff);
+  } else {
+    next_vobu = src->cur_pack + cur_output_size + 1;
+  }
 
-static int
-_read (DVDReadSrcPrivate * priv, int angle, int new_seek, GstBuffer * buf)
-{
-  unsigned char *data, static_data[DVD_VIDEO_LB_LEN];
+  g_assert (cur_output_size < 1024);
+  ++src->cur_pack;
 
-  if (buf) {
-    data = GST_BUFFER_DATA (buf);
-  } else {
-    data = static_data;
+  /* read in and output cursize packs */
+  len = DVDReadBlocks (src->dvd_title, src->cur_pack, cur_output_size, data);
+  if (len != cur_output_size) {
+    GST_ERROR_OBJECT (src, "Read failed for %d blocks at %d",
+        cur_output_size, src->cur_pack);
+    return GST_DVD_READ_ERROR;
   }
 
-  /*
-   * Playback by cell in this pgc, starting at the cell for our chapter.
-   */
-  if (new_seek)
-    priv->cur_cell = priv->start_cell;
+  GST_BUFFER_SIZE (buf) = cur_output_size * DVD_VIDEO_LB_LEN;
+  /* GST_BUFFER_OFFSET (buf) = priv->cur_pack * DVD_VIDEO_LB_LEN; */
 
-again:
+  src->cur_pack = next_vobu;
 
-  if (priv->cur_cell < priv->last_cell) {
-    if (priv->new_cell || new_seek) {
-      if (!new_seek) {
-        priv->cur_cell = priv->next_cell;
-        if (priv->cur_cell >= priv->last_cell) {
-          GST_LOG ("last cell in chapter");
-          goto again;
-        }
-      }
+  GST_LOG_OBJECT (src, "Read %u sectors", cur_output_size);
+  return GST_DVD_READ_OK;
+}
+
+static GstFlowReturn
+gst_dvd_read_src_create (GstPushSrc * pushsrc, GstBuffer ** p_buf)
+{
+  GstDvdReadSrc *src = GST_DVD_READ_SRC (pushsrc);
+  GstBuffer *buf;
+  GstPad *srcpad;
+  gint res;
 
-      /* take angle into account */
-      if (priv->cur_pgc->cell_playback[priv->cur_cell].block_type
-          == BLOCK_TYPE_ANGLE_BLOCK)
-        priv->cur_cell += angle;
+  g_return_val_if_fail (src->dvd != NULL, GST_FLOW_ERROR);
 
-      /* calculate next cell */
-      priv->next_cell = get_next_cell_for (priv, priv->cur_cell);
+  srcpad = GST_BASE_SRC (src)->srcpad;
 
-      /*
-       * We loop until we're out of this cell.
-       */
-      priv->cur_pack =
-          priv->cur_pgc->cell_playback[priv->cur_cell].first_sector;
-      priv->new_cell = FALSE;
+  /* handle events, if any */
+  if (src->seek_pend) {
+    if (src->flush_pend) {
+      gst_pad_push_event (srcpad, gst_event_new_flush_start ());
+      gst_pad_push_event (srcpad, gst_event_new_flush_stop ());
+      src->flush_pend = FALSE;
     }
 
-    if (priv->cur_pack <
-        priv->cur_pgc->cell_playback[priv->cur_cell].last_sector) {
-      dsi_t dsi_pack;
-      unsigned int next_vobu, next_ilvu_start, cur_output_size;
-      int len;
-
-      /*
-       * Read NAV packet.
-       */
-    nav_retry:
-
-      len = DVDReadBlocks (priv->dvd_title, priv->cur_pack, 1, data);
-      if (len == 0) {
-        GST_ERROR ("Read failed for block %d", priv->cur_pack);
-        return -1;
+    if (src->seek_pend_fmt != GST_FORMAT_UNDEFINED) {
+      if (src->seek_pend_fmt == title_format) {
+        gst_dvd_read_src_goto_title (src, src->title, src->angle);
       }
+      gst_dvd_read_src_goto_chapter (src, src->chapter);
 
-      if (!is_nav_pack (data)) {
-        priv->cur_pack++;
-        goto nav_retry;
+      src->seek_pend_fmt = GST_FORMAT_UNDEFINED;
+    } else {
+      if (!gst_dvd_read_src_goto_sector (src, src->angle)) {
+        GST_DEBUG_OBJECT (src, "seek to sector failed, going EOS");
+        gst_pad_push_event (srcpad, gst_event_new_eos ());
+        return GST_FLOW_UNEXPECTED;
       }
+    }
 
+    gst_pad_push_event (srcpad,
+        gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
+            src->cur_pack * DVD_VIDEO_LB_LEN, -1, 0));
 
-      /*
-       * Parse the contained dsi packet.
-       */
-      navRead_DSI (&dsi_pack, &(data[DSI_START_BYTE]));
-      assert (priv->cur_pack == dsi_pack.dsi_gi.nv_pck_lbn);
-
-
-      /*
-       * Determine where we go next.  These values are the ones we mostly
-       * care about.
-       */
-      next_ilvu_start = priv->cur_pack + dsi_pack.sml_agli.data[angle].address;
-      cur_output_size = dsi_pack.dsi_gi.vobu_ea;
-
-
-      /*
-       * If we're not at the end of this cell, we can determine the next
-       * VOBU to display using the VOBU_SRI information section of the
-       * DSI.  Using this value correctly follows the current angle,
-       * avoiding the doubled scenes in The Matrix, and makes our life
-       * really happy.
-       *
-       * Otherwise, we set our next address past the end of this cell to
-       * force the code above to go to the next cell in the program.
-       */
-      if (dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL) {
-        next_vobu = priv->cur_pack + (dsi_pack.vobu_sri.next_vobu & 0x7fffffff);
-      } else {
-        next_vobu = priv->cur_pack + cur_output_size + 1;
-      }
+    src->seek_pend = FALSE;
+  }
 
-      assert (cur_output_size < 1024);
-      priv->cur_pack++;
-
-      if (buf) {
-        /*
-         * Read in and output cursize packs.
-         */
-        len =
-            DVDReadBlocks (priv->dvd_title, priv->cur_pack, cur_output_size,
-            data);
-        if (len != cur_output_size) {
-          GST_ERROR ("Read failed for %d blocks at %d",
-              cur_output_size, priv->cur_pack);
-          return -1;
-        }
+  if (src->new_seek) {
+    gst_dvd_read_src_goto_title (src, src->title, src->angle);
+    gst_dvd_read_src_goto_chapter (src, src->chapter);
 
-        GST_BUFFER_SIZE (buf) = cur_output_size * DVD_VIDEO_LB_LEN;
-        //GST_BUFFER_OFFSET (buf) = priv->cur_pack * DVD_VIDEO_LB_LEN;
-      }
+    src->new_seek = FALSE;
+    src->change_cell = TRUE;
+  }
 
-      priv->cur_pack = next_vobu;
+  if (src->title_lang_event_pending) {
+    gst_pad_push_event (srcpad, src->title_lang_event_pending);
+    src->title_lang_event_pending = NULL;
+  }
 
-      GST_LOG ("done reading data - %u sectors", cur_output_size);
+  if (src->pending_clut_event) {
+    gst_pad_push_event (srcpad, src->pending_clut_event);
+    src->pending_clut_event = NULL;
+  }
 
-      return 0;
-    } else {
-      priv->new_cell = TRUE;
-    }
-  } else {
-    /* swap to next chapter */
-    if (priv->chapter + 1 == priv->tt_srpt->title[priv->title].nr_of_ptts) {
-      GST_LOG ("last chapter done - eos");
-      return -2;
-    }
+  /* create the buffer (TODO: use buffer pool?) */
+  buf = gst_buffer_new_and_alloc (1024 * DVD_VIDEO_LB_LEN);
 
-    GST_LOG ("end-of-chapter, switch to next");
+  /* read it in */
+  do {
+    res = gst_dvd_read_src_read (src, src->angle, src->change_cell, buf);
+  } while (res == GST_DVD_READ_AGAIN);
 
-    priv->chapter++;
-    _seek_chapter (priv, priv->chapter);
+  switch (res) {
+    case GST_DVD_READ_ERROR:{
+      GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), (NULL));
+      gst_buffer_unref (buf);
+      return GST_FLOW_ERROR;
+    }
+    case GST_DVD_READ_EOS:{
+      GST_INFO_OBJECT (src, "Reached EOS");
+      gst_pad_push_event (GST_BASE_SRC (src)->srcpad, gst_event_new_eos ());
+      gst_buffer_unref (buf);
+      return GST_FLOW_UNEXPECTED;
+    }
+    case GST_DVD_READ_OK:{
+      src->change_cell = FALSE;
+      *p_buf = buf;
+      return GST_FLOW_OK;
+    }
+    default:
+      break;
   }
 
-  /* again */
-  GST_LOG ("Need another try");
-
-  return -3;
+  g_return_val_if_reached (GST_FLOW_UNEXPECTED);
 }
 
-static gboolean
-seek_sector (DVDReadSrcPrivate * priv, int angle)
+static void
+gst_dvd_read_src_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
 {
-  gint seek_to = priv->cur_pack;
-  gint chapter, sectors, next, cur, i;
+  GstDvdReadSrc *src = GST_DVD_READ_SRC (object);
 
-  /* retrieve position */
-  priv->cur_pack = 0;
-  for (i = 0; i < priv->tt_srpt->title[priv->title].nr_of_ptts; i++) {
-    gint c1, c2;
+  GST_OBJECT_LOCK (src);
 
-    c1 = priv->cur_pgc->program_map[(priv->vts_ptt_srpt->title[priv->ttn -
-                1].ptt[i].pgn) - 1] - 1;
-    if (i + 1 == priv->tt_srpt->title[priv->title].nr_of_ptts) {
-      c2 = priv->cur_pgc->nr_of_cells;
-    } else {
-      c2 = priv->cur_pgc->program_map[(priv->vts_ptt_srpt->title[priv->ttn -
-                  1].ptt[i + 1].pgn) - 1] - 1;
-    }
+  switch (prop_id) {
+    case ARG_DEVICE:{
+      if (!GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED)) {
+        g_warning ("%s: property '%s' needs to be set before the device is "
+            "opened", GST_ELEMENT_NAME (src), pspec->name);
+        break;;
+      }
 
-    for (next = cur = c1; cur < c2;) {
-      if (next != cur) {
-        sectors =
-            priv->cur_pgc->cell_playback[cur].last_sector -
-            priv->cur_pgc->cell_playback[cur].first_sector;
-        if (priv->cur_pack + sectors > seek_to) {
-          chapter = i;
-          goto done;
-        }
-        priv->cur_pack += sectors;
+      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");
+      } else {
+        src->location = g_strdup (g_value_get_string (value));
       }
-      cur = next;
-      if (priv->cur_pgc->cell_playback[cur].block_type
-          == BLOCK_TYPE_ANGLE_BLOCK)
-        cur += angle;
-      next = get_next_cell_for (priv, cur);
+      break;
     }
+    case ARG_TITLE:
+      src->title = g_value_get_int (value);
+      src->new_seek = TRUE;
+      break;
+    case ARG_CHAPTER:
+      src->chapter = g_value_get_int (value);
+      src->new_seek = TRUE;
+      break;
+    case ARG_ANGLE:
+      src->angle = g_value_get_int (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
   }
 
-  GST_LOG ("Seek to sector %u failed", seek_to);
-  return FALSE;
+  GST_OBJECT_UNLOCK (src);
+}
 
-done:
-  /* so chapter $chapter and cell $cur contain our sector
-   * of interest. Let's go there! */
-  GST_LOG ("Seek succeeded, going to chapter %u, cell %u", chapter, cur);
+static void
+gst_dvd_read_src_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstDvdReadSrc *src = GST_DVD_READ_SRC (object);
 
-  _seek_chapter (priv, chapter);
-  priv->cur_cell = cur;
-  priv->next_cell = next;
-  priv->new_cell = FALSE;
-  priv->cur_pack = seek_to;
+  GST_OBJECT_LOCK (src);
 
-  return TRUE;
+  switch (prop_id) {
+    case ARG_DEVICE:
+      g_value_set_string (value, src->location);
+      break;
+    case ARG_TITLE:
+      g_value_set_int (value, src->title);
+      break;
+    case ARG_CHAPTER:
+      g_value_set_int (value, src->chapter);
+      break;
+    case ARG_ANGLE:
+      g_value_set_int (value, src->angle);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+
+  GST_OBJECT_UNLOCK (src);
 }
 
-static GstData *
-dvdreadsrc_get (GstPad * pad)
+/*** Querying and seeking ***/
+
+static gboolean
+gst_dvd_read_src_do_seek (GstDvdReadSrc * src, GstEvent * event)
 {
-  gint res;
-  DVDReadSrc *dvdreadsrc;
-  DVDReadSrcPrivate *priv;
-  GstBuffer *buf;
+  GstSeekFlags flags;
+  GstSeekType cur_type, end_type;
+  gint64 new_off, total, cur;
+  GstFormat format;
+  GstPad *srcpad;
+  gdouble rate;
 
-  g_return_val_if_fail (pad != NULL, NULL);
-  g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+  srcpad = GST_BASE_SRC (src)->srcpad;
 
-  dvdreadsrc = DVDREADSRC (gst_pad_get_parent (pad));
-  priv = dvdreadsrc->priv;
-  g_return_val_if_fail (GST_OBJECT_FLAG_IS_SET (dvdreadsrc, DVDREADSRC_OPEN),
-      NULL);
+  gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &new_off,
+      &end_type, NULL);
 
-  /* handle vents, if any */
-  if (priv->seek_pend) {
-    if (priv->flush_pend) {
-      priv->flush_pend = FALSE;
+  if (end_type != GST_SEEK_TYPE_NONE) {
+    GST_WARNING_OBJECT (src, "End seek type not supported, will be ignored");
+  }
 
-      return GST_DATA (gst_event_new (GST_EVENT_FLUSH));
+  switch (format) {
+    case GST_FORMAT_BYTES:{
+      new_off /= DVD_VIDEO_LB_LEN;
+      format = sector_format;
+      break;
     }
-
-    priv->seek_pend = FALSE;
-    if (priv->seek_pend_fmt != GST_FORMAT_UNDEFINED) {
-      if (priv->seek_pend_fmt == title_format) {
-        _seek_title (priv, priv->title, priv->angle);
-      }
-      _seek_chapter (priv, priv->chapter);
-
-      priv->seek_pend_fmt = GST_FORMAT_UNDEFINED;
-    } else {
-      if (!seek_sector (priv, priv->angle)) {
-        gst_element_set_eos (GST_ELEMENT (dvdreadsrc));
-        return GST_DATA (gst_event_new (GST_EVENT_EOS));
+    default:{
+      if (format != chapter_format &&
+          format != sector_format &&
+          format != angle_format && format != title_format) {
+        GST_DEBUG_OBJECT (src, "Unsupported seek format %d (%s)", format,
+            gst_format_get_name (format));
+        return FALSE;
       }
+      break;
     }
-
-    return GST_DATA (gst_event_new_discontinuous (FALSE,
-            GST_FORMAT_BYTES, (gint64) (priv->cur_pack * DVD_VIDEO_LB_LEN),
-            GST_FORMAT_UNDEFINED));
   }
 
-  /* create the buffer */
-  /* FIXME: should eventually use a bufferpool for this */
-  buf = gst_buffer_new_and_alloc (1024 * DVD_VIDEO_LB_LEN);
+  /* get current offset and length */
+  gst_pad_query_duration (srcpad, &format, &total);
+  gst_pad_query_position (srcpad, &format, &cur);
+
+  GST_DEBUG_OBJECT (src, "Current %s: %" G_GINT64_FORMAT,
+      gst_format_get_name (format), cur);
+  GST_DEBUG_OBJECT (src, "Total   %s: %" G_GINT64_FORMAT,
+      gst_format_get_name (format), total);
 
-  if (priv->new_seek) {
-    _seek_title (priv, priv->title, priv->angle);
-    _seek_chapter (priv, priv->chapter);
+  if (cur == new_off) {
+    GST_DEBUG_OBJECT (src, "We're already at that position!");
+    return TRUE;
   }
 
-  /* read it in from the file */
-  while ((res = _read (priv, priv->angle, priv->new_seek, buf)) == -3);
-  switch (res) {
-    case -1:
-      GST_ELEMENT_ERROR (dvdreadsrc, RESOURCE, READ, (NULL), (NULL));
-      gst_buffer_unref (buf);
-      return NULL;
-    case -2:
-      gst_element_set_eos (GST_ELEMENT (dvdreadsrc));
-      gst_buffer_unref (buf);
-      return GST_DATA (gst_event_new (GST_EVENT_EOS));
-    case 0:
+  /* get absolute */
+  switch (cur_type) {
+    case GST_SEEK_TYPE_SET:
+      /* no-op */
+      break;
+    case GST_SEEK_TYPE_CUR:
+      new_off += cur;
+      break;
+    case GST_SEEK_TYPE_END:
+      new_off = total - new_off;
       break;
     default:
-      g_assert_not_reached ();
+      GST_DEBUG_OBJECT (src, "Unsupported seek method");
+      return FALSE;
+  }
+
+  if (new_off < 0 || new_off >= total) {
+    GST_DEBUG_OBJECT (src, "Invalid seek position %" G_GINT64_FORMAT, new_off);
+    return FALSE;
+  }
+
+  GST_LOG_OBJECT (src, "Seeking to unit %" G_GINT64_FORMAT, new_off);
+
+  GST_OBJECT_LOCK (src);
+
+  if (format == angle_format) {
+    src->angle = new_off;
+    GST_OBJECT_UNLOCK (src);
+    return TRUE;
+  }
+
+  if (format == sector_format) {
+    src->cur_pack = new_off;
+  } else if (format == chapter_format) {
+    src->cur_pack = 0;
+    src->chapter = new_off;
+    src->seek_pend_fmt = format;
+  } else if (format == title_format) {
+    src->cur_pack = 0;
+    src->title = new_off;
+    src->chapter = 0;
+    src->seek_pend_fmt = format;
+  } else {
+    g_return_val_if_reached (FALSE);
   }
 
-  priv->new_seek = FALSE;
+  /* leave for events */
+  src->seek_pend = TRUE;
+  if ((flags & GST_SEEK_FLAG_FLUSH) != 0)
+    src->flush_pend = TRUE;
 
-  return GST_DATA (buf);
+  GST_OBJECT_UNLOCK (src);
+
+  return TRUE;
 }
 
-/* open the file, necessary to go to RUNNING state */
 static gboolean
-dvdreadsrc_open_file (DVDReadSrc * src)
+gst_dvd_read_src_src_event (GstBaseSrc * basesrc, GstEvent * event)
 {
-  g_return_val_if_fail (src != NULL, FALSE);
-  g_return_val_if_fail (GST_IS_DVDREADSRC (src), FALSE);
-  g_return_val_if_fail (!GST_OBJECT_FLAG_IS_SET (src, DVDREADSRC_OPEN), FALSE);
+  GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
+  gboolean res;
 
-  if (_open (src->priv, src->priv->location)) {
-    GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), (NULL));
-    return FALSE;
+  GST_DEBUG_OBJECT (src, "handling %s event", GST_EVENT_TYPE_NAME (event));
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_SEEK:
+      res = gst_dvd_read_src_do_seek (src, event);
+      break;
+    default:
+      res = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
+      break;
   }
-  src->priv->seek_pend_fmt = title_format;
-  src->priv->seek_pend = TRUE;
 
-  GST_OBJECT_FLAG_SET (src, DVDREADSRC_OPEN);
+  return res;
+}
+
+static GstEvent *
+gst_dvd_read_src_make_clut_change_event (GstDvdReadSrc * src,
+    const guint * clut)
+{
+  GstStructure *structure;
+  gchar name[16];
+  gint i;
+
+  structure = gst_structure_new ("application/x-gst-dvd",
+      "event", G_TYPE_STRING, "dvd-spu-clut-change", NULL);
+
+  /* Create a separate field for each value in the table. */
+  for (i = 0; i < 16; i++) {
+    g_snprintf (name, sizeof (name), "clut%02d", i);
+    gst_structure_set (structure, name, G_TYPE_INT, (int) clut[i], NULL);
+  }
+
+  /* Create the DVD event and put the structure into it. */
+  return gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
+}
+
+static gint64
+gst_dvd_read_src_convert_timecode (dvd_time_t * time)
+{
+  gint64 ret_time;
+  const gint64 one_hour = 3600 * GST_SECOND;
+  const gint64 one_min = 60 * GST_SECOND;
+
+  g_return_val_if_fail ((time->hour >> 4) < 0xa
+      && (time->hour & 0xf) < 0xa, -1);
+  g_return_val_if_fail ((time->minute >> 4) < 0x7
+      && (time->minute & 0xf) < 0xa, -1);
+  g_return_val_if_fail ((time->second >> 4) < 0x7
+      && (time->second & 0xf) < 0xa, -1);
+
+  ret_time = ((time->hour >> 4) * 10 + (time->hour & 0xf)) * one_hour;
+  ret_time += ((time->minute >> 4) * 10 + (time->minute & 0xf)) * one_min;
+  ret_time += ((time->second >> 4) * 10 + (time->second & 0xf)) * GST_SECOND;
+
+  return ret_time;
+}
 
+static gboolean
+gst_dvd_read_src_do_duration_query (GstDvdReadSrc * src, GstQuery * query)
+{
+  GstFormat format;
+  gint64 val;
+
+  gst_query_parse_duration (query, &format, NULL);
+
+  switch (format) {
+    case GST_FORMAT_TIME:{
+      if (src->cur_pgc == NULL)
+        return FALSE;
+      val = gst_dvd_read_src_convert_timecode (&src->cur_pgc->playback_time);
+      if (val < 0)
+        return FALSE;
+      break;
+    }
+    case GST_FORMAT_BYTES:{
+      val = DVDFileSize (src->dvd_title) * DVD_VIDEO_LB_LEN;
+      break;
+    }
+    default:{
+      if (format == sector_format) {
+        val = DVDFileSize (src->dvd_title);
+      } else if (format == title_format) {
+        val = src->tt_srpt->nr_of_srpts;
+      } else if (format == chapter_format) {
+        val = src->num_chapters;
+      } else if (format == angle_format) {
+        val = src->tt_srpt->title[src->title].nr_of_angles;
+      } else {
+        GST_DEBUG_OBJECT (src, "Don't know how to handle format %d (%s)",
+            format, gst_format_get_name (format));
+        return FALSE;
+      }
+      break;
+    }
+  }
+
+  gst_query_set_duration (query, format, val);
   return TRUE;
 }
 
-/* close the file */
-static void
-dvdreadsrc_close_file (DVDReadSrc * src)
+static gboolean
+gst_dvd_read_src_do_position_query (GstDvdReadSrc * src, GstQuery * query)
 {
-  g_return_if_fail (GST_OBJECT_FLAG_IS_SET (src, DVDREADSRC_OPEN));
+  GstFormat format;
+  gint64 val;
 
-  _close (src->priv);
+  gst_query_parse_position (query, &format, NULL);
 
-  GST_OBJECT_FLAG_UNSET (src, DVDREADSRC_OPEN);
+  switch (format) {
+    case GST_FORMAT_BYTES:{
+      val = src->cur_pack * DVD_VIDEO_LB_LEN;
+      break;
+    }
+    default:{
+      if (format == sector_format) {
+        val = src->cur_pack;
+      } else if (format == title_format) {
+        val = src->title;
+      } else if (format == chapter_format) {
+        val = src->chapter;
+      } else if (format == angle_format) {
+        val = src->angle;
+      } else {
+        GST_DEBUG_OBJECT (src, "Don't know how to handle format %d (%s)",
+            format, gst_format_get_name (format));
+        return FALSE;
+      }
+      break;
+    }
+  }
+
+  gst_query_set_position (query, format, val);
+  return TRUE;
 }
 
-static GstStateChangeReturn
-dvdreadsrc_change_state (GstElement * element, GstStateChange transition)
+static gboolean
+gst_dvd_read_src_src_query (GstBaseSrc * basesrc, GstQuery * query)
 {
-  DVDReadSrc *dvdreadsrc = DVDREADSRC (element);
+  GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
+  gboolean started;
+  gboolean res = TRUE;
 
-  g_return_val_if_fail (GST_IS_DVDREADSRC (element), GST_STATE_CHANGE_FAILURE);
+  GST_LOG_OBJECT (src, "handling %s query",
+      gst_query_type_get_name (GST_QUERY_TYPE (query)));
 
-  GST_DEBUG ("gstdvdreadsrc: state pending %d", GST_STATE_PENDING (element));
+  GST_OBJECT_LOCK (src);
+  started = (GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED));
+  GST_OBJECT_UNLOCK (src);
 
-  /* if going down into NULL state, close the file if it's open */
-  switch (transition) {
-    case GST_STATE_CHANGE_NULL_TO_READY:
-      if (!dvdreadsrc_open_file (DVDREADSRC (element)))
-        return GST_STATE_CHANGE_FAILURE;
-      break;
-    case GST_STATE_CHANGE_PAUSED_TO_READY:
-      dvdreadsrc->priv->new_cell = TRUE;
-      dvdreadsrc->priv->new_seek = TRUE;
-      dvdreadsrc->priv->chapter = 0;
-      dvdreadsrc->priv->title = 0;
-      dvdreadsrc->priv->flush_pend = FALSE;
-      dvdreadsrc->priv->seek_pend = FALSE;
-      dvdreadsrc->priv->seek_pend_fmt = GST_FORMAT_UNDEFINED;
+  if (!started) {
+    GST_DEBUG_OBJECT (src, "query failed: not started");
+    return FALSE;
+  }
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_DURATION:
+      GST_OBJECT_LOCK (src);
+      res = gst_dvd_read_src_do_duration_query (src, query);
+      GST_OBJECT_UNLOCK (src);
       break;
-    case GST_STATE_CHANGE_READY_TO_NULL:
-      dvdreadsrc_close_file (DVDREADSRC (element));
+    case GST_QUERY_POSITION:
+      GST_OBJECT_LOCK (src);
+      res = gst_dvd_read_src_do_position_query (src, query);
+      GST_OBJECT_UNLOCK (src);
       break;
     default:
+      res = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
       break;
   }
 
-  /* 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 res;
+}
 
-  return GST_STATE_CHANGE_SUCCESS;
+static gboolean
+gst_dvd_read_src_goto_sector (GstDvdReadSrc * src, int angle)
+{
+  gint seek_to = src->cur_pack;
+  gint chapter, sectors, next, cur, i;
+
+  /* retrieve position */
+  src->cur_pack = 0;
+  for (i = 0; i < src->num_chapters; i++) {
+    gint c1, c2;
+
+    cur_title_get_chapter_bounds (src, i, &c1, &c2);
+
+    for (next = cur = c1; cur < c2;) {
+      if (next != cur) {
+        sectors =
+            src->cur_pgc->cell_playback[cur].last_sector -
+            src->cur_pgc->cell_playback[cur].first_sector;
+        if (src->cur_pack + sectors > seek_to) {
+          chapter = i;
+          goto done;
+        }
+        src->cur_pack += sectors;
+      }
+      cur = next;
+      if (src->cur_pgc->cell_playback[cur].block_type == BLOCK_TYPE_ANGLE_BLOCK)
+        cur += angle;
+      next = gst_dvd_read_src_get_next_cell_for (src, cur);
+    }
+  }
+
+  GST_DEBUG_OBJECT (src, "Seek to sector %u failed", seek_to);
+  return FALSE;
+
+done:
+  /* so chapter $chapter and cell $cur contain our sector
+   * of interest. Let's go there! */
+  GST_INFO_OBJECT (src, "Seek succeeded, going to chapter %u, cell %u",
+      chapter, cur);
+
+  gst_dvd_read_src_goto_chapter (src, chapter);
+  src->cur_cell = cur;
+  src->next_cell = next;
+  src->new_cell = FALSE;
+  src->cur_pack = seek_to;
+
+  return TRUE;
 }
 
-/*
- * URI interface.
- */
+
+/*** URI interface ***/
 
 static GstURIType
-dvdreadsrc_uri_get_type (void)
+gst_dvd_read_src_uri_get_type (void)
 {
   return GST_URI_SRC;
 }
 
 static gchar **
-dvdreadsrc_uri_get_protocols (void)
+gst_dvd_read_src_uri_get_protocols (void)
 {
   static gchar *protocols[] = { "dvd", NULL };
 
@@ -1139,35 +1115,33 @@ dvdreadsrc_uri_get_protocols (void)
 }
 
 static const gchar *
-dvdreadsrc_uri_get_uri (GstURIHandler * handler)
+gst_dvd_read_src_uri_get_uri (GstURIHandler * handler)
 {
-  DVDReadSrc *dvdreadsrc = DVDREADSRC (handler);
+  GstDvdReadSrc *src = GST_DVD_READ_SRC (handler);
 
-  g_free (dvdreadsrc->priv->last_uri);
-  dvdreadsrc->priv->last_uri =
-      g_strdup_printf ("dvd://%d,%d,%d", dvdreadsrc->priv->title,
-      dvdreadsrc->priv->chapter, dvdreadsrc->priv->angle);
+  g_free (src->last_uri);
+  src->last_uri =
+      g_strdup_printf ("dvd://%d,%d,%d", src->title, src->chapter, src->angle);
 
-  return dvdreadsrc->priv->last_uri;
+  return src->last_uri;
 }
 
 static gboolean
-dvdreadsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)
+gst_dvd_read_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
 {
-  DVDReadSrc *dvdreadsrc = DVDREADSRC (handler);
+  GstDvdReadSrc *src = GST_DVD_READ_SRC (handler);
   gboolean ret;
-  gchar *protocol = gst_uri_get_protocol (uri);
+  gchar *protocol;
 
-  ret = (protocol && !strcmp (protocol, "dvd")) ? TRUE : FALSE;
+  protocol = gst_uri_get_protocol (uri);
+  ret = (protocol != NULL && g_str_equal (protocol, "dvd"));
   g_free (protocol);
   protocol = NULL;
 
   if (!ret)
     return ret;
 
-  /*
-   * Parse out the new t/c/a and seek to them
-   */
+  /* parse out the new t/c/a and seek to them */
   {
     gchar *location = NULL;
     gchar **strs;
@@ -1188,19 +1162,19 @@ dvdreadsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)
 
       switch (pos) {
         case 0:
-          if (val != dvdreadsrc->priv->title) {
-            dvdreadsrc->priv->title = val;
-            dvdreadsrc->priv->new_seek = TRUE;
+          if (val != src->title) {
+            src->title = val;
+            src->new_seek = TRUE;
           }
           break;
         case 1:
-          if (val != dvdreadsrc->priv->chapter) {
-            dvdreadsrc->priv->chapter = val;
-            dvdreadsrc->priv->new_seek = TRUE;
+          if (val != src->chapter) {
+            src->chapter = val;
+            src->new_seek = TRUE;
           }
           break;
         case 2:
-          dvdreadsrc->priv->angle = val;
+          src->angle = val;
           break;
       }
 
@@ -1216,28 +1190,50 @@ dvdreadsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)
 }
 
 static void
-dvdreadsrc_uri_handler_init (gpointer g_iface, gpointer iface_data)
+gst_dvd_read_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
 {
   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
 
-  iface->get_type = dvdreadsrc_uri_get_type;
-  iface->get_protocols = dvdreadsrc_uri_get_protocols;
-  iface->get_uri = dvdreadsrc_uri_get_uri;
-  iface->set_uri = dvdreadsrc_uri_set_uri;
+  iface->get_type = gst_dvd_read_src_uri_get_type;
+  iface->get_protocols = gst_dvd_read_src_uri_get_protocols;
+  iface->get_uri = gst_dvd_read_src_uri_get_uri;
+  iface->set_uri = gst_dvd_read_src_uri_set_uri;
+}
+
+static void
+gst_dvd_read_src_do_init (GType dvdreadsrc_type)
+{
+  static const GInterfaceInfo urihandler_info = {
+    gst_dvd_read_src_uri_handler_init,
+    NULL,
+    NULL
+  };
+
+  g_type_add_interface_static (dvdreadsrc_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, "dvdreadsrc", GST_RANK_NONE,
-          GST_TYPE_DVDREADSRC))
+  GST_DEBUG_CATEGORY_INIT (gstgst_dvd_read_src_debug, "dvdreadsrc", 0,
+      "DVD reader element based on dvdreadsrc");
+
+  if (!gst_element_register (plugin, "dvdreadsrc", GST_RANK_SECONDARY,
+          GST_TYPE_DVD_READ_SRC)) {
     return FALSE;
+  }
 
   return TRUE;
 }
 
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
     GST_VERSION_MINOR,
-    "dvdreadsrc",
+    "dvdread",
     "Access a DVD with dvdread",
     plugin_init, VERSION, "GPL", GST_PACKAGE, GST_ORIGIN)
index 9becfb2..1a0fc33 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
-#ifndef __DVDREADSRC_H__
-#define __DVDREADSRC_H__
+#ifndef __GST_DVD_READ_SRC_H__
+#define __GST_DVD_READ_SRC_H__
 
 #include <gst/gst.h>
+#include <gst/base/gstpushsrc.h>
+
+#include <dvdread/dvd_reader.h>
+#include <dvdread/ifo_types.h>
+#include <dvdread/ifo_read.h>
+#include <dvdread/nav_read.h>
+#include <dvdread/nav_print.h>
 
 G_BEGIN_DECLS
 
-GstElementDetails dvdreadsrc_details;
-
-#define GST_TYPE_DVDREADSRC \
-  (dvdreadsrc_get_type())
-#define DVDREADSRC(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DVDREADSRC,DVDReadSrc))
-#define DVDREADSRC_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DVDREADSRC,DVDReadSrcClass))
-#define GST_IS_DVDREADSRC(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DVDREADSRC))
-#define GST_IS_DVDREADSRC_CLASS(obj) \
-  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DVDREADSRC))
-
-/* NOTE: per-element flags start with 16 for now */
-typedef enum {
-  DVDREADSRC_OPEN               = (GST_ELEMENT_FLAG_LAST << 0),
-
-  DVDREADSRC_FLAG_LAST          = (GST_ELEMENT_FLAG_LAST << 2)
-} DVDReadSrcFlags;
-
-typedef struct _DVDReadSrc DVDReadSrc;
-typedef struct _DVDReadSrcPrivate DVDReadSrcPrivate;
-typedef struct _DVDReadSrcClass DVDReadSrcClass;
-
-struct _DVDReadSrc {
-  GstElement element;
-  DVDReadSrcPrivate *priv;
+#define GST_TYPE_DVD_READ_SRC            (gst_dvd_read_src_get_type())
+#define GST_DVD_READ_SRC(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DVD_READ_SRC,GstDvdReadSrc))
+#define GST_DVD_READ_SRC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DVD_READ_SRC,GstDvdReadSrcClass))
+#define GST_IS_DVD_READ_SRC(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DVD_READ_SRC))
+#define GST_IS_DVD_READ_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DVD_READ_SRC))
+
+typedef struct _GstDvdReadSrc GstDvdReadSrc;
+typedef struct _GstDvdReadSrcClass GstDvdReadSrcClass;
+
+struct _GstDvdReadSrc {
+  GstPushSrc       pushsrc;
+
+  /* location */
+  gchar           *location;
+  gchar           *last_uri;
+
+  gboolean         new_seek;
+  gboolean         change_cell;
+
+  gboolean         new_cell;
+
+  gint             title, chapter, angle;
+  gint             start_cell, last_cell, cur_cell;
+  gint             cur_pack;
+  gint             next_cell;
+  dvd_reader_t    *dvd;
+  ifo_handle_t    *vmg_file;
+
+  /* title stuff */
+  gint             ttn;
+  tt_srpt_t       *tt_srpt;
+  ifo_handle_t    *vts_file;
+  vts_ptt_srpt_t  *vts_ptt_srpt;
+  dvd_file_t      *dvd_title;
+  gint             num_chapters;
+
+  /* which program chain to watch (based on title and chapter number) */
+  pgc_t           *cur_pgc;
+  gint             pgc_id;
+  gint             pgn;
+
+  gboolean         seek_pend;
+  gboolean         flush_pend;
+  GstFormat        seek_pend_fmt;
+  GstEvent        *title_lang_event_pending;
+  GstEvent        *pending_clut_event;
 };
 
-struct _DVDReadSrcClass {
-  GstElementClass parent_class;
+struct _GstDvdReadSrcClass {
+  GstPushSrcClass parent_class;
 };
 
-GType dvdreadsrc_get_type (void);
+GType gst_dvd_read_src_get_type (void);
 
 G_END_DECLS
 
-#endif /* __DVDREADSRC_H__ */
+#endif /* __GST_DVD_READ_SRC_H__ */
+
diff --git a/ext/dvdread/stream_labels.c b/ext/dvdread/stream_labels.c
deleted file mode 100644 (file)
index a2e59b5..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-/* GStreamer
- * Copyright (C) <2005> Stephane Loeuillet <stephane.loeuillet@tiscali.fr>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "dvdreadsrc.h"
-#include "stream_labels.h"
-#include <gst/gst-i18n-plugin.h>
-
-GHashTable *
-dvdreadsrc_init_languagelist (void)
-{
-  GHashTable *languagelist = NULL;
-
-  languagelist = g_hash_table_new (g_str_hash, g_str_equal);
-
-  g_hash_table_insert (languagelist, "aa", _("Afar"));
-  g_hash_table_insert (languagelist, "ab", _("Abkhazian"));
-  g_hash_table_insert (languagelist, "af", _("Afrikaans"));
-  g_hash_table_insert (languagelist, "am", _("Amharic"));
-  g_hash_table_insert (languagelist, "ar", _("Arabic"));
-  g_hash_table_insert (languagelist, "as", _("Assamese"));
-  g_hash_table_insert (languagelist, "ax", _("Aymara"));
-  g_hash_table_insert (languagelist, "az", _("Azerbaijani"));
-  g_hash_table_insert (languagelist, "ba", _("Bashkir"));
-  g_hash_table_insert (languagelist, "be", _("Byelorussian"));
-  g_hash_table_insert (languagelist, "bg", _("Bulgarian"));
-  g_hash_table_insert (languagelist, "bh", _("Bislama"));
-  g_hash_table_insert (languagelist, "bn", _("Bengali"));
-  g_hash_table_insert (languagelist, "bo", _("Tibetan"));
-  g_hash_table_insert (languagelist, "br", _("Breton"));
-  g_hash_table_insert (languagelist, "ca", _("Catalan"));
-  g_hash_table_insert (languagelist, "co", _("Corsican"));
-  g_hash_table_insert (languagelist, "cs", _("Czech"));
-  g_hash_table_insert (languagelist, "cy", _("Welsh"));
-  g_hash_table_insert (languagelist, "da", _("Danish"));
-  g_hash_table_insert (languagelist, "de", _("German"));
-  g_hash_table_insert (languagelist, "dz", _("Bhutani"));
-  g_hash_table_insert (languagelist, "el", _("Greek"));
-  g_hash_table_insert (languagelist, "en", _("English"));
-  g_hash_table_insert (languagelist, "eo", _("Esperanto"));
-  g_hash_table_insert (languagelist, "es", _("Spanish"));
-  g_hash_table_insert (languagelist, "et", _("Estonian"));
-  g_hash_table_insert (languagelist, "eu", _("Basque"));
-  g_hash_table_insert (languagelist, "fa", _("Persian"));
-  g_hash_table_insert (languagelist, "fi", _("Finnish"));
-  g_hash_table_insert (languagelist, "fj", _("Fiji"));
-  g_hash_table_insert (languagelist, "fo", _("Faeroese"));
-  g_hash_table_insert (languagelist, "fr", _("French"));
-  g_hash_table_insert (languagelist, "fy", _("Frisian"));
-  g_hash_table_insert (languagelist, "ga", _("Irish"));
-  g_hash_table_insert (languagelist, "gd", _("Scots/Gaelic"));
-  g_hash_table_insert (languagelist, "gl", _("Galician"));
-  g_hash_table_insert (languagelist, "gn", _("Guarani"));
-  g_hash_table_insert (languagelist, "gu", _("Gujarati"));
-  g_hash_table_insert (languagelist, "ha", _("Hausa"));
-  g_hash_table_insert (languagelist, "hi", _("Hindi"));
-  g_hash_table_insert (languagelist, "hr", _("Croatian"));
-  g_hash_table_insert (languagelist, "hu", _("Hungarian"));
-  g_hash_table_insert (languagelist, "hy", _("Armenian"));
-  g_hash_table_insert (languagelist, "ia", _("Interlingua"));
-  g_hash_table_insert (languagelist, "in", _("Indonesian"));
-  g_hash_table_insert (languagelist, "is", _("Icelandic"));
-  g_hash_table_insert (languagelist, "it", _("Italian"));
-  g_hash_table_insert (languagelist, "iw", _("Hebrew"));
-  g_hash_table_insert (languagelist, "ja", _("Japanese"));
-  g_hash_table_insert (languagelist, "ji", _("Yiddish"));
-  g_hash_table_insert (languagelist, "jw", _("Javanese"));
-  g_hash_table_insert (languagelist, "ka", _("Georgian"));
-  g_hash_table_insert (languagelist, "kk", _("Kazakh"));
-  g_hash_table_insert (languagelist, "kl", _("Greenlandic"));
-  g_hash_table_insert (languagelist, "km", _("Cambodian"));
-  g_hash_table_insert (languagelist, "kn", _("Kannada"));
-  g_hash_table_insert (languagelist, "ko", _("Korean"));
-  g_hash_table_insert (languagelist, "ks", _("Kashmiri"));
-  g_hash_table_insert (languagelist, "ku", _("Kurdish"));
-  g_hash_table_insert (languagelist, "ky", _("Kirghiz"));
-  g_hash_table_insert (languagelist, "la", _("Latin"));
-  g_hash_table_insert (languagelist, "ln", _("Lingala"));
-  g_hash_table_insert (languagelist, "lo", _("Laothian"));
-  g_hash_table_insert (languagelist, "lt", _("Lithuanian"));
-  g_hash_table_insert (languagelist, "lv", _("Latvian/Lettish"));
-  g_hash_table_insert (languagelist, "mg", _("Malagasy"));
-  g_hash_table_insert (languagelist, "mi", _("Maori"));
-  g_hash_table_insert (languagelist, "mk", _("Macedonian"));
-  g_hash_table_insert (languagelist, "ml", _("Malayalam"));
-  g_hash_table_insert (languagelist, "mn", _("Mongolian"));
-  g_hash_table_insert (languagelist, "mo", _("Moldavian"));
-  g_hash_table_insert (languagelist, "mr", _("Marathi"));
-  g_hash_table_insert (languagelist, "ms", _("Malay"));
-  g_hash_table_insert (languagelist, "mt", _("Maltese"));
-  g_hash_table_insert (languagelist, "my", _("Burmese"));
-  g_hash_table_insert (languagelist, "na", _("Nauru"));
-  g_hash_table_insert (languagelist, "ne", _("Nepali"));
-  g_hash_table_insert (languagelist, "nl", _("Dutch"));
-  g_hash_table_insert (languagelist, "no", _("Norwegian"));
-  g_hash_table_insert (languagelist, "or", _("Oriya"));
-  g_hash_table_insert (languagelist, "pa", _("Punjabi"));
-  g_hash_table_insert (languagelist, "pl", _("Polish"));
-  g_hash_table_insert (languagelist, "ps", _("Pashto/Pushto"));
-  g_hash_table_insert (languagelist, "pt", _("Portuguese"));
-  g_hash_table_insert (languagelist, "qu", _("Quechua"));
-  g_hash_table_insert (languagelist, "rm", _("Rhaeto-Romance"));
-  g_hash_table_insert (languagelist, "ro", _("Romanian"));
-  g_hash_table_insert (languagelist, "ru", _("Russian"));
-  g_hash_table_insert (languagelist, "sa", _("Kinyarwanda"));
-  g_hash_table_insert (languagelist, "sd", _("Sanskrit"));
-  g_hash_table_insert (languagelist, "sh", _("Serbo-Croatian"));
-  g_hash_table_insert (languagelist, "si", _("Singhalese"));
-  g_hash_table_insert (languagelist, "sk", _("Slovak"));
-  g_hash_table_insert (languagelist, "sl", _("Slovenian"));
-  g_hash_table_insert (languagelist, "sm", _("Samoan"));
-  g_hash_table_insert (languagelist, "sn", _("Shona"));
-  g_hash_table_insert (languagelist, "so", _("Somali"));
-  g_hash_table_insert (languagelist, "sq", _("Albanian"));
-  g_hash_table_insert (languagelist, "sr", _("Serbian"));
-  g_hash_table_insert (languagelist, "su", _("Sundanese"));
-  g_hash_table_insert (languagelist, "sv", _("Swedish"));
-  g_hash_table_insert (languagelist, "sw", _("Swahili"));
-  g_hash_table_insert (languagelist, "ta", _("Tamil"));
-  g_hash_table_insert (languagelist, "te", _("Tegulu"));
-  g_hash_table_insert (languagelist, "tg", _("Tajik"));
-  g_hash_table_insert (languagelist, "th", _("Thai"));
-  g_hash_table_insert (languagelist, "ti", _("Tigrinya"));
-  g_hash_table_insert (languagelist, "tk", _("Turkmen"));
-  g_hash_table_insert (languagelist, "tl", _("Tagalog"));
-  g_hash_table_insert (languagelist, "to", _("Tonga"));
-  g_hash_table_insert (languagelist, "tr", _("Turkish"));
-  g_hash_table_insert (languagelist, "tt", _("Tatar"));
-  g_hash_table_insert (languagelist, "tw", _("Twi"));
-  g_hash_table_insert (languagelist, "uk", _("Ukrainian"));
-  g_hash_table_insert (languagelist, "ur", _("Urdu"));
-  g_hash_table_insert (languagelist, "uz", _("Uzbek"));
-  g_hash_table_insert (languagelist, "vi", _("Vietnamese"));
-  g_hash_table_insert (languagelist, "vo", _("Volapuk"));
-  g_hash_table_insert (languagelist, "wo", _("Wolof"));
-  g_hash_table_insert (languagelist, "xh", _("Xhosa"));
-  g_hash_table_insert (languagelist, "yo", _("Yoruba"));
-  g_hash_table_insert (languagelist, "zh", _("Chinese"));
-  g_hash_table_insert (languagelist, "zu", _("Zulu"));
-
-  return languagelist;
-}
-
-void
-dvdreadsrc_get_audio_stream_labels (ifo_handle_t * vts_file,
-    GHashTable * languagelist)
-{
-  GList *audio_stream_label = NULL;
-
-  if (vts_file->vts_pgcit) {
-    int i;
-
-    /* 8 audio streams maximum */
-    for (i = 0; i < 8; i++) {
-      const gchar *format, *channel_nb, *language = NULL;
-      guchar language_code[3] = "??";
-      gchar *streamlabel;
-
-      if (vts_file->vts_pgcit->pgci_srp[0].pgc->audio_control[i] & 0x8000) {
-        audio_attr_t *audio = &vts_file->vtsi_mat->vts_audio_attr[i];
-
-        if (audio->lang_type == 1) {
-          language_code[0] = (audio->lang_code >> 8);
-          language_code[1] = (audio->lang_code & 0xFF);
-          language = g_hash_table_lookup (languagelist, language_code);
-        }
-
-        if (!language) {
-          language = "?";
-        }
-
-        switch (audio->audio_format) {
-          case 0:
-            format = _("Dolby AC-3");
-            break;
-          case 2:
-          case 3:
-            format = _("MPEG layer I, II or III");
-            break;
-          case 4:
-            format = _("LPCM");
-            break;
-          case 6:
-            format = _("Digital Theatre System");
-            break;
-          default:
-            format = "?";
-        }
-
-        switch (audio->channels) {
-          case 1:
-            channel_nb = _("Stereo");
-            break;
-          case 5:
-            channel_nb = _("5.1");
-            break;
-          default:
-            channel_nb = "?";
-        }
-
-        streamlabel = g_strdup_printf ("%u : %s, %s %s", i + 1, language,
-            format, channel_nb);
-        audio_stream_label = g_list_append (audio_stream_label, streamlabel);   /* "French, Dolby AC-3 Stereo" */
-
-        printf ("%u : %s, %s %s\n", i + 1, language, format, channel_nb);
-      }
-    }
-  }
-
-  g_list_foreach (audio_stream_label, (GFunc) g_free, NULL);
-  g_list_free (audio_stream_label);
-}
-
-void
-dvdreadsrc_get_subtitle_stream_labels (ifo_handle_t * vts_file,
-    GHashTable * languagelist)
-{
-
-}
diff --git a/ext/dvdread/stream_labels.h b/ext/dvdread/stream_labels.h
deleted file mode 100644 (file)
index 9d4c768..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/* GStreamer
- * Copyright (C) <2005> Stephane Loeuillet <stephane.loeuillet@tiscali.fr>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <dvdread/ifo_types.h>
-
-#include "dvdreadsrc.h"
-
-GHashTable * dvdreadsrc_init_languagelist (void);
-void dvdreadsrc_get_audio_stream_labels (ifo_handle_t *vts_file, GHashTable * languagelist);
-void dvdreadsrc_get_subtitle_stream_labels (ifo_handle_t *vts_file, GHashTable * languagelist);