static gboolean vorbis_dec_src_event (GstPad * pad, GstEvent * event) { gboolean res = TRUE; GstVorbisDec *dec; dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); if (G_UNLIKELY (dec == NULL)) { gst_event_unref (event); return FALSE; } switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { GstFormat format, tformat; gdouble rate; GstEvent *real_seek; GstSeekFlags flags; GstSeekType cur_type, stop_type; gint64 cur, stop; gint64 tcur, tstop; guint32 seqnum; gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); seqnum = gst_event_get_seqnum (event); gst_event_unref (event); /* First bring the requested format to time */ tformat = GST_FORMAT_TIME; if (!(res = vorbis_dec_convert (pad, format, cur, &tformat, &tcur))) goto convert_error; if (!(res = vorbis_dec_convert (pad, format, stop, &tformat, &tstop))) goto convert_error; /* then seek with time on the peer */ real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, cur_type, tcur, stop_type, tstop); gst_event_set_seqnum (real_seek, seqnum); res = gst_pad_push_event (dec->sinkpad, real_seek); break; } default: res = gst_pad_push_event (dec->sinkpad, event); break; } done: gst_object_unref (dec); return res; /* ERRORS */ convert_error: { GST_DEBUG_OBJECT (dec, "cannot convert start/stop for seek"); goto done; } }
static gboolean seek_mode_testing (InsanityTest * test) { gboolean res; GstEvent *event; GstSeekFlags flags = GST_SEEK_FLAG_FLUSH; GstSeekType stop_type = GST_SEEK_TYPE_NONE; /* Reset global seek props */ glob_seek_first_buf_ts = GST_CLOCK_TIME_NONE; glob_seek_stop_ts = GST_CLOCK_TIME_NONE; glob_seek_segment_seektime = 0; /* Set seeking arguments */ switch (glob_in_progress) { case TEST_BACKWARD_PLAYBACK: glob_seek_rate = -1; glob_seek_stop_ts = glob_duration; stop_type = GST_SEEK_TYPE_SET; break; case TEST_FAST_FORWARD: glob_seek_rate = 2; glob_seek_stop_ts = glob_duration / 2; break; case TEST_FAST_BACKWARD: glob_seek_rate = -2; glob_seek_stop_ts = glob_duration; stop_type = GST_SEEK_TYPE_SET; break; default: return FALSE; } glob_seek_got_segment = FALSE; event = gst_event_new_seek (glob_seek_rate, GST_FORMAT_TIME, flags, GST_SEEK_TYPE_SET, glob_seek_segment_seektime, stop_type, glob_seek_stop_ts); /* We didn't find any event/message with the seqnum we previously set */ if (glob_seqnum != 0 && glob_seqnum_found == FALSE) glob_wrong_seqnum = TRUE; glob_seqnum_found = FALSE; glob_seqnum = gst_util_seqnum_next (); gst_event_set_seqnum (event, glob_seqnum); res = gst_element_send_event (glob_pipeline, event); global_last_seek = g_get_monotonic_time (); if (!res) { validate_current_test (test, FALSE, "Could not send seek event"); glob_seek_rate = 0; glob_seqnum = 0; /* ... Next test */ next_test (test); } return FALSE; }
static void _push_eos (GstAggregator * self) { GstEvent *event; _push_mandatory_events (self); self->priv->send_eos = FALSE; event = gst_event_new_eos (); gst_event_set_seqnum (event, self->priv->seqnum); gst_pad_push_event (self->srcpad, event); }
static inline void _push_mandatory_events (GstAggregator * self) { GstAggregatorPrivate *priv = self->priv; if (g_atomic_int_get (&self->priv->send_stream_start)) { gchar s_id[32]; GST_INFO_OBJECT (self, "pushing stream start"); /* stream-start (FIXME: create id based on input ids) */ g_snprintf (s_id, sizeof (s_id), "agg-%08x", g_random_int ()); if (!gst_pad_push_event (self->srcpad, gst_event_new_stream_start (s_id))) { GST_WARNING_OBJECT (self->srcpad, "Sending stream start event failed"); } g_atomic_int_set (&self->priv->send_stream_start, FALSE); } if (self->priv->srccaps) { GST_INFO_OBJECT (self, "pushing caps: %" GST_PTR_FORMAT, self->priv->srccaps); if (!gst_pad_push_event (self->srcpad, gst_event_new_caps (self->priv->srccaps))) { GST_WARNING_OBJECT (self->srcpad, "Sending caps event failed"); } gst_caps_unref (self->priv->srccaps); self->priv->srccaps = NULL; } if (g_atomic_int_get (&self->priv->send_segment)) { if (!g_atomic_int_get (&self->priv->flush_seeking)) { GstEvent *segev = gst_event_new_segment (&self->segment); if (!self->priv->seqnum) self->priv->seqnum = gst_event_get_seqnum (segev); else gst_event_set_seqnum (segev, self->priv->seqnum); GST_DEBUG_OBJECT (self, "pushing segment %" GST_PTR_FORMAT, segev); gst_pad_push_event (self->srcpad, segev); g_atomic_int_set (&self->priv->send_segment, FALSE); } } if (priv->tags && priv->tags_changed) { gst_pad_push_event (self->srcpad, gst_event_new_tag (gst_tag_list_ref (priv->tags))); priv->tags_changed = FALSE; } }
static GstEvent * check_pending_key_unit_event (GstEvent * pending_event, GstSegment * segment, GstClockTime timestamp, guint flags, GstClockTime pending_key_unit_ts) { GstClockTime running_time, stream_time; gboolean all_headers; guint count; GstEvent *event = NULL; g_return_val_if_fail (pending_event != NULL, NULL); g_return_val_if_fail (segment != NULL, NULL); if (pending_event == NULL) goto out; if (GST_CLOCK_TIME_IS_VALID (pending_key_unit_ts) && timestamp == GST_CLOCK_TIME_NONE) goto out; running_time = gst_segment_to_running_time (segment, GST_FORMAT_TIME, timestamp); GST_INFO ("now %" GST_TIME_FORMAT " wanted %" GST_TIME_FORMAT, GST_TIME_ARGS (running_time), GST_TIME_ARGS (pending_key_unit_ts)); if (GST_CLOCK_TIME_IS_VALID (pending_key_unit_ts) && running_time < pending_key_unit_ts) goto out; if (flags & GST_BUFFER_FLAG_DELTA_UNIT) { GST_INFO ("pending force key unit, waiting for keyframe"); goto out; } stream_time = gst_segment_to_stream_time (segment, GST_FORMAT_TIME, timestamp); gst_video_event_parse_upstream_force_key_unit (pending_event, NULL, &all_headers, &count); event = gst_video_event_new_downstream_force_key_unit (timestamp, stream_time, running_time, all_headers, count); gst_event_set_seqnum (event, gst_event_get_seqnum (pending_event)); out: return event; }
/* sinkpad functions */ static gboolean gst_stream_synchronizer_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent); GstPad *opad; gboolean ret = FALSE; GST_LOG_OBJECT (pad, "Handling event %s: %" GST_PTR_FORMAT, GST_EVENT_TYPE_NAME (event), event); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_STREAM_START: { GstStream *stream, *ostream; guint32 seqnum = gst_event_get_seqnum (event); GList *l; gboolean all_wait = TRUE; gboolean new_stream = TRUE; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream && stream->stream_start_seqnum != seqnum) { stream->is_eos = FALSE; stream->stream_start_seqnum = seqnum; stream->drop_discont = TRUE; /* Check if this belongs to a stream that is already there, * e.g. we got the visualizations for an audio stream */ for (l = self->streams; l; l = l->next) { ostream = l->data; if (ostream != stream && ostream->stream_start_seqnum == seqnum && !ostream->wait) { new_stream = FALSE; break; } } if (!new_stream) { GST_DEBUG_OBJECT (pad, "Stream %d belongs to running stream %d, no waiting", stream->stream_number, ostream->stream_number); stream->wait = FALSE; stream->new_stream = FALSE; } else { GST_DEBUG_OBJECT (pad, "Stream %d changed", stream->stream_number); stream->wait = TRUE; stream->new_stream = TRUE; for (l = self->streams; l; l = l->next) { GstStream *ostream = l->data; all_wait = all_wait && ostream->wait; if (!all_wait) break; } if (all_wait) { gint64 position = 0; GST_DEBUG_OBJECT (self, "All streams have changed -- unblocking"); for (l = self->streams; l; l = l->next) { GstStream *ostream = l->data; gint64 stop_running_time; gint64 position_running_time; ostream->wait = FALSE; if (ostream->segment.format == GST_FORMAT_TIME) { stop_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.stop); position_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.position); position = MAX (position, MAX (stop_running_time, position_running_time)); } } position = MAX (0, position); self->group_start_time = MAX (self->group_start_time, position); GST_DEBUG_OBJECT (self, "New group start time: %" GST_TIME_FORMAT, GST_TIME_ARGS (self->group_start_time)); for (l = self->streams; l; l = l->next) { GstStream *ostream = l->data; g_cond_broadcast (&ostream->stream_finish_cond); } } } } else { GST_DEBUG_OBJECT (self, "No stream or STREAM_START from same source"); } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_SEGMENT:{ GstStream *stream; GstSegment segment; gst_event_copy_segment (event, &segment); GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) { if (stream->wait) { GST_DEBUG_OBJECT (pad, "Stream %d is waiting", stream->stream_number); g_cond_wait (&stream->stream_finish_cond, &self->lock); stream = gst_pad_get_element_private (pad); if (stream) stream->wait = FALSE; } } if (self->shutdown) { GST_STREAM_SYNCHRONIZER_UNLOCK (self); gst_event_unref (event); goto done; } if (stream && segment.format == GST_FORMAT_TIME) { if (stream->new_stream) { stream->new_stream = FALSE; segment.base = self->group_start_time; } GST_DEBUG_OBJECT (pad, "Segment was: %" GST_SEGMENT_FORMAT, &stream->segment); gst_segment_copy_into (&segment, &stream->segment); GST_DEBUG_OBJECT (pad, "Segment now is: %" GST_SEGMENT_FORMAT, &stream->segment); stream->segment_seqnum = gst_event_get_seqnum (event); GST_DEBUG_OBJECT (pad, "Stream start running time: %" GST_TIME_FORMAT, GST_TIME_ARGS (stream->segment.base)); { GstEvent *tmpev; tmpev = gst_event_new_segment (&stream->segment); gst_event_set_seqnum (tmpev, stream->segment_seqnum); gst_event_unref (event); event = tmpev; } } else if (stream) { GST_WARNING_OBJECT (pad, "Non-TIME segment: %s", gst_format_get_name (segment.format)); gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED); /* Since this stream is not time-based, we mark it so that * other streams don't wait forever on it */ stream->wait = TRUE; } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_FLUSH_START:{ GstStream *stream; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) { GST_DEBUG_OBJECT (pad, "Flushing streams"); g_cond_broadcast (&stream->stream_finish_cond); } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_FLUSH_STOP:{ GstStream *stream; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) { GST_DEBUG_OBJECT (pad, "Resetting segment for stream %d", stream->stream_number); gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED); stream->is_eos = FALSE; stream->wait = FALSE; stream->new_stream = FALSE; stream->drop_discont = FALSE; stream->seen_data = FALSE; g_cond_broadcast (&stream->stream_finish_cond); } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_EOS:{ GstStream *stream; GList *l; gboolean all_eos = TRUE; gboolean seen_data; GSList *pads = NULL; GstPad *srcpad; GstClockTime timestamp; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (!stream) { GST_STREAM_SYNCHRONIZER_UNLOCK (self); GST_WARNING_OBJECT (pad, "EOS for unknown stream"); break; } GST_DEBUG_OBJECT (pad, "Have EOS for stream %d", stream->stream_number); stream->is_eos = TRUE; seen_data = stream->seen_data; srcpad = gst_object_ref (stream->srcpad); if (seen_data && stream->segment.position != -1) timestamp = stream->segment.position; else if (stream->segment.rate < 0.0 || stream->segment.stop == -1) timestamp = stream->segment.start; else timestamp = stream->segment.stop; for (l = self->streams; l; l = l->next) { GstStream *ostream = l->data; all_eos = all_eos && ostream->is_eos; if (!all_eos) break; } if (all_eos) { GST_DEBUG_OBJECT (self, "All streams are EOS -- forwarding"); for (l = self->streams; l; l = l->next) { GstStream *ostream = l->data; /* local snapshot of current pads */ gst_object_ref (ostream->srcpad); pads = g_slist_prepend (pads, ostream->srcpad); } } GST_STREAM_SYNCHRONIZER_UNLOCK (self); /* drop lock when sending eos, which may block in e.g. preroll */ if (pads) { GstPad *pad; GSList *epad; ret = TRUE; epad = pads; while (epad) { pad = epad->data; GST_DEBUG_OBJECT (pad, "Pushing EOS"); ret = ret && gst_pad_push_event (pad, gst_event_new_eos ()); gst_object_unref (pad); epad = g_slist_next (epad); } g_slist_free (pads); } else { /* if EOS, but no data has passed, then send something to replace EOS * for preroll purposes */ if (!seen_data) { GstEvent *gap_event; gap_event = gst_event_new_gap (timestamp, GST_CLOCK_TIME_NONE); ret = gst_pad_push_event (srcpad, gap_event); } else { GstEvent *gap_event; /* FIXME: Also send a GAP event to let audio sinks start their * clock in case they did not have enough data yet */ gap_event = gst_event_new_gap (timestamp, GST_CLOCK_TIME_NONE); ret = gst_pad_push_event (srcpad, gap_event); } } gst_object_unref (srcpad); gst_event_unref (event); goto done; } default: break; } opad = gst_stream_get_other_pad_from_pad (self, pad); if (opad) { ret = gst_pad_push_event (opad, event); gst_object_unref (opad); } done: return ret; }
/* sinkpad functions */ static gboolean gst_stream_synchronizer_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstStreamSynchronizer *self = GST_STREAM_SYNCHRONIZER (parent); gboolean ret = FALSE; GST_LOG_OBJECT (pad, "Handling event %s: %" GST_PTR_FORMAT, GST_EVENT_TYPE_NAME (event), event); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_STREAM_START: { GstSyncStream *stream, *ostream; guint32 seqnum = gst_event_get_seqnum (event); guint group_id; gboolean have_group_id; GList *l; gboolean all_wait = TRUE; gboolean new_stream = TRUE; have_group_id = gst_event_parse_group_id (event, &group_id); GST_STREAM_SYNCHRONIZER_LOCK (self); self->have_group_id &= have_group_id; have_group_id = self->have_group_id; stream = gst_pad_get_element_private (pad); if (!stream) { GST_DEBUG_OBJECT (self, "No stream or STREAM_START from same source"); GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } gst_event_parse_stream_flags (event, &stream->flags); if ((have_group_id && stream->group_id != group_id) || (!have_group_id && stream->stream_start_seqnum != seqnum)) { stream->is_eos = FALSE; stream->eos_sent = FALSE; stream->flushing = FALSE; stream->stream_start_seqnum = seqnum; stream->group_id = group_id; if (!have_group_id) { /* Check if this belongs to a stream that is already there, * e.g. we got the visualizations for an audio stream */ for (l = self->streams; l; l = l->next) { ostream = l->data; if (ostream != stream && ostream->stream_start_seqnum == seqnum && !ostream->wait) { new_stream = FALSE; break; } } if (!new_stream) { GST_DEBUG_OBJECT (pad, "Stream %d belongs to running stream %d, no waiting", stream->stream_number, ostream->stream_number); stream->wait = FALSE; GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } } else if (group_id == self->group_id) { GST_DEBUG_OBJECT (pad, "Stream %d belongs to running group %d, " "no waiting", stream->stream_number, group_id); GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } GST_DEBUG_OBJECT (pad, "Stream %d changed", stream->stream_number); stream->wait = TRUE; for (l = self->streams; l; l = l->next) { GstSyncStream *ostream = l->data; all_wait = all_wait && ((ostream->flags & GST_STREAM_FLAG_SPARSE) || (ostream->wait && (!have_group_id || ostream->group_id == group_id))); if (!all_wait) break; } if (all_wait) { gint64 position = 0; if (have_group_id) GST_DEBUG_OBJECT (self, "All streams have changed to group id %u -- unblocking", group_id); else GST_DEBUG_OBJECT (self, "All streams have changed -- unblocking"); self->group_id = group_id; for (l = self->streams; l; l = l->next) { GstSyncStream *ostream = l->data; gint64 stop_running_time; gint64 position_running_time; ostream->wait = FALSE; if (ostream->segment.format == GST_FORMAT_TIME) { stop_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.stop); position_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.position); position_running_time = MAX (position_running_time, stop_running_time); position_running_time -= gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.start); position_running_time = MAX (0, position_running_time); position = MAX (position, position_running_time); } } self->group_start_time += position; GST_DEBUG_OBJECT (self, "New group start time: %" GST_TIME_FORMAT, GST_TIME_ARGS (self->group_start_time)); for (l = self->streams; l; l = l->next) { GstSyncStream *ostream = l->data; ostream->wait = FALSE; g_cond_broadcast (&ostream->stream_finish_cond); } } } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_SEGMENT:{ GstSyncStream *stream; GstSegment segment; gst_event_copy_segment (event, &segment); GST_STREAM_SYNCHRONIZER_LOCK (self); gst_stream_synchronizer_wait (self, pad); if (self->shutdown) { GST_STREAM_SYNCHRONIZER_UNLOCK (self); gst_event_unref (event); goto done; } stream = gst_pad_get_element_private (pad); if (stream && segment.format == GST_FORMAT_TIME) { GST_DEBUG_OBJECT (pad, "New stream, updating base from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, GST_TIME_ARGS (segment.base), GST_TIME_ARGS (segment.base + self->group_start_time)); segment.base += self->group_start_time; GST_DEBUG_OBJECT (pad, "Segment was: %" GST_SEGMENT_FORMAT, &stream->segment); gst_segment_copy_into (&segment, &stream->segment); GST_DEBUG_OBJECT (pad, "Segment now is: %" GST_SEGMENT_FORMAT, &stream->segment); stream->segment_seqnum = gst_event_get_seqnum (event); GST_DEBUG_OBJECT (pad, "Stream start running time: %" GST_TIME_FORMAT, GST_TIME_ARGS (stream->segment.base)); { GstEvent *tmpev; tmpev = gst_event_new_segment (&stream->segment); gst_event_set_seqnum (tmpev, stream->segment_seqnum); gst_event_unref (event); event = tmpev; } } else if (stream) { GST_WARNING_OBJECT (pad, "Non-TIME segment: %s", gst_format_get_name (segment.format)); gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED); } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_FLUSH_START:{ GstSyncStream *stream; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); self->eos = FALSE; if (stream) { GST_DEBUG_OBJECT (pad, "Flushing streams"); stream->flushing = TRUE; g_cond_broadcast (&stream->stream_finish_cond); } GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } case GST_EVENT_FLUSH_STOP:{ GstSyncStream *stream; GList *l; GstClockTime new_group_start_time = 0; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) { GST_DEBUG_OBJECT (pad, "Resetting segment for stream %d", stream->stream_number); gst_segment_init (&stream->segment, GST_FORMAT_UNDEFINED); stream->is_eos = FALSE; stream->eos_sent = FALSE; stream->flushing = FALSE; stream->wait = FALSE; g_cond_broadcast (&stream->stream_finish_cond); } for (l = self->streams; l; l = l->next) { GstSyncStream *ostream = l->data; GstClockTime start_running_time; if (ostream == stream || ostream->flushing) continue; if (ostream->segment.format == GST_FORMAT_TIME) { start_running_time = gst_segment_to_running_time (&ostream->segment, GST_FORMAT_TIME, ostream->segment.start); new_group_start_time = MAX (new_group_start_time, start_running_time); } } GST_DEBUG_OBJECT (pad, "Updating group start time from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT, GST_TIME_ARGS (self->group_start_time), GST_TIME_ARGS (new_group_start_time)); self->group_start_time = new_group_start_time; GST_STREAM_SYNCHRONIZER_UNLOCK (self); break; } /* unblocking EOS wait when track switch. */ case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:{ if (gst_event_has_name (event, "playsink-custom-video-flush") || gst_event_has_name (event, "playsink-custom-audio-flush") || gst_event_has_name (event, "playsink-custom-subtitle-flush")) { GstSyncStream *stream; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) { stream->is_eos = FALSE; stream->eos_sent = FALSE; stream->wait = FALSE; g_cond_broadcast (&stream->stream_finish_cond); } GST_STREAM_SYNCHRONIZER_UNLOCK (self); } break; } case GST_EVENT_EOS:{ GstSyncStream *stream; GList *l; gboolean all_eos = TRUE; gboolean seen_data; GSList *pads = NULL; GstPad *srcpad; GstClockTime timestamp; GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (!stream) { GST_STREAM_SYNCHRONIZER_UNLOCK (self); GST_WARNING_OBJECT (pad, "EOS for unknown stream"); break; } GST_DEBUG_OBJECT (pad, "Have EOS for stream %d", stream->stream_number); stream->is_eos = TRUE; seen_data = stream->seen_data; srcpad = gst_object_ref (stream->srcpad); if (seen_data && stream->segment.position != -1) timestamp = stream->segment.position; else if (stream->segment.rate < 0.0 || stream->segment.stop == -1) timestamp = stream->segment.start; else timestamp = stream->segment.stop; stream->segment.position = timestamp; for (l = self->streams; l; l = l->next) { GstSyncStream *ostream = l->data; all_eos = all_eos && ostream->is_eos; if (!all_eos) break; } if (all_eos) { GST_DEBUG_OBJECT (self, "All streams are EOS -- forwarding"); self->eos = TRUE; for (l = self->streams; l; l = l->next) { GstSyncStream *ostream = l->data; /* local snapshot of current pads */ gst_object_ref (ostream->srcpad); pads = g_slist_prepend (pads, ostream->srcpad); } } if (pads) { GstPad *pad; GSList *epad; GstSyncStream *ostream; ret = TRUE; epad = pads; while (epad) { pad = epad->data; ostream = gst_pad_get_element_private (pad); if (ostream) { g_cond_broadcast (&ostream->stream_finish_cond); } gst_object_unref (pad); epad = g_slist_next (epad); } g_slist_free (pads); } else { if (seen_data) { self->send_gap_event = TRUE; stream->gap_duration = GST_CLOCK_TIME_NONE; stream->wait = TRUE; ret = gst_stream_synchronizer_wait (self, srcpad); } } /* send eos if haven't seen data. seen_data will be true if data buffer * of the track have received in anytime. sink is ready if seen_data is * true, so can send GAP event. Will send EOS if sink isn't ready. The * scenario for the case is one track haven't any media data and then * send EOS. Or no any valid media data in one track, so decoder can't * get valid CAPS for the track. sink can't ready without received CAPS.*/ if (!seen_data || self->eos) { GST_DEBUG_OBJECT (pad, "send EOS event"); /* drop lock when sending eos, which may block in e.g. preroll */ GST_STREAM_SYNCHRONIZER_UNLOCK (self); ret = gst_pad_push_event (srcpad, gst_event_new_eos ()); GST_STREAM_SYNCHRONIZER_LOCK (self); stream = gst_pad_get_element_private (pad); if (stream) { stream->eos_sent = TRUE; } } gst_object_unref (srcpad); gst_event_unref (event); GST_STREAM_SYNCHRONIZER_UNLOCK (self); goto done; } default: break; } ret = gst_pad_event_default (pad, parent, event); done: return ret; }
gboolean mpegts_base_handle_seek_event (MpegTSBase * base, GstPad * pad, GstEvent * event) { MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base); GstFlowReturn ret = GST_FLOW_ERROR; gdouble rate; gboolean flush; GstFormat format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; GstEvent *flush_event = NULL; gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, &stop_type, &stop); if (format != GST_FORMAT_TIME) return FALSE; if (GST_EVENT_SEQNUM (event) == base->last_seek_seqnum) { GST_DEBUG_OBJECT (base, "Skipping already handled seek"); return TRUE; } if (base->mode == BASE_MODE_PUSHING) { /* First try if upstream supports seeking in TIME format */ if (gst_pad_push_event (base->sinkpad, gst_event_ref (event))) { GST_DEBUG ("upstream handled SEEK event"); return TRUE; } /* If the subclass can seek, do that */ if (klass->seek) { ret = klass->seek (base, event); if (G_UNLIKELY (ret != GST_FLOW_OK)) GST_WARNING ("seeking failed %s", gst_flow_get_name (ret)); else { GstEvent *new_seek; if (GST_CLOCK_TIME_IS_VALID (base->seek_offset)) { base->mode = BASE_MODE_SEEKING; new_seek = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET, base->seek_offset, GST_SEEK_TYPE_NONE, -1); gst_event_set_seqnum (new_seek, GST_EVENT_SEQNUM (event)); if (!gst_pad_push_event (base->sinkpad, new_seek)) ret = GST_FLOW_ERROR; else base->last_seek_seqnum = GST_EVENT_SEQNUM (event); } base->mode = BASE_MODE_PUSHING; } } else { GST_WARNING ("subclass has no seek implementation"); } return ret == GST_FLOW_OK; } if (!klass->seek) { GST_WARNING ("subclass has no seek implementation"); return FALSE; } if (rate <= 0.0) { GST_WARNING ("Negative rate not supported"); return FALSE; } GST_DEBUG ("seek event, rate: %f start: %" GST_TIME_FORMAT " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); flush = flags & GST_SEEK_FLAG_FLUSH; /* stop streaming, either by flushing or by pausing the task */ base->mode = BASE_MODE_SEEKING; if (flush) { GST_DEBUG_OBJECT (base, "sending flush start"); flush_event = gst_event_new_flush_start (); gst_event_set_seqnum (flush_event, GST_EVENT_SEQNUM (event)); gst_pad_push_event (base->sinkpad, gst_event_ref (flush_event)); GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, flush_event); } else gst_pad_pause_task (base->sinkpad); /* wait for streaming to finish */ GST_PAD_STREAM_LOCK (base->sinkpad); if (flush) { /* send a FLUSH_STOP for the sinkpad, since we need data for seeking */ GST_DEBUG_OBJECT (base, "sending flush stop"); flush_event = gst_event_new_flush_stop (TRUE); gst_event_set_seqnum (flush_event, GST_EVENT_SEQNUM (event)); /* ref for it to be reused later */ gst_pad_push_event (base->sinkpad, gst_event_ref (flush_event)); /* And actually flush our pending data but allow to preserve some info * to perform the seek */ mpegts_base_flush (base, FALSE); mpegts_packetizer_flush (base->packetizer, FALSE); } if (flags & (GST_SEEK_FLAG_SEGMENT)) { GST_WARNING ("seek flags 0x%x are not supported", (int) flags); goto done; } /* If the subclass can seek, do that */ ret = klass->seek (base, event); if (G_UNLIKELY (ret != GST_FLOW_OK)) GST_WARNING ("seeking failed %s", gst_flow_get_name (ret)); else base->last_seek_seqnum = GST_EVENT_SEQNUM (event); if (flush_event) { /* if we sent a FLUSH_START, we now send a FLUSH_STOP */ GST_DEBUG_OBJECT (base, "sending flush stop"); GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, flush_event); flush_event = NULL; } done: if (flush_event) gst_event_unref (flush_event); gst_pad_start_task (base->sinkpad, (GstTaskFunction) mpegts_base_loop, base, NULL); GST_PAD_STREAM_UNLOCK (base->sinkpad); return ret == GST_FLOW_OK; }
static gboolean gst_midi_parse_perform_seek (GstMidiParse * midiparse, GstEvent * event) { gboolean res = TRUE, tres; gdouble rate; GstFormat seek_format; GstSeekFlags flags; GstSeekType start_type, stop_type; gint64 start, stop; gboolean flush; gboolean update; GstSegment seeksegment; guint32 seqnum; GstEvent *tevent; GST_DEBUG_OBJECT (midiparse, "doing seek: %" GST_PTR_FORMAT, event); if (event) { gst_event_parse_seek (event, &rate, &seek_format, &flags, &start_type, &start, &stop_type, &stop); if (seek_format != GST_FORMAT_TIME) goto invalid_format; flush = flags & GST_SEEK_FLAG_FLUSH; seqnum = gst_event_get_seqnum (event); } else { flush = FALSE; /* get next seqnum */ seqnum = gst_util_seqnum_next (); } /* send flush start */ if (flush) { tevent = gst_event_new_flush_start (); gst_event_set_seqnum (tevent, seqnum); gst_pad_push_event (midiparse->srcpad, tevent); } else gst_pad_pause_task (midiparse->srcpad); /* grab streaming lock, this should eventually be possible, either * because the task is paused, our streaming thread stopped * or because our peer is flushing. */ GST_PAD_STREAM_LOCK (midiparse->sinkpad); if (G_UNLIKELY (midiparse->seqnum == seqnum)) { /* we have seen this event before, issue a warning for now */ GST_WARNING_OBJECT (midiparse, "duplicate event found %" G_GUINT32_FORMAT, seqnum); } else { midiparse->seqnum = seqnum; GST_DEBUG_OBJECT (midiparse, "seek with seqnum %" G_GUINT32_FORMAT, seqnum); } /* Copy the current segment info into the temp segment that we can actually * attempt the seek with. We only update the real segment if the seek succeeds. */ memcpy (&seeksegment, &midiparse->segment, sizeof (GstSegment)); /* now configure the final seek segment */ if (event) { gst_segment_do_seek (&seeksegment, rate, seek_format, flags, start_type, start, stop_type, stop, &update); } /* Else, no seek event passed, so we're just (re)starting the current segment. */ GST_DEBUG_OBJECT (midiparse, "segment configured from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT, seeksegment.start, seeksegment.stop, seeksegment.position); /* do the seek, segment.position contains the new position. */ res = gst_midi_parse_do_seek (midiparse, &seeksegment); /* and prepare to continue streaming */ if (flush) { tevent = gst_event_new_flush_stop (TRUE); gst_event_set_seqnum (tevent, seqnum); /* send flush stop, peer will accept data and events again. We * are not yet providing data as we still have the STREAM_LOCK. */ gst_pad_push_event (midiparse->srcpad, tevent); } /* if the seek was successful, we update our real segment and push * out the new segment. */ if (res) { GST_OBJECT_LOCK (midiparse); memcpy (&midiparse->segment, &seeksegment, sizeof (GstSegment)); GST_OBJECT_UNLOCK (midiparse); if (seeksegment.flags & GST_SEGMENT_FLAG_SEGMENT) { GstMessage *message; message = gst_message_new_segment_start (GST_OBJECT (midiparse), seeksegment.format, seeksegment.position); gst_message_set_seqnum (message, seqnum); gst_element_post_message (GST_ELEMENT (midiparse), message); } /* for deriving a stop position for the playback segment from the seek * segment, we must take the duration when the stop is not set */ if ((stop = seeksegment.stop) == -1) stop = seeksegment.duration; midiparse->segment_pending = TRUE; midiparse->discont = TRUE; } /* and restart the task in case it got paused explicitly or by * the FLUSH_START event we pushed out. */ tres = gst_pad_start_task (midiparse->sinkpad, (GstTaskFunction) gst_midi_parse_loop, midiparse->sinkpad, NULL); if (res && !tres) res = FALSE; /* and release the lock again so we can continue streaming */ GST_PAD_STREAM_UNLOCK (midiparse->sinkpad); return res; /* ERROR */ invalid_format: { GST_DEBUG_OBJECT (midiparse, "Unsupported seek format %s", gst_format_get_name (seek_format)); return FALSE; } }
static gboolean theora_dec_src_event (GstPad * pad, GstEvent * event) { gboolean res = TRUE; GstTheoraDec *dec; dec = GST_THEORA_DEC (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: { GstFormat format, tformat; gdouble rate; GstEvent *real_seek; GstSeekFlags flags; GstSeekType cur_type, stop_type; gint64 cur, stop; gint64 tcur, tstop; guint32 seqnum; gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, &stop_type, &stop); seqnum = gst_event_get_seqnum (event); gst_event_unref (event); /* we have to ask our peer to seek to time here as we know * nothing about how to generate a granulepos from the src * formats or anything. * * First bring the requested format to time */ tformat = GST_FORMAT_TIME; if (!(res = theora_dec_src_convert (pad, format, cur, &tformat, &tcur))) goto convert_error; if (!(res = theora_dec_src_convert (pad, format, stop, &tformat, &tstop))) goto convert_error; /* then seek with time on the peer */ real_seek = gst_event_new_seek (rate, GST_FORMAT_TIME, flags, cur_type, tcur, stop_type, tstop); gst_event_set_seqnum (real_seek, seqnum); res = gst_pad_push_event (dec->sinkpad, real_seek); break; } case GST_EVENT_QOS: { gdouble proportion; GstClockTimeDiff diff; GstClockTime timestamp; gst_event_parse_qos (event, &proportion, &diff, ×tamp); /* we cannot randomly skip frame decoding since we don't have * B frames. we can however use the timestamp and diff to not * push late frames. This would at least save us the time to * crop/memcpy the data. */ GST_OBJECT_LOCK (dec); dec->proportion = proportion; dec->earliest_time = timestamp + diff; GST_OBJECT_UNLOCK (dec); GST_DEBUG_OBJECT (dec, "got QoS %" GST_TIME_FORMAT ", %" G_GINT64_FORMAT, GST_TIME_ARGS (timestamp), diff); res = gst_pad_push_event (dec->sinkpad, event); break; } default: res = gst_pad_push_event (dec->sinkpad, event); break; } done: gst_object_unref (dec); return res; /* ERRORS */ convert_error: { GST_DEBUG_OBJECT (dec, "could not convert format"); goto done; } }