+static void
+_generate_update_newsegment_event (GstPad * pad, GstSegment * segment,
+ GstEvent ** event1)
+{
+ GstEvent *event;
+ GstStructure *structure;
+ event = gst_event_new_segment (segment);
+ structure = gst_event_writable_structure (event);
+ gst_structure_id_set (structure,
+ _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
+ *event1 = event;
+}
+
+static gboolean
+gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
+ const gchar * sink_type,
+ gboolean * sink_ignore_wrong_state,
+ gboolean * sink_custom_flush_finished,
+ gboolean * sink_pending_flush, GstSegment * sink_segment)
+{
+ GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
+ gboolean ret;
+ const GstStructure *structure = gst_event_get_structure (event);
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
+ gchar *custom_flush;
+ gchar *custom_flush_finish;
+
+ custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
+ custom_flush_finish =
+ g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
+ if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
+ GST_DEBUG_OBJECT (pad,
+ "Custom %s flush event received, marking to flush %s", sink_type,
+ sink_type);
+ GST_PLAY_SINK_LOCK (playsink);
+ *sink_ignore_wrong_state = TRUE;
+ *sink_custom_flush_finished = FALSE;
+ GST_PLAY_SINK_UNLOCK (playsink);
+ } else if (strcmp (gst_structure_get_name (structure),
+ custom_flush_finish) == 0) {
+ GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
+ sink_type);
+ GST_PLAY_SINK_LOCK (playsink);
+ *sink_pending_flush = TRUE;
+ *sink_custom_flush_finished = TRUE;
+ GST_PLAY_SINK_UNLOCK (playsink);
+ }
+
+ g_free (custom_flush);
+ g_free (custom_flush_finish);
+ } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
+ GST_PLAY_SINK_LOCK (playsink);
+ GST_DEBUG_OBJECT (pad, "Resetting %s segment because of flush-stop event",
+ sink_type);
+ gst_segment_init (sink_segment, GST_FORMAT_UNDEFINED);
+ GST_PLAY_SINK_UNLOCK (playsink);
+ }
+
+ GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
+ ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
+ const GstSegment *segment;
+
+ gst_event_parse_segment (event, &segment);
+ GST_DEBUG_OBJECT (pad, "Segment event: %" GST_SEGMENT_FORMAT, segment);
+
+ GST_PLAY_SINK_LOCK (playsink);
+ if (sink_segment->format != segment->format) {
+ GST_DEBUG_OBJECT (pad, "%s segment format changed: %s -> %s",
+ sink_type,
+ gst_format_get_name (sink_segment->format),
+ gst_format_get_name (segment->format));
+ gst_segment_init (sink_segment, segment->format);
+ }
+
+ GST_DEBUG_OBJECT (pad, "Old %s segment: %" GST_SEGMENT_FORMAT,
+ sink_type, sink_segment);
+ gst_segment_copy_into (&playsink->text_segment, sink_segment);
+ GST_DEBUG_OBJECT (pad, "New %s segment: %" GST_SEGMENT_FORMAT,
+ sink_type, sink_segment);
+ GST_PLAY_SINK_UNLOCK (playsink);
+ }
+
+ gst_event_unref (event);
+ gst_object_unref (playsink);
+ return ret;
+}
+
+static GstFlowReturn
+gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
+ const gchar * sink_type,
+ gboolean * sink_ignore_wrong_state,
+ gboolean * sink_custom_flush_finished,
+ gboolean * sink_pending_flush, GstSegment * sink_segment)
+{
+ GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
+ GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
+ GstFlowReturn ret;
+
+ GST_PLAY_SINK_LOCK (playsink);
+
+ if (*sink_pending_flush) {
+ GstEvent *event;
+ GstStructure *structure;
+
+ *sink_pending_flush = FALSE;
+
+ GST_PLAY_SINK_UNLOCK (playsink);
+
+ /* make the bin drop all cached data.
+ * This event will be dropped on the src pad, if any. */
+ event = gst_event_new_flush_start ();
+ structure = gst_event_writable_structure (event);
+ gst_structure_id_set (structure,
+ _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
+
+ GST_DEBUG_OBJECT (pad,
+ "Pushing %s flush-start event with reset segment marker set: %"
+ GST_PTR_FORMAT, sink_type, event);
+ gst_pad_send_event (pad, event);
+
+ /* make queue drop all cached data.
+ * This event will be dropped on the src pad. */
+ event = gst_event_new_flush_stop (TRUE);
+ structure = gst_event_writable_structure (event);
+ gst_structure_id_set (structure,
+ _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
+
+ GST_DEBUG_OBJECT (pad,
+ "Pushing %s flush-stop event with reset segment marker set: %"
+ GST_PTR_FORMAT, sink_type, event);
+ gst_pad_send_event (pad, event);
+
+ /* Re-sync queue segment info after flush-stop.
+ * This event will be dropped on the src pad. */
+ if (sink_segment->format != GST_FORMAT_UNDEFINED) {
+ GstEvent *event1;
+
+ _generate_update_newsegment_event (pad, sink_segment, &event1);
+ GST_DEBUG_OBJECT (playsink,
+ "Pushing segment event with reset "
+ "segment marker set: %" GST_PTR_FORMAT, event1);
+ gst_pad_send_event (pad, event1);
+ }
+ } else {
+ GST_PLAY_SINK_UNLOCK (playsink);
+ }
+
+ ret = gst_proxy_pad_chain_default (pad, parent, buffer);
+
+ GST_PLAY_SINK_LOCK (playsink);
+ if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
+ GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
+ sink_type);
+ if (*sink_custom_flush_finished) {
+ GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
+ "wrong state for %s", sink_type);
+ *sink_ignore_wrong_state = FALSE;
+ }
+
+ ret = GST_FLOW_OK;
+ }
+ GST_PLAY_SINK_UNLOCK (playsink);
+
+ gst_object_unref (playsink);
+ gst_object_unref (tbin);
+ return ret;
+}
+
+/* sending audio/video flushes break stream changes when the pipeline
+ * is paused and played again in 0.10 */
+#if 0
+static gboolean
+gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
+{
+ GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
+ GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
+ gboolean ret;
+
+ ret = gst_play_sink_sink_event (pad, event, "video",
+ &playsink->video_ignore_wrong_state,
+ &playsink->video_custom_flush_finished,
+ &playsink->video_pending_flush, &playsink->video_segment);
+
+ gst_object_unref (playsink);
+ gst_object_unref (tbin);
+ return ret;
+}
+
+static GstFlowReturn
+gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
+ GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
+ gboolean ret;
+
+ ret = gst_play_sink_sink_chain (pad, buffer, "video",
+ &playsink->video_ignore_wrong_state,
+ &playsink->video_custom_flush_finished,
+ &playsink->video_pending_flush, &playsink->video_segment);
+
+ gst_object_unref (playsink);
+ gst_object_unref (tbin);
+ return ret;
+}
+
+static gboolean
+gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
+{
+ GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
+ GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
+ gboolean ret;
+
+ ret = gst_play_sink_sink_event (pad, event, "audio",
+ &playsink->audio_ignore_wrong_state,
+ &playsink->audio_custom_flush_finished,
+ &playsink->audio_pending_flush, &playsink->audio_segment);
+
+ gst_object_unref (playsink);
+ gst_object_unref (tbin);
+ return ret;
+}
+
+static GstFlowReturn
+gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
+ GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
+ gboolean ret;
+
+ ret = gst_play_sink_sink_chain (pad, buffer, "audio",
+ &playsink->audio_ignore_wrong_state,
+ &playsink->audio_custom_flush_finished,
+ &playsink->audio_pending_flush, &playsink->audio_segment);
+
+ gst_object_unref (playsink);
+ gst_object_unref (tbin);
+ return ret;
+}
+#endif
+
+static gboolean
+gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
+ GstEvent * event)
+{
+ GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
+ gboolean ret;
+
+ ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
+ &playsink->text_ignore_wrong_state,
+ &playsink->text_custom_flush_finished,
+ &playsink->text_pending_flush, &playsink->text_segment);
+
+ gst_object_unref (playsink);
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
+ GstBuffer * buffer)
+{
+ gboolean ret;
+ GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
+
+ ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
+ &playsink->text_ignore_wrong_state,
+ &playsink->text_custom_flush_finished,
+ &playsink->text_pending_flush, &playsink->text_segment);
+
+ gst_object_unref (playsink);
+ return ret;
+}
+
+static gboolean
+gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
+ GstEvent * event)
+{
+ gboolean ret;
+ const GstStructure *structure;
+
+ GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
+
+ structure = gst_event_get_structure (event);
+
+ if (structure &&
+ gst_structure_id_has_field (structure,
+ _playsink_reset_segment_event_marker_id)) {
+ /* the events marked with a reset segment marker
+ * are sent internally to reset the queue and
+ * must be dropped here */
+ GST_DEBUG_OBJECT (pad, "Dropping event with reset "
+ "segment marker set: %" GST_PTR_FORMAT, event);
+ ret = TRUE;
+ goto out;
+ }
+
+ ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
+
+out:
+ gst_event_unref (event);
+ return ret;
+}
+