+2004-03-27 Martin Soto <martinsoto@users.sourceforge.net>
+
+ * 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 <otte@gnome.org>
* gst/typefind/gsttypefindfunctions.c: (theora_type_find):
#include <unistd.h>
#include <errno.h>
#include <assert.h>
+
+#include <gst/gst-i18n-plugin.h>
#include <gst/gst.h>
#include <dvdnav/dvdnav.h>
#include <dvdnav/nav_print.h>
+
+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) \
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);
};
};
-/* DVDNavSrc signals and args */
+/* DVDNavSrc signals and args */
enum
{
- BUTTON_PRESSED_SIGNAL,
- POINTER_SELECT_SIGNAL,
- POINTER_ACTIVATE_SIGNAL,
USER_OP_SIGNAL,
LAST_SIGNAL
};
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
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 void dvdnavsrc_set_clock (GstElement * element, GstClock * clock);
-static GstData *dvdnavsrc_get (GstPad * pad);
+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 GstBuffer * dvdnavsrc_get_region (GstPad *pad,gulong offset,gulong size); */
+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);
(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;
}
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,
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,
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
{
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); */
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
}
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;
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;
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)
{
/* 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;
}
* 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;
}
/**
* 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;
}
/**
* 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;
}
*/
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;
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);
}
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;
+ DVDNavSrcDomainType domain;
+
+ 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;
+ }
+
+ /* FIXME: We may send a signal if we have a new domain. */
+ src->domain = domain;
+}
+
+/**
+ * Check for a new highlighted area, and send an spu highlight event if
+ * necessary.
+ */
+static void
+dvdnavsrc_update_highlight (DVDNavSrc * src)
+{
+ int button;
pci_t *pci;
+ dvdnav_highlight_area_t area;
+ GstEvent *event;
+
+ DVDNAV_CALL (dvdnav_get_current_highlight, (src->dvdnav, &button), src);
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 (src->buttoninfo) {
- gint btn_ns;
+ if (button == 0) {
+ if (src->button != 0) {
+ src->button = 0;
- /* 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;
+ event = dvdnavsrc_make_dvd_event (src, "dvd-spu-reset-highlight", NULL);
+ gst_pad_push (src->srcpad, GST_DATA (event));
}
+ return;
}
- 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");
-}
-
-static void
-dvdnavsrc_button_pressed (DVDNavSrc * src, int button)
-{
-}
+ 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);
+ }
-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);
-}
+ src->button = button;
-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);
+ gst_pad_push (src->srcpad, GST_DATA (event));
+ }
}
static void
{
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 */
}
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;
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)
{
case DVDNAV_STILL_FRAME:
return "DVDNAV_STILL_FRAME";
break;
+ case DVDNAV_WAIT:
+ return "DVDNAV_WAIT";
+ break;
case DVDNAV_SPU_STREAM_CHANGE:
return "DVDNAV_SPU_STREAM_CHANGE";
break;
case DVDNAV_HOP_CHANNEL:
return "DVDNAV_HOP_CHANNEL";
break;
- case DVDNAV_WAIT:
- return "DVDNAV_WAIT";
- break;
}
return "UNKNOWN";
}
+#else
+#define dvdnav_get_event_name(event) "<unknown>"
+#endif /* GST_DISABLE_GST_DEBUG */
static gchar *
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:
{
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:
{
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;
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;
-
- g_return_val_if_fail (pad != NULL, NULL);
- g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+ GstData *send_data;
- 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;
- }
- data = GST_BUFFER_DATA (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;
+ /* Loop processing blocks until there is data to send. */
+ send_data = NULL;
+ while (send_data == NULL) {
+ if (src->cur_buf == NULL) {
+ src->cur_buf = gst_buffer_new_and_alloc (DVD_VIDEO_LB_LEN);
+ if (src->cur_buf == NULL) {
+ GST_ELEMENT_ERROR (src, CORE, TOO_LAZY, (NULL),
+ ("Failed to create a new GstBuffer"));
+ return;
+ }
}
+ data = GST_BUFFER_DATA (src->cur_buf);
+
+ DVDNAV_CALL (dvdnav_get_next_block, (src->dvdnav, data, &event, &len), src);
switch (event) {
case DVDNAV_NOP:
break;
+
case DVDNAV_BLOCK_OK:
- 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);
+ {
+ pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav);
+
+ /* Check for forced buttons. */
+ if (pci->hli.hl_gi.hli_ss == 1) {
+ GST_LOG_OBJECT (src, "menu ahead");
+ if (pci->hli.hl_gi.fosl_btnn > 0) {
+ GST_DEBUG_OBJECT (src, "forced button");
+ dvdnav_button_select (src->dvdnav, pci, pci->hli.hl_gi.fosl_btnn);
+ }
+ }
+
+ dvdnavsrc_update_highlight (src);
+
+ /* Send a dvd nav packet event. */
+ send_data = GST_DATA (dvdnavsrc_make_dvd_nav_packet_event (src, pci));
+ }
break;
- case DVDNAV_WAIT:
- /* FIXME: supposed to make sure all the data has made
- * it to the sinks before skipping the wait
- */
+
+ case DVDNAV_SPU_CLUT_CHANGE:
+ send_data = GST_DATA (dvdnavsrc_make_clut_change_event (src,
+ (guint *) data));
+ break;
+
+ case DVDNAV_VTS_CHANGE:
+ {
+ dvdnavsrc_set_domain (src);
+
+ send_data = GST_DATA (dvdnavsrc_make_dvd_event (src,
+ "dvd-vts-change", "domain",
+ G_TYPE_INT, (gint) src->domain, NULL));
+ }
+ break;
+
+ case DVDNAV_AUDIO_STREAM_CHANGE:
+ {
+ dvdnav_audio_stream_change_event_t *info =
+ (dvdnav_audio_stream_change_event_t *) data;
+ int phys = info->physical;
+
dvdnavsrc_print_event (src, data, event, len);
- dvdnav_wait_skip (src->dvdnav);
+
+ 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_HOP_CHANNEL:
- /* Indicates a time discontinuity, and the downstream should
- * flush
- */
+
+ 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);
- buf = GST_BUFFER (gst_event_new (GST_EVENT_DISCONTINUOUS));
- have_buf = TRUE;
+
+ 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;
- /* 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:
+ 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 */
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);
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));
- return FALSE;
- }
+ DVDNAV_CALLVAL (dvdnav_get_next_block,
+ (src->dvdnav, buf, &event, &buflen), src, 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))
+ if (!dvdnavsrc_tca_seek (src, src->title, src->chapter, src->angle)) {
return FALSE;
+ }
}
return TRUE;
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);
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;
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
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;
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;
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
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2004 Martin Soto <martinsoto@users.sourceforge.net>
+ *
+ * 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 <string.h>
+
+#include <gstdvddemux.h>
+
+
+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 <soto@informatik.uni-kl.de>"
+};
+
+/* 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);
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2004 Martin Soto <martinsoto@users.sourceforge.net>
+ *
+ * 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 <gst/gst.h>
+#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__ */
*/
-/*#define GST_DEBUG_ENABLED*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gstmpegdemux.h>
+
+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",
/* 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,
"mpegversion = (int) { 1, 2 }, " "systemstream = (boolean) FALSE")
);
-static GstStaticPadTemplate private1_factory =
-GST_STATIC_PAD_TEMPLATE ("private_stream_1_%02d",
- GST_PAD_SRC,
- GST_PAD_SOMETIMES,
- GST_STATIC_CAPS ("audio/x-ac3")
- );
-
-static GstStaticPadTemplate private2_factory =
-GST_STATIC_PAD_TEMPLATE ("private_stream_2",
- GST_PAD_SRC,
- GST_PAD_SOMETIMES,
- GST_STATIC_CAPS_ANY);
-
-static GstStaticPadTemplate pcm_factory =
-GST_STATIC_PAD_TEMPLATE ("pcm_stream_%02d",
+static GstStaticPadTemplate audio_template =
+GST_STATIC_PAD_TEMPLATE ("audio_%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 ]")
+ GST_STATIC_CAPS ("audio/mpeg, " "mpegversion = (int) 1"
+ /* FIXME "layer = (int) { 1, 2 }" */
+ )
);
-static GstStaticPadTemplate subtitle_factory =
-GST_STATIC_PAD_TEMPLATE ("subtitle_stream_%d",
+static GstStaticPadTemplate private_template =
+GST_STATIC_PAD_TEMPLATE ("private_%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,
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;
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;
}
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;
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
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;
+ }
- GST_FLAG_SET (mpeg_demux, GST_ELEMENT_EVENT_AWARE);
-}
-
-static GstMPEGStream *
-gst_mpeg_demux_new_stream (void)
-{
- GstMPEGStream *stream;
-
- stream = g_new0 (GstMPEGStream, 1);
+ mpeg_demux->adjust = 0;
- return stream;
+ GST_FLAG_SET (mpeg_demux, GST_ELEMENT_EVENT_AWARE);
}
static void
}
}
}
+
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;
-
- parent_class->parse_packhead (mpeg_parse, buffer);
-
- GST_DEBUG ("in parse_packhead");
- buf = GST_BUFFER_DATA (buffer);
- /* do something usefull here */
+ 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);
- return TRUE;
+ gst_pad_push (mpeg_demux->private_stream[i]->pad, GST_DATA (discont));
+ }
+ }
}
static gint
}
}
+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)
{
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 */
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;
}
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++;
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;
/* 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;
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;
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:
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++;
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);
-
- 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;
+ GST_DEBUG_OBJECT (mpeg_demux, "headerlen is %d, datalen is %d",
+ headerlen, datalen);
- /* 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;
{
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;
-
- GST_DEBUG ("in parse_pes");
+ GstMPEGStream *outstream = NULL;
+ guint8 *buf;
- 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, */
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;
-
- /* 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);
-
- 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);
-
- 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);
- }
- } 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");
- }
-
- 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);
- }
+ 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 (*outstream) {
- gint64 timestamp;
+ 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;
- outpad = &((*outstream)->pad);
+ 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;
- /* attach pts, if any */
- if (pts != -1) {
- pts += mpeg_parse->adjust;
- timestamp = MPEGTIME_TO_GSTTIME (pts);
+ 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;
- 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);
+ outstream = CLASS (mpeg_demux)->get_video_stream (mpeg_demux,
+ id - 0xE0, GST_MPEG_DEMUX_VIDEO_MPEG, &mpeg_version);
}
- } 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;
+ CLASS (mpeg_demux)->send_subbuffer (mpeg_demux, outstream, buffer,
+ timestamp, headerlen + 4, datalen);
+ break;
- 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;
+ if (!GST_PAD_IS_USABLE (outstream->pad)) {
+ return;
}
- /* Determine the number of channels. */
- channels = (sample_info & 0x7) + 1;
+ GST_DEBUG_OBJECT (mpeg_demux, "Creating subbuffer size %d", size);
+ outbuf = gst_buffer_create_sub (buffer, offset, size);
- 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);
+ GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
+ GST_BUFFER_OFFSET (outbuf) = GST_BUFFER_OFFSET (buffer) + offset;
+
+ gst_pad_push (outstream->pad, GST_DATA (outbuf));
}
-/**
- * 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)
+gst_mpeg_demux_process_private (GstMPEGDemux * mpeg_demux,
+ GstBuffer * buffer,
+ guint stream_nr, GstClockTime timestamp, guint headerlen, guint datalen)
{
- GstMPEGStream **stream = NULL;
+ GstMPEGStream *outstream;
- 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) {
- return;
- }
-
- gst_pad_unlink ((*stream)->pad, gst_pad_get_peer ((*stream)->pad));
- gst_element_remove_pad (GST_ELEMENT (mpeg_demux), (*stream)->pad);
-
- g_free (*stream);
- *stream = NULL;
+ 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)
{
#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))
-typedef struct _GstMPEGDemux GstMPEGDemux;
-typedef struct _GstMPEGDemuxClass GstMPEGDemuxClass;
+/* 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;
+/* 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 {
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];
-
- /* 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];
+ 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];
- 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
}
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",
ARG_0,
ARG_SYNC,
ARG_MAX_DISCONT,
- ARG_STREAMINFO,
+ ARG_DO_ADJUST,
/* FILL ME */
};
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);
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;
}
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;
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)
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);
}
#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)
{
}
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;
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;
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)) {
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;
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:
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;
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 {
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);
}
}
}
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;
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;
/* 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;
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);
#endif
#include "gstmpegparse.h"
#include "gstmpegdemux.h"
+#include "gstdvddemux.h"
#include "gstrfc2250enc.h"
static gboolean
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;