From 50501c07be8ae337ea1af27a397d6e2996522da6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim-Philipp=20M=C3=BCller?= Date: Thu, 9 Feb 2006 17:27:57 +0000 Subject: [PATCH] Half-baked port to 0.10. Needs some love in the seeking department, but at least it does something. 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 | 17 + configure.ac | 7 + ext/Makefile.am | 12 +- ext/dvdread/Makefile.am | 25 +- ext/dvdread/dvdreadsrc.c | 1808 +++++++++++++++++++++---------------------- ext/dvdread/dvdreadsrc.h | 93 ++- ext/dvdread/stream_labels.c | 238 ------ ext/dvdread/stream_labels.h | 26 - 8 files changed, 1006 insertions(+), 1220 deletions(-) delete mode 100644 ext/dvdread/stream_labels.c delete mode 100644 ext/dvdread/stream_labels.h diff --git a/ChangeLog b/ChangeLog index c73086b..e31a650 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2006-02-09 Tim-Philipp Müller + + * 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 * ext/amrnb/amrnbdec.c: (gst_amrnbdec_init), diff --git a/configure.ac b/configure.ac index 1c9421c..f11e3e3 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/ext/Makefile.am b/ext/Makefile.am index 686f476..5b89c18 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -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 \ diff --git a/ext/dvdread/Makefile.am b/ext/dvdread/Makefile.am index 3dba019..e3b33b2 100644 --- a/ext/dvdread/Makefile.am +++ b/ext/dvdread/Makefile.am @@ -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 diff --git a/ext/dvdread/dvdreadsrc.c b/ext/dvdread/dvdreadsrc.c index 16c68cd..cb5e6b8 100644 --- a/ext/dvdread/dvdreadsrc.c +++ b/ext/dvdread/dvdreadsrc.c @@ -1,5 +1,5 @@ /* GStreamer - * Copyright (C) <1999> Erik Walthinsen + * Copyright (C) 1999 Erik Walthinsen * Copyright (C) 2001 Billy Biggs . * * This library is free software; you can redistribute it and/or @@ -22,1116 +22,1092 @@ #include "config.h" #endif +#include "_stdint.h" + #include #include -#include -#include #include -#include -#include -#include -#include -#include -#include +#include #include -//#include -#include #include "dvdreadsrc.h" -#include "stream_labels.h" - -#include -#include -#include -#include -#include - -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 */ +/* 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 ", -}; +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 ", +}; -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) diff --git a/ext/dvdread/dvdreadsrc.h b/ext/dvdread/dvdreadsrc.h index 9becfb2..1a0fc33 100644 --- a/ext/dvdread/dvdreadsrc.h +++ b/ext/dvdread/dvdreadsrc.h @@ -17,48 +17,75 @@ * Boston, MA 02111-1307, USA. */ -#ifndef __DVDREADSRC_H__ -#define __DVDREADSRC_H__ +#ifndef __GST_DVD_READ_SRC_H__ +#define __GST_DVD_READ_SRC_H__ #include +#include + +#include +#include +#include +#include +#include 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 index a2e59b5..0000000 --- a/ext/dvdread/stream_labels.c +++ /dev/null @@ -1,238 +0,0 @@ -/* GStreamer - * Copyright (C) <2005> Stephane Loeuillet - * - * 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 - -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 index 9d4c768..0000000 --- a/ext/dvdread/stream_labels.h +++ /dev/null @@ -1,26 +0,0 @@ -/* GStreamer - * Copyright (C) <2005> Stephane Loeuillet - * - * 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 - -#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); -- 2.7.4