resindvd: Initial partial port to 0.11
authorJan Schmidt <thaytan@noraisin.net>
Sat, 9 Jun 2012 12:36:06 +0000 (22:36 +1000)
committerJan Schmidt <thaytan@noraisin.net>
Fri, 31 Aug 2012 19:46:29 +0000 (12:46 -0700)
16 files changed:
configure.ac
ext/resindvd/Makefile.am
ext/resindvd/gstmpegdefs.h
ext/resindvd/gstmpegdemux.c
ext/resindvd/gstmpegdemux.h
ext/resindvd/gstpesfilter.c
ext/resindvd/gstpesfilter.h
ext/resindvd/plugin.c
ext/resindvd/resindvdbin.c
ext/resindvd/resindvdsrc.c
ext/resindvd/rsnaudiomunge.c
ext/resindvd/rsndec.c
ext/resindvd/rsninputselector.c [new file with mode: 0644]
ext/resindvd/rsninputselector.h [new file with mode: 0644]
ext/resindvd/rsnstreamselector.c [deleted file]
ext/resindvd/rsnstreamselector.h [deleted file]

index d58c47bdaab933f4c57e87991b3080a51c70d9e3..0f7a00dcd999f8e10226fa071102b931a8ee60b1 100644 (file)
@@ -321,7 +321,7 @@ GST_PLUGINS_NONPORTED=" aiff \
  sdi siren speed subenc stereo tta videofilters \
  videomeasure videosignal vmnc \
  decklink fbdev linsys vcd \
- apexsink cdaudio cog dc1394 dirac directfb resindvd \
+ apexsink cdaudio cog dc1394 dirac directfb \
  gsettings jasper ladspa \
  musepack musicbrainz nas neon ofa openal rsvg sdl sndfile spandsp spc timidity \
  directsound directdraw direct3d9 acm wininet \
index 9c3651e7288adb8d085d5e59ac7c98025b33dc8b..cf2460ce24418d26e0113e2878c1f63f00aeedb5 100644 (file)
@@ -5,22 +5,21 @@ plugin_LTLIBRARIES = libgstresindvd.la
 libgstresindvd_la_SOURCES = \
         plugin.c \
         resindvdbin.c \
-       rsnaudiomunge.c \
-       rsndec.c \
-       rsnstreamselector.c \
         resindvdsrc.c  \
+       rsndec.c \
         gstmpegdesc.c   \
         gstmpegdemux.c  \
         gstpesfilter.c \
-       rsnparsetter.c \
-       rsnwrappedbuffer.c
+       rsninputselector.c \
+       # rsnparsetter.c \
+       # rsnwrappedbuffer.c \
+       # rsnaudiomunge.c
 
 libgstresindvd_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) \
 $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \
 $(GST_CFLAGS) $(DVDNAV_CFLAGS)
 libgstresindvd_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \
--lgstinterfaces-$(GST_API_VERSION) -lgstvideo-$(GST_API_VERSION) \
--lgstpbutils-$(GST_API_VERSION) \
+-lgstvideo-$(GST_API_VERSION) -lgstpbutils-$(GST_API_VERSION) \
 $(GST_BASE_LIBS) $(GST_LIBS) $(DVDNAV_LIBS)
 libgstresindvd_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
 libgstresindvd_la_LIBTOOLFLAGS = --tag=disable-static
@@ -28,7 +27,7 @@ libgstresindvd_la_LIBTOOLFLAGS = --tag=disable-static
 noinst_HEADERS = resindvdbin.h \
        rsnaudiomunge.h \
        rsndec.h \
-       rsnstreamselector.h \
+       rsninputselector.h \
         resindvdsrc.h \
         gstmpegdefs.h \
         gstmpegdesc.h \
index 608769ae0d91a27cca17034e85ecfe86242d99c1..a0429e7a2fdc921d7112efafa5842156e5d16197 100644 (file)
  * 0x0F-0x7F ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved
  * 0x80-0xFF User Private
  */
-#define ST_RESERVED                    0x00
-#define ST_VIDEO_MPEG1                 0x01
-#define ST_VIDEO_MPEG2                 0x02
-#define ST_AUDIO_MPEG1                 0x03
-#define ST_AUDIO_MPEG2                 0x04
-#define ST_PRIVATE_SECTIONS            0x05
-#define ST_PRIVATE_DATA                        0x06
-#define ST_MHEG                                0x07
-#define ST_DSMCC                       0x08
-#define ST_H222_1                      0x09
+#define ST_RESERVED                     0x00
+#define ST_VIDEO_MPEG1                  0x01
+#define ST_VIDEO_MPEG2                  0x02
+#define ST_AUDIO_MPEG1                  0x03
+#define ST_AUDIO_MPEG2                  0x04
+#define ST_PRIVATE_SECTIONS             0x05
+#define ST_PRIVATE_DATA                 0x06
+#define ST_MHEG                         0x07
+#define ST_DSMCC                        0x08
+#define ST_H222_1                       0x09
 
 /* later extensions */
-#define ST_AUDIO_AAC                   0x0f
-#define ST_VIDEO_MPEG4                 0x10
-#define ST_VIDEO_H264                  0x1b
+#define ST_AUDIO_AAC_ADTS               0x0f
+/* LATM/LOAS AAC syntax */
+#define ST_AUDIO_AAC_LOAS               0x11
+#define ST_VIDEO_MPEG4                  0x10
+#define ST_VIDEO_H264                   0x1b
 
 /* Un-official Dirac extension */
-#define ST_VIDEO_DIRAC                 0xd1
+#define ST_VIDEO_DIRAC                  0xd1
 
 /* private stream types */
-#define ST_PS_AUDIO_AC3                        0x81
-#define ST_PS_AUDIO_DTS                        0x8a
-#define ST_PS_AUDIO_LPCM               0x8b
+#define ST_PS_AUDIO_AC3                 0x81
+#define ST_PS_AUDIO_DTS                 0x8a
+#define ST_PS_AUDIO_LPCM                0x8b
 #define ST_PS_DVD_SUBPICTURE            0xff
+/* Blu-ray related */
+#define ST_BD_AUDIO_LPCM                0x80
+#define ST_BD_AUDIO_AC3                 0x81
+#define ST_BD_AUDIO_DTS                 0x82
+#define ST_BD_AUDIO_AC3_TRUE_HD         0x83
+#define ST_BD_AUDIO_AC3_PLUS            0x84
+#define ST_BD_AUDIO_DTS_HD              0x85
+#define ST_BD_AUDIO_DTS_HD_MASTER_AUDIO 0x86
+#define ST_BD_AUDIO_EAC3                0x87
+#define ST_BD_PGS_SUBPICTURE            0x90
+#define ST_BD_IGS                       0x91
+#define ST_BD_SUBTITLE                  0x92
+#define ST_BD_SECONDARY_AC3_PLUS        0xa1
+#define ST_BD_SECONDARY_DTS_HD          0xa2
+
+/* defined for VC1 extension in RP227 */
+#define ST_PRIVATE_EA                   0xea
+
+/* HDV AUX stream mapping
+ * 0xA0      ISO/IEC 61834-11
+ * 0xA1      ISO/IEC 61834-11
+ */
+#define ST_HDV_AUX_A                    0xa0
+#define ST_HDV_AUX_V                    0xa1
 
 /* Un-official time-code stream */
 #define ST_PS_TIMECODE                  0xd2
 
 #define MPEG_MUX_RATE_MULT      50
 
+/* sync:4 == 00xx ! pts:3 ! 1 ! pts:15 ! 1 | pts:15 ! 1 */
+#define READ_TS(data, target, lost_sync_label)          \
+    if ((*data & 0x01) != 0x01) goto lost_sync_label;   \
+    target  = ((guint64) (*data++ & 0x0E)) << 29;       \
+    target |= ((guint64) (*data++       )) << 22;       \
+    if ((*data & 0x01) != 0x01) goto lost_sync_label;   \
+    target |= ((guint64) (*data++ & 0xFE)) << 14;       \
+    target |= ((guint64) (*data++       )) << 7;        \
+    if ((*data & 0x01) != 0x01) goto lost_sync_label;   \
+    target |= ((guint64) (*data++ & 0xFE)) >> 1;
+
 /* some extra GstFlowReturn values used internally */
-#define GST_FLOW_NEED_MORE_DATA                GST_FLOW_CUSTOM_SUCCESS
-#define GST_FLOW_LOST_SYNC             GST_FLOW_CUSTOM_SUCCESS_1
+#define GST_FLOW_NEED_MORE_DATA   GST_FLOW_CUSTOM_SUCCESS
+#define GST_FLOW_LOST_SYNC        GST_FLOW_CUSTOM_SUCCESS_1
 
 #endif /* __GST_MPEG_DEFS_H__ */
index 527cd605aff4c793eb14fedccd6095c189468644..efac042239b57f0d96bec68e225766573c6231a1 100644 (file)
@@ -45,7 +45,7 @@
 GST_DEBUG_CATEGORY_STATIC (gstflupsdemux_debug);
 #define GST_CAT_DEFAULT (gstflupsdemux_debug)
 
-GST_DEBUG_CATEGORY_EXTERN (gstflupesfilter_debug);
+GST_DEBUG_CATEGORY_EXTERN (mpegpspesfilter_debug);
 
 /* MPEG2Demux signals and args */
 enum
@@ -104,16 +104,35 @@ static void gst_flups_demux_init (GstFluPSDemux * demux);
 static void gst_flups_demux_finalize (GstFluPSDemux * demux);
 static void gst_flups_demux_reset (GstFluPSDemux * demux);
 
-static gboolean gst_flups_demux_sink_event (GstPad * pad, GstEvent * event);
-static gboolean gst_flups_demux_src_event (GstPad * pad, GstEvent * event);
-static gboolean gst_flups_demux_src_query (GstPad * pad, GstQuery * query);
-static GstFlowReturn gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer);
+static gboolean gst_flups_demux_sink_event (GstPad * pad, GstObject * parent,
+    GstEvent * event);
+static GstFlowReturn gst_flups_demux_chain (GstPad * pad, GstObject * parent,
+    GstBuffer * buffer);
+static gboolean gst_flups_demux_sink_activate (GstPad * sinkpad,
+    GstObject * parent);
+static gboolean gst_flups_demux_sink_activate_mode (GstPad * pad,
+    GstObject * parent, GstPadMode mode, gboolean active);
+// static void gst_flups_demux_loop (GstPad * pad);
+
+static gboolean gst_flups_demux_src_event (GstPad * pad, GstObject * parent,
+    GstEvent * event);
+static gboolean gst_flups_demux_src_query (GstPad * pad, GstObject * parent,
+    GstQuery * query);
 
 static GstStateChangeReturn gst_flups_demux_change_state (GstElement * element,
     GstStateChange transition);
 
+static inline void gst_flups_demux_send_segment_updates (GstFluPSDemux * demux,
+    GstClockTime new_time);
+static inline void gst_flups_demux_clear_times (GstFluPSDemux * demux);
+
 static GstElementClass *parent_class = NULL;
 
+static void gst_segment_set_position (GstSegment * segment, GstFormat format,
+    guint64 position);
+//static void gst_segment_set_duration (GstSegment * segment, GstFormat format,
+//    guint64 duration);
+
 /*static guint gst_flups_demux_signals[LAST_SIGNAL] = { 0 };*/
 
 GType
@@ -132,6 +151,7 @@ gst_flups_demux_get_type (void)
       sizeof (GstFluPSDemux),
       0,
       (GInstanceInitFunc) gst_flups_demux_init,
+      NULL
     };
 
     flups_demux_type =
@@ -159,14 +179,15 @@ gst_flups_demux_base_init (GstFluPSDemuxClass * klass)
 
   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_add_pad_template (element_class,
       klass->subpicture_template);
+  gst_element_class_add_pad_template (element_class, klass->private_template);
   gst_element_class_add_pad_template (element_class, klass->sink_template);
 
-  gst_element_class_set_details_simple (element_class, "MPEG Program Demuxer",
-      "Codec/Demuxer",
-      "Demultiplexes MPEG Program Streams", "Wim Taymans <wim@fluendo.com>");
+  gst_element_class_set_details_simple (element_class,
+      "MPEG Program Demuxer", "Codec/Demuxer",
+      "Demultiplexes MPEG Program Streams",
+      "Jan Schmidt <thaytan@noraisin.net>");
 }
 
 static void
@@ -191,8 +212,15 @@ gst_flups_demux_init (GstFluPSDemux * demux)
   GstFluPSDemuxClass *klass = GST_FLUPS_DEMUX_GET_CLASS (demux);
 
   demux->sinkpad = gst_pad_new_from_template (klass->sink_template, "sink");
-  gst_pad_set_event_function (demux->sinkpad, gst_flups_demux_sink_event);
-  gst_pad_set_chain_function (demux->sinkpad, gst_flups_demux_chain);
+  gst_pad_set_event_function (demux->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_flups_demux_sink_event));
+  gst_pad_set_chain_function (demux->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_flups_demux_chain));
+  gst_pad_set_activate_function (demux->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_flups_demux_sink_activate));
+  gst_pad_set_activatemode_function (demux->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_flups_demux_sink_activate_mode));
+
   gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
 
   demux->streams =
@@ -230,8 +258,8 @@ gst_flups_demux_reset (GstFluPSDemux * demux)
   }
 
   p_ev = &demux->lang_codes;
-  gst_event_replace (p_ev, NULL);
 
+  gst_event_replace (p_ev, NULL);
   demux->scr_adjust = GSTTIME_TO_MPEGTIME (SCR_MUNGE);
 }
 
@@ -259,7 +287,6 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
     case ST_GST_VIDEO_MPEG1_OR_2:
     {
       gint mpeg_version = 1;
-
       if (stream_type == ST_VIDEO_MPEG2 ||
           (stream_type == ST_GST_VIDEO_MPEG1_OR_2 && demux->is_mpeg2_pack)) {
         mpeg_version = 2;
@@ -287,45 +314,62 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
     case ST_PRIVATE_DATA:
     case ST_MHEG:
     case ST_DSMCC:
-    case ST_AUDIO_AAC:
+      break;
+    case ST_AUDIO_AAC_ADTS:
+      template = klass->audio_template;
+      name = g_strdup_printf ("audio_%02x", id);
+      caps = gst_caps_new_simple ("audio/mpeg",
+          "mpegversion", G_TYPE_INT, 4,
+          "stream-format", G_TYPE_STRING, "adts", NULL);
+      break;
+    case ST_AUDIO_AAC_LOAS:    // LATM/LOAS AAC syntax
+      template = klass->audio_template;
+      name = g_strdup_printf ("audio_%02x", id);
+      caps = gst_caps_new_simple ("audio/mpeg",
+          "mpegversion", G_TYPE_INT, 4,
+          "stream-format", G_TYPE_STRING, "loas", NULL);
       break;
     case ST_VIDEO_H264:
       template = klass->video_template;
       name = g_strdup_printf ("video_%02x", id);
-      caps = gst_caps_new_simple ("video/x-h264", NULL);
+      caps = gst_caps_new_empty_simple ("video/x-h264");
       threshold = VIDEO_SEGMENT_THRESHOLD;
       break;
     case ST_PS_AUDIO_AC3:
       template = klass->audio_template;
       name = g_strdup_printf ("audio_%02x", id);
-      caps = gst_caps_new_simple ("audio/x-private1-ac3", NULL);
+      caps = gst_caps_new_empty_simple ("audio/x-private1-ac3");
       break;
     case ST_PS_AUDIO_DTS:
       template = klass->audio_template;
       name = g_strdup_printf ("audio_%02x", id);
-      caps = gst_caps_new_simple ("audio/x-private1-dts", NULL);
+      caps = gst_caps_new_empty_simple ("audio/x-private1-dts");
       break;
     case ST_PS_AUDIO_LPCM:
       template = klass->audio_template;
       name = g_strdup_printf ("audio_%02x", id);
-      caps = gst_caps_new_simple ("audio/x-private1-lpcm", NULL);
+      caps = gst_caps_new_empty_simple ("audio/x-private1-lpcm");
       break;
     case ST_PS_DVD_SUBPICTURE:
       template = klass->subpicture_template;
       name = g_strdup_printf ("subpicture_%02x", id);
-      caps = gst_caps_new_simple ("subpicture/x-dvd", NULL);
+      caps = gst_caps_new_empty_simple ("subpicture/x-dvd");
       break;
     case ST_GST_AUDIO_RAWA52:
       template = klass->audio_template;
       name = g_strdup_printf ("audio_%02x", id);
-      caps = gst_caps_new_simple ("audio/ac3", NULL);
+      caps = gst_caps_new_empty_simple ("audio/ac3");
       break;
     default:
       break;
   }
 
-  if (name == NULL || template == NULL || caps == NULL)
-    return NULL;
+  if (name == NULL || template == NULL || caps == NULL) {
+    g_free (name);
+    if (caps)
+      gst_caps_unref (caps);
+    return FALSE;
+  }
 
   stream = g_new0 (GstFluPSStream, 1);
   stream->id = id;
@@ -334,12 +378,21 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type)
   stream->type = stream_type;
   stream->pad = gst_pad_new_from_template (template, name);
   stream->segment_thresh = threshold;
-  gst_pad_set_event_function (stream->pad, gst_flups_demux_src_event);
-  gst_pad_set_query_function (stream->pad, gst_flups_demux_src_query);
+  gst_pad_set_event_function (stream->pad,
+      GST_DEBUG_FUNCPTR (gst_flups_demux_src_event));
+  gst_pad_set_query_function (stream->pad,
+      GST_DEBUG_FUNCPTR (gst_flups_demux_src_query));
   gst_pad_use_fixed_caps (stream->pad);
+
+  /* needed for set_caps to work */
+  if (!gst_pad_set_active (stream->pad, TRUE)) {
+    GST_WARNING_OBJECT (demux, "Failed to activate pad %" GST_PTR_FORMAT,
+        stream->pad);
+  }
+
   gst_pad_set_caps (stream->pad, caps);
-  gst_caps_unref (caps);
   GST_DEBUG_OBJECT (demux, "create pad %s, caps %" GST_PTR_FORMAT, name, caps);
+  gst_caps_unref (caps);
   g_free (name);
 
   return stream;
@@ -357,7 +410,6 @@ gst_flups_demux_get_stream (GstFluPSDemux * demux, gint id, gint type)
     GST_DEBUG_OBJECT (demux, "adding pad for stream id 0x%02x type 0x%02x", id,
         type);
 
-    gst_pad_set_active (stream->pad, TRUE);
     gst_element_add_pad (GST_ELEMENT (demux), stream->pad);
 
     demux->streams[id] = stream;
@@ -384,7 +436,7 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream,
     goto no_stream;
 
   /* timestamps */
-  if (demux->next_pts != G_MAXUINT64)
+  if (G_UNLIKELY (demux->next_pts != G_MAXUINT64))
     timestamp = MPEGTIME_TO_GSTTIME (demux->next_pts);
   else
     timestamp = GST_CLOCK_TIME_NONE;
@@ -403,19 +455,17 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream,
   }
 
   /* OK, sent new segment now prepare the buffer for sending */
-  /* caps */
-  gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad));
   GST_BUFFER_TIMESTAMP (buf) = timestamp;
 
   /* Set the buffer discont flag, and clear discont state on the stream */
   if (stream->discont) {
     GST_DEBUG_OBJECT (demux, "discont buffer to pad %" GST_PTR_FORMAT
         " with TS %" GST_TIME_FORMAT, stream->pad, GST_TIME_ARGS (timestamp));
-
     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
+
     stream->discont = FALSE;
   }
-  size = GST_BUFFER_SIZE (buf);
+  size = gst_buffer_get_size (buf);
 
   demux->next_pts = G_MAXUINT64;
   demux->next_dts = G_MAXUINT64;
@@ -437,7 +487,7 @@ no_stream:
   }
 }
 
-static void
+static inline void
 gst_flups_demux_mark_discont (GstFluPSDemux * demux)
 {
   gint id;
@@ -453,111 +503,7 @@ gst_flups_demux_mark_discont (GstFluPSDemux * demux)
   }
 }
 
-static void
-gst_flups_demux_clear_times (GstFluPSDemux * demux)
-{
-  gint id;
-
-  /* Clear the last ts for all streams */
-  for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
-    GstFluPSStream *stream = demux->streams[id];
-
-    if (stream) {
-      stream->last_seg_start = stream->last_ts = GST_CLOCK_TIME_NONE;
-    }
-  }
-}
-
-static void
-gst_flups_demux_send_segment_updates (GstFluPSDemux * demux,
-    GstClockTime new_time)
-{
-  /* Advance all lagging streams by sending a segment update */
-  gint id;
-  GstEvent *event = NULL;
-
-  if (new_time > demux->src_segment.stop)
-    return;
-
-  for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
-    GstFluPSStream *stream = demux->streams[id];
-
-    if (stream) {
-      if (stream->last_ts == GST_CLOCK_TIME_NONE ||
-          stream->last_ts < demux->src_segment.start)
-        stream->last_ts = demux->src_segment.start;
-      if (stream->last_ts + stream->segment_thresh < new_time) {
-#if 0
-        g_print ("Segment update to pad %s time %" GST_TIME_FORMAT " stop now %"
-            GST_TIME_FORMAT " last_stop %" GST_TIME_FORMAT "\n",
-            GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time),
-            GST_TIME_ARGS (demux->src_segment.stop),
-            GST_TIME_ARGS (demux->src_segment.last_stop));
-#endif
-        GST_DEBUG_OBJECT (demux,
-            "Segment update to pad %s time %" GST_TIME_FORMAT,
-            GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time));
-        if (event == NULL) {
-          event = gst_event_new_new_segment_full (TRUE,
-              demux->src_segment.rate, demux->src_segment.applied_rate,
-              GST_FORMAT_TIME, new_time,
-              demux->src_segment.stop,
-              demux->src_segment.time + (new_time - demux->src_segment.start));
-        }
-        gst_event_ref (event);
-        gst_pad_push_event (stream->pad, event);
-        stream->last_seg_start = stream->last_ts = new_time;
-      }
-    }
-  }
-
-  if (event)
-    gst_event_unref (event);
-}
-
-static void
-gst_flups_demux_send_segment_close (GstFluPSDemux * demux)
-{
-  gint id;
-  GstEvent *event = NULL;
-  GstClockTime stop = demux->src_segment.stop;
-
-  if (demux->src_segment.last_stop != -1 && demux->src_segment.last_stop > stop)
-    stop = demux->src_segment.last_stop;
-
-  for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
-    GstFluPSStream *stream = demux->streams[id];
-
-    if (stream) {
-      GstClockTime start = demux->src_segment.start;
-
-      if (stream->last_seg_start != GST_CLOCK_TIME_NONE &&
-          stream->last_seg_start > start)
-        start = stream->last_seg_start;
-
-#if 0
-      g_print ("Segment close to pad %s start %" GST_TIME_FORMAT
-          " stop %" GST_TIME_FORMAT "\n",
-          GST_PAD_NAME (stream->pad), GST_TIME_ARGS (start),
-          GST_TIME_ARGS (stop));
-#endif
-      if (start > stop) {
-        g_print ("Problem on pad %s with start %" GST_TIME_FORMAT " > stop %"
-            GST_TIME_FORMAT "\n",
-            gst_object_get_name (GST_OBJECT (stream->pad)),
-            GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
-      }
-      event = gst_event_new_new_segment_full (TRUE,
-          demux->src_segment.rate, demux->src_segment.applied_rate,
-          GST_FORMAT_TIME, start,
-          stop, demux->src_segment.time + (start - demux->src_segment.start));
-      if (event)
-        gst_pad_push_event (stream->pad, event);
-    }
-  }
-}
-
-static gboolean
+static inline gboolean
 gst_flups_demux_send_event (GstFluPSDemux * demux, GstEvent * event)
 {
   gint id;
@@ -811,13 +757,139 @@ gst_flups_demux_handle_dvd_event (GstFluPSDemux * demux, GstEvent * event)
   return ret;
 }
 
+static void
+gst_flups_demux_flush (GstFluPSDemux * demux)
+{
+  GST_DEBUG_OBJECT (demux, "flushing demuxer");
+  gst_segment_init (&demux->src_segment, GST_FORMAT_TIME);
+  gst_adapter_clear (demux->adapter);
+  gst_adapter_clear (demux->rev_adapter);
+  gst_pes_filter_drain (&demux->filter);
+  gst_flups_demux_clear_times (demux);
+  demux->adapter_offset = G_MAXUINT64;
+  demux->current_scr = G_MAXUINT64;
+  demux->bytes_since_scr = 0;
+  demux->scr_adjust = GSTTIME_TO_MPEGTIME (SCR_MUNGE);
+}
+
+static inline void
+gst_flups_demux_clear_times (GstFluPSDemux * demux)
+{
+  gint id;
+
+  /* Clear the last ts for all streams */
+  for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
+    GstFluPSStream *stream = demux->streams[id];
+
+    if (stream) {
+      stream->last_seg_start = stream->last_ts = GST_CLOCK_TIME_NONE;
+    }
+  }
+}
+
+static inline void
+gst_flups_demux_send_segment_updates (GstFluPSDemux * demux,
+    GstClockTime new_time)
+{
+  gint id;
+  GstEvent *event = NULL;
+
+  /* Advance all lagging streams by sending a segment update */
+  if (new_time > demux->src_segment.stop)
+    return;
+
+  for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
+    GstFluPSStream *stream = demux->streams[id];
+
+    if (stream) {
+      if (stream->last_ts == GST_CLOCK_TIME_NONE ||
+          stream->last_ts < demux->src_segment.start)
+        stream->last_ts = demux->src_segment.start;
+      if (stream->last_ts + stream->segment_thresh < new_time) {
+#if 0
+        g_print ("Segment update to pad %s time %" GST_TIME_FORMAT " stop now %"
+            GST_TIME_FORMAT " position %" GST_TIME_FORMAT "\n",
+            GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time),
+            GST_TIME_ARGS (demux->src_segment.stop),
+            GST_TIME_ARGS (demux->src_segment.position));
+#endif
+        GST_DEBUG_OBJECT (demux,
+            "Segment update to pad %s time %" GST_TIME_FORMAT,
+            GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time));
+        if (event == NULL) {
+          GstSegment segment;
+          gst_segment_init (&segment, GST_FORMAT_TIME);
+          segment.rate = demux->src_segment.rate;
+          segment.applied_rate = demux->src_segment.applied_rate;
+          segment.start = new_time;
+          segment.stop = demux->src_segment.stop;
+          segment.time =
+              demux->src_segment.time + (new_time - demux->src_segment.start);
+          event = gst_event_new_segment (&segment);
+        }
+        gst_event_ref (event);
+        gst_pad_push_event (stream->pad, event);
+        stream->last_seg_start = stream->last_ts = new_time;
+      }
+    }
+  }
+
+  if (event)
+    gst_event_unref (event);
+}
+
+static inline void
+gst_flups_demux_close_segment (GstFluPSDemux * demux)
+{
+  gint id;
+  GstEvent *event = NULL;
+  GstClockTime stop = demux->src_segment.stop;
+
+  if (demux->src_segment.position != -1 && demux->src_segment.position > stop)
+    stop = demux->src_segment.position;
+
+  for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) {
+    GstFluPSStream *stream = demux->streams[id];
+
+    if (stream) {
+      GstClockTime start = demux->src_segment.start;
+      GstSegment segment;
+
+      if (stream->last_seg_start != GST_CLOCK_TIME_NONE &&
+          stream->last_seg_start > start)
+        start = stream->last_seg_start;
+
+#if 0
+      g_print ("Segment close to pad %s start %" GST_TIME_FORMAT
+          " stop %" GST_TIME_FORMAT "\n",
+          GST_PAD_NAME (stream->pad), GST_TIME_ARGS (start),
+          GST_TIME_ARGS (stop));
+#endif
+      if (start > stop) {
+        GST_WARNING_OBJECT (demux,
+            "Problem on pad %s with start %" GST_TIME_FORMAT " > stop %"
+            GST_TIME_FORMAT "\n",
+            gst_object_get_name (GST_OBJECT (stream->pad)),
+            GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
+      }
+      gst_segment_init (&segment, GST_FORMAT_TIME);
+      segment.rate = demux->src_segment.rate;
+      segment.applied_rate = demux->src_segment.applied_rate;
+      segment.start = start;
+      segment.stop = stop;
+      segment.time =
+          demux->src_segment.time + (start - demux->src_segment.start);
+      event = gst_event_new_segment (&segment);
+      gst_pad_push_event (stream->pad, event);
+    }
+  }
+}
+
 static gboolean
-gst_flups_demux_sink_event (GstPad * pad, GstEvent * event)
+gst_flups_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
 {
   gboolean res = TRUE;
-  GstFluPSDemux *demux;
-
-  demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad));
+  GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent);
 
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_FLUSH_START:
@@ -825,54 +897,46 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event)
       break;
     case GST_EVENT_FLUSH_STOP:
       gst_flups_demux_send_event (demux, event);
-
       gst_segment_init (&demux->sink_segment, GST_FORMAT_UNDEFINED);
-      gst_segment_init (&demux->src_segment, GST_FORMAT_TIME);
-      gst_adapter_clear (demux->adapter);
-      gst_adapter_clear (demux->rev_adapter);
-      demux->adapter_offset = G_MAXUINT64;
-      gst_pes_filter_drain (&demux->filter);
-      demux->current_scr = G_MAXUINT64;
-      demux->bytes_since_scr = 0;
-      demux->scr_adjust = GSTTIME_TO_MPEGTIME (SCR_MUNGE);
-      gst_flups_demux_clear_times (demux);
+      gst_flups_demux_flush (demux);
       break;
-    case GST_EVENT_NEWSEGMENT:
+    case GST_EVENT_SEGMENT:
     {
-      gboolean update;
-      gdouble rate;
-      GstFormat format;
+      const GstSegment *segment;
+
       gint64 start, stop, time;
-      gint64 accum, dur;
-      gdouble arate;
+      gint64 base, dur;
       GstClockTimeDiff adjust;
 
-      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
-          &start, &stop, &time);
+      /* Close current segment */
+      gst_flups_demux_close_segment (demux);
 
-      if (format != GST_FORMAT_TIME)
+      gst_event_parse_segment (event, &segment);
+
+      if (segment->format != GST_FORMAT_TIME)
         return FALSE;
 
-      dur = stop - start;
+      gst_segment_copy_into (segment, &demux->sink_segment);
+
+      dur = segment->stop - segment->start;
+
+      base = demux->sink_segment.base;
+      start = demux->sink_segment.start;
+      stop = demux->sink_segment.stop;
+      time = demux->sink_segment.time;
 
       demux->first_scr = GSTTIME_TO_MPEGTIME (start);
       demux->current_scr = demux->first_scr + demux->scr_adjust;
       demux->base_time = time;
       demux->bytes_since_scr = 0;
 
-      gst_segment_set_newsegment_full (&demux->sink_segment, update, rate,
-          arate, format, start, stop, time);
-
       GST_DEBUG_OBJECT (demux,
-          "demux: got segment update %d start %" G_GINT64_FORMAT " stop %"
-          G_GINT64_FORMAT " time %" G_GINT64_FORMAT, update, start, stop, time);
+          "demux: received new segment start %" G_GINT64_FORMAT " stop %"
+          G_GINT64_FORMAT " time %" G_GINT64_FORMAT, start, stop, time);
 
-      accum = demux->sink_segment.accum;
-      start = demux->sink_segment.start;
-      stop = demux->sink_segment.stop;
 
-      adjust = accum - start + SCR_MUNGE;
-      start = accum + SCR_MUNGE;
+      adjust = base - start + SCR_MUNGE;
+      start = base + SCR_MUNGE;
 
       if (adjust >= 0)
         demux->scr_adjust = GSTTIME_TO_MPEGTIME (adjust);
@@ -881,31 +945,29 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event)
 
       if (stop != -1) {
         stop = start + dur;
-        if (demux->src_segment.last_stop != -1
-            && demux->src_segment.last_stop > stop)
-          stop = demux->src_segment.last_stop;
+        if (demux->src_segment.position != -1
+            && demux->src_segment.position > stop)
+          stop = demux->src_segment.position;
       }
 
       GST_DEBUG_OBJECT (demux,
-          "sending new segment: update %d rate %g format %d, start: %"
+          "sending new segment: rate %g format %d, start: %"
           G_GINT64_FORMAT ", stop: %" G_GINT64_FORMAT ", time: %"
           G_GINT64_FORMAT " scr_adjust: %" G_GINT64_FORMAT "(%" GST_TIME_FORMAT
-          ")", update, rate, format, start, stop, time, demux->scr_adjust,
+          ")", segment->rate, segment->format, start, stop, time,
+          demux->scr_adjust,
           GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (demux->scr_adjust)));
 
-      gst_segment_set_newsegment_full (&demux->src_segment, update,
-          rate, arate, format, start, stop, time);
+      demux->src_segment.rate = segment->rate;
+      demux->src_segment.applied_rate = segment->applied_rate;
+      demux->src_segment.format = segment->format;
+      demux->src_segment.start = segment->start;
+      demux->src_segment.stop = segment->stop;
+      demux->src_segment.time = segment->time;
 
       gst_event_unref (event);
-      if (update) {
-        /* Segment closing, send it as per-pad updates to manage the accum
-         * properly */
-        gst_flups_demux_send_segment_close (demux);
-      } else {
-        event = gst_event_new_new_segment_full (update,
-            rate, arate, GST_FORMAT_TIME, start, stop, time);
-        gst_flups_demux_send_event (demux, event);
-      }
+      event = gst_event_new_segment (&demux->src_segment);
+      gst_flups_demux_send_event (demux, event);
 
       break;
     }
@@ -930,23 +992,22 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event)
       }
       break;
     }
+    case GST_EVENT_CAPS:
+      gst_event_unref (event);
+      break;
     default:
       gst_flups_demux_send_event (demux, event);
       break;
   }
 
-  gst_object_unref (demux);
-
   return res;
 }
 
 static gboolean
-gst_flups_demux_src_event (GstPad * pad, GstEvent * event)
+gst_flups_demux_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
 {
   gboolean res = FALSE;
-  GstFluPSDemux *demux;
-
-  demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad));
+  GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent);
 
   switch (GST_EVENT_TYPE (event)) {
     case GST_EVENT_SEEK:
@@ -1011,13 +1072,10 @@ gst_flups_demux_src_event (GstPad * pad, GstEvent * event)
       break;
   }
 
-  gst_object_unref (demux);
-
   return res;
 
 not_supported:
   {
-    gst_object_unref (demux);
     gst_event_unref (event);
 
     return FALSE;
@@ -1025,12 +1083,10 @@ not_supported:
 }
 
 static gboolean
-gst_flups_demux_src_query (GstPad * pad, GstQuery * query)
+gst_flups_demux_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
 {
   gboolean res = FALSE;
-  GstFluPSDemux *demux;
-
-  demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad));
+  GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent);
 
   GST_LOG_OBJECT (demux, "Have query of type %d on pad %" GST_PTR_FORMAT,
       GST_QUERY_TYPE (query), pad);
@@ -1038,18 +1094,15 @@ gst_flups_demux_src_query (GstPad * pad, GstQuery * query)
   switch (GST_QUERY_TYPE (query)) {
     case GST_QUERY_POSITION:
     {
-      GstPad *peer;
+      GstClockTime pos;
       GstFormat format;
-      gint64 position;
 
-      gst_query_parse_position (query, &format, NULL);
+      /* See if upstream can immediately answer */
+      res = gst_pad_peer_query (demux->sinkpad, query);
+      if (res)
+        break;
 
-      if ((peer = gst_pad_get_peer (demux->sinkpad)) != NULL) {
-        res = gst_pad_query (peer, query);
-        gst_object_unref (peer);
-        if (res)
-          break;
-      }
+      gst_query_parse_position (query, &format, NULL);
 
       if (format != GST_FORMAT_TIME) {
         GST_DEBUG_OBJECT (demux, "position not supported for format %d",
@@ -1057,17 +1110,17 @@ gst_flups_demux_src_query (GstPad * pad, GstQuery * query)
         goto not_supported;
       }
 
-      position = demux->base_time;
+      pos = demux->base_time;
       if (demux->current_scr != G_MAXUINT64 && demux->first_scr != G_MAXUINT64) {
-        position +=
+        pos +=
             MPEGTIME_TO_GSTTIME (demux->current_scr - demux->scr_adjust -
             demux->first_scr);
       }
 
       GST_LOG_OBJECT (demux, "Position at GStreamer Time:%" GST_TIME_FORMAT,
-          GST_TIME_ARGS (position));
+          GST_TIME_ARGS (pos));
 
-      gst_query_set_position (query, format, position);
+      gst_query_set_position (query, format, pos);
       res = TRUE;
       break;
     }
@@ -1075,76 +1128,79 @@ gst_flups_demux_src_query (GstPad * pad, GstQuery * query)
     {
       GstFormat format;
       gint64 duration;
-      GstPad *peer;
+      GstQuery *byte_query;
 
       gst_query_parse_duration (query, &format, NULL);
 
-      if ((peer = gst_pad_get_peer (demux->sinkpad)) == NULL) {
-        GST_DEBUG_OBJECT (demux, "duration not possible, no peer");
-        goto not_supported;
+      if (G_LIKELY (format == GST_FORMAT_TIME &&
+              GST_CLOCK_TIME_IS_VALID (demux->src_segment.duration))) {
+        gst_query_set_duration (query, GST_FORMAT_TIME,
+            demux->src_segment.duration);
+        res = TRUE;
+        break;
       }
 
       /* For any format other than bytes, see if upstream knows first */
       if (format == GST_FORMAT_BYTES) {
         GST_DEBUG_OBJECT (demux, "duration not supported for format %d",
             format);
-        gst_object_unref (peer);
         goto not_supported;
       }
 
-      if (gst_pad_query (peer, query)) {
-        gst_object_unref (peer);
+      if (gst_pad_peer_query (demux->sinkpad, query)) {
         res = TRUE;
         break;
       }
 
-      /* Upstream didn't know, so we can only answer TIME queries from 
+      /* Upstream didn't know, so we can only answer TIME queries from
        * here on */
       if (format != GST_FORMAT_TIME) {
         GST_DEBUG_OBJECT (demux, "duration not supported for format %d",
             format);
-        gst_object_unref (peer);
         goto not_supported;
       }
 
       if (demux->mux_rate == -1) {
         GST_DEBUG_OBJECT (demux, "duration not possible, no mux_rate");
-        gst_object_unref (peer);
         goto not_supported;
       }
 
-      gst_query_set_duration (query, GST_FORMAT_BYTES, -1);
+      byte_query = gst_query_new_duration (GST_FORMAT_BYTES);
 
-      if (!gst_pad_query (peer, query)) {
+      if (!gst_pad_peer_query (demux->sinkpad, byte_query)) {
         GST_LOG_OBJECT (demux, "query on peer pad failed");
-        gst_object_unref (peer);
+        gst_query_unref (byte_query);
         goto not_supported;
       }
-      gst_object_unref (peer);
 
-      gst_query_parse_duration (query, &format, &duration);
+      gst_query_parse_duration (byte_query, &format, &duration);
+      gst_query_unref (byte_query);
+
+      GST_LOG_OBJECT (demux,
+          "query on peer pad reported bytes %" G_GUINT64_FORMAT, duration);
 
-      duration = BYTES_TO_GSTTIME (duration);
+      duration = BYTES_TO_GSTTIME ((guint64) duration);
+
+      GST_LOG_OBJECT (demux, "converted to time %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (duration));
 
       gst_query_set_duration (query, GST_FORMAT_TIME, duration);
       res = TRUE;
       break;
     }
+    case GST_QUERY_SEEKING:{
+      /* Just ask upstream */
+      res = gst_pad_peer_query (demux->sinkpad, query);
+      break;
+    }
     default:
-      res = gst_pad_query_default (pad, query);
+      res = gst_pad_query_default (pad, parent, query);
       break;
   }
 
-  gst_object_unref (demux);
-
   return res;
-
 not_supported:
-  {
-    gst_object_unref (demux);
-
-    return FALSE;
-  }
+  return FALSE;
 }
 
 static void
@@ -1170,6 +1226,33 @@ gst_flups_demux_reset_psm (GstFluPSDemux * demux)
 #undef FILL_TYPE
 }
 
+/* ISO/IEC 13818-1:
+ * pack_header() {
+ *     pack_start_code                                   32  bslbf  -+
+ *     '01'                                               2  bslbf   |
+ *     system_clock_reference_base [32..30]               3  bslbf   |
+ *     marker_bit                                         1  bslbf   |
+ *     system_clock_reference_base [29..15]              15  bslbf   |
+ *     marker_bit                                         1  bslbf   |
+ *     system_clock_reference_base [14..0]               15  bslbf   |
+ *     marker_bit                                         1  bslbf   | 112 bits
+ *     system_clock_reference_extension                   9  ubslbf  |
+ *     marker_bit                                         1  bslbf   |
+ *     program_mux_rate                                  22  ubslbf  |
+ *     marker_bit                                         1  bslbf   |
+ *     marker_bit                                         1  bslbf   |
+ *     reserved                                           5  bslbf   |
+ *     pack_stuffing_length                               3  ubslbf -+
+ *
+ *     for (i = 0; i < pack_stuffing_length; i++) {
+ *         stuffing_byte '1111 1111'                      8  bslbf
+ *     }
+ *
+ * 112 bits = 14 bytes, as max value for pack_stuffing_length is 7, then
+ * in total it's needed 14 + 7 = 21 bytes.
+ */
+#define PACK_START_SIZE     21
+
 static GstFlowReturn
 gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
 {
@@ -1177,22 +1260,27 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
   guint length;
   guint32 scr1, scr2;
   guint64 scr, scr_adjusted, new_rate;
+  guint64 scr_rate_n;
+  guint64 scr_rate_d;
   GstClockTime new_time;
+  guint avail = gst_adapter_available (demux->adapter);
 
   GST_DEBUG ("parsing pack start");
 
-  /* fixed length to begin with, start code and two scr values */
-  length = 8 + 4;
-
-  if (!(data = gst_adapter_peek (demux->adapter, length)))
+  if (G_UNLIKELY (avail < PACK_START_SIZE))
     goto need_more_data;
 
+  data = gst_adapter_map (demux->adapter, PACK_START_SIZE);
+
   /* skip start code */
   data += 4;
 
   scr1 = GST_READ_UINT32_BE (data);
   scr2 = GST_READ_UINT32_BE (data + 4);
 
+  /* fixed length to begin with, start code and two scr values */
+  length = 8 + 4;
+
   /* start parsing the stream */
   if ((*data & 0xc0) == 0x40) {
     guint32 scr_ext;
@@ -1204,13 +1292,11 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
 
     /* mpeg2 has more data */
     length += 2;
-    if (gst_adapter_available (demux->adapter) < length)
-      goto need_more_data;
 
     /* :2=01 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 */
 
     /* check markers */
-    if ((scr1 & 0xc4000400) != 0x44000400)
+    if (G_UNLIKELY ((scr1 & 0xc4000400) != 0x44000400))
       goto lost_sync;
 
     scr = ((guint64) scr1 & 0x38000000) << 3;
@@ -1219,14 +1305,15 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
     scr |= ((guint64) scr2 & 0xf8000000) >> 27;
 
     /* marker:1==1 ! scr_ext:9 ! marker:1==1 */
-    if ((scr2 & 0x04010000) != 0x04010000)
+    if (G_UNLIKELY ((scr2 & 0x04010000) != 0x04010000))
       goto lost_sync;
 
     scr_ext = (scr2 & 0x03fe0000) >> 17;
     /* We keep the offset of this scr */
-    demux->last_scr_offset = demux->adapter_offset + 12;
+    demux->cur_scr_offset = demux->adapter_offset + 12;
 
-    GST_DEBUG_OBJECT (demux, "SCR: 0x%08x SCRE: 0x%08x", (guint) scr, scr_ext);
+    GST_DEBUG_OBJECT (demux, "SCR: 0x%08" G_GINT64_MODIFIER "x SCRE: 0x%08x",
+        scr, scr_ext);
 
     if (scr_ext) {
       scr = (scr * 300 + scr_ext % 300) / 300;
@@ -1236,7 +1323,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
     data += 6;
     /* PMR:22 ! :2==11 ! reserved:5 ! stuffing_len:3 */
     next32 = GST_READ_UINT32_BE (data);
-    if ((next32 & 0x00000300) != 0x00000300)
+    if (G_UNLIKELY ((next32 & 0x00000300) != 0x00000300))
       goto lost_sync;
 
     new_rate = (next32 & 0xfffffc00) >> 10;
@@ -1245,6 +1332,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
     GST_DEBUG_OBJECT (demux, "stuffing bytes: %d", stuffing_bytes);
 
     data += 4;
+    length += stuffing_bytes;
     while (stuffing_bytes--) {
       if (*data++ != 0xff)
         goto lost_sync;
@@ -1254,10 +1342,10 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
     demux->is_mpeg2_pack = FALSE;
 
     /* check markers */
-    if ((scr1 & 0xf1000100) != 0x21000100)
+    if (G_UNLIKELY ((scr1 & 0xf1000100) != 0x21000100))
       goto lost_sync;
 
-    if ((scr2 & 0x01800001) != 0x01800001)
+    if (G_UNLIKELY ((scr2 & 0x01800001) != 0x01800001))
       goto lost_sync;
 
     /* :4=0010 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 ! marker:1==1 */
@@ -1267,7 +1355,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
     scr |= ((guint64) scr2 & 0xfe000000) >> 25;
 
     /* We keep the offset of this scr */
-    demux->last_scr_offset = demux->adapter_offset + 8;
+    demux->cur_scr_offset = demux->adapter_offset + 8;
 
     /* marker:1==1 ! mux_rate:22 ! marker:1==1 */
     new_rate = (scr2 & 0x007ffffe) >> 1;
@@ -1279,10 +1367,16 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
   /* scr adjusted is the new scr found + the colected adjustment */
   scr_adjusted = scr + demux->scr_adjust;
 
+  GST_LOG_OBJECT (demux,
+      "SCR: %" G_GINT64_FORMAT " (%" G_GINT64_FORMAT "), mux_rate %"
+      G_GINT64_FORMAT ", GStreamer Time:%" GST_TIME_FORMAT,
+      scr, scr_adjusted, new_rate,
+      GST_TIME_ARGS (MPEGTIME_TO_GSTTIME ((guint64) scr)));
+
   /* keep the first src in order to calculate delta time */
-  if (demux->first_scr == G_MAXUINT64) {
+  if (G_UNLIKELY (demux->first_scr == G_MAXUINT64)) {
     demux->first_scr = scr;
-    demux->first_scr_offset = demux->last_scr_offset;
+    demux->first_scr_offset = demux->cur_scr_offset;
 
     if (demux->sink_segment.format == GST_FORMAT_TIME) {
       demux->base_time = demux->sink_segment.time;
@@ -1290,12 +1384,15 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
       demux->base_time = MPEGTIME_TO_GSTTIME (demux->first_scr);
     }
     /* at begin consider the new_rate as the scr rate, bytes/clock ticks */
-    demux->scr_rate_n = new_rate;
-    demux->scr_rate_d = CLOCK_FREQ;
-  } else if (demux->first_scr_offset != demux->last_scr_offset) {
+    scr_rate_n = new_rate;
+    scr_rate_d = CLOCK_FREQ;
+  } else if (G_LIKELY (demux->first_scr_offset != demux->cur_scr_offset)) {
     /* estimate byte rate related to the SCR */
-    demux->scr_rate_n = demux->last_scr_offset - demux->first_scr_offset;
-    demux->scr_rate_d = scr - demux->first_scr;
+    scr_rate_n = demux->cur_scr_offset - demux->first_scr_offset;
+    scr_rate_d = scr_adjusted - demux->first_scr;
+  } else {
+    scr_rate_n = demux->scr_rate_n;
+    scr_rate_d = demux->scr_rate_d;
   }
 
   GST_DEBUG_OBJECT (demux,
@@ -1309,15 +1406,14 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
       " at %" G_GUINT64_FORMAT ", scr rate: %" G_GUINT64_FORMAT
       "/%" G_GUINT64_FORMAT "(%f)",
       ((demux->sink_segment.rate >= 0.0) ? "forward" : "backward"),
-      scr, demux->last_scr_offset,
+      scr, demux->cur_scr_offset,
       demux->first_scr, demux->first_scr_offset,
-      demux->scr_rate_n, demux->scr_rate_d,
-      (float) demux->scr_rate_n / demux->scr_rate_d);
+      scr_rate_n, scr_rate_d, (float) scr_rate_n / scr_rate_d);
 
   /* adjustment of the SCR */
-  if (demux->current_scr != G_MAXUINT64) {
+  if (G_LIKELY (demux->current_scr != G_MAXUINT64)) {
     gint64 diff;
-    guint64 old_scr, old_mux_rate, bss, adjust;
+    guint64 old_scr, old_mux_rate, bss, adjust = 0;
 
     /* keep SCR of the previous packet */
     old_scr = demux->current_scr;
@@ -1326,12 +1422,13 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
     /* Bytes since SCR is the amount we placed in the adapter since then
      * (demux->bytes_since_scr) minus the amount remaining in the adapter,
      * clamped to >= 0 */
-    bss = MAX (0, (gint) (demux->bytes_since_scr -
-            gst_adapter_available (demux->adapter)));
+    bss = MAX (0, (gint) (demux->bytes_since_scr - avail));
 
     /* estimate the new SCR using the previous one according the notes
        on point 2.5.2.2 of the ISO/IEC 13818-1 document */
-    adjust = (bss * CLOCK_FREQ) / old_mux_rate;
+    if (old_mux_rate != 0)
+      adjust = (bss * CLOCK_FREQ) / old_mux_rate;
+
     if (demux->sink_segment.rate >= 0.0)
       demux->next_scr = old_scr + adjust;
     else
@@ -1344,7 +1441,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
 
     /* calculate the absolute deference between the last scr and
        the new one */
-    if (old_scr > scr_adjusted)
+    if (G_UNLIKELY (old_scr > scr_adjusted))
       diff = old_scr - scr_adjusted;
     else
       diff = scr_adjusted - old_scr;
@@ -1369,18 +1466,21 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
   /* update the current_scr and rate members */
   demux->mux_rate = new_rate;
   demux->current_scr = scr_adjusted;
+  demux->scr_rate_n = scr_rate_n;
+  demux->scr_rate_d = scr_rate_d;
 
   new_time = MPEGTIME_TO_GSTTIME (scr_adjusted);
   if (new_time != GST_CLOCK_TIME_NONE) {
     // g_print ("SCR now %" GST_TIME_FORMAT "\n", GST_TIME_ARGS (new_time));
-    gst_segment_set_last_stop (&demux->src_segment, GST_FORMAT_TIME, new_time);
+    gst_segment_set_position (&demux->src_segment, GST_FORMAT_TIME, new_time);
     gst_flups_demux_send_segment_updates (demux, new_time);
   }
 
   /* Reset the bytes_since_scr value to count the data remaining in the
    * adapter */
-  demux->bytes_since_scr = gst_adapter_available (demux->adapter);
+  demux->bytes_since_scr = avail;
 
+  gst_adapter_unmap (demux->adapter);
   gst_adapter_flush (demux->adapter, length);
   ADAPTER_OFFSET_FLUSH (length);
   return GST_FLOW_OK;
@@ -1388,6 +1488,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux)
 lost_sync:
   {
     GST_DEBUG_OBJECT (demux, "lost sync");
+    gst_adapter_unmap (demux->adapter);
     return GST_FLOW_LOST_SYNC;
   }
 need_more_data:
@@ -1397,6 +1498,32 @@ need_more_data:
   }
 }
 
+/* ISO/IEC 13818-1:
+ * system_header () {
+ *     system_header_start_code                          32  bslbf  -+
+ *     header_length                                     16  uimsbf  |
+ *     marker_bit                                         1  bslbf   |
+ *     rate_bound                                        22  uimsbf  |
+ *     marker_bit                                         1  bslbf   |
+ *     audio_bound                                        6  uimsbf  |
+ *     fixed_flag                                         1  bslbf   |
+ *     CSPS_flag                                          1  bslbf   | 96 bits
+ *     system_audio_lock_flag                             1  bslbf   |
+ *     system_video_lock_flag                             1  bslbf   |
+ *     marker_bit                                         1  bslbf   |
+ *     video_bound                                        5  uimsbf  |
+ *     packet_rate_restriction_flag                       1  bslbf   |
+ *     reserved_bits                                      7  bslbf  -+
+ *     while (nextbits () = = '1') {
+ *         stream_id                                      8  uimsbf -+
+ *         '11'                                           2  bslbf   | 24 bits
+ *         P-STD_buffer_bound_scale                       1  bslbf   |
+ *         P-STD_buffer_size_bound                       13  uimsbf -+
+ *     }
+ * }
+ * 96 bits = 12 bytes, 24 bits = 3 bytes.
+ */
+
 static GstFlowReturn
 gst_flups_demux_parse_sys_head (GstFluPSDemux * demux)
 {
@@ -1404,10 +1531,12 @@ gst_flups_demux_parse_sys_head (GstFluPSDemux * demux)
   const guint8 *data;
   gboolean csps;
 
-  /* start code + length */
-  if (!(data = gst_adapter_peek (demux->adapter, 6)))
+  if (gst_adapter_available (demux->adapter) < 6)
     goto need_more_data;
 
+  /* start code + length */
+  data = gst_adapter_map (demux->adapter, 6);
+
   /* skip start code */
   data += 4;
 
@@ -1416,9 +1545,12 @@ gst_flups_demux_parse_sys_head (GstFluPSDemux * demux)
 
   length += 6;
 
-  if (!(data = gst_adapter_peek (demux->adapter, length)))
+  gst_adapter_unmap (demux->adapter);
+  if (gst_adapter_available (demux->adapter) < length)
     goto need_more_data;
 
+  data = gst_adapter_map (demux->adapter, length);
+
   /* skip start code and length */
   data += 6;
 
@@ -1533,6 +1665,7 @@ gst_flups_demux_parse_sys_head (GstFluPSDemux * demux)
     }
   }
 
+  gst_adapter_unmap (demux->adapter);
   gst_adapter_flush (demux->adapter, length);
   ADAPTER_OFFSET_FLUSH (length);
   return GST_FLOW_OK;
@@ -1541,22 +1674,26 @@ gst_flups_demux_parse_sys_head (GstFluPSDemux * demux)
 marker_expected:
   {
     GST_DEBUG_OBJECT (demux, "expecting marker");
+    gst_adapter_unmap (demux->adapter);
     return GST_FLOW_LOST_SYNC;
   }
 no_placeholder_bits:
   {
     GST_DEBUG_OBJECT (demux, "expecting placeholder bit values"
         " '11' after stream id");
+    gst_adapter_unmap (demux->adapter);
     return GST_FLOW_LOST_SYNC;
   }
 sys_len_error:
   {
     GST_DEBUG_OBJECT (demux, "error in system header length");
+    gst_adapter_unmap (demux->adapter);
     return GST_FLOW_LOST_SYNC;
   }
 need_more_data:
   {
     GST_DEBUG_OBJECT (demux, "need more data");
+    gst_adapter_unmap (demux->adapter);
     return GST_FLOW_NEED_MORE_DATA;
   }
 }
@@ -1569,10 +1706,12 @@ gst_flups_demux_parse_psm (GstFluPSDemux * demux)
   const guint8 *data, *es_map_base;
   gboolean applicable;
 
-  /* start code + length */
-  if (!(data = gst_adapter_peek (demux->adapter, 6)))
+  if (gst_adapter_available (demux->adapter) < 6)
     goto need_more_data;
 
+  /* start code + length */
+  data = gst_adapter_map (demux->adapter, 6);
+
   /* skip start code */
   data += 4;
 
@@ -1584,9 +1723,13 @@ gst_flups_demux_parse_psm (GstFluPSDemux * demux)
 
   length += 6;
 
-  if (!(data = gst_adapter_peek (demux->adapter, length)))
+  gst_adapter_unmap (demux->adapter);
+
+  if (gst_adapter_available (demux->adapter) < length)
     goto need_more_data;
 
+  data = gst_adapter_map (demux->adapter, length);
+
   /* skip start code and length */
   data += 6;
 
@@ -1636,10 +1779,19 @@ gst_flups_demux_parse_psm (GstFluPSDemux * demux)
 
     GST_DEBUG_OBJECT (demux, "Stream type %02X with id %02X and %u bytes info",
         stream_type, stream_id, stream_info_length);
-    demux->psm[stream_id] = stream_type;
+    if (G_LIKELY (stream_id != 0xbd))
+      demux->psm[stream_id] = stream_type;
+    else {
+      /* Ignore stream type for private_stream_1 and discover it looking at
+       * the stream data.
+       * Fixes demuxing some clips with lpcm that was wrongly declared as
+       * mpeg audio */
+      GST_DEBUG_OBJECT (demux, "stream type for private_stream_1 ignored");
+    }
     es_map_base += stream_info_length;
   }
 
+  gst_adapter_unmap (demux->adapter);
   gst_adapter_flush (demux->adapter, length);
   ADAPTER_OFFSET_FLUSH (length);
   return GST_FLOW_OK;
@@ -1647,6 +1799,7 @@ gst_flups_demux_parse_psm (GstFluPSDemux * demux)
 psm_len_error:
   {
     GST_DEBUG_OBJECT (demux, "error in PSM length");
+    gst_adapter_unmap (demux->adapter);
     return GST_FLOW_LOST_SYNC;
   }
 need_more_data:
@@ -1670,12 +1823,12 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first,
   gint stream_type;
   guint32 start_code;
   guint8 id;
-  guint8 *data;
-  guint datalen;
+  GstMapInfo map;
+  gsize datalen;
   guint offset = 0;
 
-  data = GST_BUFFER_DATA (buffer);
-  datalen = GST_BUFFER_SIZE (buffer);
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
+  datalen = map.size;
 
   start_code = filter->start_code;
   id = filter->id;
@@ -1688,10 +1841,10 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first,
       if (start_code == ID_PRIVATE_STREAM_1 && datalen >= 2) {
         guint8 nframes;
 
-        /* VDR writes A52 streams without any header bytes 
+        /* VDR writes A52 streams without any header bytes
          * (see ftp://ftp.mplayerhq.hu/MPlayer/samples/MPEG-VOB/vdr-AC3) */
         if (datalen >= 4) {
-          guint hdr = GST_READ_UINT32_BE (data);
+          guint hdr = GST_READ_UINT32_BE (map.data);
 
           if (G_UNLIKELY ((hdr & 0xffff0000) == AC3_SYNC_WORD)) {
             id = 0x80;
@@ -1702,7 +1855,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first,
 
         if (G_LIKELY (stream_type == -1)) {
           /* new id is in the first byte */
-          id = data[offset++];
+          id = map.data[offset++];
           datalen--;
 
           /* and remap */
@@ -1713,7 +1866,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first,
            * streams and our backwards compat convention is to strip it off */
           if (stream_type != ST_PS_DVD_SUBPICTURE) {
             /* Number of audio frames in this packet */
-            nframes = data[offset++];
+            nframes = map.data[offset++];
             datalen--;
             GST_DEBUG_OBJECT (demux, "private type 0x%02x, %d frames", id,
                 nframes);
@@ -1744,7 +1897,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first,
     demux->current_stream = gst_flups_demux_get_stream (demux, id, stream_type);
   }
 
-  if (demux->current_stream == NULL) {
+  if (G_UNLIKELY (demux->current_stream == NULL)) {
     GST_DEBUG_OBJECT (demux, "Dropping buffer for unknown stream id 0x%02x",
         id);
     goto done;
@@ -1768,7 +1921,8 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first,
   }
 
   if (demux->current_stream->notlinked == FALSE) {
-    out_buf = gst_buffer_create_sub (buffer, offset, datalen);
+    out_buf =
+        gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, offset, datalen);
 
     ret = gst_flups_demux_send_data (demux, demux->current_stream, out_buf);
     if (ret == GST_FLOW_NOT_LINKED) {
@@ -1778,6 +1932,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first,
   }
 
 done:
+  gst_buffer_unmap (buffer, &map);
   gst_buffer_unref (buffer);
 
   return ret;
@@ -1801,11 +1956,11 @@ gst_flups_demux_resync (GstFluPSDemux * demux, gboolean save)
   gboolean found;
 
   avail = gst_adapter_available (demux->adapter);
-  if (avail < 4)
+  if (G_UNLIKELY (avail < 4))
     goto need_data;
 
   /* Common case, read 4 bytes an check it */
-  data = gst_adapter_peek (demux->adapter, 4);
+  data = gst_adapter_map (demux->adapter, 4);
 
   /* read currect code */
   code = GST_READ_UINT32_BE (data);
@@ -1814,22 +1969,25 @@ gst_flups_demux_resync (GstFluPSDemux * demux, gboolean save)
   if (G_LIKELY ((code & 0xffffff00) == 0x100L)) {
     GST_LOG_OBJECT (demux, "Found resync code %08x after 0 bytes", code);
     demux->last_sync_code = code;
+    gst_adapter_unmap (demux->adapter);
     return TRUE;
   }
 
-  /* Otherwise, we are starting at byte 4 and we need to search 
+  /* Otherwise, we are starting at byte 4 and we need to search
      the sync code in all available data in the adapter */
   offset = 4;
   if (offset >= avail)
     goto need_data;             /* Not enough data to find sync */
 
-  data = gst_adapter_peek (demux->adapter, avail);
+  data = gst_adapter_map (demux->adapter, avail);
 
   do {
     code = (code << 8) | data[offset++];
     found = (code & 0xffffff00) == 0x100L;
   } while (offset < avail && !found);
 
+  gst_adapter_unmap (demux->adapter);
+
   if (!save || demux->sink_segment.rate >= 0.0) {
     GST_LOG_OBJECT (demux, "flushing %d bytes", offset - 4);
     /* forward playback, we can discard and flush the skipped bytes */
@@ -1875,10 +2033,78 @@ gst_flups_demux_is_pes_sync (guint32 sync)
       ((sync & 0xe0) == 0xc0) || ((sync & 0xf0) == 0xe0);
 }
 
+/* If we can pull that's prefered */
+static gboolean
+gst_flups_demux_sink_activate (GstPad * sinkpad, GstObject * parent)
+{
+  gboolean res = FALSE;
+  GstQuery *query = gst_query_new_scheduling ();
+
+  if (gst_pad_peer_query (sinkpad, query)) {
+    if (gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL)) {
+      res = gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
+    } else {
+      res = gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
+    }
+  } else {
+    res = gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
+  }
+
+  gst_query_unref (query);
+
+  return res;
+}
+
+/* This function gets called when we activate ourselves in push mode. */
+static gboolean
+gst_flups_demux_sink_activate_push (GstPad * sinkpad, GstObject * parent,
+    gboolean active)
+{
+  GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent);
+
+  demux->random_access = FALSE;
+
+  return TRUE;
+}
+
+#if 0
+/* this function gets called when we activate ourselves in pull mode.
+ * We can perform  random access to the resource and we start a task
+ * to start reading */
+static gboolean
+gst_flups_demux_sink_activate_pull (GstPad * sinkpad, GstObject * parent,
+    gboolean active)
+{
+  GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent);
+
+  if (active) {
+    GST_DEBUG ("pull mode activated");
+    demux->random_access = TRUE;
+    return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_flups_demux_loop,
+        sinkpad, NULL);
+  } else {
+    demux->random_access = FALSE;
+    return gst_pad_stop_task (sinkpad);
+  }
+}
+#endif
+
+static gboolean
+gst_flups_demux_sink_activate_mode (GstPad * pad, GstObject * parent,
+    GstPadMode mode, gboolean active)
+{
+  if (mode == GST_PAD_MODE_PUSH) {
+    return gst_flups_demux_sink_activate_push (pad, parent, active);
+  } else if (mode == GST_PAD_MODE_PULL) {
+//    return gst_flups_demux_sink_activate_pull (pad, parent, active);
+  }
+  return FALSE;
+}
+
 static GstFlowReturn
-gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer)
+gst_flups_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
 {
-  GstFluPSDemux *demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad));
+  GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent);
   GstFlowReturn ret = GST_FLOW_OK;
   guint32 avail;
   gboolean save, discont;
@@ -1906,7 +2132,7 @@ gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer)
   demux->adapter_offset = GST_BUFFER_OFFSET (buffer);
 
   gst_adapter_push (demux->adapter, buffer);
-  demux->bytes_since_scr += GST_BUFFER_SIZE (buffer);
+  demux->bytes_since_scr += gst_buffer_get_size (buffer);
 
   avail = gst_adapter_available (demux->rev_adapter);
   if (avail > 0) {
@@ -2006,8 +2232,6 @@ gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer)
     }
   }
 done:
-  gst_object_unref (demux);
-
   return ret;
 }
 
@@ -2050,6 +2274,7 @@ gst_flups_demux_change_state (GstElement * element, GstStateChange transition)
       gst_flups_demux_reset_psm (demux);
       gst_segment_init (&demux->sink_segment, GST_FORMAT_UNDEFINED);
       gst_segment_init (&demux->src_segment, GST_FORMAT_TIME);
+      gst_flups_demux_flush (demux);
       break;
     default:
       break;
@@ -2075,10 +2300,20 @@ gst_flups_demux_change_state (GstElement * element, GstStateChange transition)
   return result;
 }
 
+static void
+gst_segment_set_position (GstSegment * segment, GstFormat format,
+    guint64 position)
+{
+  if (segment->format == GST_FORMAT_UNDEFINED) {
+    segment->format = format;
+  }
+  segment->position = position;
+}
+
 gboolean
 gst_flups_demux_plugin_init (GstPlugin * plugin)
 {
-  GST_DEBUG_CATEGORY_INIT (gstflupesfilter_debug, "rsnpesfilter", 0,
+  GST_DEBUG_CATEGORY_INIT (mpegpspesfilter_debug, "rsnpesfilter", 0,
       "MPEG program stream PES filter debug");
 
   GST_DEBUG_CATEGORY_INIT (gstflupsdemux_debug, "rsndvddemux", 0,
index f62915008de2743d7c1c8cb80ed035e2e3e09682..1696e25fb7872840513537fbcc6b2e8d24ecc94f 100644 (file)
@@ -1,4 +1,10 @@
-/* 
+/*
+ * This library is licensed under 2 different licenses and you
+ * can choose to use it under the terms of either one of them. The
+ * two licenses are the MPL 1.1 and the LGPL.
+ *
+ * MPL:
+ *
  * The contents of this file are subject to the Mozilla Public License
  * Version 1.1 (the "License"); you may not use this file except in
  * compliance with the License. You may obtain a copy of the License at
@@ -9,6 +15,23 @@
  * License for the specific language governing rights and limitations
  * under the License.
  *
+ * LGPL:
+ *
+ * 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.
+ *
  * The Original Code is Fluendo MPEG Demuxer plugin.
  *
  * The Initial Developer of the Original Code is Fluendo, S.L.
@@ -72,6 +95,9 @@ struct _GstFluPSDemux {
   GstElement     parent;
 
   GstPad         * sinkpad;
+  gboolean random_access;       /* If we operate in pull mode */
+  gboolean flushing;
+
 
   GstAdapter     * adapter;
   GstAdapter     * rev_adapter;
@@ -90,7 +116,7 @@ struct _GstFluPSDemux {
   guint64        scr_rate_n;
   guint64        scr_rate_d;
   guint64        first_scr_offset;
-  guint64        last_scr_offset;
+  guint64        cur_scr_offset;
 
   gint16         psm[GST_FLUPS_DEMUX_MAX_PSM];
 
index f2ccc7849d306219c7c569e714f56c48879774fe..d58c72602625668b72a9db8a46c3a2d7075c455f 100644 (file)
@@ -1,23 +1,45 @@
-/* 
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/.
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
- * License for the specific language governing rights and limitations
- * under the License.
- *
- * The Original Code is Fluendo MPEG Demuxer plugin.
- *
- * The Initial Developer of the Original Code is Fluendo, S.L.
- * Portions created by Fluendo, S.L. are Copyright (C) 2005
- * Fluendo, S.L. All Rights Reserved.
- *
- * Contributor(s): Wim Taymans <wim@fluendo.com>
- *                 Jan Schmidt <thaytan@noraisin.net>
- */
+ /*
+  * This library is licensed under 2 different licenses and you
+  * can choose to use it under the terms of either one of them. The
+  * two licenses are the MPL 1.1 and the LGPL.
+  *
+  * MPL:
+  *
+  * The contents of this file are subject to the Mozilla Public License
+  * Version 1.1 (the "License"); you may not use this file except in
+  * compliance with the License. You may obtain a copy of the License at
+  * http://www.mozilla.org/MPL/.
+  *
+  * Software distributed under the License is distributed on an "AS IS"
+  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+  * License for the specific language governing rights and limitations
+  * under the License.
+  *
+  * LGPL:
+  *
+  * 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.
+  *
+  * The Original Code is Fluendo MPEG Demuxer plugin.
+  *
+  * The Initial Developer of the Original Code is Fluendo, S.L.
+  * Portions created by Fluendo, S.L. are Copyright (C) 2005
+  * Fluendo, S.L. All Rights Reserved.
+  *
+  * Contributor(s): Wim Taymans <wim@fluendo.com>
+  */
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -26,8 +48,8 @@
 #include "gstmpegdefs.h"
 #include "gstpesfilter.h"
 
-GST_DEBUG_CATEGORY (gstflupesfilter_debug);
-#define GST_CAT_DEFAULT (gstflupesfilter_debug)
+GST_DEBUG_CATEGORY (mpegpspesfilter_debug);
+#define GST_CAT_DEFAULT (mpegpspesfilter_debug)
 
 static GstFlowReturn gst_pes_filter_data_push (GstPESFilter * filter,
     gboolean first, GstBuffer * buffer);
@@ -75,17 +97,6 @@ gst_pes_filter_set_callbacks (GstPESFilter * filter,
   filter->user_data = user_data;
 }
 
-/* sync:4 == 00xx ! pts:3 ! 1 ! pts:15 ! 1 | pts:15 ! 1 */
-#define READ_TS(data, target, lost_sync_label)          \
-    if ((*data & 0x01) != 0x01) goto lost_sync_label;   \
-    target  = ((guint64) (*data++ & 0x0E)) << 29;      \
-    target |= ((guint64) (*data++       )) << 22;      \
-    if ((*data & 0x01) != 0x01) goto lost_sync_label;   \
-    target |= ((guint64) (*data++ & 0xFE)) << 14;      \
-    target |= ((guint64) (*data++       )) << 7;       \
-    if ((*data & 0x01) != 0x01) goto lost_sync_label;   \
-    target |= ((guint64) (*data++ & 0xFE)) >> 1;
-
 static gboolean
 gst_pes_filter_is_sync (guint32 sync)
 {
@@ -101,15 +112,21 @@ gst_pes_filter_parse (GstPESFilter * filter)
   GstFlowReturn ret;
   guint32 start_code;
 
+  gboolean STD_buffer_bound_scale G_GNUC_UNUSED;
   guint16 STD_buffer_size_bound;
   const guint8 *data;
   gint avail, datalen;
   gboolean have_size = FALSE;
 
-  /* read start code and length */
-  if (!(data = gst_adapter_peek (filter->adapter, 6)))
+  avail = gst_adapter_available (filter->adapter);
+
+  if (avail < 6)
     goto need_more_data;
 
+  data = gst_adapter_map (filter->adapter, 6);
+
+  /* read start code and length */
+
   /* get start code */
   start_code = GST_READ_UINT32_BE (data);
   if (!gst_pes_filter_is_sync (start_code))
@@ -124,9 +141,6 @@ gst_pes_filter_parse (GstPESFilter * filter)
   /* start parsing length */
   filter->length = GST_READ_UINT16_BE (data);
 
-  /* see how much is available */
-  avail = gst_adapter_available (filter->adapter);
-
   GST_DEBUG ("id 0x%02x length %d, avail %d start code 0x%02x", filter->id,
       filter->length, avail, filter->start_code);
 
@@ -139,6 +153,7 @@ gst_pes_filter_parse (GstPESFilter * filter)
    * to set the allow_unbounded flag if they want */
   if (filter->length == 0 &&
       ((filter->start_code & 0xFFFFFFF0) == PACKET_VIDEO_START_CODE ||
+          filter->start_code == ID_EXTENDED_STREAM_ID ||
           filter->allow_unbounded)) {
     GST_DEBUG ("id 0x%02x, unbounded length", filter->id);
     filter->unbounded_packet = TRUE;
@@ -155,10 +170,14 @@ gst_pes_filter_parse (GstPESFilter * filter)
     avail = MIN (avail, filter->length + 6);
   }
 
+  if (avail < 6)
+    goto need_more_data;
+
+  gst_adapter_unmap (filter->adapter);
+
   /* read more data, either the whole packet if there is a length
    * or whatever we have available if this in an unbounded packet. */
-  if (!(data = gst_adapter_peek (filter->adapter, avail)))
-    goto need_more_data;
+  data = gst_adapter_map (filter->adapter, avail);
 
   /* This will make us flag LOST_SYNC if we run out of data from here onward */
   have_size = TRUE;
@@ -177,7 +196,8 @@ gst_pes_filter_parse (GstPESFilter * filter)
     case ID_PROGRAM_STREAM_DIRECTORY:
     case ID_DSMCC_STREAM:
     case ID_ITU_TREC_H222_TYPE_E_STREAM:
-      goto skip;
+      /* Push directly out */
+      goto push_out;
     case ID_PADDING_STREAM:
       GST_DEBUG ("skipping padding stream");
       goto skip;
@@ -185,9 +205,8 @@ gst_pes_filter_parse (GstPESFilter * filter)
       break;
   }
 
-  if (datalen < 1)
+  if (datalen == 0)
     goto need_more_data;
-
   filter->pts = filter->dts = -1;
 
   /* stuffing bits, first two bits are '10' for mpeg2 pes so this code is
@@ -212,7 +231,7 @@ gst_pes_filter_parse (GstPESFilter * filter)
     if (datalen < 3)
       goto need_more_data;
 
-    /* STD_buffer_bound_scale = *data & 0x20; */
+    STD_buffer_bound_scale = *data & 0x20;
     STD_buffer_size_bound = ((guint16) (*data++ & 0x1F)) << 8;
     STD_buffer_size_bound |= *data++;
 
@@ -264,7 +283,7 @@ gst_pes_filter_parse (GstPESFilter * filter)
 
     /* check PES scrambling control */
     if ((flags & 0x30) != 0)
-      goto encrypted;
+      GST_DEBUG ("PES scrambling control: %x", (flags >> 4) & 0x3);
 
     /* 2: PTS_DTS_flags
      * 1: ESCR_flag
@@ -376,9 +395,53 @@ gst_pes_filter_parse (GstPESFilter * filter)
     }
     /* PES_extension_flag  */
     if ((flags & 0x01)) {
-      GST_DEBUG ("%x PES_extension", filter->id);
+      flags = *data++;
+      header_data_length -= 1;
+      datalen -= 1;
+      GST_DEBUG ("%x PES_extension, flags 0x%02x", filter->id, flags);
+      /* PES_private_data_flag */
+      if ((flags & 0x80)) {
+        GST_DEBUG ("%x PES_private_data_flag", filter->id);
+        data += 16;
+        header_data_length -= 16;
+        datalen -= 16;
+      }
+      /* pack_header_field_flag */
+      if ((flags & 0x40)) {
+        guint8 pack_field_length = *data;
+        GST_DEBUG ("%x pack_header_field_flag, pack_field_length %d",
+            filter->id, pack_field_length);
+        data += pack_field_length + 1;
+        header_data_length -= pack_field_length + 1;
+        datalen -= pack_field_length + 1;
+      }
+      /* program_packet_sequence_counter_flag */
+      if ((flags & 0x20)) {
+        GST_DEBUG ("%x program_packet_sequence_counter_flag", filter->id);
+        data += 2;
+        header_data_length -= 2;
+        datalen -= 2;
+      }
+      /* P-STD_buffer_flag */
+      if ((flags & 0x10)) {
+        GST_DEBUG ("%x P-STD_buffer_flag", filter->id);
+        data += 2;
+        header_data_length -= 2;
+        datalen -= 2;
+      }
+      /* PES_extension_flag_2 */
+      if ((flags & 0x01)) {
+        guint8 PES_extension_field_length = *data++;
+        GST_DEBUG ("%x PES_extension_flag_2, len %d",
+            filter->id, PES_extension_field_length & 0x7f);
+        if (PES_extension_field_length == 0x81) {
+          GST_DEBUG ("%x substream id 0x%02x", filter->id, *data);
+        }
+        data += PES_extension_field_length & 0x7f;
+        header_data_length -= (PES_extension_field_length & 0x7f) + 1;
+        datalen -= (PES_extension_field_length & 0x7f) + 1;
+      }
     }
-
     /* calculate the amount of real data in this PES packet */
     data += header_data_length;
     datalen -= header_data_length;
@@ -392,6 +455,7 @@ gst_pes_filter_parse (GstPESFilter * filter)
     goto lost_sync;
   }
 
+push_out:
   {
     GstBuffer *out;
     guint16 consumed;
@@ -408,11 +472,8 @@ gst_pes_filter_parse (GstPESFilter * filter)
     }
 
     if (datalen > 0) {
-      out = gst_buffer_new ();
-      GST_BUFFER_DATA (out) = g_memdup (data, datalen);
-      GST_BUFFER_SIZE (out) = datalen;
-      GST_BUFFER_MALLOCDATA (out) = GST_BUFFER_DATA (out);
-
+      out = gst_buffer_new_allocate (NULL, datalen, NULL);
+      gst_buffer_fill (out, 0, data, datalen);
       ret = gst_pes_filter_data_push (filter, TRUE, out);
       filter->first = FALSE;
     } else {
@@ -425,6 +486,7 @@ gst_pes_filter_parse (GstPESFilter * filter)
       filter->state = STATE_DATA_PUSH;
   }
 
+  gst_adapter_unmap (filter->adapter);
   gst_adapter_flush (filter->adapter, avail);
   ADAPTER_OFFSET_FLUSH (avail);
 
@@ -434,36 +496,27 @@ need_more_data:
   {
     if (filter->unbounded_packet == FALSE) {
       if (have_size == TRUE) {
-        GST_DEBUG ("bounded need more data %d, lost sync",
+        GST_DEBUG ("bounded need more data %" G_GSIZE_FORMAT " , lost sync",
             gst_adapter_available (filter->adapter));
         ret = GST_FLOW_LOST_SYNC;
       } else {
-        GST_DEBUG ("bounded need more data %d, breaking for more",
-            gst_adapter_available (filter->adapter));
+        GST_DEBUG ("bounded need more data %" G_GSIZE_FORMAT
+            ", breaking for more", gst_adapter_available (filter->adapter));
         ret = GST_FLOW_NEED_MORE_DATA;
       }
     } else {
-      GST_DEBUG ("unbounded need more data %d",
+      GST_DEBUG ("unbounded need more data %" G_GSIZE_FORMAT,
           gst_adapter_available (filter->adapter));
       ret = GST_FLOW_NEED_MORE_DATA;
     }
-
+    gst_adapter_unmap (filter->adapter);
     return ret;
   }
 skip:
   {
-    GST_DEBUG ("skipping 0x%02x", filter->id);
-    gst_adapter_flush (filter->adapter, avail);
-    ADAPTER_OFFSET_FLUSH (avail);
+    gst_adapter_unmap (filter->adapter);
 
-    filter->length -= avail - 6;
-    if (filter->length > 0 || filter->unbounded_packet)
-      filter->state = STATE_DATA_SKIP;
-    return GST_FLOW_OK;
-  }
-encrypted:
-  {
-    GST_DEBUG ("skipping encrypted 0x%02x", filter->id);
+    GST_DEBUG ("skipping 0x%02x", filter->id);
     gst_adapter_flush (filter->adapter, avail);
     ADAPTER_OFFSET_FLUSH (avail);
 
@@ -474,6 +527,7 @@ encrypted:
   }
 lost_sync:
   {
+    gst_adapter_unmap (filter->adapter);
     GST_DEBUG ("lost sync");
     gst_adapter_flush (filter->adapter, 4);
     ADAPTER_OFFSET_FLUSH (4);
@@ -562,14 +616,8 @@ gst_pes_filter_process (GstPESFilter * filter)
           ret = GST_FLOW_OK;
         } else {
           GstBuffer *out;
-          guint8 *data;
-
-          data = gst_adapter_take (filter->adapter, avail);
 
-          out = gst_buffer_new ();
-          GST_BUFFER_DATA (out) = data;
-          GST_BUFFER_SIZE (out) = avail;
-          GST_BUFFER_MALLOCDATA (out) = data;
+          out = gst_adapter_take_buffer (filter->adapter, avail);
 
           ret = gst_pes_filter_data_push (filter, filter->first, out);
           filter->first = FALSE;
index 27c618c2d44a4c55d1e1add4f11a23ef93b4dddf..ccc8461da625a64b73c83c9b262504a469f4554f 100644 (file)
@@ -1,4 +1,10 @@
-/* 
+/*
+ * This library is licensed under 2 different licenses and you
+ * can choose to use it under the terms of either one of them. The
+ * two licenses are the MPL 1.1 and the LGPL.
+ *
+ * MPL:
+ *
  * The contents of this file are subject to the Mozilla Public License
  * Version 1.1 (the "License"); you may not use this file except in
  * compliance with the License. You may obtain a copy of the License at
@@ -9,6 +15,23 @@
  * License for the specific language governing rights and limitations
  * under the License.
  *
+ * LGPL:
+ *
+ * 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.
+ *
  * The Original Code is Fluendo MPEG Demuxer plugin.
  *
  * The Initial Developer of the Original Code is Fluendo, S.L.
@@ -16,7 +39,6 @@
  * Fluendo, S.L. All Rights Reserved.
  *
  * Contributor(s): Wim Taymans <wim@fluendo.com>
- *                 Jan Schmidt <thaytan@noraisin.net>
  */
 
 #ifndef __GST_PES_FILTER_H__
@@ -62,8 +84,6 @@ struct _GstPESFilter {
   gboolean           unbounded_packet;
   guint16            length;
 
-  guint8             type;
-
   gint64             pts;
   gint64             dts;
 };
index ea939eb6231d3475c1f93cddae575deb26c9745d..3b2ac6b4231489fb2c5b01c2e73c8f9b4d192f65 100644 (file)
@@ -35,7 +35,7 @@ plugin_init (GstPlugin * plugin)
 {
   gboolean result = TRUE;
 
-  GST_DEBUG_CATEGORY_INIT (resindvd_debug, "resindvd elements",
+  GST_DEBUG_CATEGORY_INIT (resindvd_debug, "resindvd",
       0, "DVD playback elements from resindvd");
 
 #ifdef ENABLE_NLS
index 78a74e2bbeb567d780a4958cd903078a614152e1..119c2b6af6dd39036c13e3ba6cd1e389c01cdc58 100644 (file)
 #include <gst/gst.h>
 #include <gst/glib-compat-private.h>
 #include <gst/pbutils/missing-plugins.h>
+#include <gst/video/video.h>
+#include <gst/audio/audio.h>
 
 #include "resindvdbin.h"
 #include "resindvdsrc.h"
-#include "rsnstreamselector.h"
-#include "rsnaudiomunge.h"
+#include "rsninputselector.h"
+// #include "rsnaudiomunge.h"
 #include "rsndec.h"
-#include "rsnparsetter.h"
+// #include "rsnparsetter.h"
 
 #include "gstmpegdemux.h"
 
+#define RSN_TYPE_INPUT_SELECTOR GST_TYPE_INPUT_SELECTOR
+
 GST_DEBUG_CATEGORY_EXTERN (resindvd_debug);
 #define GST_CAT_DEFAULT resindvd_debug
 
@@ -62,14 +66,14 @@ static GstStaticPadTemplate video_src_template =
 GST_STATIC_PAD_TEMPLATE ("video",
     GST_PAD_SRC,
     GST_PAD_SOMETIMES,
-    GST_STATIC_CAPS ("video/x-raw-yuv")
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL))
     );
 
 static GstStaticPadTemplate audio_src_template =
-    GST_STATIC_PAD_TEMPLATE ("audio",
+GST_STATIC_PAD_TEMPLATE ("audio",
     GST_PAD_SRC,
     GST_PAD_SOMETIMES,
-    GST_STATIC_CAPS ("audio/x-raw-int; audio/x-raw-float")
+    GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL))
     );
 
 static GstStaticPadTemplate subpicture_src_template =
@@ -79,12 +83,12 @@ GST_STATIC_PAD_TEMPLATE ("subpicture",
     GST_STATIC_CAPS ("subpicture/x-dvd")
     );
 
-static void rsn_dvdbin_do_init (GType rsn_dvdbin_type);
 static void rsn_dvdbin_finalize (GObject * object);
 static void rsn_dvdbin_uri_handler_init (gpointer g_iface, gpointer iface_data);
 
-GST_BOILERPLATE_FULL (RsnDvdBin, rsn_dvdbin, GstBin,
-    GST_TYPE_BIN, rsn_dvdbin_do_init);
+#define rsn_dvdbin_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (RsnDvdBin, rsn_dvdbin, GST_TYPE_BIN,
+    G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, rsn_dvdbin_uri_handler_init));
 
 static void demux_pad_added (GstElement * element, GstPad * pad,
     RsnDvdBin * dvdbin);
@@ -96,31 +100,14 @@ static void rsn_dvdbin_get_property (GObject * object, guint prop_id,
 static GstStateChangeReturn rsn_dvdbin_change_state (GstElement * element,
     GstStateChange transition);
 
-static void
-rsn_dvdbin_base_init (gpointer gclass)
-{
-
-  GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
-
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&video_src_template));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&audio_src_template));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&subpicture_src_template));
-  gst_element_class_set_details_simple (element_class, "rsndvdbin",
-      "Generic/Bin/Player",
-      "DVD playback element", "Jan Schmidt <thaytan@noraisin.net>");
-
-  element_class->change_state = GST_DEBUG_FUNCPTR (rsn_dvdbin_change_state);
-}
-
 static void
 rsn_dvdbin_class_init (RsnDvdBinClass * klass)
 {
   GObjectClass *gobject_class;
+  GstElementClass *element_class;
 
   gobject_class = (GObjectClass *) klass;
+  element_class = (GstElementClass *) klass;
 
   gobject_class->finalize = rsn_dvdbin_finalize;
   gobject_class->set_property = rsn_dvdbin_set_property;
@@ -129,23 +116,23 @@ rsn_dvdbin_class_init (RsnDvdBinClass * klass)
   g_object_class_install_property (gobject_class, ARG_DEVICE,
       g_param_spec_string ("device", "Device", "DVD device location",
           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-}
 
-static void
-rsn_dvdbin_do_init (GType rsn_dvdbin_type)
-{
-  static const GInterfaceInfo urihandler_info = {
-    rsn_dvdbin_uri_handler_init,
-    NULL,
-    NULL
-  };
-
-  g_type_add_interface_static (rsn_dvdbin_type, GST_TYPE_URI_HANDLER,
-      &urihandler_info);
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&video_src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&audio_src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&subpicture_src_template));
+
+  element_class->change_state = GST_DEBUG_FUNCPTR (rsn_dvdbin_change_state);
+
+  gst_element_class_set_details_simple (element_class, "rsndvdbin",
+      "Generic/Bin/Player",
+      "DVD playback element", "Jan Schmidt <thaytan@noraisin.net>");
 }
 
 static void
-rsn_dvdbin_init (RsnDvdBin * dvdbin, RsnDvdBinClass * gclass)
+rsn_dvdbin_init (RsnDvdBin * dvdbin)
 {
   dvdbin->dvd_lock = g_mutex_new ();
   dvdbin->preroll_lock = g_mutex_new ();
@@ -166,20 +153,20 @@ rsn_dvdbin_finalize (GObject * object)
 
 /* URI interface */
 static GstURIType
-rsn_dvdbin_uri_get_type (void)
+rsn_dvdbin_uri_get_type (GType type)
 {
   return GST_URI_SRC;
 }
 
 static const gchar *const *
-rsn_dvdbin_uri_get_protocols (void)
+rsn_dvdbin_uri_get_protocols (GType type)
 {
   static const gchar *protocols[] = { "dvd", NULL };
 
   return protocols;
 }
 
-static const gchar *
+static gchar *
 rsn_dvdbin_uri_get_uri (GstURIHandler * handler)
 {
   RsnDvdBin *dvdbin = RESINDVDBIN (handler);
@@ -192,11 +179,12 @@ rsn_dvdbin_uri_get_uri (GstURIHandler * handler)
     dvdbin->last_uri = g_strdup ("dvd://");
   DVDBIN_UNLOCK (dvdbin);
 
-  return dvdbin->last_uri;
+  return g_strdup (dvdbin->last_uri);
 }
 
 static gboolean
-rsn_dvdbin_uri_set_uri (GstURIHandler * handler, const gchar * uri)
+rsn_dvdbin_uri_set_uri (GstURIHandler * handler, const gchar * uri,
+    GError ** error)
 {
   RsnDvdBin *dvdbin = RESINDVDBIN (handler);
   gboolean ret;
@@ -335,10 +323,12 @@ typedef struct
 {
   RsnDvdBin *dvdbin;
   GstPad *pad;
+  gulong pad_block_id;
 } RsnDvdBinPadBlockCtx;
 
-static void dvdbin_pad_blocked_cb (GstPad * pad, gboolean blocked,
-    RsnDvdBinPadBlockCtx * ctx);
+static GstPadProbeReturn dvdbin_pad_blocked_cb (GstPad * pad,
+    GstPadProbeInfo * info, RsnDvdBinPadBlockCtx * ctx);
+
 static void
 _pad_block_destroy_notify (RsnDvdBinPadBlockCtx * ctx)
 {
@@ -366,6 +356,7 @@ create_elements (RsnDvdBin * dvdbin)
         "device", dvdbin->device, NULL);
   }
 
+  /* FIXME: Import and use local copy of mpeg PS demuxer */
   if (!try_create_piece (dvdbin, DVD_ELEM_DEMUX,
           NULL, GST_TYPE_FLUPS_DEMUX, "dvddemux", "DVD demuxer"))
     return FALSE;
@@ -394,7 +385,8 @@ create_elements (RsnDvdBin * dvdbin)
           "viddec", "video decoder"))
     return FALSE;
 
-  if (!try_create_piece (dvdbin, DVD_ELEM_PARSET, NULL, RSN_TYPE_RSNPARSETTER,
+  /* FIXME: Replace identity */
+  if (!try_create_piece (dvdbin, DVD_ELEM_PARSET, "identity", 0,        //RSN_TYPE_RSNPARSETTER,
           "rsnparsetter", "Aspect ratio adjustment"))
     return FALSE;
 
@@ -420,14 +412,16 @@ create_elements (RsnDvdBin * dvdbin)
   bctx = g_slice_new (RsnDvdBinPadBlockCtx);
   bctx->dvdbin = gst_object_ref (dvdbin);
   bctx->pad = gst_object_ref (dvdbin->video_pad);
-  gst_pad_set_blocked_async_full (src, TRUE,
-      (GstPadBlockCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify)
+  bctx->pad_block_id =
+      gst_pad_add_probe (src, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+      (GstPadProbeCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify)
       _pad_block_destroy_notify);
   gst_object_unref (src);
   src = NULL;
 
+  /* FIXME: Core input selector OK? */
   if (!try_create_piece (dvdbin, DVD_ELEM_SPU_SELECT, NULL,
-          RSN_TYPE_STREAM_SELECTOR, "subpselect", "Subpicture stream selector"))
+          RSN_TYPE_INPUT_SELECTOR, "subpselect", "Subpicture stream selector"))
     return FALSE;
 
   /* Add a single standalone queue to hold a single buffer of SPU data */
@@ -461,18 +455,20 @@ create_elements (RsnDvdBin * dvdbin)
   bctx = g_slice_new (RsnDvdBinPadBlockCtx);
   bctx->dvdbin = gst_object_ref (dvdbin);
   bctx->pad = gst_object_ref (dvdbin->subpicture_pad);
-  gst_pad_set_blocked_async_full (src, TRUE,
-      (GstPadBlockCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify)
+  bctx->pad_block_id =
+      gst_pad_add_probe (src, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+      (GstPadProbeCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify)
       _pad_block_destroy_notify);
   gst_object_unref (src);
   src = NULL;
 
-  if (!try_create_piece (dvdbin, DVD_ELEM_AUD_SELECT, NULL,
-          RSN_TYPE_STREAM_SELECTOR, "audioselect", "Audio stream selector"))
+  if (!try_create_piece (dvdbin, DVD_ELEM_AUD_SELECT, "input-selector",
+          RSN_TYPE_INPUT_SELECTOR, "audioselect", "Audio stream selector"))
     return FALSE;
 
-  if (!try_create_piece (dvdbin, DVD_ELEM_AUD_MUNGE, NULL,
-          RSN_TYPE_AUDIOMUNGE, "audioearlymunge", "Audio output filter"))
+  if (!try_create_piece (dvdbin, DVD_ELEM_AUD_MUNGE, "identity",
+          0 /* RSN_TYPE_AUDIOMUNGE */ , "audioearlymunge",
+          "Audio output filter"))
     return FALSE;
 
   if (!try_create_piece (dvdbin, DVD_ELEM_AUDDEC, NULL,
@@ -513,8 +509,9 @@ create_elements (RsnDvdBin * dvdbin)
   bctx = g_slice_new (RsnDvdBinPadBlockCtx);
   bctx->dvdbin = gst_object_ref (dvdbin);
   bctx->pad = gst_object_ref (dvdbin->audio_pad);
-  gst_pad_set_blocked_async_full (src, TRUE,
-      (GstPadBlockCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify)
+  bctx->pad_block_id =
+      gst_pad_add_probe (src, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
+      (GstPadProbeCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify)
       _pad_block_destroy_notify);
   gst_object_unref (src);
   src = NULL;
@@ -634,7 +631,7 @@ connect_thru_mq (RsnDvdBin * dvdbin, GstPad * pad)
     return FALSE;
 
   sinkname = gst_pad_get_name (mq_sink);
-  tmp = sinkname + 4;
+  tmp = sinkname + 5;
   srcname = g_strdup_printf ("src_%s", tmp);
 
   mq_src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_MQUEUE],
@@ -653,9 +650,9 @@ can_sink_caps (GstElement * e, GstCaps * caps)
   GstPad *sink = gst_element_get_static_pad (e, "sink");
 
   if (sink) {
-    GstCaps *sink_caps = gst_pad_get_caps (sink);
+    GstCaps *sink_caps = gst_pad_query_caps (sink, caps);
     if (sink_caps) {
-      res = gst_caps_can_intersect (sink_caps, caps);
+      res = !gst_caps_is_empty (sink_caps);
       gst_caps_unref (sink_caps);
     }
     gst_object_unref (sink);
@@ -675,7 +672,7 @@ demux_pad_added (GstElement * element, GstPad * pad, RsnDvdBin * dvdbin)
 
   GST_DEBUG_OBJECT (dvdbin, "New pad: %" GST_PTR_FORMAT, pad);
 
-  caps = gst_pad_get_caps (pad);
+  caps = gst_pad_query_caps (pad, NULL);
   if (caps == NULL) {
     GST_WARNING_OBJECT (dvdbin, "NULL caps from pad %" GST_PTR_FORMAT, pad);
     return;
@@ -694,9 +691,12 @@ demux_pad_added (GstElement * element, GstPad * pad, RsnDvdBin * dvdbin)
   g_return_if_fail (s != NULL);
 
   if (can_sink_caps (dvdbin->pieces[DVD_ELEM_VIDDEC], caps)) {
+    GST_LOG_OBJECT (dvdbin, "Found video pad w/ caps %" GST_PTR_FORMAT, caps);
     dest_pad =
         gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_VIDDEC], "sink");
   } else if (g_str_equal (gst_structure_get_name (s), "subpicture/x-dvd")) {
+    GST_LOG_OBJECT (dvdbin, "Found subpicture pad w/ caps %" GST_PTR_FORMAT,
+        caps);
     dest_pad =
         gst_element_get_request_pad (dvdbin->pieces[DVD_ELEM_SPU_SELECT],
         "sink_%u");
@@ -720,7 +720,7 @@ demux_pad_added (GstElement * element, GstPad * pad, RsnDvdBin * dvdbin)
           ("No MPEG video decoder found"));
     } else {
       GST_ELEMENT_WARNING (dvdbin, STREAM, CODEC_NOT_FOUND, (NULL),
-          ("No MPEG audio decoder found"));
+          ("No audio decoder found"));
     }
   }
 
@@ -763,6 +763,7 @@ demux_no_more_pads (GstElement * element, RsnDvdBin * dvdbin)
   gboolean no_more_pads = FALSE;
   guint n_audio_pads = 0;
 
+  GST_DEBUG_OBJECT (dvdbin, "Received no more pads from demuxer");
   DVDBIN_PREROLL_LOCK (dvdbin);
 
   g_object_get (dvdbin->pieces[DVD_ELEM_AUD_SELECT], "n-pads", &n_audio_pads,
@@ -781,21 +782,15 @@ demux_no_more_pads (GstElement * element, RsnDvdBin * dvdbin)
   }
 }
 
-static void
-dvdbin_pad_blocked_cb (GstPad * opad, gboolean blocked,
-    RsnDvdBinPadBlockCtx * ctx)
+static GstPadProbeReturn
+dvdbin_pad_blocked_cb (GstPad * opad,
+    GstPadProbeInfo * info, RsnDvdBinPadBlockCtx * ctx)
 {
   RsnDvdBin *dvdbin;
   GstPad *pad;
   gboolean added_last_pad = FALSE;
   gboolean added = FALSE;
 
-  /* If not blocked ctx is NULL! */
-  if (!blocked) {
-    GST_DEBUG_OBJECT (opad, "Pad unblocked");
-    return;
-  }
-
   dvdbin = ctx->dvdbin;
   pad = ctx->pad;
 
@@ -812,8 +807,8 @@ dvdbin_pad_blocked_cb (GstPad * opad, gboolean blocked,
     }
     DVDBIN_PREROLL_UNLOCK (dvdbin);
 
-    gst_pad_set_blocked_async (opad, FALSE,
-        (GstPadBlockCallback) dvdbin_pad_blocked_cb, NULL);
+    if (ctx->pad_block_id)
+      gst_pad_remove_probe (opad, ctx->pad_block_id);
   } else if (pad == dvdbin->audio_pad) {
     GST_DEBUG_OBJECT (opad, "Pad block -> audio pad");
     DVDBIN_PREROLL_LOCK (dvdbin);
@@ -826,8 +821,8 @@ dvdbin_pad_blocked_cb (GstPad * opad, gboolean blocked,
     }
     DVDBIN_PREROLL_UNLOCK (dvdbin);
 
-    gst_pad_set_blocked_async (opad, FALSE,
-        (GstPadBlockCallback) dvdbin_pad_blocked_cb, NULL);
+    if (ctx->pad_block_id)
+      gst_pad_remove_probe (opad, ctx->pad_block_id);
   } else if (pad == dvdbin->video_pad) {
     GST_DEBUG_OBJECT (opad, "Pad block -> video pad");
 
@@ -842,14 +837,16 @@ dvdbin_pad_blocked_cb (GstPad * opad, gboolean blocked,
     }
     DVDBIN_PREROLL_UNLOCK (dvdbin);
 
-    gst_pad_set_blocked_async (opad, FALSE,
-        (GstPadBlockCallback) dvdbin_pad_blocked_cb, NULL);
+    if (ctx->pad_block_id)
+      gst_pad_remove_probe (opad, ctx->pad_block_id);
   }
 
   if (added_last_pad) {
     GST_DEBUG_OBJECT (dvdbin, "Firing no more pads from pad-blocked cb");
     gst_element_no_more_pads (GST_ELEMENT (dvdbin));
   }
+
+  return GST_PAD_PROBE_OK;
 }
 
 static void
index fff79b22f43cf92167121f59af7c9eccde4830e3..f5807069cf895763463548957f2990c30ddf27ed 100644 (file)
 #include <stdio.h>
 #include <string.h>
 
+#include <gmodule.h>
 #include <gst/gst.h>
 #include <gst/glib-compat-private.h>
 #include <gst/gst-i18n-plugin.h>
-#include <gst/interfaces/navigation.h>
 #include <gst/video/video.h>
+#include <gst/video/navigation.h>
 
 #include "resindvdsrc.h"
 
@@ -89,6 +90,7 @@ typedef struct
 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
+    // GST_STATIC_CAPS ("video/mpeg,mpegversion=2,systemstream=true")
     GST_STATIC_CAPS ("application/x-resin-dvd")
     );
 
@@ -100,8 +102,9 @@ static GstFormat chapter_format;
 
 static void rsn_dvdsrc_register_extra (GType rsn_dvdsrc_type);
 
-GST_BOILERPLATE_FULL (resinDvdSrc, rsn_dvdsrc, GstBaseSrc,
-    GST_TYPE_BASE_SRC, rsn_dvdsrc_register_extra);
+#define rsn_dvdsrc_parent_class parent_class
+G_DEFINE_TYPE_EXTENDED (resinDvdSrc, rsn_dvdsrc, GST_TYPE_BASE_SRC,
+    0, rsn_dvdsrc_register_extra (g_define_type_id));
 
 static gboolean read_vts_info (resinDvdSrc * src);
 
@@ -118,11 +121,11 @@ static gboolean rsn_dvdsrc_unlock (GstBaseSrc * bsrc);
 static gboolean rsn_dvdsrc_unlock_stop (GstBaseSrc * bsrc);
 
 static gboolean rsn_dvdsrc_is_seekable (GstBaseSrc * bsrc);
-static gboolean rsn_dvdsrc_prepare_seek (GstBaseSrc * bsrc, GstEvent * event,
-    GstSegment * segment);
+static gboolean rsn_dvdsrc_prepare_seek (GstBaseSrc * bsrc,
+    GstEvent * event, GstSegment * segment);
 static gboolean rsn_dvdsrc_do_seek (GstBaseSrc * bsrc, GstSegment * segment);
-static GstStateChangeReturn
-rsn_dvdsrc_change_state (GstElement * element, GstStateChange transition);
+static GstStateChangeReturn rsn_dvdsrc_change_state (GstElement * element,
+    GstStateChange transition);
 
 static void rsn_dvdsrc_prepare_spu_stream_event (resinDvdSrc * src,
     guint8 logical_stream, guint8 phys_stream, gboolean forced_only);
@@ -142,7 +145,6 @@ static void rsn_dvdsrc_check_nav_blocks (resinDvdSrc * src);
 static void rsn_dvdsrc_schedule_nav_cb (resinDvdSrc * src,
     RsnDvdPendingNav * next_nav);
 
-static gboolean rsn_dvdsrc_check_get_range (GstBaseSrc * src);
 static GstFlowReturn rsn_dvdsrc_create (GstBaseSrc * bsrc, guint64 offset,
     guint length, GstBuffer ** buf);
 static gboolean rsn_dvdsrc_src_event (GstBaseSrc * basesrc, GstEvent * event);
@@ -188,18 +190,6 @@ rsn_dvdsrc_register_extra (GType rsn_dvdsrc_type)
   chapter_format = gst_format_register ("chapter", "DVD chapter format");
 }
 
-static void
-rsn_dvdsrc_base_init (gpointer gclass)
-{
-
-  GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
-
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&src_factory));
-  gst_element_class_set_details_simple (element_class, "Resin DVD Src",
-      "Source/DVD", "DVD source element", "Jan Schmidt <thaytan@noraisin.net>");
-}
-
 static void
 rsn_dvdsrc_class_init (resinDvdSrcClass * klass)
 {
@@ -228,8 +218,6 @@ rsn_dvdsrc_class_init (resinDvdSrcClass * klass)
       GST_DEBUG_FUNCPTR (rsn_dvdsrc_prepare_seek);
   gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (rsn_dvdsrc_do_seek);
 
-  gstbasesrc_class->check_get_range =
-      GST_DEBUG_FUNCPTR (rsn_dvdsrc_check_get_range);
   gstbasesrc_class->create = GST_DEBUG_FUNCPTR (rsn_dvdsrc_create);
 
   g_object_class_install_property (gobject_class, ARG_DEVICE,
@@ -240,10 +228,15 @@ rsn_dvdsrc_class_init (resinDvdSrcClass * klass)
       g_param_spec_boolean ("fast-start", "Fast start",
           "Skip straight to the DVD menu on start", DEFAULT_FASTSTART,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&src_factory));
+  gst_element_class_set_details_simple (gstelement_class, "Resin DVD Src",
+      "Source/DVD", "DVD source element", "Jan Schmidt <thaytan@noraisin.net>");
 }
 
 static void
-rsn_dvdsrc_init (resinDvdSrc * rsndvdsrc, resinDvdSrcClass * gclass)
+rsn_dvdsrc_init (resinDvdSrc * rsndvdsrc)
 {
   const gchar *envvar;
 
@@ -637,11 +630,9 @@ rsn_dvdsrc_do_still (resinDvdSrc * src, int duration)
      * event, then sleep */
     still_event = gst_video_event_new_still_frame (TRUE);
 
-    gst_segment_set_last_stop (segment, GST_FORMAT_TIME, src->cur_end_ts);
+    segment->position = src->cur_end_ts;
 
-    seg_event = gst_event_new_new_segment_full (TRUE,
-        segment->rate, segment->applied_rate, segment->format,
-        segment->start, segment->last_stop, segment->time);
+    seg_event = gst_event_new_segment (segment);
 
     /* Grab any pending highlight event to send too */
     hl_event = src->highlight_event;
@@ -757,9 +748,8 @@ rsn_dvdsrc_do_still (resinDvdSrc * src, int duration)
     still_event = gst_video_event_new_still_frame (FALSE);
 
     /* If the segment was too short in a timed still, it may need extending */
-    if (segment->last_stop < segment->start + GST_SECOND * duration)
-      gst_segment_set_last_stop (segment, GST_FORMAT_TIME,
-          segment->start + (GST_SECOND * duration));
+    if (segment->position < segment->start + GST_SECOND * duration)
+      segment->position = segment->start + (GST_SECOND * duration);
 
     g_mutex_unlock (src->dvd_lock);
     gst_pad_push_event (GST_BASE_SRC_PAD (src), still_event);
@@ -875,9 +865,7 @@ update_title_info (resinDvdSrc * src, gboolean force)
       }
     }
     if (title_str) {
-      GstTagList *tags = gst_tag_list_new ();
-      gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE,
-          title_str, NULL);
+      GstTagList *tags = gst_tag_list_new (GST_TAG_TITLE, title_str, NULL);
       g_free (title_str);
       return tags;
     }
@@ -910,19 +898,18 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
 {
   GstFlowReturn ret = GST_FLOW_OK;
   dvdnav_status_t dvdnav_ret;
-  guint8 *data;
   gint event, len;
+  GstMapInfo mmap;
 
   /* Allocate an output buffer if there isn't a pending one */
-  if (src->alloc_buf == NULL) {
-    src->alloc_buf = gst_buffer_new_and_alloc (DVD_VIDEO_LB_LEN);
-    gst_buffer_set_caps (src->alloc_buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (src)));
-  }
+  if (src->alloc_buf == NULL)
+    src->alloc_buf = gst_buffer_new_allocate (NULL, DVD_VIDEO_LB_LEN, NULL);
+
+  gst_buffer_map (src->alloc_buf, &mmap, GST_MAP_WRITE);
 
-  data = GST_BUFFER_DATA (src->alloc_buf);
   len = DVD_VIDEO_LB_LEN;
 
-  dvdnav_ret = dvdnav_get_next_block (src->dvdnav, data, &event, &len);
+  dvdnav_ret = dvdnav_get_next_block (src->dvdnav, mmap.data, &event, &len);
   if (dvdnav_ret != DVDNAV_STATUS_OK)
     goto read_error;
   g_mutex_lock (src->branch_lock);
@@ -933,10 +920,12 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
   switch (event) {
     case DVDNAV_BLOCK_OK:
       /* Data block that needs outputting */
+      gst_buffer_unmap (src->alloc_buf, &mmap);
       src->next_buf = src->alloc_buf;
+      src->alloc_buf = NULL;
+
       src->next_is_nav_block = FALSE;
       src->next_nav_ts = GST_CLOCK_TIME_NONE;
-      src->alloc_buf = NULL;
       src->in_still_state = FALSE;
       break;
     case DVDNAV_NAV_PACKET:
@@ -988,6 +977,7 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
       src->cur_vobu_base_ts = new_base_time;
 
       /* NAV packet is also a data block that needs sending */
+      gst_buffer_unmap (src->alloc_buf, &mmap);
       src->next_buf = src->alloc_buf;
       src->alloc_buf = NULL;
 
@@ -1011,11 +1001,11 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
     case DVDNAV_STOP:
       /* End of the disc. EOS */
       dvdnav_reset (src->dvdnav);
-      ret = GST_FLOW_UNEXPECTED;
+      ret = GST_FLOW_EOS;
       break;
     case DVDNAV_STILL_FRAME:
     {
-      dvdnav_still_event_t *info = (dvdnav_still_event_t *) data;
+      dvdnav_still_event_t *info = (dvdnav_still_event_t *) mmap.data;
 
       if (!have_dvd_lock) {
         /* At a still frame but can't block, handle it later */
@@ -1042,7 +1032,8 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
         goto internal_error;
       break;
     case DVDNAV_CELL_CHANGE:{
-      dvdnav_cell_change_event_t *event = (dvdnav_cell_change_event_t *) data;
+      dvdnav_cell_change_event_t *event =
+          (dvdnav_cell_change_event_t *) mmap.data;
       GstMessage *message;
 
       src->pgc_duration = MPEGTIME_TO_GSTTIME (event->pgc_length);
@@ -1067,10 +1058,11 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
       break;
     }
     case DVDNAV_SPU_CLUT_CHANGE:
-      rsn_dvdsrc_prepare_clut_change_event (src, (const guint32 *) data);
+      rsn_dvdsrc_prepare_clut_change_event (src, (const guint32 *) mmap.data);
       break;
     case DVDNAV_VTS_CHANGE:{
-      dvdnav_vts_change_event_t *event = (dvdnav_vts_change_event_t *) data;
+      dvdnav_vts_change_event_t *event =
+          (dvdnav_vts_change_event_t *) mmap.data;
 
       if (dvdnav_is_domain_vmgm (src->dvdnav)) {
         src->vts_n = 0;
@@ -1089,7 +1081,7 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
     }
     case DVDNAV_AUDIO_STREAM_CHANGE:{
       dvdnav_audio_stream_change_event_t *event =
-          (dvdnav_audio_stream_change_event_t *) data;
+          (dvdnav_audio_stream_change_event_t *) mmap.data;
 
       rsn_dvdsrc_prepare_audio_stream_event (src,
           event->logical, event->physical);
@@ -1100,7 +1092,7 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
     }
     case DVDNAV_SPU_STREAM_CHANGE:{
       dvdnav_spu_stream_change_event_t *event =
-          (dvdnav_spu_stream_change_event_t *) data;
+          (dvdnav_spu_stream_change_event_t *) mmap.data;
       gint phys_track = event->physical_wide & 0x1f;
       gboolean forced_only = (event->physical_wide & 0x80) ? TRUE : FALSE;
 
@@ -1117,7 +1109,7 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
     }
     case DVDNAV_HIGHLIGHT:{
       GST_DEBUG_OBJECT (src, "highlight change event, button %d",
-          ((dvdnav_highlight_event_t *) data)->buttonN);
+          ((dvdnav_highlight_event_t *) mmap.data)->buttonN);
       rsn_dvdsrc_update_highlight (src);
       break;
     }
@@ -1131,6 +1123,9 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
       GST_WARNING_OBJECT (src, "Unknown dvdnav event %d", event);
       break;
   }
+  if (src->alloc_buf) {
+    gst_buffer_unmap (src->alloc_buf, &mmap);
+  }
 
   if (src->highlight_event && have_dvd_lock && src->in_playing) {
     GstEvent *hl_event = src->highlight_event;
@@ -1148,6 +1143,7 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
 /* ERRORS */
 read_error:
   {
+    gst_buffer_unmap (src->alloc_buf, &mmap);
     if (!rsn_descrambler_available ()) {
       GST_ELEMENT_ERROR (src, RESOURCE, READ,
           (_("Could not read DVD. This may be because the DVD is encrypted "
@@ -1163,6 +1159,7 @@ read_error:
   }
 internal_error:
   {
+    gst_buffer_unmap (src->alloc_buf, &mmap);
     GST_ELEMENT_ERROR (src, RESOURCE, READ, (_("Could not read DVD.")),
         ("Internal error processing DVD commands. Error: %s",
             dvdnav_err_to_string (src->dvdnav)));
@@ -1171,6 +1168,7 @@ internal_error:
 branching:
   {
     g_mutex_unlock (src->branch_lock);
+    gst_buffer_unmap (src->alloc_buf, &mmap);
     return GST_FLOW_FLUSHING;
   }
 }
@@ -1288,14 +1286,6 @@ rsn_dvdsrc_prepare_next_block (resinDvdSrc * src, gboolean have_dvd_lock)
   return ret;
 }
 
-static gboolean
-rsn_dvdsrc_check_get_range (GstBaseSrc * src)
-{
-  /* ResinDVD never operates in pull mode. There might be
-   * a reason to in the future though? */
-  return FALSE;
-}
-
 static GstFlowReturn
 rsn_dvdsrc_create (GstBaseSrc * bsrc, guint64 offset,
     guint length, GstBuffer ** outbuf)
@@ -1396,11 +1386,11 @@ rsn_dvdsrc_create (GstBaseSrc * bsrc, guint64 offset,
   }
 
   if (src->cur_end_ts != GST_CLOCK_TIME_NONE)
-    gst_segment_set_last_stop (segment, GST_FORMAT_TIME, src->cur_end_ts);
+    segment->position = src->cur_end_ts;
 
   if (tags) {
-    gst_element_found_tags_for_pad (GST_ELEMENT_CAST (src),
-        GST_BASE_SRC_PAD (src), tags);
+    GstEvent *tag_event = gst_event_new_tag (tags);
+    gst_pad_push_event (GST_BASE_SRC_PAD (src), tag_event);
     tags = NULL;
   }
   g_mutex_lock (src->dvd_lock);
@@ -2254,7 +2244,15 @@ rsn_dvdsrc_activate_nav_block (resinDvdSrc * src, GstBuffer * nav_buf)
 {
   int32_t forced_button;
 
-  navRead_PCI (&src->cur_pci, GST_BUFFER_DATA (nav_buf) + 0x2d);
+  {
+    GstMapInfo mmap;
+    gst_buffer_map (nav_buf, &mmap, GST_MAP_READ);
+
+    navRead_PCI (&src->cur_pci, mmap.data + 0x2d);
+
+    gst_buffer_unmap (nav_buf, &mmap);
+  }
+
   src->have_pci = TRUE;
 
   forced_button = src->cur_pci.hli.hl_gi.fosl_btnn & 0x3f;
@@ -2374,7 +2372,8 @@ rsn_dvdsrc_schedule_nav_cb (resinDvdSrc * src, RsnDvdPendingNav * next_nav)
       GST_TIME_ARGS (next_nav->running_ts));
 
   g_mutex_unlock (src->dvd_lock);
-  gst_clock_id_wait_async (src->nav_clock_id, rsn_dvdsrc_nav_clock_cb, src);
+  gst_clock_id_wait_async (src->nav_clock_id, rsn_dvdsrc_nav_clock_cb, src,
+      NULL);
   gst_object_unref (clock);
   g_mutex_lock (src->dvd_lock);
 }
@@ -2615,6 +2614,16 @@ rsn_dvdsrc_src_query (GstBaseSrc * basesrc, GstQuery * query)
         res = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
       break;
     }
+    case GST_QUERY_SCHEDULING:
+    {
+      /* Make sure we operate in pull mode */
+      gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEQUENTIAL, 1, -1,
+          0);
+      gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH);
+
+      res = TRUE;
+      break;
+    }
     default:
       res = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
       break;
@@ -2653,7 +2662,7 @@ rsn_dvdsrc_prepare_seek (GstBaseSrc * bsrc, GstEvent * event,
     /* Seeks in our internal formats are passed directly through to the do_seek
      * method. */
     gst_segment_init (segment, seek_format);
-    gst_segment_set_seek (segment, rate, seek_format, flags, cur_type, cur,
+    gst_segment_do_seek (segment, rate, seek_format, flags, cur_type, cur,
         stop_type, stop, &update);
 
     return TRUE;
@@ -2898,7 +2907,7 @@ rsn_dvdsrc_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
 
     segment->format = GST_FORMAT_TIME;
     /* The first TS output: */
-    segment->last_stop = segment->start = src->cur_start_ts;
+    segment->position = segment->start = src->cur_start_ts;
 
     /* time field = position is the 'logical' stream time here: */
     segment->time = 0;
index 2b78dfea960d0dc9330508eb4a87586d55a48efa..da0f73122b7d7b54a3b7b53e71777f83390ab3fe 100644 (file)
@@ -103,8 +103,6 @@ static void
 rsn_audiomunge_init (RsnAudioMunge * munge)
 {
   munge->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
-  gst_pad_set_setcaps_function (munge->sinkpad,
-      GST_DEBUG_FUNCPTR (rsn_audiomunge_set_caps));
   gst_pad_set_getcaps_function (munge->sinkpad,
       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
   gst_pad_set_chain_function (munge->sinkpad,
@@ -156,7 +154,6 @@ rsn_audiomunge_set_caps (GstPad * pad, GstCaps * caps)
 
   otherpad = (pad == munge->srcpad) ? munge->sinkpad : munge->srcpad;
 
-  ret = gst_pad_set_caps (otherpad, caps);
   gst_object_unref (munge);
   return ret;
 }
@@ -238,6 +235,14 @@ rsn_audiomunge_sink_event (GstPad * pad, GstEvent * event)
   RsnAudioMunge *munge = RSN_AUDIOMUNGE (gst_pad_get_parent (pad));
 
   switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_CAPS:
+    {
+      GstCaps *caps;
+
+      gst_event_parse_caps (event, &caps);
+      ret = gst_pad_set_caps (munge->src_pad, caps);
+      gst_event_unref (caps);
+    }
     case GST_EVENT_FLUSH_STOP:
       rsn_audiomunge_reset (munge);
       ret = gst_pad_push_event (munge->srcpad, event);
index 788857a3bec4557f48491a6ff382295d76a1444f..2c999d5e0319812cd1fcc811061aa13845466db6 100644 (file)
@@ -23,6 +23,8 @@
 #endif
 
 #include <string.h>
+#include <gst/video/video.h>
+#include <gst/audio/audio.h>
 
 #include "rsndec.h"
 
@@ -53,9 +55,9 @@ rsn_dec_class_init (RsnDecClass * klass)
 }
 
 static gboolean
-rsn_dec_sink_event (GstPad * pad, GstEvent * event)
+rsn_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
 {
-  RsnDec *self = RSN_DEC (gst_pad_get_parent (pad));
+  RsnDec *self = RSN_DEC (parent);
   gboolean ret = TRUE;
   const GstStructure *s = gst_event_get_structure (event);
   const gchar *name = (s ? gst_structure_get_name (s) : NULL);
@@ -63,9 +65,7 @@ rsn_dec_sink_event (GstPad * pad, GstEvent * event)
   if (name && g_str_equal (name, "application/x-gst-dvd"))
     ret = gst_pad_push_event (GST_PAD_CAST (self->srcpad), event);
   else
-    ret = self->sink_event_func (pad, event);
-
-  gst_object_unref (self);
+    ret = self->sink_event_func (pad, parent, event);
 
   return ret;
 }
@@ -246,10 +246,13 @@ _get_decoder_factories (gpointer arg)
   RsnDecFactoryFilterCtx ctx = { NULL, };
   GstCaps *raw;
   gboolean raw_audio;
+  GstRegistry *registry = gst_registry_get ();
 
   ctx.desired_caps = gst_pad_template_get_caps (templ);
 
-  raw = gst_caps_from_string ("audio/x-raw-float");
+  raw =
+      gst_caps_from_string
+      ("audio/x-raw,format=(string){ F32LE, F32BE, F64LE, F64BE }");
   raw_audio = gst_caps_can_intersect (raw, ctx.desired_caps);
   if (raw_audio) {
     GstCaps *sub = gst_caps_subtract (ctx.desired_caps, raw);
@@ -263,7 +266,7 @@ _get_decoder_factories (gpointer arg)
   ctx.decoder_caps = gst_caps_new_empty ();
   GST_DEBUG ("Finding factories for caps: %" GST_PTR_FORMAT, ctx.desired_caps);
 
-  factories = gst_default_registry_feature_filter (
+  factories = gst_registry_feature_filter (registry,
       (GstPluginFeatureFilter) rsndec_factory_filter, FALSE, &ctx);
 
   /* If these are audio caps, we add audioconvert, which is not a decoder,
@@ -274,7 +277,7 @@ _get_decoder_factories (gpointer arg)
     GstPluginFeature *feature;
     GST_DEBUG ("These are audio caps, adding audioconvert");
     feature =
-        gst_default_registry_find_feature ("audioconvert",
+        gst_registry_find_feature (registry, "audioconvert",
         GST_TYPE_ELEMENT_FACTORY);
     if (feature) {
       factories = g_list_append (factories, feature);
@@ -372,23 +375,14 @@ static GstStaticPadTemplate audio_sink_template =
     GST_STATIC_CAPS ("audio/mpeg,mpegversion=(int)1;"
         "audio/x-private1-lpcm;"
         "audio/x-private1-ac3;" "audio/ac3;" "audio/x-ac3;"
-        "audio/x-private1-dts; audio/x-raw-float")
+        "audio/x-private1-dts; audio/x-raw,format=(string)"
+        GST_AUDIO_FORMATS_ALL)
     );
 
 static GstStaticPadTemplate audio_src_template = GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("audio/x-raw-float, "
-        "rate = (int) [ 1, MAX ], "
-        "channels = (int) [ 1, MAX ], "
-        "endianness = (int) BYTE_ORDER, "
-        "width = (int) { 32, 64 }; "
-        "audio/x-raw-int, "
-        "rate = (int) [ 1, MAX ], "
-        "channels = (int) [ 1, MAX ], "
-        "endianness = (int) { 1234, 4321 },"
-        "width = (int) [ 1, 32 ], "
-        "depth = (int) [ 1, 32 ], " "signed = (boolean) { false, true }")
+    GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL))
     );
 
 G_DEFINE_TYPE (RsnAudioDec, rsn_audiodec, RSN_TYPE_DEC);
@@ -438,7 +432,7 @@ GST_STATIC_PAD_TEMPLATE ("sink",
 static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("video/x-raw-yuv")
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL))
     );
 
 G_DEFINE_TYPE (RsnVideoDec, rsn_videodec, RSN_TYPE_DEC);
diff --git a/ext/resindvd/rsninputselector.c b/ext/resindvd/rsninputselector.c
new file mode 100644 (file)
index 0000000..88bcc05
--- /dev/null
@@ -0,0 +1,1784 @@
+/* GStreamer input selector
+ * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com>
+ * Copyright (C) 2007 Wim Taymans <wim.taymans@gmail.com>
+ * Copyright (C) 2007 Andy Wingo <wingo@pobox.com>
+ * Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
+ * Copyright (C) 2011 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:element-input-selector
+ * @see_also: #GstOutputSelector
+ *
+ * Direct one out of N input streams to the output pad.
+ *
+ * The input pads are from a GstPad subclass and have additional
+ * properties, which users may find useful, namely:
+ *
+ * <itemizedlist>
+ * <listitem>
+ * "running-time": Running time of stream on pad (#gint64)
+ * </listitem>
+ * <listitem>
+ * "tags": The currently active tags on the pad (#GstTagList, boxed type)
+ * </listitem>
+ * <listitem>
+ * "active": If the pad is currently active (#gboolean)
+ * </listitem>
+ * <listitem>
+ * "always-ok" : Make an inactive pads return #GST_FLOW_OK instead of
+ * #GST_FLOW_NOT_LINKED
+ * </listitem>
+ * </itemizedlist>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "rsninputselector.h"
+
+#define DEBUG_CACHED_BUFFERS 0
+
+GST_DEBUG_CATEGORY_STATIC (input_selector_debug);
+#define GST_CAT_DEFAULT input_selector_debug
+
+#define GST_TYPE_INPUT_SELECTOR_SYNC_MODE (gst_input_selector_sync_mode_get_type())
+static GType
+gst_input_selector_sync_mode_get_type (void)
+{
+  static GType type = 0;
+  static const GEnumValue data[] = {
+    {GST_INPUT_SELECTOR_SYNC_MODE_ACTIVE_SEGMENT,
+          "Sync using the current active segment",
+        "active-segment"},
+    {GST_INPUT_SELECTOR_SYNC_MODE_CLOCK, "Sync using the clock", "clock"},
+    {0, NULL, NULL},
+  };
+
+  if (!type) {
+    type = g_enum_register_static ("RsnInputSelectorSyncMode", data);
+  }
+  return type;
+}
+
+#define GST_INPUT_SELECTOR_GET_LOCK(sel) (&((RsnInputSelector*)(sel))->lock)
+#define GST_INPUT_SELECTOR_GET_COND(sel) (&((RsnInputSelector*)(sel))->cond)
+#define GST_INPUT_SELECTOR_LOCK(sel) (g_mutex_lock (GST_INPUT_SELECTOR_GET_LOCK(sel)))
+#define GST_INPUT_SELECTOR_UNLOCK(sel) (g_mutex_unlock (GST_INPUT_SELECTOR_GET_LOCK(sel)))
+#define GST_INPUT_SELECTOR_WAIT(sel) (g_cond_wait (GST_INPUT_SELECTOR_GET_COND(sel), \
+                       GST_INPUT_SELECTOR_GET_LOCK(sel)))
+#define GST_INPUT_SELECTOR_BROADCAST(sel) (g_cond_broadcast (GST_INPUT_SELECTOR_GET_COND(sel)))
+
+static GstStaticPadTemplate gst_input_selector_sink_factory =
+GST_STATIC_PAD_TEMPLATE ("sink_%u",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate gst_input_selector_src_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS_ANY);
+
+enum
+{
+  PROP_0,
+  PROP_N_PADS,
+  PROP_ACTIVE_PAD,
+  PROP_SYNC_STREAMS,
+  PROP_SYNC_MODE,
+  PROP_CACHE_BUFFERS
+};
+
+#define DEFAULT_SYNC_STREAMS TRUE
+#define DEFAULT_SYNC_MODE GST_INPUT_SELECTOR_SYNC_MODE_ACTIVE_SEGMENT
+#define DEFAULT_CACHE_BUFFERS FALSE
+#define DEFAULT_PAD_ALWAYS_OK TRUE
+
+enum
+{
+  PROP_PAD_0,
+  PROP_PAD_RUNNING_TIME,
+  PROP_PAD_TAGS,
+  PROP_PAD_ACTIVE,
+  PROP_PAD_ALWAYS_OK
+};
+
+enum
+{
+  /* methods */
+  SIGNAL_BLOCK,
+  SIGNAL_SWITCH,
+  LAST_SIGNAL
+};
+static guint gst_input_selector_signals[LAST_SIGNAL] = { 0 };
+
+static void gst_input_selector_active_pad_changed (RsnInputSelector * sel,
+    GParamSpec * pspec, gpointer user_data);
+static inline gboolean gst_input_selector_is_active_sinkpad (RsnInputSelector *
+    sel, GstPad * pad);
+static GstPad *gst_input_selector_activate_sinkpad (RsnInputSelector * sel,
+    GstPad * pad);
+static gboolean gst_input_selector_set_active_pad (RsnInputSelector * self,
+    GstPad * pad);
+static GstPad *gst_input_selector_get_linked_pad (RsnInputSelector * sel,
+    GstPad * pad, gboolean strict);
+static gboolean forward_sticky_events (GstPad * sinkpad, GstEvent ** event,
+    gpointer user_data);
+
+#define GST_TYPE_SELECTOR_PAD \
+  (gst_selector_pad_get_type())
+#define GST_SELECTOR_PAD(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SELECTOR_PAD, RsnSelectorPad))
+#define GST_SELECTOR_PAD_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SELECTOR_PAD, RsnSelectorPadClass))
+#define GST_IS_SELECTOR_PAD(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SELECTOR_PAD))
+#define GST_IS_SELECTOR_PAD_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SELECTOR_PAD))
+#define GST_SELECTOR_PAD_CAST(obj) \
+  ((RsnSelectorPad *)(obj))
+
+typedef struct _RsnSelectorPad RsnSelectorPad;
+typedef struct _RsnSelectorPadClass RsnSelectorPadClass;
+typedef struct _RsnSelectorPadCachedBuffer RsnSelectorPadCachedBuffer;
+
+struct _RsnSelectorPad
+{
+  GstPad parent;
+
+  gboolean active;              /* when buffer have passed the pad */
+  gboolean pushed;              /* when buffer was pushed downstream since activation */
+  gboolean eos;                 /* when EOS has been received */
+  gboolean eos_sent;            /* when EOS was sent downstream */
+  gboolean discont;             /* after switching we create a discont */
+  gboolean flushing;            /* set after flush-start and before flush-stop */
+  gboolean always_ok;
+  GstTagList *tags;             /* last tags received on the pad */
+
+  GstClockTime position;        /* the current position in the segment */
+  GstSegment segment;           /* the current segment on the pad */
+  guint32 segment_seqnum;       /* sequence number of the current segment */
+
+  gboolean events_pending;      /* TRUE if sticky events need to be updated */
+
+  gboolean sending_cached_buffers;
+  GQueue *cached_buffers;
+};
+
+struct _RsnSelectorPadCachedBuffer
+{
+  GstBuffer *buffer;
+  GstSegment segment;
+};
+
+struct _RsnSelectorPadClass
+{
+  GstPadClass parent;
+};
+
+GType gst_selector_pad_get_type (void);
+static void gst_selector_pad_finalize (GObject * object);
+static void gst_selector_pad_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+static void gst_selector_pad_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+
+static gint64 gst_selector_pad_get_running_time (RsnSelectorPad * pad);
+static void gst_selector_pad_reset (RsnSelectorPad * pad);
+static gboolean gst_selector_pad_event (GstPad * pad, GstObject * parent,
+    GstEvent * event);
+static gboolean gst_selector_pad_query (GstPad * pad, GstObject * parent,
+    GstQuery * query);
+static GstIterator *gst_selector_pad_iterate_linked_pads (GstPad * pad,
+    GstObject * parent);
+static GstFlowReturn gst_selector_pad_chain (GstPad * pad, GstObject * parent,
+    GstBuffer * buf);
+static void gst_selector_pad_cache_buffer (RsnSelectorPad * selpad,
+    GstBuffer * buffer);
+static void gst_selector_pad_free_cached_buffers (RsnSelectorPad * selpad);
+
+G_DEFINE_TYPE (RsnSelectorPad, gst_selector_pad, GST_TYPE_PAD);
+
+static void
+gst_selector_pad_class_init (RsnSelectorPadClass * klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = (GObjectClass *) klass;
+
+  gobject_class->finalize = gst_selector_pad_finalize;
+
+  gobject_class->get_property = gst_selector_pad_get_property;
+  gobject_class->set_property = gst_selector_pad_set_property;
+
+  g_object_class_install_property (gobject_class, PROP_PAD_RUNNING_TIME,
+      g_param_spec_int64 ("running-time", "Running time",
+          "Running time of stream on pad", 0, G_MAXINT64, 0,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_class, PROP_PAD_TAGS,
+      g_param_spec_boxed ("tags", "Tags",
+          "The currently active tags on the pad", GST_TYPE_TAG_LIST,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_class, PROP_PAD_ACTIVE,
+      g_param_spec_boolean ("active", "Active",
+          "If the pad is currently active", FALSE,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+  /* FIXME: better property name? */
+  g_object_class_install_property (gobject_class, PROP_PAD_ALWAYS_OK,
+      g_param_spec_boolean ("always-ok", "Always OK",
+          "Make an inactive pad return OK instead of NOT_LINKED",
+          DEFAULT_PAD_ALWAYS_OK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+gst_selector_pad_init (RsnSelectorPad * pad)
+{
+  pad->always_ok = DEFAULT_PAD_ALWAYS_OK;
+  gst_selector_pad_reset (pad);
+}
+
+static void
+gst_selector_pad_finalize (GObject * object)
+{
+  RsnSelectorPad *pad;
+
+  pad = GST_SELECTOR_PAD_CAST (object);
+
+  if (pad->tags)
+    gst_tag_list_unref (pad->tags);
+  gst_selector_pad_free_cached_buffers (pad);
+
+  G_OBJECT_CLASS (gst_selector_pad_parent_class)->finalize (object);
+}
+
+static void
+gst_selector_pad_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  RsnSelectorPad *spad = GST_SELECTOR_PAD_CAST (object);
+
+  switch (prop_id) {
+    case PROP_PAD_ALWAYS_OK:
+      GST_OBJECT_LOCK (object);
+      spad->always_ok = g_value_get_boolean (value);
+      GST_OBJECT_UNLOCK (object);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_selector_pad_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  RsnSelectorPad *spad = GST_SELECTOR_PAD_CAST (object);
+
+  switch (prop_id) {
+    case PROP_PAD_RUNNING_TIME:
+      g_value_set_int64 (value, gst_selector_pad_get_running_time (spad));
+      break;
+    case PROP_PAD_TAGS:
+      GST_OBJECT_LOCK (object);
+      g_value_set_boxed (value, spad->tags);
+      GST_OBJECT_UNLOCK (object);
+      break;
+    case PROP_PAD_ACTIVE:
+    {
+      RsnInputSelector *sel;
+
+      sel = GST_INPUT_SELECTOR (gst_pad_get_parent (spad));
+      g_value_set_boolean (value, gst_input_selector_is_active_sinkpad (sel,
+              GST_PAD_CAST (spad)));
+      gst_object_unref (sel);
+      break;
+    }
+    case PROP_PAD_ALWAYS_OK:
+      GST_OBJECT_LOCK (object);
+      g_value_set_boolean (value, spad->always_ok);
+      GST_OBJECT_UNLOCK (object);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static gint64
+gst_selector_pad_get_running_time (RsnSelectorPad * pad)
+{
+  gint64 ret = 0;
+
+  GST_OBJECT_LOCK (pad);
+  if (pad->active) {
+    guint64 position = pad->position;
+    GstFormat format = pad->segment.format;
+
+    ret = gst_segment_to_running_time (&pad->segment, format, position);
+  }
+  GST_OBJECT_UNLOCK (pad);
+
+  GST_DEBUG_OBJECT (pad, "running time: %" GST_TIME_FORMAT
+      " segment: %" GST_SEGMENT_FORMAT, GST_TIME_ARGS (ret), &pad->segment);
+
+  return ret;
+}
+
+/* must be called with the SELECTOR_LOCK */
+static void
+gst_selector_pad_reset (RsnSelectorPad * pad)
+{
+  GST_OBJECT_LOCK (pad);
+  pad->active = FALSE;
+  pad->pushed = FALSE;
+  pad->eos = FALSE;
+  pad->eos_sent = FALSE;
+  pad->events_pending = FALSE;
+  pad->discont = FALSE;
+  pad->flushing = FALSE;
+  pad->position = GST_CLOCK_TIME_NONE;
+  gst_segment_init (&pad->segment, GST_FORMAT_UNDEFINED);
+  pad->sending_cached_buffers = FALSE;
+  gst_selector_pad_free_cached_buffers (pad);
+  GST_OBJECT_UNLOCK (pad);
+}
+
+static RsnSelectorPadCachedBuffer *
+gst_selector_pad_new_cached_buffer (RsnSelectorPad * selpad, GstBuffer * buffer)
+{
+  RsnSelectorPadCachedBuffer *cached_buffer =
+      g_slice_new (RsnSelectorPadCachedBuffer);
+  cached_buffer->buffer = buffer;
+  cached_buffer->segment = selpad->segment;
+  return cached_buffer;
+}
+
+static void
+gst_selector_pad_free_cached_buffer (RsnSelectorPadCachedBuffer * cached_buffer)
+{
+  gst_buffer_unref (cached_buffer->buffer);
+  g_slice_free (RsnSelectorPadCachedBuffer, cached_buffer);
+}
+
+/* must be called with the SELECTOR_LOCK */
+static void
+gst_selector_pad_cache_buffer (RsnSelectorPad * selpad, GstBuffer * buffer)
+{
+  if (selpad->segment.format != GST_FORMAT_TIME) {
+    GST_DEBUG_OBJECT (selpad, "Buffer %p with segment not in time format, "
+        "not caching", buffer);
+    return;
+  }
+
+  GST_DEBUG_OBJECT (selpad, "Caching buffer %p", buffer);
+  if (!selpad->cached_buffers)
+    selpad->cached_buffers = g_queue_new ();
+  g_queue_push_tail (selpad->cached_buffers,
+      gst_selector_pad_new_cached_buffer (selpad, buffer));
+}
+
+/* must be called with the SELECTOR_LOCK */
+static void
+gst_selector_pad_free_cached_buffers (RsnSelectorPad * selpad)
+{
+  RsnSelectorPadCachedBuffer *cached_buffer;
+
+  if (!selpad->cached_buffers)
+    return;
+
+  GST_DEBUG_OBJECT (selpad, "Freeing cached buffers");
+  while ((cached_buffer = g_queue_pop_head (selpad->cached_buffers)))
+    gst_selector_pad_free_cached_buffer (cached_buffer);
+  g_queue_free (selpad->cached_buffers);
+  selpad->cached_buffers = NULL;
+}
+
+/* strictly get the linked pad from the sinkpad. If the pad is active we return
+ * the srcpad else we return NULL */
+static GstIterator *
+gst_selector_pad_iterate_linked_pads (GstPad * pad, GstObject * parent)
+{
+  RsnInputSelector *sel;
+  GstPad *otherpad;
+  GstIterator *it = NULL;
+  GValue val = { 0, };
+
+  sel = GST_INPUT_SELECTOR (parent);
+
+  otherpad = gst_input_selector_get_linked_pad (sel, pad, TRUE);
+  if (otherpad) {
+    g_value_init (&val, GST_TYPE_PAD);
+    g_value_set_object (&val, otherpad);
+    it = gst_iterator_new_single (GST_TYPE_PAD, &val);
+    g_value_unset (&val);
+    gst_object_unref (otherpad);
+  }
+
+  return it;
+}
+
+static gboolean
+gst_selector_pad_event (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+  gboolean res = TRUE;
+  gboolean forward;
+  gboolean new_tags = FALSE;
+  RsnInputSelector *sel;
+  RsnSelectorPad *selpad;
+  GstPad *prev_active_sinkpad;
+  GstPad *active_sinkpad;
+
+  sel = GST_INPUT_SELECTOR (parent);
+  selpad = GST_SELECTOR_PAD_CAST (pad);
+  GST_DEBUG_OBJECT (selpad, "received event %" GST_PTR_FORMAT, event);
+
+  GST_INPUT_SELECTOR_LOCK (sel);
+  prev_active_sinkpad = sel->active_sinkpad;
+  active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
+  GST_INPUT_SELECTOR_UNLOCK (sel);
+
+  if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) {
+    g_object_notify (G_OBJECT (sel), "active-pad");
+  }
+
+  GST_INPUT_SELECTOR_LOCK (sel);
+  active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
+
+  /* only forward if we are dealing with the active sinkpad */
+  forward = (pad == active_sinkpad);
+
+  switch (GST_EVENT_TYPE (event)) {
+    case GST_EVENT_FLUSH_START:
+      /* Unblock the pad if it's waiting */
+      selpad->flushing = TRUE;
+      GST_INPUT_SELECTOR_BROADCAST (sel);
+      break;
+    case GST_EVENT_FLUSH_STOP:
+      gst_selector_pad_reset (selpad);
+      break;
+    case GST_EVENT_SEGMENT:
+    {
+      gst_event_copy_segment (event, &selpad->segment);
+      selpad->segment_seqnum = gst_event_get_seqnum (event);
+
+      /* Update the position */
+      if (selpad->position == GST_CLOCK_TIME_NONE
+          || selpad->segment.position > selpad->position) {
+        selpad->position = selpad->segment.position;
+      } else if (selpad->position != GST_CLOCK_TIME_NONE
+          && selpad->position > selpad->segment.position) {
+        selpad->segment.position = selpad->position;
+
+        if (forward) {
+          gst_event_unref (event);
+          event = gst_event_new_segment (&selpad->segment);
+          gst_event_set_seqnum (event, selpad->segment_seqnum);
+        }
+      }
+      GST_DEBUG_OBJECT (pad, "configured SEGMENT %" GST_SEGMENT_FORMAT,
+          &selpad->segment);
+      break;
+    }
+    case GST_EVENT_TAG:
+    {
+      GstTagList *tags, *oldtags, *newtags;
+
+      gst_event_parse_tag (event, &tags);
+
+      oldtags = selpad->tags;
+
+      newtags = gst_tag_list_merge (oldtags, tags, GST_TAG_MERGE_REPLACE);
+      selpad->tags = newtags;
+      if (oldtags)
+        gst_tag_list_unref (oldtags);
+      GST_DEBUG_OBJECT (pad, "received tags %" GST_PTR_FORMAT, newtags);
+
+      new_tags = TRUE;
+      break;
+    }
+    case GST_EVENT_EOS:
+      selpad->eos = TRUE;
+
+      if (forward) {
+        selpad->eos_sent = TRUE;
+      } else {
+        RsnSelectorPad *active_selpad;
+
+        /* If the active sinkpad is in EOS state but EOS
+         * was not sent downstream this means that the pad
+         * got EOS before it was set as active pad and that
+         * the previously active pad got EOS after it was
+         * active
+         */
+        active_selpad = GST_SELECTOR_PAD (active_sinkpad);
+        forward = (active_selpad->eos && !active_selpad->eos_sent);
+        active_selpad->eos_sent = TRUE;
+      }
+      GST_DEBUG_OBJECT (pad, "received EOS");
+      break;
+    case GST_EVENT_CUSTOM_DOWNSTREAM:
+    {
+      const GstStructure *structure = gst_event_get_structure (event);
+      if (structure != NULL &&
+          gst_structure_has_name (structure, "application/x-gst-dvd")) {
+        const char *type = gst_structure_get_string (structure, "event");
+        if (strcmp (type, "select-pad") == 0) {
+          gst_input_selector_set_active_pad (sel, pad);
+          forward = FALSE;
+        }
+      }
+    }
+      break;
+
+    default:
+      break;
+  }
+  GST_INPUT_SELECTOR_UNLOCK (sel);
+  if (new_tags)
+    g_object_notify (G_OBJECT (selpad), "tags");
+  if (forward) {
+    GST_DEBUG_OBJECT (pad, "forwarding event");
+    res = gst_pad_push_event (sel->srcpad, event);
+  } else {
+    /* If we aren't forwarding the event because the pad is not the
+     * active_sinkpad, then set the flag on the pad
+     * that says a segment needs sending if/when that pad is activated.
+     * For all other cases, we send the event immediately, which makes
+     * sparse streams and other segment updates work correctly downstream.
+     */
+    if (GST_EVENT_IS_STICKY (event))
+      selpad->events_pending = TRUE;
+    gst_event_unref (event);
+  }
+
+  return res;
+}
+
+static gboolean
+gst_selector_pad_query (GstPad * pad, GstObject * parent, GstQuery * query)
+{
+  gboolean res = FALSE;
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_ALLOCATION:{
+      GstPad *active_sinkpad;
+      RsnInputSelector *sel = GST_INPUT_SELECTOR (parent);
+
+      /* Only do the allocation query for the active sinkpad,
+       * after switching a reconfigure event is sent and upstream
+       * should reconfigure and do a new allocation query
+       */
+      if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) {
+        GST_INPUT_SELECTOR_LOCK (sel);
+        active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
+        GST_INPUT_SELECTOR_UNLOCK (sel);
+
+        if (pad != active_sinkpad) {
+          res = FALSE;
+          goto done;
+        }
+      }
+    }
+      /* fall through */
+    default:
+      res = gst_pad_query_default (pad, parent, query);
+      break;
+  }
+
+done:
+  return res;
+}
+
+/* must be called with the SELECTOR_LOCK, will block while the pad is blocked 
+ * or return TRUE when flushing */
+static gboolean
+gst_input_selector_wait (RsnInputSelector * self, RsnSelectorPad * pad)
+{
+  while (self->blocked && !self->flushing && !pad->flushing) {
+    /* we can be unlocked here when we are shutting down (flushing) or when we
+     * get unblocked */
+    GST_INPUT_SELECTOR_WAIT (self);
+  }
+  return self->flushing;
+}
+
+/* must be called without the SELECTOR_LOCK, will wait until the running time
+ * of the active pad is after this pad or return TRUE when flushing */
+static gboolean
+gst_input_selector_wait_running_time (RsnInputSelector * sel,
+    RsnSelectorPad * selpad, GstBuffer * buf)
+{
+  GstSegment *seg;
+
+  GST_DEBUG_OBJECT (selpad, "entering wait for buffer %p", buf);
+
+  /* If we have no valid timestamp we can't sync this buffer */
+  if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
+    GST_DEBUG_OBJECT (selpad, "leaving wait for buffer with "
+        "invalid timestamp");
+    return FALSE;
+  }
+
+  seg = &selpad->segment;
+
+  /* Wait until
+   *   a) this is the active pad
+   *   b) the pad or the selector is flushing
+   *   c) the selector is not blocked
+   *   d) the buffer running time is before the current running time
+   *      (either active-seg or clock, depending on sync-mode)
+   */
+
+  GST_INPUT_SELECTOR_LOCK (sel);
+  while (TRUE) {
+    GstPad *active_sinkpad;
+    RsnSelectorPad *active_selpad;
+    GstClock *clock;
+    gint64 cur_running_time;
+    GstClockTime running_time;
+
+    active_sinkpad =
+        gst_input_selector_activate_sinkpad (sel, GST_PAD_CAST (selpad));
+    active_selpad = GST_SELECTOR_PAD_CAST (active_sinkpad);
+
+    if (seg->format != GST_FORMAT_TIME) {
+      GST_DEBUG_OBJECT (selpad,
+          "Not waiting because we don't have a TIME segment");
+      GST_INPUT_SELECTOR_UNLOCK (sel);
+      return FALSE;
+    }
+
+    running_time = GST_BUFFER_TIMESTAMP (buf);
+    /* If possible try to get the running time at the end of the buffer */
+    if (GST_BUFFER_DURATION_IS_VALID (buf))
+      running_time += GST_BUFFER_DURATION (buf);
+    /* Only use the segment to convert to running time if the segment is
+     * in TIME format, otherwise do our best to try to sync */
+    if (GST_CLOCK_TIME_IS_VALID (seg->stop)) {
+      if (running_time > seg->stop) {
+        running_time = seg->stop;
+      }
+    }
+    running_time =
+        gst_segment_to_running_time (seg, GST_FORMAT_TIME, running_time);
+    /* If this is outside the segment don't sync */
+    if (running_time == -1) {
+      GST_DEBUG_OBJECT (selpad,
+          "Not waiting because buffer is outside segment");
+      GST_INPUT_SELECTOR_UNLOCK (sel);
+      return FALSE;
+    }
+
+    cur_running_time = GST_CLOCK_TIME_NONE;
+    if (sel->sync_mode == GST_INPUT_SELECTOR_SYNC_MODE_CLOCK) {
+      clock = gst_element_get_clock (GST_ELEMENT_CAST (sel));
+      if (clock) {
+        GstClockTime base_time;
+
+        cur_running_time = gst_clock_get_time (clock);
+        base_time = gst_element_get_base_time (GST_ELEMENT_CAST (sel));
+        if (base_time <= cur_running_time)
+          cur_running_time -= base_time;
+        else
+          cur_running_time = 0;
+      }
+    } else {
+      GstSegment *active_seg;
+
+      active_seg = &active_selpad->segment;
+
+      /* If the active segment is configured but not to time format
+       * we can't do any syncing at all */
+      if (active_seg->format != GST_FORMAT_TIME
+          && active_seg->format != GST_FORMAT_UNDEFINED) {
+        GST_DEBUG_OBJECT (selpad,
+            "Not waiting because active segment isn't in TIME format");
+        GST_INPUT_SELECTOR_UNLOCK (sel);
+        return FALSE;
+      }
+
+      /* Get active pad's running time, if no configured segment yet keep at -1 */
+      if (active_seg->format == GST_FORMAT_TIME)
+        cur_running_time = gst_segment_to_running_time (active_seg,
+            GST_FORMAT_TIME, active_seg->position);
+    }
+
+    if (selpad != active_selpad && !sel->flushing && !selpad->flushing &&
+        (sel->blocked || cur_running_time == -1
+            || running_time >= cur_running_time)) {
+      if (!sel->blocked) {
+        GST_DEBUG_OBJECT (selpad,
+            "Waiting for active streams to advance. %" GST_TIME_FORMAT " >= %"
+            GST_TIME_FORMAT, GST_TIME_ARGS (running_time),
+            GST_TIME_ARGS (cur_running_time));
+      } else
+        GST_DEBUG_OBJECT (selpad, "Waiting for selector to unblock");
+
+      GST_INPUT_SELECTOR_WAIT (sel);
+    } else {
+      GST_INPUT_SELECTOR_UNLOCK (sel);
+      break;
+    }
+  }
+
+  /* Return TRUE if the selector or the pad is flushing */
+  return (sel->flushing || selpad->flushing);
+}
+
+static gboolean
+forward_sticky_events (GstPad * sinkpad, GstEvent ** event, gpointer user_data)
+{
+  RsnInputSelector *sel = GST_INPUT_SELECTOR (user_data);
+
+  if (GST_EVENT_TYPE (*event) == GST_EVENT_SEGMENT) {
+    GstSegment *seg = &GST_SELECTOR_PAD (sinkpad)->segment;
+    GstEvent *e;
+
+    e = gst_event_new_segment (seg);
+    gst_event_set_seqnum (e, GST_SELECTOR_PAD_CAST (sinkpad)->segment_seqnum);
+
+    gst_pad_push_event (sel->srcpad, e);
+  } else if (GST_EVENT_TYPE (*event) != GST_EVENT_STREAM_START) {
+    GST_WARNING ("Pushing event %" GST_PTR_FORMAT, *event);
+    gst_pad_push_event (sel->srcpad, gst_event_ref (*event));
+  }
+
+  return TRUE;
+}
+
+#if DEBUG_CACHED_BUFFERS
+static void
+gst_input_selector_debug_cached_buffers (RsnInputSelector * sel)
+{
+  GList *walk;
+
+  for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk; walk = g_list_next (walk)) {
+    RsnSelectorPad *selpad;
+    GString *timestamps;
+    gchar *str;
+    int i;
+
+    selpad = GST_SELECTOR_PAD_CAST (walk->data);
+    if (!selpad->cached_buffers) {
+      GST_DEBUG_OBJECT (selpad, "Cached buffers timestamps: <none>");
+      continue;
+    }
+
+    timestamps = g_string_new ("Cached buffers timestamps:");
+    for (i = 0; i < selpad->cached_buffers->length; ++i) {
+      RsnSelectorPadCachedBuffer *cached_buffer;
+
+      cached_buffer = g_queue_peek_nth (selpad->cached_buffers, i);
+      g_string_append_printf (timestamps, " %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (cached_buffer->buffer)));
+    }
+    str = g_string_free (timestamps, FALSE);
+    GST_DEBUG_OBJECT (selpad, str);
+    g_free (str);
+  }
+}
+#endif
+
+/* must be called with the SELECTOR_LOCK */
+static void
+gst_input_selector_cleanup_old_cached_buffers (RsnInputSelector * sel,
+    GstPad * pad)
+{
+  GstClock *clock;
+  gint64 cur_running_time;
+  GList *walk;
+
+  cur_running_time = GST_CLOCK_TIME_NONE;
+  if (sel->sync_mode == GST_INPUT_SELECTOR_SYNC_MODE_CLOCK) {
+    clock = gst_element_get_clock (GST_ELEMENT_CAST (sel));
+    if (clock) {
+      GstClockTime base_time;
+
+      cur_running_time = gst_clock_get_time (clock);
+      base_time = gst_element_get_base_time (GST_ELEMENT_CAST (sel));
+      if (base_time <= cur_running_time)
+        cur_running_time -= base_time;
+      else
+        cur_running_time = 0;
+    }
+  } else {
+    GstPad *active_sinkpad;
+    RsnSelectorPad *active_selpad;
+    GstSegment *active_seg;
+
+    active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
+    active_selpad = GST_SELECTOR_PAD_CAST (active_sinkpad);
+    active_seg = &active_selpad->segment;
+
+    /* Get active pad's running time, if no configured segment yet keep at -1 */
+    if (active_seg->format == GST_FORMAT_TIME)
+      cur_running_time = gst_segment_to_running_time (active_seg,
+          GST_FORMAT_TIME, active_seg->position);
+  }
+
+  if (!GST_CLOCK_TIME_IS_VALID (cur_running_time))
+    return;
+
+  GST_DEBUG_OBJECT (sel, "Cleaning up old cached buffers");
+  for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk; walk = g_list_next (walk)) {
+    RsnSelectorPad *selpad;
+    GstSegment *seg;
+    RsnSelectorPadCachedBuffer *cached_buffer;
+    GSList *maybe_remove;
+    guint queue_position;
+
+    selpad = GST_SELECTOR_PAD_CAST (walk->data);
+    if (!selpad->cached_buffers)
+      continue;
+
+    seg = &selpad->segment;
+
+    maybe_remove = NULL;
+    queue_position = 0;
+    while ((cached_buffer = g_queue_peek_nth (selpad->cached_buffers,
+                queue_position))) {
+      GstBuffer *buffer = cached_buffer->buffer;
+      GstClockTime running_time;
+      GSList *l;
+
+      /* If we have no valid timestamp we can't sync this buffer */
+      if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
+        maybe_remove = g_slist_append (maybe_remove, cached_buffer);
+        queue_position = g_slist_length (maybe_remove);
+        continue;
+      }
+
+      /* the buffer is still valid if its duration is valid and the
+       * timestamp + duration is >= time, or if its duration is invalid
+       * and the timestamp is >= time */
+      running_time = GST_BUFFER_TIMESTAMP (buffer);
+      /* If possible try to get the running time at the end of the buffer */
+      if (GST_BUFFER_DURATION_IS_VALID (buffer))
+        running_time += GST_BUFFER_DURATION (buffer);
+      /* Only use the segment to convert to running time if the segment is
+       * in TIME format, otherwise do our best to try to sync */
+      if (GST_CLOCK_TIME_IS_VALID (seg->stop)) {
+        if (running_time > seg->stop) {
+          running_time = seg->stop;
+        }
+      }
+      running_time =
+          gst_segment_to_running_time (seg, GST_FORMAT_TIME, running_time);
+
+      GST_DEBUG_OBJECT (selpad,
+          "checking if buffer %p running time=%" GST_TIME_FORMAT
+          " >= stream time=%" GST_TIME_FORMAT, buffer,
+          GST_TIME_ARGS (running_time), GST_TIME_ARGS (cur_running_time));
+      if (running_time >= cur_running_time) {
+        break;
+      }
+
+      GST_DEBUG_OBJECT (selpad, "Removing old cached buffer %p", buffer);
+      g_queue_pop_nth (selpad->cached_buffers, queue_position);
+      gst_selector_pad_free_cached_buffer (cached_buffer);
+
+      for (l = maybe_remove; l != NULL; l = g_slist_next (l)) {
+        /* A buffer after some invalid buffers was removed, it means the invalid buffers
+         * are old, lets also remove them */
+        cached_buffer = l->data;
+        g_queue_remove (selpad->cached_buffers, cached_buffer);
+        gst_selector_pad_free_cached_buffer (cached_buffer);
+      }
+
+      g_slist_free (maybe_remove);
+      maybe_remove = NULL;
+      queue_position = 0;
+    }
+
+    g_slist_free (maybe_remove);
+    maybe_remove = NULL;
+
+    if (g_queue_is_empty (selpad->cached_buffers)) {
+      g_queue_free (selpad->cached_buffers);
+      selpad->cached_buffers = NULL;
+    }
+  }
+
+#if DEBUG_CACHED_BUFFERS
+  gst_input_selector_debug_cached_buffers (sel);
+#endif
+}
+
+static GstFlowReturn
+gst_selector_pad_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
+{
+  RsnInputSelector *sel;
+  GstFlowReturn res;
+  GstPad *active_sinkpad;
+  GstPad *prev_active_sinkpad;
+  RsnSelectorPad *selpad;
+  GstClockTime start_time;
+
+  sel = GST_INPUT_SELECTOR (parent);
+  selpad = GST_SELECTOR_PAD_CAST (pad);
+
+  GST_DEBUG_OBJECT (selpad,
+      "entering chain for buf %p with timestamp %" GST_TIME_FORMAT, buf,
+      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+
+  GST_INPUT_SELECTOR_LOCK (sel);
+  /* wait or check for flushing */
+  if (gst_input_selector_wait (sel, selpad)) {
+    GST_INPUT_SELECTOR_UNLOCK (sel);
+    goto flushing;
+  }
+
+  GST_LOG_OBJECT (pad, "getting active pad");
+
+  prev_active_sinkpad = sel->active_sinkpad;
+  active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
+
+  /* In sync mode wait until the active pad has advanced
+   * after the running time of the current buffer */
+  if (sel->sync_streams) {
+    /* call chain for each cached buffer if we are not the active pad
+     * or if we are the active pad but didn't push anything yet. */
+    if (active_sinkpad != pad || !selpad->pushed) {
+      /* no need to check for sel->cache_buffers as selpad->cached_buffers
+       * will only be valid if cache_buffers is TRUE */
+      if (selpad->cached_buffers && !selpad->sending_cached_buffers) {
+        RsnSelectorPadCachedBuffer *cached_buffer;
+        GstSegment saved_segment;
+
+        saved_segment = selpad->segment;
+
+        selpad->sending_cached_buffers = TRUE;
+        while (!sel->flushing && !selpad->flushing &&
+            (cached_buffer = g_queue_pop_head (selpad->cached_buffers))) {
+          GST_DEBUG_OBJECT (pad, "Cached buffers found, "
+              "invoking chain for cached buffer %p", cached_buffer->buffer);
+
+          selpad->segment = cached_buffer->segment;
+          selpad->events_pending = TRUE;
+          GST_INPUT_SELECTOR_UNLOCK (sel);
+          gst_selector_pad_chain (pad, parent, cached_buffer->buffer);
+          GST_INPUT_SELECTOR_LOCK (sel);
+
+          /* we may have cleaned up the queue in the meantime because of
+           * old buffers */
+          if (!selpad->cached_buffers) {
+            break;
+          }
+        }
+        selpad->sending_cached_buffers = FALSE;
+
+        /* all cached buffers sent, restore segment for current buffer */
+        selpad->segment = saved_segment;
+        selpad->events_pending = TRUE;
+
+        /* Might have changed while calling chain for cached buffers */
+        active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
+      }
+    }
+
+    if (active_sinkpad != pad) {
+      GST_INPUT_SELECTOR_UNLOCK (sel);
+      if (gst_input_selector_wait_running_time (sel, selpad, buf))
+        goto flushing;
+      GST_INPUT_SELECTOR_LOCK (sel);
+    }
+
+    /* Might have changed while waiting */
+    active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
+  }
+
+  /* update the segment on the srcpad */
+  start_time = GST_BUFFER_TIMESTAMP (buf);
+  if (GST_CLOCK_TIME_IS_VALID (start_time)) {
+    GST_LOG_OBJECT (pad, "received start time %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (start_time));
+    if (GST_BUFFER_DURATION_IS_VALID (buf))
+      GST_LOG_OBJECT (pad, "received end time %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (start_time + GST_BUFFER_DURATION (buf)));
+
+    GST_OBJECT_LOCK (pad);
+    selpad->position = start_time;
+    selpad->segment.position = start_time;
+    GST_OBJECT_UNLOCK (pad);
+  }
+
+  /* Ignore buffers from pads except the selected one */
+  if (pad != active_sinkpad)
+    goto ignore;
+
+  /* Tell all non-active pads that we advanced the running time */
+  if (sel->sync_streams)
+    GST_INPUT_SELECTOR_BROADCAST (sel);
+
+  GST_INPUT_SELECTOR_UNLOCK (sel);
+
+  if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) {
+    g_object_notify (G_OBJECT (sel), "active-pad");
+  }
+
+  /* if we have a pending events, push them now */
+  if (G_UNLIKELY (prev_active_sinkpad != active_sinkpad
+          || selpad->events_pending)) {
+    gst_pad_sticky_events_foreach (GST_PAD_CAST (selpad), forward_sticky_events,
+        sel);
+    selpad->events_pending = FALSE;
+  }
+
+  if (selpad->discont) {
+    buf = gst_buffer_make_writable (buf);
+
+    GST_DEBUG_OBJECT (pad, "Marking discont buffer %p", buf);
+    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
+    selpad->discont = FALSE;
+  }
+
+  /* forward */
+  GST_LOG_OBJECT (pad, "Forwarding buffer %p with timestamp %" GST_TIME_FORMAT,
+      buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
+
+  res = gst_pad_push (sel->srcpad, gst_buffer_ref (buf));
+  GST_LOG_OBJECT (pad, "Buffer %p forwarded result=%d", buf, res);
+
+  GST_INPUT_SELECTOR_LOCK (sel);
+
+  if (sel->sync_streams && sel->cache_buffers) {
+    /* Might have changed while pushing */
+    active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad);
+    /* only set pad to pushed if we are still the active pad */
+    if (active_sinkpad == pad)
+      selpad->pushed = TRUE;
+
+    /* cache buffer as we may need it again if we change pads */
+    gst_selector_pad_cache_buffer (selpad, buf);
+    gst_input_selector_cleanup_old_cached_buffers (sel, pad);
+  } else {
+    selpad->pushed = TRUE;
+    gst_buffer_unref (buf);
+  }
+  GST_INPUT_SELECTOR_UNLOCK (sel);
+
+done:
+  return res;
+
+  /* dropped buffers */
+ignore:
+  {
+    gboolean active_pad_pushed = GST_SELECTOR_PAD_CAST (active_sinkpad)->pushed;
+
+    GST_DEBUG_OBJECT (pad, "Pad not active, discard buffer %p", buf);
+    /* when we drop a buffer, we're creating a discont on this pad */
+    selpad->discont = TRUE;
+    GST_INPUT_SELECTOR_UNLOCK (sel);
+    gst_buffer_unref (buf);
+
+    /* figure out what to return upstream */
+    GST_OBJECT_LOCK (selpad);
+    if (selpad->always_ok || !active_pad_pushed)
+      res = GST_FLOW_OK;
+    else
+      res = GST_FLOW_NOT_LINKED;
+    GST_OBJECT_UNLOCK (selpad);
+
+    goto done;
+  }
+flushing:
+  {
+    GST_DEBUG_OBJECT (pad, "We are flushing, discard buffer %p", buf);
+    gst_buffer_unref (buf);
+    res = GST_FLOW_FLUSHING;
+    goto done;
+  }
+}
+
+static void gst_input_selector_dispose (GObject * object);
+static void gst_input_selector_finalize (GObject * object);
+
+static void gst_input_selector_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_input_selector_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+
+static GstPad *gst_input_selector_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * unused, const GstCaps * caps);
+static void gst_input_selector_release_pad (GstElement * element, GstPad * pad);
+
+static GstStateChangeReturn gst_input_selector_change_state (GstElement *
+    element, GstStateChange transition);
+
+static gboolean gst_input_selector_event (GstPad * pad, GstObject * parent,
+    GstEvent * event);
+static gboolean gst_input_selector_query (GstPad * pad, GstObject * parent,
+    GstQuery * query);
+static gint64 gst_input_selector_block (RsnInputSelector * self);
+
+#define _do_init \
+    GST_DEBUG_CATEGORY_INIT (input_selector_debug, \
+        "rsninputselector", 0, "An input stream selector element");
+#define gst_input_selector_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (RsnInputSelector, gst_input_selector, GST_TYPE_ELEMENT,
+    _do_init);
+
+static void
+gst_input_selector_class_init (RsnInputSelectorClass * klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+
+  gobject_class->dispose = gst_input_selector_dispose;
+  gobject_class->finalize = gst_input_selector_finalize;
+
+  gobject_class->set_property = gst_input_selector_set_property;
+  gobject_class->get_property = gst_input_selector_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_N_PADS,
+      g_param_spec_uint ("n-pads", "Number of Pads",
+          "The number of sink pads", 0, G_MAXUINT, 0,
+          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD,
+      g_param_spec_object ("active-pad", "Active pad",
+          "The currently active sink pad", GST_TYPE_PAD,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * RsnInputSelector:sync-streams
+   *
+   * If set to %TRUE all inactive streams will be synced to the
+   * running time of the active stream or to the current clock.
+   *
+   * To make sure no buffers are dropped by input-selector
+   * that might be needed when switching the active pad,
+   * sync-mode should be set to "clock" and cache-buffers to TRUE.
+   */
+  g_object_class_install_property (gobject_class, PROP_SYNC_STREAMS,
+      g_param_spec_boolean ("sync-streams", "Sync Streams",
+          "Synchronize inactive streams to the running time of the active "
+          "stream or to the current clock",
+          DEFAULT_SYNC_STREAMS,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+          GST_PARAM_MUTABLE_READY));
+
+  /**
+   * RsnInputSelector:sync-mode
+   *
+   * Select how input-selector will sync buffers when in sync-streams mode.
+   *
+   * Note that when using the "active-segment" mode, the "active-segment" may
+   * be ahead of current clock time when switching the active pad, as the current
+   * active pad may have pushed more buffers than what was displayed/consumed,
+   * which may cause delays and some missing buffers.
+   */
+  g_object_class_install_property (gobject_class, PROP_SYNC_MODE,
+      g_param_spec_enum ("sync-mode", "Sync mode",
+          "Behavior in sync-streams mode", GST_TYPE_INPUT_SELECTOR_SYNC_MODE,
+          DEFAULT_SYNC_MODE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+          GST_PARAM_MUTABLE_READY));
+
+  /**
+   * RsnInputSelector:cache-buffers
+   *
+   * If set to %TRUE and RsnInputSelector:sync-streams is also set to %TRUE,
+   * the active pad will cache the buffers still considered valid (after current
+   * running time, see sync-mode) to avoid missing frames if/when the pad is
+   * reactivated.
+   *
+   * The active pad may push more buffers than what is currently displayed/consumed
+   * and when changing pads those buffers will be discarded and the only way to
+   * reactivate that pad without loosing the already consumed buffers is to enable cache.
+   */
+  g_object_class_install_property (gobject_class, PROP_CACHE_BUFFERS,
+      g_param_spec_boolean ("cache-buffers", "Cache Buffers",
+          "Cache buffers for active-pad",
+          DEFAULT_CACHE_BUFFERS,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+          GST_PARAM_MUTABLE_READY));
+
+  /**
+   * RsnInputSelector::block:
+   * @inputselector: the #RsnInputSelector
+   *
+   * Block all sink pads in preparation for a switch. Returns the stop time of
+   * the current switch segment, as a running time, or 0 if there is no current
+   * active pad or the current active pad never received data.
+   */
+  gst_input_selector_signals[SIGNAL_BLOCK] =
+      g_signal_new ("block", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+      G_STRUCT_OFFSET (RsnInputSelectorClass, block), NULL, NULL,
+      g_cclosure_marshal_generic, G_TYPE_INT64, 0);
+
+  gst_element_class_set_static_metadata (gstelement_class, "Input selector",
+      "Generic", "N-to-1 input stream selector",
+      "Julien Moutte <julien@moutte.net>, "
+      "Jan Schmidt <thaytan@mad.scientist.com>, "
+      "Wim Taymans <wim.taymans@gmail.com>");
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&gst_input_selector_sink_factory));
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&gst_input_selector_src_factory));
+
+  gstelement_class->request_new_pad = gst_input_selector_request_new_pad;
+  gstelement_class->release_pad = gst_input_selector_release_pad;
+  gstelement_class->change_state = gst_input_selector_change_state;
+
+  klass->block = GST_DEBUG_FUNCPTR (gst_input_selector_block);
+}
+
+static void
+gst_input_selector_init (RsnInputSelector * sel)
+{
+  sel->srcpad = gst_pad_new ("src", GST_PAD_SRC);
+  gst_pad_set_iterate_internal_links_function (sel->srcpad,
+      GST_DEBUG_FUNCPTR (gst_selector_pad_iterate_linked_pads));
+  gst_pad_set_query_function (sel->srcpad,
+      GST_DEBUG_FUNCPTR (gst_input_selector_query));
+  gst_pad_set_event_function (sel->srcpad,
+      GST_DEBUG_FUNCPTR (gst_input_selector_event));
+  GST_OBJECT_FLAG_SET (sel->srcpad, GST_PAD_FLAG_PROXY_CAPS);
+  gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad);
+  /* sinkpad management */
+  sel->active_sinkpad = NULL;
+  sel->padcount = 0;
+  sel->sync_streams = DEFAULT_SYNC_STREAMS;
+
+  g_mutex_init (&sel->lock);
+  g_cond_init (&sel->cond);
+  sel->blocked = FALSE;
+
+  /* lets give a change for downstream to do something on
+   * active-pad change before we start pushing new buffers */
+  g_signal_connect_data (sel, "notify::active-pad",
+      (GCallback) gst_input_selector_active_pad_changed, NULL,
+      NULL, G_CONNECT_AFTER);
+}
+
+static void
+gst_input_selector_dispose (GObject * object)
+{
+  RsnInputSelector *sel = GST_INPUT_SELECTOR (object);
+
+  if (sel->active_sinkpad) {
+    gst_object_unref (sel->active_sinkpad);
+    sel->active_sinkpad = NULL;
+  }
+  G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_input_selector_finalize (GObject * object)
+{
+  RsnInputSelector *sel = GST_INPUT_SELECTOR (object);
+
+  g_mutex_clear (&sel->lock);
+  g_cond_clear (&sel->cond);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/* this function must be called with the SELECTOR_LOCK. It returns TRUE when the
+ * active pad changed. */
+static gboolean
+gst_input_selector_set_active_pad (RsnInputSelector * self, GstPad * pad)
+{
+  RsnSelectorPad *old, *new;
+  GstPad **active_pad_p;
+
+  if (pad == self->active_sinkpad)
+    return FALSE;
+
+  old = GST_SELECTOR_PAD_CAST (self->active_sinkpad);
+  new = GST_SELECTOR_PAD_CAST (pad);
+
+  GST_DEBUG_OBJECT (self, "setting active pad to %s:%s",
+      GST_DEBUG_PAD_NAME (new));
+
+  if (old)
+    old->pushed = FALSE;
+  if (new)
+    new->pushed = FALSE;
+
+  /* Send a new SEGMENT event on the new pad next */
+  if (old != new && new)
+    new->events_pending = TRUE;
+
+  active_pad_p = &self->active_sinkpad;
+  gst_object_replace ((GstObject **) active_pad_p, GST_OBJECT_CAST (pad));
+
+  if (old && old != new)
+    gst_pad_push_event (GST_PAD_CAST (old), gst_event_new_reconfigure ());
+  if (new)
+    gst_pad_push_event (GST_PAD_CAST (new), gst_event_new_reconfigure ());
+
+  GST_DEBUG_OBJECT (self, "New active pad is %" GST_PTR_FORMAT,
+      self->active_sinkpad);
+
+  return TRUE;
+}
+
+static void
+gst_input_selector_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  RsnInputSelector *sel = GST_INPUT_SELECTOR (object);
+
+  switch (prop_id) {
+    case PROP_ACTIVE_PAD:
+    {
+      GstPad *pad;
+
+      pad = g_value_get_object (value);
+
+      GST_INPUT_SELECTOR_LOCK (sel);
+
+#if DEBUG_CACHED_BUFFERS
+      gst_input_selector_debug_cached_buffers (sel);
+#endif
+
+      gst_input_selector_set_active_pad (sel, pad);
+
+#if DEBUG_CACHED_BUFFERS
+      gst_input_selector_debug_cached_buffers (sel);
+#endif
+
+      GST_INPUT_SELECTOR_UNLOCK (sel);
+      break;
+    }
+    case PROP_SYNC_STREAMS:
+      GST_INPUT_SELECTOR_LOCK (sel);
+      sel->sync_streams = g_value_get_boolean (value);
+      GST_INPUT_SELECTOR_UNLOCK (sel);
+      break;
+    case PROP_SYNC_MODE:
+      GST_INPUT_SELECTOR_LOCK (sel);
+      sel->sync_mode = g_value_get_enum (value);
+      GST_INPUT_SELECTOR_UNLOCK (sel);
+      break;
+    case PROP_CACHE_BUFFERS:
+      GST_INPUT_SELECTOR_LOCK (object);
+      sel->cache_buffers = g_value_get_boolean (value);
+      GST_INPUT_SELECTOR_UNLOCK (object);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_input_selector_active_pad_changed (RsnInputSelector * sel,
+    GParamSpec * pspec, gpointer user_data)
+{
+  /* Wake up all non-active pads in sync mode, they might be
+   * the active pad now */
+  if (sel->sync_streams)
+    GST_INPUT_SELECTOR_BROADCAST (sel);
+}
+
+static void
+gst_input_selector_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  RsnInputSelector *sel = GST_INPUT_SELECTOR (object);
+
+  switch (prop_id) {
+    case PROP_N_PADS:
+      GST_INPUT_SELECTOR_LOCK (object);
+      g_value_set_uint (value, sel->n_pads);
+      GST_INPUT_SELECTOR_UNLOCK (object);
+      break;
+    case PROP_ACTIVE_PAD:
+      GST_INPUT_SELECTOR_LOCK (object);
+      g_value_set_object (value, sel->active_sinkpad);
+      GST_INPUT_SELECTOR_UNLOCK (object);
+      break;
+    case PROP_SYNC_STREAMS:
+      GST_INPUT_SELECTOR_LOCK (object);
+      g_value_set_boolean (value, sel->sync_streams);
+      GST_INPUT_SELECTOR_UNLOCK (object);
+      break;
+    case PROP_SYNC_MODE:
+      GST_INPUT_SELECTOR_LOCK (object);
+      g_value_set_enum (value, sel->sync_mode);
+      GST_INPUT_SELECTOR_UNLOCK (object);
+      break;
+    case PROP_CACHE_BUFFERS:
+      GST_INPUT_SELECTOR_LOCK (object);
+      g_value_set_boolean (value, sel->cache_buffers);
+      GST_INPUT_SELECTOR_UNLOCK (object);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static GstPad *
+gst_input_selector_get_linked_pad (RsnInputSelector * sel, GstPad * pad,
+    gboolean strict)
+{
+  GstPad *otherpad = NULL;
+
+  GST_INPUT_SELECTOR_LOCK (sel);
+  if (pad == sel->srcpad)
+    otherpad = sel->active_sinkpad;
+  else if (pad == sel->active_sinkpad || !strict)
+    otherpad = sel->srcpad;
+  if (otherpad)
+    gst_object_ref (otherpad);
+  GST_INPUT_SELECTOR_UNLOCK (sel);
+
+  return otherpad;
+}
+
+static gboolean
+gst_input_selector_event (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+  RsnInputSelector *sel;
+  gboolean result = FALSE;
+  GstIterator *iter;
+  gboolean done = FALSE;
+  GValue item = { 0, };
+  GstPad *eventpad;
+  GList *pushed_pads = NULL;
+
+  sel = GST_INPUT_SELECTOR (parent);
+  /* Send upstream events to all sinkpads */
+  iter = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (sel));
+
+  /* This is now essentially a copy of gst_pad_event_default_dispatch
+   * with a different iterator */
+  while (!done) {
+    switch (gst_iterator_next (iter, &item)) {
+      case GST_ITERATOR_OK:
+        eventpad = g_value_get_object (&item);
+
+        /* if already pushed,  skip */
+        if (g_list_find (pushed_pads, eventpad)) {
+          g_value_reset (&item);
+          break;
+        }
+
+        gst_event_ref (event);
+        result |= gst_pad_push_event (eventpad, event);
+
+        g_value_reset (&item);
+        break;
+      case GST_ITERATOR_RESYNC:
+        /* We don't reset the result here because we don't push the event
+         * again on pads that got the event already and because we need
+         * to consider the result of the previous pushes */
+        gst_iterator_resync (iter);
+        break;
+      case GST_ITERATOR_ERROR:
+        GST_ERROR_OBJECT (pad, "Could not iterate over sinkpads");
+        done = TRUE;
+        break;
+      case GST_ITERATOR_DONE:
+        done = TRUE;
+        break;
+    }
+  }
+  g_value_unset (&item);
+  gst_iterator_free (iter);
+
+  g_list_free (pushed_pads);
+
+  gst_event_unref (event);
+
+  return result;
+}
+
+/* query on the srcpad. We override this function because by default it will
+ * only forward the query to one random sinkpad */
+static gboolean
+gst_input_selector_query (GstPad * pad, GstObject * parent, GstQuery * query)
+{
+  gboolean res = FALSE;
+  RsnInputSelector *sel;
+
+  sel = GST_INPUT_SELECTOR (parent);
+
+  switch (GST_QUERY_TYPE (query)) {
+    case GST_QUERY_LATENCY:
+    {
+      GList *walk;
+      GstClockTime resmin, resmax;
+      gboolean reslive;
+
+      resmin = 0;
+      resmax = -1;
+      reslive = FALSE;
+
+      /* perform the query on all sinkpads and combine the results. We take the
+       * max of min and the min of max for the result latency. */
+      GST_INPUT_SELECTOR_LOCK (sel);
+      for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk;
+          walk = g_list_next (walk)) {
+        GstPad *sinkpad = GST_PAD_CAST (walk->data);
+
+        if (gst_pad_peer_query (sinkpad, query)) {
+          GstClockTime min, max;
+          gboolean live;
+
+          /* one query succeeded, we succeed too */
+          res = TRUE;
+
+          gst_query_parse_latency (query, &live, &min, &max);
+
+          GST_DEBUG_OBJECT (sinkpad,
+              "peer latency min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT
+              ", live %d", GST_TIME_ARGS (min), GST_TIME_ARGS (max), live);
+
+          if (live) {
+            if (min > resmin)
+              resmin = min;
+            if (resmax == -1)
+              resmax = max;
+            else if (max < resmax)
+              resmax = max;
+            if (reslive == FALSE)
+              reslive = live;
+          }
+        }
+      }
+      GST_INPUT_SELECTOR_UNLOCK (sel);
+      if (res) {
+        gst_query_set_latency (query, reslive, resmin, resmax);
+
+        GST_DEBUG_OBJECT (sel,
+            "total latency min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT
+            ", live %d", GST_TIME_ARGS (resmin), GST_TIME_ARGS (resmax),
+            reslive);
+      }
+
+      break;
+    }
+    default:
+      res = gst_pad_query_default (pad, parent, query);
+      break;
+  }
+
+  return res;
+}
+
+/* check if the pad is the active sinkpad */
+static inline gboolean
+gst_input_selector_is_active_sinkpad (RsnInputSelector * sel, GstPad * pad)
+{
+  gboolean res;
+
+  GST_INPUT_SELECTOR_LOCK (sel);
+  res = (pad == sel->active_sinkpad);
+  GST_INPUT_SELECTOR_UNLOCK (sel);
+
+  return res;
+}
+
+/* Get or create the active sinkpad, must be called with SELECTOR_LOCK */
+static GstPad *
+gst_input_selector_activate_sinkpad (RsnInputSelector * sel, GstPad * pad)
+{
+  GstPad *active_sinkpad;
+  RsnSelectorPad *selpad;
+
+  selpad = GST_SELECTOR_PAD_CAST (pad);
+
+  selpad->active = TRUE;
+  active_sinkpad = sel->active_sinkpad;
+  if (sel->active_sinkpad == NULL) {
+    GValue item = G_VALUE_INIT;
+    GstIterator *iter = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (sel));
+    GstIteratorResult ires;
+
+    while ((ires = gst_iterator_next (iter, &item)) == GST_ITERATOR_RESYNC)
+      gst_iterator_resync (iter);
+    if (ires == GST_ITERATOR_OK) {
+      /* If no pad is currently selected, we return the first usable pad to
+       * guarantee consistency */
+
+      active_sinkpad = sel->active_sinkpad = g_value_dup_object (&item);
+      g_value_reset (&item);
+      GST_DEBUG_OBJECT (sel, "Activating pad %s:%s",
+          GST_DEBUG_PAD_NAME (active_sinkpad));
+    } else
+      GST_WARNING_OBJECT (sel, "Couldn't find a default sink pad");
+    gst_iterator_free (iter);
+  }
+
+  return active_sinkpad;
+}
+
+static GstPad *
+gst_input_selector_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * unused, const GstCaps * caps)
+{
+  RsnInputSelector *sel;
+  gchar *name = NULL;
+  GstPad *sinkpad = NULL;
+
+  g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
+
+  sel = GST_INPUT_SELECTOR (element);
+
+  GST_INPUT_SELECTOR_LOCK (sel);
+
+  GST_LOG_OBJECT (sel, "Creating new pad %d", sel->padcount);
+  name = g_strdup_printf ("sink_%u", sel->padcount++);
+  sinkpad = g_object_new (GST_TYPE_SELECTOR_PAD,
+      "name", name, "direction", templ->direction, "template", templ, NULL);
+  g_free (name);
+
+  sel->n_pads++;
+
+  gst_pad_set_event_function (sinkpad,
+      GST_DEBUG_FUNCPTR (gst_selector_pad_event));
+  gst_pad_set_query_function (sinkpad,
+      GST_DEBUG_FUNCPTR (gst_selector_pad_query));
+  gst_pad_set_chain_function (sinkpad,
+      GST_DEBUG_FUNCPTR (gst_selector_pad_chain));
+  gst_pad_set_iterate_internal_links_function (sinkpad,
+      GST_DEBUG_FUNCPTR (gst_selector_pad_iterate_linked_pads));
+
+  GST_OBJECT_FLAG_SET (sinkpad, GST_PAD_FLAG_PROXY_CAPS);
+  GST_OBJECT_FLAG_SET (sinkpad, GST_PAD_FLAG_PROXY_ALLOCATION);
+  gst_pad_set_active (sinkpad, TRUE);
+  gst_element_add_pad (GST_ELEMENT (sel), sinkpad);
+  GST_INPUT_SELECTOR_UNLOCK (sel);
+
+  return sinkpad;
+}
+
+static void
+gst_input_selector_release_pad (GstElement * element, GstPad * pad)
+{
+  RsnInputSelector *sel;
+
+  sel = GST_INPUT_SELECTOR (element);
+  GST_LOG_OBJECT (sel, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+
+  GST_INPUT_SELECTOR_LOCK (sel);
+  /* if the pad was the active pad, makes us select a new one */
+  if (sel->active_sinkpad == pad) {
+    GST_DEBUG_OBJECT (sel, "Deactivating pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+    gst_object_unref (sel->active_sinkpad);
+    sel->active_sinkpad = NULL;
+  }
+  sel->n_pads--;
+
+  gst_pad_set_active (pad, FALSE);
+  gst_element_remove_pad (GST_ELEMENT (sel), pad);
+  GST_INPUT_SELECTOR_UNLOCK (sel);
+}
+
+static void
+gst_input_selector_reset (RsnInputSelector * sel)
+{
+  GList *walk;
+
+  GST_INPUT_SELECTOR_LOCK (sel);
+  /* clear active pad */
+  if (sel->active_sinkpad) {
+    gst_object_unref (sel->active_sinkpad);
+    sel->active_sinkpad = NULL;
+  }
+  /* reset each of our sinkpads state */
+  for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk; walk = g_list_next (walk)) {
+    RsnSelectorPad *selpad = GST_SELECTOR_PAD_CAST (walk->data);
+
+    gst_selector_pad_reset (selpad);
+
+    if (selpad->tags) {
+      gst_tag_list_unref (selpad->tags);
+      selpad->tags = NULL;
+    }
+  }
+  GST_INPUT_SELECTOR_UNLOCK (sel);
+}
+
+static GstStateChangeReturn
+gst_input_selector_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  RsnInputSelector *self = GST_INPUT_SELECTOR (element);
+  GstStateChangeReturn result;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      GST_INPUT_SELECTOR_LOCK (self);
+      self->blocked = FALSE;
+      self->flushing = FALSE;
+      GST_INPUT_SELECTOR_UNLOCK (self);
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      /* first unlock before we call the parent state change function, which
+       * tries to acquire the stream lock when going to ready. */
+      GST_INPUT_SELECTOR_LOCK (self);
+      self->blocked = FALSE;
+      self->flushing = TRUE;
+      GST_INPUT_SELECTOR_BROADCAST (self);
+      GST_INPUT_SELECTOR_UNLOCK (self);
+      break;
+    default:
+      break;
+  }
+
+  result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      gst_input_selector_reset (self);
+      break;
+    default:
+      break;
+  }
+
+  return result;
+}
+
+static gint64
+gst_input_selector_block (RsnInputSelector * self)
+{
+  gint64 ret = 0;
+  RsnSelectorPad *spad;
+
+  GST_INPUT_SELECTOR_LOCK (self);
+
+  if (self->blocked)
+    GST_WARNING_OBJECT (self, "switch already blocked");
+
+  self->blocked = TRUE;
+  spad = GST_SELECTOR_PAD_CAST (self->active_sinkpad);
+
+  if (spad)
+    ret = gst_selector_pad_get_running_time (spad);
+  else
+    GST_DEBUG_OBJECT (self, "no active pad while blocking");
+
+  GST_INPUT_SELECTOR_UNLOCK (self);
+
+  return ret;
+}
diff --git a/ext/resindvd/rsninputselector.h b/ext/resindvd/rsninputselector.h
new file mode 100644 (file)
index 0000000..d7d2a8d
--- /dev/null
@@ -0,0 +1,91 @@
+/* GStreamer
+ * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
+ *
+ * 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 __RSN_INPUT_SELECTOR_H__
+#define __RSN_INPUT_SELECTOR_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_INPUT_SELECTOR \
+  (gst_input_selector_get_type())
+#define GST_INPUT_SELECTOR(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_INPUT_SELECTOR, RsnInputSelector))
+#define GST_INPUT_SELECTOR_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_INPUT_SELECTOR, RsnInputSelectorClass))
+#define GST_IS_INPUT_SELECTOR(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_INPUT_SELECTOR))
+#define GST_IS_INPUT_SELECTOR_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_INPUT_SELECTOR))
+
+typedef struct _RsnInputSelector RsnInputSelector;
+typedef struct _RsnInputSelectorClass RsnInputSelectorClass;
+
+#define GST_INPUT_SELECTOR_GET_LOCK(sel) (&((RsnInputSelector*)(sel))->lock)
+#define GST_INPUT_SELECTOR_GET_COND(sel) (&((RsnInputSelector*)(sel))->cond)
+#define GST_INPUT_SELECTOR_LOCK(sel) (g_mutex_lock (GST_INPUT_SELECTOR_GET_LOCK(sel)))
+#define GST_INPUT_SELECTOR_UNLOCK(sel) (g_mutex_unlock (GST_INPUT_SELECTOR_GET_LOCK(sel)))
+#define GST_INPUT_SELECTOR_WAIT(sel) (g_cond_wait (GST_INPUT_SELECTOR_GET_COND(sel), \
+                       GST_INPUT_SELECTOR_GET_LOCK(sel)))
+#define GST_INPUT_SELECTOR_BROADCAST(sel) (g_cond_broadcast (GST_INPUT_SELECTOR_GET_COND(sel)))
+
+/**
+ * RsnInputSelectorSyncMode:
+ * @GST_INPUT_SELECTOR_SYNC_MODE_ACTIVE_SEGMENT: Sync using the current active segment.
+ * @GST_INPUT_SELECTOR_SYNC_MODE_CLOCK: Sync using the clock.
+ *
+ * The different ways that input-selector can behave when in sync-streams mode.
+ */
+typedef enum {
+  GST_INPUT_SELECTOR_SYNC_MODE_ACTIVE_SEGMENT,
+  GST_INPUT_SELECTOR_SYNC_MODE_CLOCK
+} RsnInputSelectorSyncMode;
+
+struct _RsnInputSelector {
+  GstElement element;
+
+  GstPad *srcpad;
+
+  GstPad *active_sinkpad;
+  guint n_pads;
+  guint padcount;
+  gboolean sync_streams;
+  RsnInputSelectorSyncMode sync_mode;
+  gboolean cache_buffers;
+
+  GMutex lock;
+  GCond cond;
+  gboolean blocked;
+  gboolean flushing;
+};
+
+struct _RsnInputSelectorClass {
+  GstElementClass parent_class;
+
+  gint64 (*block)      (RsnInputSelector *self);
+};
+
+G_GNUC_INTERNAL GType gst_input_selector_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_INPUT_SELECTOR_H__ */
diff --git a/ext/resindvd/rsnstreamselector.c b/ext/resindvd/rsnstreamselector.c
deleted file mode 100644 (file)
index d610677..0000000
+++ /dev/null
@@ -1,769 +0,0 @@
-/* GStreamer
- * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
- * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
- * Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com>
- * Copyright (C) 2007 Wim Taymans <wim.taymans@gmail.com>
- *
- * 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 "rsnstreamselector.h"
-
-GST_DEBUG_CATEGORY_STATIC (stream_selector_debug);
-#define GST_CAT_DEFAULT stream_selector_debug
-
-static GstStaticPadTemplate rsn_stream_selector_sink_factory =
-GST_STATIC_PAD_TEMPLATE ("sink_%u",
-    GST_PAD_SINK,
-    GST_PAD_REQUEST,
-    GST_STATIC_CAPS_ANY);
-
-static GstStaticPadTemplate rsn_stream_selector_src_factory =
-GST_STATIC_PAD_TEMPLATE ("src",
-    GST_PAD_SRC,
-    GST_PAD_ALWAYS,
-    GST_STATIC_CAPS_ANY);
-
-enum
-{
-  PROP_0,
-  PROP_N_PADS,
-  PROP_ACTIVE_PAD,
-  PROP_LAST
-};
-
-static gboolean rsn_stream_selector_is_active_sinkpad (RsnStreamSelector * sel,
-    GstPad * pad);
-static GstPad *rsn_stream_selector_get_active (RsnStreamSelector * sel,
-    GstPad * pad);
-static void rsn_stream_selector_set_active (RsnStreamSelector * sel,
-    GstPad * pad);
-static GstPad *rsn_stream_selector_get_linked_pad (GstPad * pad,
-    gboolean strict);
-
-#define RSN_TYPE_SELECTOR_PAD \
-  (gst_selector_pad_get_type())
-#define GST_SELECTOR_PAD(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST ((obj), RSN_TYPE_SELECTOR_PAD, RsnSelectorPad))
-#define GST_SELECTOR_PAD_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST ((klass), RSN_TYPE_SELECTOR_PAD, RsnSelectorPadClass))
-#define RSN_IS_SELECTOR_PAD(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RSN_TYPE_SELECTOR_PAD))
-#define RSN_IS_SELECTOR_PAD_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_TYPE ((klass), RSN_TYPE_SELECTOR_PAD))
-#define GST_SELECTOR_PAD_CAST(obj) \
-  ((RsnSelectorPad *)(obj))
-
-typedef struct _RsnSelectorPad RsnSelectorPad;
-typedef struct _RsnSelectorPadClass RsnSelectorPadClass;
-
-struct _RsnSelectorPad
-{
-  GstPad parent;
-
-  gboolean active;
-  gboolean eos;
-  GstSegment segment;
-  GstTagList *tags;
-};
-
-struct _RsnSelectorPadClass
-{
-  GstPadClass parent;
-};
-
-static void gst_selector_pad_class_init (RsnSelectorPadClass * klass);
-static void gst_selector_pad_init (RsnSelectorPad * pad);
-static void gst_selector_pad_finalize (GObject * object);
-
-static void gst_selector_pad_get_property (GObject * object,
-    guint prop_id, GValue * value, GParamSpec * pspec);
-
-static GstPadClass *selector_pad_parent_class = NULL;
-
-static void gst_selector_pad_reset (RsnSelectorPad * pad);
-static gboolean gst_selector_pad_event (GstPad * pad, GstEvent * event);
-static GstCaps *gst_selector_pad_getcaps (GstPad * pad);
-static GstFlowReturn gst_selector_pad_chain (GstPad * pad, GstBuffer * buf);
-static GstFlowReturn gst_selector_pad_bufferalloc (GstPad * pad,
-    guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
-
-enum
-{
-  PROP_PAD_0,
-  PROP_PAD_TAGS,
-  PROP_PAD_ACTIVE,
-  PROP_PAD_LAST
-};
-
-static GType
-gst_selector_pad_get_type (void)
-{
-  static GType selector_pad_type = 0;
-
-  if (!selector_pad_type) {
-    static const GTypeInfo selector_pad_info = {
-      sizeof (RsnSelectorPadClass),
-      NULL,
-      NULL,
-      (GClassInitFunc) gst_selector_pad_class_init,
-      NULL,
-      NULL,
-      sizeof (RsnSelectorPad),
-      0,
-      (GInstanceInitFunc) gst_selector_pad_init,
-    };
-
-    selector_pad_type =
-        g_type_register_static (GST_TYPE_PAD, "RsnSelectorPad",
-        &selector_pad_info, 0);
-  }
-  return selector_pad_type;
-}
-
-static void
-gst_selector_pad_class_init (RsnSelectorPadClass * klass)
-{
-  GObjectClass *gobject_class;
-
-  gobject_class = (GObjectClass *) klass;
-
-  selector_pad_parent_class = g_type_class_peek_parent (klass);
-
-  gobject_class->finalize = gst_selector_pad_finalize;
-  gobject_class->get_property =
-      GST_DEBUG_FUNCPTR (gst_selector_pad_get_property);
-
-  g_object_class_install_property (gobject_class, PROP_PAD_TAGS,
-      g_param_spec_boxed ("tags", "Tags",
-          "The currently active tags on the pad", GST_TYPE_TAG_LIST,
-          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-  g_object_class_install_property (gobject_class, PROP_PAD_ACTIVE,
-      g_param_spec_boolean ("active", "Active",
-          "If the pad is currently active", FALSE,
-          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-}
-
-static void
-gst_selector_pad_init (RsnSelectorPad * pad)
-{
-  gst_selector_pad_reset (pad);
-}
-
-static void
-gst_selector_pad_finalize (GObject * object)
-{
-  RsnSelectorPad *pad;
-
-  pad = GST_SELECTOR_PAD_CAST (object);
-
-  if (pad->tags)
-    gst_tag_list_unref (pad->tags);
-
-  G_OBJECT_CLASS (selector_pad_parent_class)->finalize (object);
-}
-
-static void
-gst_selector_pad_get_property (GObject * object,
-    guint prop_id, GValue * value, GParamSpec * pspec)
-{
-  RsnSelectorPad *pad;
-
-  pad = GST_SELECTOR_PAD (object);
-
-  switch (prop_id) {
-    case PROP_PAD_TAGS:
-      GST_OBJECT_LOCK (object);
-      g_value_set_boxed (value, pad->tags);
-      GST_OBJECT_UNLOCK (object);
-      break;
-    case PROP_PAD_ACTIVE:
-    {
-      RsnStreamSelector *sel;
-
-      sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
-      g_value_set_boolean (value, rsn_stream_selector_is_active_sinkpad (sel,
-              GST_PAD_CAST (pad)));
-      gst_object_unref (sel);
-      break;
-    }
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
-}
-
-static void
-gst_selector_pad_reset (RsnSelectorPad * pad)
-{
-  pad->active = FALSE;
-  pad->eos = FALSE;
-  gst_segment_init (&pad->segment, GST_FORMAT_UNDEFINED);
-}
-
-static gboolean
-gst_selector_pad_event (GstPad * pad, GstEvent * event)
-{
-  gboolean res = TRUE;
-  gboolean forward = TRUE;
-  RsnStreamSelector *sel;
-  RsnSelectorPad *selpad;
-  GstPad *active_sinkpad;
-
-  sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
-  selpad = GST_SELECTOR_PAD_CAST (pad);
-
-  /* only forward if we are dealing with the active sinkpad */
-  active_sinkpad = rsn_stream_selector_get_active (sel, pad);
-  forward = (active_sinkpad == pad);
-
-  switch (GST_EVENT_TYPE (event)) {
-    case GST_EVENT_FLUSH_STOP:
-      gst_selector_pad_reset (selpad);
-      break;
-    case GST_EVENT_NEWSEGMENT:
-    {
-      gboolean update;
-      GstFormat format;
-      gdouble rate, arate;
-      gint64 start, stop, time;
-
-      gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
-          &start, &stop, &time);
-
-      GST_DEBUG_OBJECT (selpad,
-          "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
-          "format %d, "
-          "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %"
-          G_GINT64_FORMAT, update, rate, arate, format, start, stop, time);
-
-      gst_segment_set_newsegment_full (&selpad->segment, update,
-          rate, arate, format, start, stop, time);
-      break;
-    }
-    case GST_EVENT_TAG:
-    {
-      GstTagList *tags;
-
-      GST_OBJECT_LOCK (selpad);
-      if (selpad->tags)
-        gst_tag_list_unref (selpad->tags);
-      gst_event_parse_tag (event, &tags);
-      if (tags)
-        tags = gst_tag_list_copy (tags);
-      selpad->tags = tags;
-      GST_DEBUG_OBJECT (sel, "received tags %" GST_PTR_FORMAT, selpad->tags);
-      GST_OBJECT_UNLOCK (selpad);
-      break;
-    }
-    case GST_EVENT_CUSTOM_DOWNSTREAM:
-    {
-      const GstStructure *structure = gst_event_get_structure (event);
-      if (structure != NULL &&
-          gst_structure_has_name (structure, "application/x-gst-dvd")) {
-        const char *type = gst_structure_get_string (structure, "event");
-        if (strcmp (type, "select-pad") == 0) {
-          rsn_stream_selector_set_active (sel, pad);
-          forward = FALSE;
-        }
-      }
-    }
-    case GST_EVENT_EOS:
-      selpad->eos = TRUE;
-      break;
-    default:
-      break;
-  }
-  if (forward)
-    res = gst_pad_push_event (sel->srcpad, event);
-  else
-    gst_event_unref (event);
-
-  gst_object_unref (sel);
-
-  return res;
-}
-
-static GstCaps *
-gst_selector_pad_getcaps (GstPad * pad)
-{
-  RsnStreamSelector *sel;
-  GstCaps *caps;
-
-  sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
-
-  GST_DEBUG_OBJECT (sel, "Getting caps of srcpad peer");
-  caps = gst_pad_peer_get_caps (sel->srcpad);
-  if (caps == NULL)
-    caps = gst_caps_new_any ();
-
-  gst_object_unref (sel);
-
-  return caps;
-}
-
-static GstFlowReturn
-gst_selector_pad_bufferalloc (GstPad * pad, guint64 offset,
-    guint size, GstCaps * caps, GstBuffer ** buf)
-{
-  RsnStreamSelector *sel;
-  GstFlowReturn result;
-  GstPad *active_sinkpad;
-
-  sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
-
-  active_sinkpad = rsn_stream_selector_get_active (sel, pad);
-
-  /* Fallback allocation for buffers from pads except the selected one */
-  if (pad != active_sinkpad) {
-    GST_DEBUG_OBJECT (sel,
-        "Pad %s:%s is not selected. Performing fallback allocation",
-        GST_DEBUG_PAD_NAME (pad));
-
-    *buf = NULL;
-    result = GST_FLOW_OK;
-  } else {
-    result = gst_pad_alloc_buffer (sel->srcpad, offset, size, caps, buf);
-
-    /* FIXME: HACK. If buffer alloc returns not-linked, perform a fallback
-     * allocation.  This should NOT be necessary, because playbin should
-     * properly block the source pad from running until it's finished hooking 
-     * everything up, but playbin needs refactoring first. */
-    if (result == GST_FLOW_NOT_LINKED) {
-      GST_DEBUG_OBJECT (sel,
-          "No peer pad yet - performing fallback allocation for pad %s:%s",
-          GST_DEBUG_PAD_NAME (pad));
-
-      *buf = NULL;
-      result = GST_FLOW_OK;
-    }
-  }
-
-  gst_object_unref (sel);
-
-  return result;
-}
-
-static GstFlowReturn
-gst_selector_pad_chain (GstPad * pad, GstBuffer * buf)
-{
-  RsnStreamSelector *sel;
-  GstFlowReturn res;
-  GstPad *active_sinkpad;
-  RsnSelectorPad *selpad;
-  GstClockTime timestamp;
-  GstSegment *seg;
-  gboolean discont;
-
-  sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
-  selpad = GST_SELECTOR_PAD_CAST (pad);
-  seg = &selpad->segment;
-
-  active_sinkpad = rsn_stream_selector_get_active (sel, pad);
-
-  timestamp = GST_BUFFER_TIMESTAMP (buf);
-  if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
-    GST_DEBUG_OBJECT (sel, "received timestamp %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (timestamp));
-    gst_segment_set_last_stop (seg, seg->format, timestamp);
-  }
-
-  /* Ignore buffers from pads except the selected one */
-  if (pad != active_sinkpad)
-    goto ignore;
-
-  /* If we just switched pads, mark a discont buffer */
-  GST_OBJECT_LOCK (sel);
-  discont = sel->mark_discont;
-  sel->mark_discont = FALSE;
-  GST_OBJECT_UNLOCK (sel);
-
-  if (discont) {
-    GST_DEBUG_OBJECT (sel, "Marking buffer discont due to pad switch");
-    buf = gst_buffer_make_metadata_writable (buf);
-    GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
-  }
-
-  /* forward */
-  GST_LOG_OBJECT (sel, "Forwarding buffer %p from pad %s:%s", buf,
-      GST_DEBUG_PAD_NAME (pad));
-  res = gst_pad_push (sel->srcpad, buf);
-done:
-  gst_object_unref (sel);
-  return res;
-  /* dropped buffers */
-ignore:
-  {
-    GST_DEBUG_OBJECT (sel, "Ignoring buffer %p from pad %s:%s",
-        buf, GST_DEBUG_PAD_NAME (pad));
-    gst_buffer_unref (buf);
-    res = GST_FLOW_NOT_LINKED;
-    goto done;
-  }
-}
-
-static void rsn_stream_selector_dispose (GObject * object);
-
-static void rsn_stream_selector_init (RsnStreamSelector * sel);
-static void rsn_stream_selector_base_init (RsnStreamSelectorClass * klass);
-static void rsn_stream_selector_class_init (RsnStreamSelectorClass * klass);
-
-static void rsn_stream_selector_set_property (GObject * object,
-    guint prop_id, const GValue * value, GParamSpec * pspec);
-static void rsn_stream_selector_get_property (GObject * object,
-    guint prop_id, GValue * value, GParamSpec * pspec);
-
-static GstPad *rsn_stream_selector_request_new_pad (GstElement * element,
-    GstPadTemplate * templ, const gchar * unused);
-static void rsn_stream_selector_release_pad (GstElement * element,
-    GstPad * pad);
-static GstIterator *rsn_stream_selector_iterate_linked_pads (GstPad * pad);
-static GstCaps *rsn_stream_selector_getcaps (GstPad * pad);
-
-static GstElementClass *parent_class = NULL;
-
-GType
-rsn_stream_selector_get_type (void)
-{
-  static GType stream_selector_type = 0;
-
-  if (!stream_selector_type) {
-    static const GTypeInfo stream_selector_info = {
-      sizeof (RsnStreamSelectorClass),
-      (GBaseInitFunc) rsn_stream_selector_base_init,
-      NULL,
-      (GClassInitFunc) rsn_stream_selector_class_init,
-      NULL,
-      NULL,
-      sizeof (RsnStreamSelector),
-      0,
-      (GInstanceInitFunc) rsn_stream_selector_init,
-    };
-    stream_selector_type =
-        g_type_register_static (GST_TYPE_ELEMENT,
-        "RsnStreamSelector", &stream_selector_info, 0);
-    GST_DEBUG_CATEGORY_INIT (stream_selector_debug,
-        "streamselector", 0, "A stream-selector element");
-  }
-
-  return stream_selector_type;
-}
-
-static void
-rsn_stream_selector_base_init (RsnStreamSelectorClass * klass)
-{
-  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-
-  gst_element_class_set_details_simple (element_class, "StreamSelector",
-      "Generic", "N-to-1 input stream_selectoring",
-      "Julien Moutte <julien@moutte.net>, "
-      "Ronald S. Bultje <rbultje@ronald.bitfreak.net>, "
-      "Jan Schmidt <thaytan@mad.scientist.com>, "
-      "Wim Taymans <wim.taymans@gmail.com>");
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&rsn_stream_selector_sink_factory));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&rsn_stream_selector_src_factory));
-}
-
-static void
-rsn_stream_selector_class_init (RsnStreamSelectorClass * klass)
-{
-  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-  GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
-
-  parent_class = g_type_class_peek_parent (klass);
-
-  gobject_class->dispose = rsn_stream_selector_dispose;
-
-  gobject_class->set_property =
-      GST_DEBUG_FUNCPTR (rsn_stream_selector_set_property);
-  gobject_class->get_property =
-      GST_DEBUG_FUNCPTR (rsn_stream_selector_get_property);
-
-  g_object_class_install_property (gobject_class, PROP_N_PADS,
-      g_param_spec_uint ("n-pads", "Number of Pads",
-          "The number of sink pads", 0, G_MAXUINT, 0,
-          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-  g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD,
-      g_param_spec_object ("active-pad", "Active Pad",
-          "The currently active sink pad", GST_TYPE_PAD,
-          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
-  gstelement_class->request_new_pad = rsn_stream_selector_request_new_pad;
-  gstelement_class->release_pad = rsn_stream_selector_release_pad;
-}
-
-static void
-rsn_stream_selector_init (RsnStreamSelector * sel)
-{
-  sel->srcpad = gst_pad_new ("src", GST_PAD_SRC);
-  gst_pad_set_iterate_internal_links_function (sel->srcpad,
-      GST_DEBUG_FUNCPTR (rsn_stream_selector_iterate_linked_pads));
-  gst_pad_set_getcaps_function (sel->srcpad,
-      GST_DEBUG_FUNCPTR (rsn_stream_selector_getcaps));
-  gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad);
-  /* sinkpad management */
-  sel->padcount = 0;
-  sel->active_sinkpad = NULL;
-  gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED);
-}
-
-static void
-rsn_stream_selector_dispose (GObject * object)
-{
-  RsnStreamSelector *sel = RSN_STREAM_SELECTOR (object);
-
-  if (sel->active_sinkpad) {
-    gst_object_unref (sel->active_sinkpad);
-    sel->active_sinkpad = NULL;
-  }
-
-  G_OBJECT_CLASS (parent_class)->dispose (object);
-}
-
-static void
-rsn_stream_selector_set_property (GObject * object, guint prop_id,
-    const GValue * value, GParamSpec * pspec)
-{
-  RsnStreamSelector *sel = RSN_STREAM_SELECTOR (object);
-
-  switch (prop_id) {
-    case PROP_ACTIVE_PAD:
-    {
-      GstPad *pad = NULL;
-
-      pad = g_value_get_object (value);
-      rsn_stream_selector_set_active (sel, pad);
-
-      break;
-    }
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
-}
-
-static void
-rsn_stream_selector_get_property (GObject * object, guint prop_id,
-    GValue * value, GParamSpec * pspec)
-{
-  RsnStreamSelector *sel = RSN_STREAM_SELECTOR (object);
-
-  switch (prop_id) {
-    case PROP_N_PADS:
-      GST_OBJECT_LOCK (object);
-      g_value_set_uint (value, sel->n_pads);
-      GST_OBJECT_UNLOCK (object);
-      break;
-    case PROP_ACTIVE_PAD:{
-      GST_OBJECT_LOCK (object);
-      g_value_set_object (value, sel->active_sinkpad);
-      GST_OBJECT_UNLOCK (object);
-      break;
-    }
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-  }
-}
-
-static GstPad *
-rsn_stream_selector_get_linked_pad (GstPad * pad, gboolean strict)
-{
-  RsnStreamSelector *sel;
-  GstPad *otherpad = NULL;
-
-  sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
-
-  GST_OBJECT_LOCK (sel);
-  if (pad == sel->srcpad)
-    otherpad = sel->active_sinkpad;
-  else if (pad == sel->active_sinkpad || !strict)
-    otherpad = sel->srcpad;
-  if (otherpad)
-    gst_object_ref (otherpad);
-  GST_OBJECT_UNLOCK (sel);
-  gst_object_unref (sel);
-  return otherpad;
-}
-
-static GstCaps *
-rsn_stream_selector_getcaps (GstPad * pad)
-{
-  GstPad *otherpad;
-  GstObject *parent;
-  GstCaps *caps;
-
-  otherpad = rsn_stream_selector_get_linked_pad (pad, FALSE);
-  parent = gst_object_get_parent (GST_OBJECT (pad));
-  if (!otherpad) {
-    GST_DEBUG_OBJECT (parent,
-        "Pad %s:%s not linked, returning ANY", GST_DEBUG_PAD_NAME (pad));
-    caps = gst_caps_new_any ();
-  } else {
-    GST_DEBUG_OBJECT (parent,
-        "Pad %s:%s is linked (to %s:%s), returning peer caps",
-        GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (otherpad));
-    /* if the peer has caps, use those. If the pad is not linked, this function
-     * returns NULL and we return ANY */
-    if (!(caps = gst_pad_peer_get_caps (otherpad)))
-      caps = gst_caps_new_any ();
-    gst_object_unref (otherpad);
-  }
-
-  gst_object_unref (parent);
-  return caps;
-}
-
-/* check if the pad is the active sinkpad */
-static gboolean
-rsn_stream_selector_is_active_sinkpad (RsnStreamSelector * sel, GstPad * pad)
-{
-  gboolean res;
-
-  GST_OBJECT_LOCK (sel);
-  res = (pad == sel->active_sinkpad);
-  GST_OBJECT_UNLOCK (sel);
-
-  return res;
-}
-
-/* Get or create the active sinkpad */
-static GstPad *
-rsn_stream_selector_get_active (RsnStreamSelector * sel, GstPad * pad)
-{
-  GstPad *active_sinkpad;
-  RsnSelectorPad *selpad;
-
-  selpad = GST_SELECTOR_PAD_CAST (pad);
-
-  GST_OBJECT_LOCK (sel);
-  selpad->active = TRUE;
-  active_sinkpad = sel->active_sinkpad;
-  if (active_sinkpad == NULL) {
-    /* first pad we get an alloc on becomes the activated pad by default */
-    active_sinkpad = sel->active_sinkpad = gst_object_ref (pad);
-    GST_DEBUG_OBJECT (sel, "Activating pad %s:%s", GST_DEBUG_PAD_NAME (pad));
-  }
-  GST_OBJECT_UNLOCK (sel);
-
-  return active_sinkpad;
-}
-
-static void
-rsn_stream_selector_set_active (RsnStreamSelector * sel, GstPad * pad)
-{
-  GstPad **active_pad_p;
-
-  GST_OBJECT_LOCK (GST_OBJECT_CAST (sel));
-  if (pad != sel->active_sinkpad) {
-    RsnSelectorPad *selpad;
-
-    selpad = GST_SELECTOR_PAD_CAST (pad);
-    /* we can only activate pads that have data received */
-    if (selpad && !selpad->active) {
-      GST_DEBUG_OBJECT (sel, "No data received on pad %" GST_PTR_FORMAT, pad);
-    } else {
-      active_pad_p = &sel->active_sinkpad;
-      gst_object_replace ((GstObject **) active_pad_p, GST_OBJECT_CAST (pad));
-      GST_DEBUG_OBJECT (sel, "New active pad is %" GST_PTR_FORMAT,
-          sel->active_sinkpad);
-    }
-    /* Mark the next buffer as discontinuous */
-    sel->mark_discont = TRUE;
-  }
-  GST_OBJECT_UNLOCK (GST_OBJECT_CAST (sel));
-}
-
-static GstIterator *
-rsn_stream_selector_iterate_linked_pads (GstPad * pad)
-{
-  RsnStreamSelector *sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad));
-  GstPad *otherpad = rsn_stream_selector_get_linked_pad (pad, TRUE);
-  GstIterator *it = gst_iterator_new_single (GST_TYPE_PAD, otherpad,
-      (GstCopyFunction) gst_object_ref, (GFreeFunc) gst_object_unref);
-
-  if (otherpad)
-    gst_object_unref (otherpad);
-  gst_object_unref (sel);
-
-  return it;
-}
-
-static GstPad *
-rsn_stream_selector_request_new_pad (GstElement * element,
-    GstPadTemplate * templ, const gchar * unused)
-{
-  RsnStreamSelector *sel;
-  gchar *name = NULL;
-  GstPad *sinkpad = NULL;
-
-  sel = RSN_STREAM_SELECTOR (element);
-  g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL);
-  GST_LOG_OBJECT (sel, "Creating new pad %d", sel->padcount);
-  GST_OBJECT_LOCK (sel);
-  name = g_strdup_printf ("sink_%u", sel->padcount++);
-  sinkpad = g_object_new (RSN_TYPE_SELECTOR_PAD,
-      "name", name, "direction", templ->direction, "template", templ, NULL);
-  g_free (name);
-  sel->n_pads++;
-  GST_OBJECT_UNLOCK (sel);
-
-  gst_pad_set_event_function (sinkpad,
-      GST_DEBUG_FUNCPTR (gst_selector_pad_event));
-  gst_pad_set_getcaps_function (sinkpad,
-      GST_DEBUG_FUNCPTR (gst_selector_pad_getcaps));
-  gst_pad_set_chain_function (sinkpad,
-      GST_DEBUG_FUNCPTR (gst_selector_pad_chain));
-  gst_pad_set_iterate_internal_links_function (sinkpad,
-      GST_DEBUG_FUNCPTR (rsn_stream_selector_iterate_linked_pads));
-  gst_pad_set_bufferalloc_function (sinkpad,
-      GST_DEBUG_FUNCPTR (gst_selector_pad_bufferalloc));
-
-  gst_pad_set_active (sinkpad, TRUE);
-  gst_element_add_pad (GST_ELEMENT (sel), sinkpad);
-  return sinkpad;
-}
-
-static void
-rsn_stream_selector_release_pad (GstElement * element, GstPad * pad)
-{
-  RsnStreamSelector *sel;
-
-  sel = RSN_STREAM_SELECTOR (element);
-  GST_LOG_OBJECT (sel, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
-
-  GST_OBJECT_LOCK (sel);
-  /* if the pad was the active pad, makes us select a new one */
-  if (sel->active_sinkpad == pad) {
-    GST_DEBUG_OBJECT (sel, "Deactivating pad %s:%s", GST_DEBUG_PAD_NAME (pad));
-    sel->active_sinkpad = NULL;
-  }
-  sel->n_pads--;
-  GST_OBJECT_UNLOCK (sel);
-
-  gst_pad_set_active (pad, FALSE);
-  gst_element_remove_pad (GST_ELEMENT (sel), pad);
-}
diff --git a/ext/resindvd/rsnstreamselector.h b/ext/resindvd/rsnstreamselector.h
deleted file mode 100644 (file)
index a8ff1af..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/* GStreamer
- * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
- * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.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 __RSN_STREAM_SELECTOR_H__
-#define __RSN_STREAM_SELECTOR_H__
-
-#include <gst/gst.h>
-
-G_BEGIN_DECLS
-
-#define RSN_TYPE_STREAM_SELECTOR \
-  (rsn_stream_selector_get_type())
-#define RSN_STREAM_SELECTOR(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST ((obj), RSN_TYPE_STREAM_SELECTOR, RsnStreamSelector))
-#define RSN_STREAM_SELECTOR_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST ((klass), RSN_TYPE_STREAM_SELECTOR, RsnStreamSelectorClass))
-#define RSN_IS_STREAM_SELECTOR(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RSN_TYPE_STREAM_SELECTOR))
-#define RSN_IS_STREAM_SELECTOR_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_TYPE ((klass), RSN_TYPE_STREAM_SELECTOR))
-
-typedef struct _RsnStreamSelector RsnStreamSelector;
-typedef struct _RsnStreamSelectorClass RsnStreamSelectorClass;
-
-struct _RsnStreamSelector {
-  GstElement element;
-
-  GstPad *srcpad;
-
-  GstPad *active_sinkpad;
-  guint n_pads;
-  guint padcount;
-
-  GstSegment segment;
-  gboolean mark_discont;
-};
-
-struct _RsnStreamSelectorClass {
-  GstElementClass parent_class;
-};
-
-GType rsn_stream_selector_get_type (void);
-
-G_END_DECLS
-
-#endif /* __RSN_STREAM_SELECTOR_H__ */