adaptive: avoid pad switching 97/162597/2
authorEunhae Choi <eunhae1.choi@samsung.com>
Mon, 4 Dec 2017 10:01:02 +0000 (19:01 +0900)
committerEunhae Choi <eunhae1.choi@samsung.com>
Mon, 4 Dec 2017 10:03:30 +0000 (19:03 +0900)
Change-Id: I29fae423a03e4d642866a218058ca2efa9486f06

ext/hls/gsthlsdemux.c
ext/hls/gsthlsdemux.h
gst-libs/gst/adaptivedemux/gstadaptivedemux.c
gst-libs/gst/adaptivedemux/gstadaptivedemux.h
gst/mpegtsdemux/mpegtsbase.c
gst/mpegtsdemux/mpegtsbase.h
gst/mpegtsdemux/tsdemux.c
gst/mpegtsdemux/tsdemux.h

index ea49fd0ee96a2e948bf36cc7f25baeb2f0bfa66c..ef0d097c28d483659eb9c00b23ce63d4ed57a400 100644 (file)
@@ -235,6 +235,15 @@ gst_hls_demux_create_pad (GstHLSDemux * hlsdemux)
   return pad;
 }
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+static GstPad *
+gst_hls_demux_stream_create_pad (GstAdaptiveDemuxStream * stream)
+{
+  GstHLSDemux *hlsdemux = GST_HLS_DEMUX_CAST (stream->demux);
+  return gst_hls_demux_create_pad (hlsdemux);
+}
+#endif
+
 static guint64
 gst_hls_demux_get_bitrate (GstHLSDemux * hlsdemux)
 {
@@ -856,8 +865,15 @@ gst_hls_demux_handle_buffer (GstAdaptiveDemux * demux,
     gst_hlsdemux_tsreader_set_type (&hls_stream->tsreader,
         hls_stream->stream_type);
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+    gst_adaptive_demux_stream_check_switch_pad (stream, caps,
+        gst_hls_demux_stream_create_pad);
+    GST_DEBUG_OBJECT (stream->pad, "Overwriting PTS to %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (hls_stream->current_pts));
+    stream->fragment.timestamp = hls_stream->current_pts;
+#else
     gst_adaptive_demux_stream_set_caps (stream, caps);
-
+#endif
     hls_stream->do_typefind = FALSE;
   }
   g_assert (hls_stream->pending_typefind_buffer == NULL);
@@ -1093,6 +1109,9 @@ gst_hls_demux_update_fragment_info (GstAdaptiveDemuxStream * stream)
     discont = TRUE;
 
   /* set up our source for download */
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+  hlsdemux_stream->current_pts = sequence_pos;
+#endif
   if (hlsdemux_stream->reset_pts || discont
       || stream->demux->segment.rate < 0.0) {
     stream->fragment.timestamp = sequence_pos;
@@ -1150,8 +1169,10 @@ gst_hls_demux_select_bitrate (GstAdaptiveDemuxStream * stream, guint64 bitrate)
 
   gst_hls_demux_change_playlist (hlsdemux, bitrate / MAX (1.0,
           ABS (demux->segment.rate)), &changed);
+#ifndef TIZEN_FEATURE_AVOID_PAD_SWITCHING
   if (changed)
     gst_hls_demux_setup_streams (GST_ADAPTIVE_DEMUX_CAST (hlsdemux));
+#endif
   return changed;
 }
 
index 4a2b9c0fe4fbdc8904bd29c43fb5844903d54628..2007489a0c3822543e8770f03dbc5ebea47bd5af 100644 (file)
@@ -98,6 +98,9 @@ struct _GstHLSDemuxStream
                                           We only know that it is the last at EOS */
   guint64 current_offset;              /* offset we're currently at */
   gboolean reset_pts;
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+  GstClockTime current_pts;
+#endif
 
   /* decryption tooling */
 #if defined(HAVE_OPENSSL)
@@ -141,7 +144,6 @@ struct _GstHLSDemux
   /* FIXME: check locking, protected automatically by manifest_lock already? */
   /* The master playlist with the available variant streams */
   GstHLSMasterPlaylist *master;
-
   GstHLSVariantStream  *current_variant;
 };
 
index e09ebab095b0c515bfa80bcd8e9881e85f19de7f..0a27fa48fdd14bdbff601e5b6555187140dd41bf 100644 (file)
@@ -969,12 +969,56 @@ gst_adaptive_demux_set_stream_struct_size (GstAdaptiveDemux * demux,
   GST_API_UNLOCK (demux);
 }
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+static void
+gst_adaptive_demux_stream_push_stream_start (GstAdaptiveDemux * demux,
+    GstAdaptiveDemuxStream * stream)
+{
+  GstPad *pad = stream->pad;
+  gchar *stream_id;
+  GstEvent *event;
+  gchar *name = gst_pad_get_name (pad);
+
+  stream_id =
+      gst_pad_create_stream_id_printf (pad, GST_ELEMENT_CAST (demux), "%s-%04d",
+      name, stream->stream_id_counter++);
+
+  event =
+      gst_pad_get_sticky_event (GST_ADAPTIVE_DEMUX_SINK_PAD (demux),
+      GST_EVENT_STREAM_START, 0);
+  if (event) {
+    if (gst_event_parse_group_id (event, &demux->group_id))
+      demux->have_group_id = TRUE;
+    else
+      demux->have_group_id = FALSE;
+    gst_event_unref (event);
+  } else if (!demux->have_group_id) {
+    demux->have_group_id = TRUE;
+    demux->group_id = gst_util_group_id_next ();
+  }
+  event = gst_event_new_stream_start (stream_id);
+  if (demux->have_group_id)
+    gst_event_set_group_id (event, demux->group_id);
+
+  gst_pad_push_event (pad, event);
+  g_free (stream_id);
+  g_free (name);
+}
+#endif
+
 /* must be called with manifest_lock taken */
 static gboolean
 gst_adaptive_demux_prepare_stream (GstAdaptiveDemux * demux,
     GstAdaptiveDemuxStream * stream)
 {
   GstPad *pad = stream->pad;
+
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+  gst_pad_set_active (pad, TRUE);
+  stream->need_header = TRUE;
+
+  gst_adaptive_demux_stream_push_stream_start (demux, stream);
+#else
   gchar *name = gst_pad_get_name (pad);
   GstEvent *event;
   gchar *stream_id;
@@ -1004,6 +1048,7 @@ gst_adaptive_demux_prepare_stream (GstAdaptiveDemux * demux,
   gst_pad_push_event (pad, event);
   g_free (stream_id);
   g_free (name);
+#endif
 
   GST_DEBUG_OBJECT (demux, "Preparing srcpad %s:%s", GST_DEBUG_PAD_NAME (pad));
 
@@ -2113,6 +2158,73 @@ gst_adaptive_demux_stream_set_caps (GstAdaptiveDemuxStream * stream,
   gst_caps_unref (caps);
 }
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+/**
+ * gst_adaptive_demux_stream_check_switch_pad:
+ * @stream:
+ * @caps: the #GstCaps of the new stream
+ * @create_pad_func: callback to create a pad if needed
+ *
+ * Compares @caps with the current caps on pad and with the allowed
+ * downstream caps. If the caps is compatible it just sets a new caps
+ * on the pad, otherwise it will request a new pad to be created and
+ * switch pads.
+ *
+ * This only works currently for streams that only have a single output
+ * due to the way pad switching works in groups. (HLS demux can use it)
+ *
+ * Return: %TRUE if a new pad was created
+ */
+gboolean
+gst_adaptive_demux_stream_check_switch_pad (GstAdaptiveDemuxStream * stream,
+    GstCaps * caps, GstAdaptiveDemuxStreamCreatePadFunc create_pad_func)
+{
+  GstCaps *current_caps;
+  gboolean ret;
+
+  current_caps = gst_pad_get_current_caps (stream->pad);
+  if (current_caps == NULL || gst_caps_is_equal (current_caps, caps) ||
+      gst_pad_query_accept_caps (stream->pad, caps)) {
+
+    /* no need to switch pads */
+    ret = FALSE;
+    GST_DEBUG_OBJECT (stream->pad, "New caps compatible with old one."
+        " Scheduling new stream-start, caps and segment events");
+    gst_adaptive_demux_stream_push_stream_start (stream->demux, stream);
+    gst_adaptive_demux_stream_set_caps (stream, caps);
+    if (stream->pending_segment)
+      gst_event_unref (stream->pending_segment);
+    stream->pending_segment = gst_event_new_segment (&stream->segment);
+  } else {
+    GstPad *old_pad;
+
+    /* need to switch pad for a new one */
+    ret = TRUE;
+
+    GST_DEBUG_OBJECT (stream->pad, "New caps is incompatible with old "
+        "one. Need to switch pads");
+    old_pad = stream->pad;
+    stream->pad = create_pad_func (stream);
+    gst_adaptive_demux_stream_set_caps (stream, caps);
+    gst_adaptive_demux_expose_stream (stream->demux, stream);
+
+    /* FIXME currently there is no way of properly replacing a single
+     * pad in the stream. This will only work for streams that have
+     * a single source pad */
+    gst_element_no_more_pads (GST_ELEMENT_CAST (stream->demux));
+
+    gst_pad_push_event (old_pad, gst_event_new_eos ());
+    gst_pad_set_active (old_pad, FALSE);
+    gst_element_remove_pad (GST_ELEMENT_CAST (stream->demux), old_pad);
+    gst_object_unref (old_pad);
+  }
+
+  if (current_caps)
+    gst_caps_unref (current_caps);
+  return ret;
+}
+#endif
+
 /* must be called with manifest_lock taken */
 void
 gst_adaptive_demux_stream_set_tags (GstAdaptiveDemuxStream * stream,
index d72990aeee1dd2b9c56e6143680779bf8ae892b9..445fdef41e70bc0bb8310e1ecb50f0302e47783f 100644 (file)
@@ -94,6 +94,9 @@ typedef struct _GstAdaptiveDemuxStream GstAdaptiveDemuxStream;
 typedef struct _GstAdaptiveDemux GstAdaptiveDemux;
 typedef struct _GstAdaptiveDemuxClass GstAdaptiveDemuxClass;
 typedef struct _GstAdaptiveDemuxPrivate GstAdaptiveDemuxPrivate;
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+typedef GstPad* (*GstAdaptiveDemuxStreamCreatePadFunc)(GstAdaptiveDemuxStream * stream);
+#endif
 
 struct _GstAdaptiveDemuxStreamFragment
 {
@@ -131,6 +134,9 @@ struct _GstAdaptiveDemuxStream
 
   GstAdaptiveDemux *demux;
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+  gint stream_id_counter;
+#endif
   GstSegment segment;
 
   GstCaps *pending_caps;
@@ -513,6 +519,11 @@ void gst_adaptive_demux_stream_queue_event (GstAdaptiveDemuxStream * stream,
 GstClockTime gst_adaptive_demux_get_monotonic_time (GstAdaptiveDemux * demux);
 GDateTime *gst_adaptive_demux_get_client_now_utc (GstAdaptiveDemux * demux);
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+gboolean gst_adaptive_demux_stream_check_switch_pad (GstAdaptiveDemuxStream * stream,
+    GstCaps * caps, GstAdaptiveDemuxStreamCreatePadFunc create_pad_func);
+#endif
+
 G_END_DECLS
 
 #endif
index ee2460c2812199bcddc41b9a66f11cf5c1a184ee..46c1e72b254b7c407ff1eec4b2b74058e2a17eab 100644 (file)
@@ -170,12 +170,54 @@ mpegts_base_get_property (GObject * object, guint prop_id,
   }
 }
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+static void
+mpegts_base_clear (MpegTSBase * base)
+{
+  mpegts_packetizer_clear (base->packetizer);
+  memset (base->is_pes, 0, 1024);
+  memset (base->known_psi, 0, 1024);
+
+  /* FIXME : Actually these are not *always* know SI streams
+   * depending on the variant of mpeg-ts being used. */
+
+  /* Known PIDs : PAT, TSDT, IPMP CIT */
+  MPEGTS_BIT_SET (base->known_psi, 0);
+  MPEGTS_BIT_SET (base->known_psi, 2);
+  MPEGTS_BIT_SET (base->known_psi, 3);
+  /* TDT, TOT, ST */
+  MPEGTS_BIT_SET (base->known_psi, 0x14);
+  /* network synchronization */
+  MPEGTS_BIT_SET (base->known_psi, 0x15);
+
+  /* ATSC */
+  MPEGTS_BIT_SET (base->known_psi, 0x1ffb);
+
+  if (base->pat) {
+    g_ptr_array_unref (base->pat);
+    base->pat = NULL;
+  }
+
+  gst_segment_init (&base->segment, GST_FORMAT_UNDEFINED);
+  base->last_seek_seqnum = (guint32) - 1;
+
+  base->seen_pat = FALSE;
+  base->seek_offset = -1;
+
+  g_hash_table_foreach_remove (base->programs, (GHRFunc) remove_each_program,
+      base);
+}
+#endif
 
 static void
 mpegts_base_reset (MpegTSBase * base)
 {
   MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base);
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+  mpegts_base_clear (base);
+  base->mode = BASE_MODE_STREAMING;
+#else
   mpegts_packetizer_clear (base->packetizer);
   memset (base->is_pes, 0, 1024);
   memset (base->known_psi, 0, 1024);
@@ -214,6 +256,7 @@ mpegts_base_reset (MpegTSBase * base)
       && GST_OBJECT_FLAG_IS_SET (GST_OBJECT_PARENT (base),
       GST_BIN_FLAG_STREAMS_AWARE);
   GST_DEBUG_OBJECT (base, "Streams aware : %d", base->streams_aware);
+#endif
 
   if (klass->reset)
     klass->reset (base);
@@ -278,7 +321,6 @@ mpegts_base_finalize (GObject * object)
     G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
-
 /* returns NULL if no matching descriptor found *
  * otherwise returns a descriptor that needs to *
  * be freed */
@@ -486,6 +528,34 @@ mpegts_base_free_program (MpegTSBaseProgram * program)
   g_free (program);
 }
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+MpegTSBaseStream *
+mpegts_base_stream_ref (MpegTSBaseStream * stream)
+{
+  g_return_val_if_fail (stream != NULL, NULL);
+
+  GST_TRACE ("%p ref %d->%d", stream, stream->refcount, stream->refcount + 1);
+
+  g_atomic_int_inc (&stream->refcount);
+
+  return stream;
+}
+
+void
+mpegts_base_stream_unref (MpegTSBaseStream * stream)
+{
+  g_return_if_fail (stream != NULL);
+
+  GST_TRACE ("%p unref %d->%d", stream, stream->refcount, stream->refcount - 1);
+
+  g_return_if_fail (stream->refcount > 0);
+
+  if (g_atomic_int_dec_and_test (&stream->refcount)) {
+    mpegts_base_free_stream (stream);
+  }
+}
+#endif
+
 void
 mpegts_base_deactivate_and_free_program (MpegTSBase * base,
     MpegTSBaseProgram * program)
@@ -541,6 +611,9 @@ mpegts_base_program_add_stream (MpegTSBase * base,
   }
 
   bstream = g_malloc0 (base->stream_size);
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+  bstream->refcount = 1;
+#endif
   bstream->stream_id =
       g_strdup_printf ("%s/%08x",
       gst_stream_collection_get_upstream_id (program->collection), pid);
@@ -591,7 +664,11 @@ mpegts_base_program_remove_stream (MpegTSBase * base,
     klass->stream_removed (base, stream);
 
   program->stream_list = g_list_remove_all (program->stream_list, stream);
-  mpegts_base_free_stream (stream);
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+    mpegts_base_stream_unref (stream);
+#else
+    mpegts_base_free_stream (stream);
+#endif
   program->streams[pid] = NULL;
 }
 
@@ -1328,6 +1405,15 @@ mpegts_base_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
       res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event);
       break;
     case GST_EVENT_STREAM_START:
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+      GST_DEBUG_OBJECT (base,
+          "Handling stream-start, flushing all pending data");
+      mpegts_base_drain (base);
+      mpegts_base_flush (base, TRUE);
+      mpegts_packetizer_flush (base->packetizer, TRUE);
+      mpegts_base_clear (base);
+      mpegts_packetizer_clear (base->packetizer);
+#endif
       gst_event_unref (event);
       break;
     case GST_EVENT_CAPS:
index 0dd78599c4bfd037ca2d4e21eca2c27f26df44bb..e5117c15dbb24a40434ea65e5e0aecdcf1a6762c 100644 (file)
@@ -57,6 +57,10 @@ typedef struct _MpegTSBaseProgram MpegTSBaseProgram;
 
 struct _MpegTSBaseStream
 {
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+  gint                refcount;
+#endif
+
   guint16             pid;
   guint8              stream_type;
 
@@ -219,6 +223,10 @@ struct _MpegTSBaseClass {
 
 G_GNUC_INTERNAL GType mpegts_base_get_type(void);
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+G_GNUC_INTERNAL MpegTSBaseStream *mpegts_base_stream_ref (MpegTSBaseStream * stream);
+G_GNUC_INTERNAL void mpegts_base_stream_unref (MpegTSBaseStream * stream);
+#endif
 G_GNUC_INTERNAL MpegTSBaseProgram *mpegts_base_get_program (MpegTSBase * base, gint program_number);
 G_GNUC_INTERNAL MpegTSBaseProgram *mpegts_base_add_program (MpegTSBase * base, gint program_number, guint16 pmt_pid);
 
index 20357a87f208644d8b7b43abb391aef5726fbac8..10156f8796f7724ae485c7a45901e84fa626f2bd 100644 (file)
@@ -200,6 +200,10 @@ struct _TSDemuxStream
 
   GstTsDemuxKeyFrameScanFunction scan_function;
   TSDemuxH264ParsingInfos h264infos;
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+  /* For pad matching to avoid switching pads */
+  TSDemuxStream *matched_stream;
+#endif
 };
 
 #define VIDEO_CAPS \
@@ -312,6 +316,12 @@ gst_ts_demux_push_pending_data (GstTSDemux * demux, TSDemuxStream * stream,
 static void gst_ts_demux_stream_flush (TSDemuxStream * stream,
     GstTSDemux * demux, gboolean hard);
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+static void gst_ts_demux_remove_stream (GstTSDemux * tsdemux,
+    TSDemuxStream * stream, gboolean push_eos);
+static void gst_ts_demux_remove_old_streams (GstTSDemux * demux,
+    gboolean push_eos);
+#endif
 static gboolean push_event (MpegTSBase * base, GstEvent * event);
 static void gst_ts_demux_check_and_sync_streams (GstTSDemux * demux,
     GstClockTime time);
@@ -423,6 +433,9 @@ gst_ts_demux_reset (MpegTSBase * base)
 
   demux->last_seek_offset = -1;
   demux->program_generation = 0;
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+  gst_ts_demux_remove_old_streams (demux, FALSE);
+#endif
 }
 
 static void
@@ -1053,6 +1066,43 @@ gst_ts_demux_create_tags (TSDemuxStream * stream)
   }
 }
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+static void
+gst_ts_demux_stream_send_stream_start (MpegTSBase * base,
+    MpegTSBaseStream * bstream, GstPad * pad)
+{
+  GstTSDemux *demux = GST_TS_DEMUX_CAST (base);
+  TSDemuxStream *stream = (TSDemuxStream *) bstream;
+  GstEvent *event;
+  gchar *stream_id;
+
+  stream_id = gst_stream_get_stream_id (bstream->stream_object);
+  event = gst_pad_get_sticky_event (base->sinkpad, GST_EVENT_STREAM_START, 0);
+  if (event) {
+    if (gst_event_parse_group_id (event, &demux->group_id))
+      demux->have_group_id = TRUE;
+    else
+      demux->have_group_id = FALSE;
+    gst_event_unref (event);
+  } else if (!demux->have_group_id) {
+    demux->have_group_id = TRUE;
+    demux->group_id = gst_util_group_id_next ();
+  }
+  event = gst_event_new_stream_start (stream_id);
+  gst_event_set_stream (event, bstream->stream_object);
+  if (demux->have_group_id)
+    gst_event_set_group_id (event, demux->group_id);
+
+  if (stream->sparse) {
+    gst_event_set_stream_flags (event, GST_STREAM_FLAG_SPARSE);
+    gst_stream_set_stream_flags (bstream->stream_object,
+        GST_STREAM_FLAG_SPARSE);
+  }
+
+  gst_pad_push_event (pad, event);
+}
+#endif
+
 static GstPad *
 create_pad_for_stream (MpegTSBase * base, MpegTSBaseStream * bstream,
     MpegTSBaseProgram * program)
@@ -1587,6 +1637,16 @@ done:
   }
 
   if (template && name && caps) {
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+    GST_LOG ("stream:%p creating pad with name %s and caps %" GST_PTR_FORMAT,
+        stream, name, caps);
+    pad = gst_pad_new_from_template (template, name);
+    gst_pad_set_active (pad, TRUE);
+    gst_pad_use_fixed_caps (pad);
+
+    stream->sparse = sparse;
+    gst_ts_demux_stream_send_stream_start (base, bstream, pad);
+#else
     GstEvent *event;
     const gchar *stream_id;
 
@@ -1595,8 +1655,8 @@ done:
     pad = gst_pad_new_from_template (template, name);
     gst_pad_set_active (pad, TRUE);
     gst_pad_use_fixed_caps (pad);
-    stream_id = gst_stream_get_stream_id (bstream->stream_object);
 
+    stream_id = gst_stream_get_stream_id (bstream->stream_object);
     event = gst_pad_get_sticky_event (base->sinkpad, GST_EVENT_STREAM_START, 0);
     if (event) {
       if (gst_event_parse_group_id (event, &demux->group_id))
@@ -1612,12 +1672,19 @@ done:
     gst_event_set_stream (event, bstream->stream_object);
     if (demux->have_group_id)
       gst_event_set_group_id (event, demux->group_id);
+    if (stream->sparse) {
+      gst_event_set_stream_flags (event, GST_STREAM_FLAG_SPARSE);
+      gst_stream_set_stream_flags (bstream->stream_object,
+          GST_STREAM_FLAG_SPARSE);
+    }
     if (sparse) {
       gst_event_set_stream_flags (event, GST_STREAM_FLAG_SPARSE);
       gst_stream_set_stream_flags (bstream->stream_object,
           GST_STREAM_FLAG_SPARSE);
     }
     stream->sparse = sparse;
+    gst_pad_push_event (pad, event);
+#endif
     gst_stream_set_caps (bstream->stream_object, caps);
     if (!stream->taglist)
       stream->taglist = gst_tag_list_new_empty ();
@@ -1625,7 +1692,6 @@ done:
         caps);
     gst_stream_set_tags (bstream->stream_object, stream->taglist);
 
-    gst_pad_push_event (pad, event);
     gst_pad_set_caps (pad, caps);
     gst_pad_set_query_function (pad, gst_ts_demux_srcpad_query);
     gst_pad_set_event_function (pad, gst_ts_demux_srcpad_event);
@@ -1640,6 +1706,25 @@ done:
   return pad;
 }
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+static void
+gst_ts_demux_remove_old_streams (GstTSDemux * demux, gboolean push_eos)
+{
+  if (demux->old_streams) {
+    GList *iter;
+    for (iter = demux->old_streams; iter; iter = g_list_next (iter)) {
+      TSDemuxStream *stream = iter->data;
+
+      gst_ts_demux_remove_stream (demux, stream, push_eos);
+      mpegts_base_stream_unref ((MpegTSBaseStream *) stream);
+    }
+    g_list_free (demux->old_streams);
+    demux->old_streams = NULL;
+  }
+
+}
+#endif
+
 static gboolean
 gst_ts_demux_stream_added (MpegTSBase * base, MpegTSBaseStream * bstream,
     MpegTSBaseProgram * program)
@@ -1700,10 +1785,49 @@ tsdemux_h264_parsing_info_clear (TSDemuxH264ParsingInfos * h264infos)
   }
 }
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+static void
+gst_ts_demux_stream_rename_stopping_pad (GstTSDemux * demux,
+    TSDemuxStream * stream)
+{
+  if (stream->pad) {
+    gchar *name;
+
+    GST_DEBUG_OBJECT (stream->pad, "Renaming stopping pad: %s",
+        GST_PAD_NAME (stream->pad));
+    name = g_strdup_printf ("%s_stopping", GST_PAD_NAME (stream->pad));
+    gst_object_set_name (GST_OBJECT_CAST (stream->pad), name);
+
+    g_free (name);
+  }
+}
+
+static void
+gst_ts_demux_remove_stream (GstTSDemux * tsdemux, TSDemuxStream * stream,
+    gboolean push_eos)
+{
+  if (stream->pad) {
+    if (push_eos && stream->active) {
+      GST_DEBUG_OBJECT (stream->pad, "Pushing out EOS");
+      gst_pad_push_event (stream->pad, gst_event_new_eos ());
+      gst_pad_set_active (stream->pad, FALSE);
+    }
+
+    GST_DEBUG_OBJECT (stream->pad, "Removing pad");
+    gst_element_remove_pad (GST_ELEMENT_CAST (tsdemux), stream->pad);
+    stream->active = FALSE;
+    stream->pad = NULL;
+  }
+}
+#endif
+
 static void
 gst_ts_demux_stream_removed (MpegTSBase * base, MpegTSBaseStream * bstream)
 {
   TSDemuxStream *stream = (TSDemuxStream *) bstream;
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+  GstTSDemux *tsdemux = (GstTSDemux *) base;
+#endif
 
   if (stream->pad) {
     gst_flow_combiner_remove_pad (GST_TS_DEMUX_CAST (base)->flowcombiner,
@@ -1714,19 +1838,23 @@ gst_ts_demux_stream_removed (MpegTSBase * base, MpegTSBaseStream * bstream)
         /* Flush out all data */
         GST_DEBUG_OBJECT (stream->pad, "Flushing out pending data");
         gst_ts_demux_push_pending_data ((GstTSDemux *) base, stream, NULL);
-
+#ifndef TIZEN_FEATURE_AVOID_PAD_SWITCHING
         GST_DEBUG_OBJECT (stream->pad, "Pushing out EOS");
         gst_pad_push_event (stream->pad, gst_event_new_eos ());
         gst_pad_set_active (stream->pad, FALSE);
+#endif
       }
-
+#ifndef TIZEN_FEATURE_AVOID_PAD_SWITCHING
       GST_DEBUG_OBJECT (stream->pad, "Removing pad");
       gst_element_remove_pad (GST_ELEMENT_CAST (base), stream->pad);
       stream->active = FALSE;
+#endif
     } else {
       gst_object_unref (stream->pad);
     }
+#ifndef TIZEN_FEATURE_AVOID_PAD_SWITCHING
     stream->pad = NULL;
+#endif
   }
 
   gst_ts_demux_stream_flush (stream, GST_TS_DEMUX_CAST (base), TRUE);
@@ -1737,6 +1865,13 @@ gst_ts_demux_stream_removed (MpegTSBase * base, MpegTSBaseStream * bstream)
   }
 
   tsdemux_h264_parsing_info_clear (&stream->h264infos);
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+  /* Keep our reference as we can only finish the stream once we added
+   * pads for the new program or the pipeline might go EOS */
+  mpegts_base_stream_ref (bstream);
+  tsdemux->old_streams = g_list_append (tsdemux->old_streams, bstream);
+#endif
+
 }
 
 static void
@@ -1855,6 +1990,118 @@ gst_ts_demux_update_program (MpegTSBase * base, MpegTSBaseProgram * program)
   }
 }
 
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+static gboolean
+gst_ts_demux_find_matching_stream (GstTSDemux * demux, TSDemuxStream * stream,
+    GList * streams_list)
+{
+  GstCaps *this_caps;
+
+  if (stream->pad == NULL)
+    return TRUE;
+
+  this_caps = gst_pad_get_current_caps (stream->pad);
+  for (; streams_list; streams_list = g_list_next (streams_list)) {
+    TSDemuxStream *other_stream = streams_list->data;
+
+    if (other_stream->pad == NULL)
+      continue;
+
+    if (other_stream->matched_stream)
+      continue;
+
+    if (gst_pad_peer_query_accept_caps (other_stream->pad, this_caps)) {
+      /* TODO we are not checking the PIDs so the pad names will be
+       * inconsistent with the new streams' PIDs */
+      other_stream->matched_stream = stream;
+      stream->matched_stream = other_stream;
+      gst_caps_unref (this_caps);
+      return TRUE;
+    }
+  }
+
+  gst_caps_unref (this_caps);
+  GST_DEBUG_OBJECT (demux, "No match found for stream: %p %" GST_PTR_FORMAT,
+      stream, stream->pad);
+  return FALSE;
+}
+
+static gboolean
+push_sticky_event (GstPad * pad, GstEvent ** event, gpointer udata)
+{
+  GstPad *other_pad = udata;
+
+  gst_pad_push_event (other_pad, gst_event_ref (*event));
+
+  return TRUE;
+}
+
+static gboolean
+gst_ts_demux_check_streams_match (GstTSDemux * demux)
+{
+  GList *iter;
+  gint old_length = 0;
+  gint new_length = 0;
+
+  if (!demux->old_streams) {
+    GST_DEBUG_OBJECT (demux, "No old streams present, streams don't match");
+    return FALSE;
+  }
+
+  /* Initialize matching variables */
+  for (iter = demux->program->stream_list; iter; iter = g_list_next (iter)) {
+    TSDemuxStream *stream = iter->data;
+    if (stream->pad) {
+      new_length++;
+      stream->matched_stream = NULL;
+    }
+  }
+  for (iter = demux->old_streams; iter; iter = g_list_next (iter)) {
+    TSDemuxStream *stream = iter->data;
+    if (stream->pad) {
+      old_length++;
+      stream->matched_stream = NULL;
+    }
+  }
+
+  if (new_length != old_length) {
+    GST_DEBUG_OBJECT (demux,
+        "Number of streams is different, no match possible");
+    return FALSE;
+  }
+
+  for (iter = demux->program->stream_list; iter; iter = g_list_next (iter)) {
+    if (!gst_ts_demux_find_matching_stream (demux, iter->data,
+            demux->old_streams))
+      return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (demux, "Streams matched, no need for pad switching");
+
+  /* do the pad replacement, unref pads from new streams and use the pads
+   * from the old ones */
+  for (iter = demux->program->stream_list; iter; iter = g_list_next (iter)) {
+    TSDemuxStream *stream = iter->data;
+
+    if (stream->pad && stream->matched_stream) {
+      GstPad *pad = stream->pad;
+
+      stream->pad = stream->matched_stream->pad;
+      stream->matched_stream->pad = NULL;
+
+      gst_ts_demux_stream_send_stream_start ((MpegTSBase *) demux,
+          (MpegTSBaseStream *) stream, stream->pad);
+
+      gst_pad_sticky_events_foreach (pad, push_sticky_event, stream->pad);
+
+      gst_object_unref (pad);
+    }
+  }
+
+  return TRUE;
+}
+#endif
+
 static void
 gst_ts_demux_program_started (MpegTSBase * base, MpegTSBaseProgram * program)
 {
@@ -1887,7 +2134,27 @@ gst_ts_demux_program_started (MpegTSBase * base, MpegTSBaseProgram * program)
       gst_event_unref (demux->segment_event);
       demux->segment_event = NULL;
     }
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+    /* Check if the new streams match the old ones to
+     * prevent switching pads if not needed */
+    if (gst_ts_demux_check_streams_match (demux))
+      return;
+
+    /* 1) Rename old pad names to avoid clashes (matching PIDs)
+     * 2) add new streams
+     * 3) Fire no-more-pads */
+    for (tmp = demux->old_streams; tmp; tmp = tmp->next) {
+      TSDemuxStream *stream = (TSDemuxStream *) tmp->data;
+      gst_ts_demux_stream_rename_stopping_pad (demux, stream);
+    }
+    for (tmp = program->stream_list; tmp; tmp = tmp->next) {
+      TSDemuxStream *stream = (TSDemuxStream *) tmp->data;
+      activate_pad_for_stream (demux, stream);
+    }
+    gst_element_no_more_pads ((GstElement *) demux);
 
+    gst_ts_demux_remove_old_streams (demux, TRUE);
+#else
     /* DRAIN ALL STREAMS FIRST ! */
     if (demux->previous_program) {
       GList *tmp;
@@ -1942,6 +2209,7 @@ gst_ts_demux_program_started (MpegTSBase * base, MpegTSBaseProgram * program)
     }
 
     gst_element_no_more_pads ((GstElement *) demux);
+#endif
   }
 }
 
index 32cbde27cef9dcad02d08227e82fd0b4002ce00f..0a6e19ad0a9fee4c8d243d10fbab88936c3066f3 100644 (file)
@@ -68,6 +68,9 @@ struct _GstTSDemux
   MpegTSBaseProgram *program;  /* Current program */
   MpegTSBaseProgram *previous_program; /* Previous program, to deactivate once
                                        * the new program becomes active */
+#ifdef TIZEN_FEATURE_AVOID_PAD_SWITCHING
+  GList *old_streams;
+#endif
 
   /* segments to be sent */
   GstSegment segment;