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