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