static gboolean gst_raw_parse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstRawParse *rp = GST_RAW_PARSE (parent); gboolean ret; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: case GST_EVENT_FLUSH_STOP: /* Only happens in push mode */ gst_raw_parse_reset (rp); ret = gst_pad_push_event (rp->srcpad, event); break; case GST_EVENT_SEGMENT: { GstSegment segment; /* Only happens in push mode */ gst_event_copy_segment (event, &segment); if (segment.format != GST_FORMAT_TIME) { gst_event_unref (event); ret = gst_raw_parse_convert (rp, segment.format, segment.start, GST_FORMAT_TIME, (gint64 *) & segment.start); ret &= gst_raw_parse_convert (rp, segment.format, segment.time, GST_FORMAT_TIME, (gint64 *) & segment.time); ret &= gst_raw_parse_convert (rp, segment.format, segment.stop, GST_FORMAT_TIME, (gint64 *) & segment.stop); if (!ret) { GST_ERROR_OBJECT (rp, "Failed converting to GST_FORMAT_TIME format (%d)", segment.format); break; } segment.format = GST_FORMAT_TIME; event = gst_event_new_segment (&segment); } gst_segment_copy_into (&segment, &rp->segment); if (rp->start_segment) gst_event_unref (rp->start_segment); rp->start_segment = event; ret = TRUE; break; } default: ret = gst_pad_event_default (pad, parent, event); break; } return ret; }
/** * gst_event_copy_segment: * @event: The event to parse * @segment: a pointer to a #GstSegment * * Parses a segment @event and copies the #GstSegment into the location * given by @segment. */ void gst_event_copy_segment (GstEvent * event, GstSegment * segment) { const GstSegment *src; g_return_if_fail (GST_IS_EVENT (event)); g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT); if (segment) { gst_event_parse_segment (event, &src); gst_segment_copy_into (src, segment); } }
static gboolean gst_dvdec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstDVDec *dvdec; gboolean res = TRUE; dvdec = GST_DVDEC (parent); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP: gst_segment_init (&dvdec->segment, GST_FORMAT_UNDEFINED); dvdec->need_segment = FALSE; break; case GST_EVENT_SEGMENT:{ const GstSegment *segment; gst_event_parse_segment (event, &segment); GST_DEBUG_OBJECT (dvdec, "Got SEGMENT %" GST_SEGMENT_FORMAT, &segment); gst_segment_copy_into (segment, &dvdec->segment); if (!gst_pad_has_current_caps (dvdec->srcpad)) { dvdec->need_segment = TRUE; gst_event_unref (event); event = NULL; res = TRUE; } else { dvdec->need_segment = FALSE; } break; } case GST_EVENT_CAPS: { GstCaps *caps; gst_event_parse_caps (event, &caps); res = gst_dvdec_sink_setcaps (dvdec, caps); gst_event_unref (event); event = NULL; break; } default: break; } if (event) res = gst_pad_push_event (dvdec->srcpad, event); return res; }
static gboolean gst_segment_clip_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstSegmentClip *self = GST_SEGMENT_CLIP (parent); gboolean ret = TRUE; GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: { GstCaps *caps; /* should be only downstream */ g_assert (pad == self->sinkpad); gst_event_parse_caps (event, &caps); ret = gst_segment_clip_sink_setcaps (self, caps); break; } case GST_EVENT_SEGMENT: { const GstSegment *segment; gst_event_parse_segment (event, &segment); GST_DEBUG_OBJECT (pad, "Got NEWSEGMENT event %" GST_SEGMENT_FORMAT, segment); gst_segment_copy_into (segment, &self->segment); break; } case GST_EVENT_FLUSH_STOP: gst_segment_clip_reset (self); break; default: break; } if (ret) ret = gst_pad_event_default (pad, parent, event); else gst_event_unref (event); return ret; }
/** * gst_sample_new: * @buffer: (transfer none) (allow-none): a #GstBuffer, or NULL * @caps: (transfer none) (allow-none): a #GstCaps, or NULL * @segment: transfer none) (allow-none): a #GstSegment, or NULL * @info: (transfer full) (allow-none): a #GstStructure, or NULL * * Create a new #GstSample with the provided details. * * Free-function: gst_sample_unref * * Returns: (transfer full): the new #GstSample. gst_sample_unref() * after usage. */ GstSample * gst_sample_new (GstBuffer * buffer, GstCaps * caps, const GstSegment * segment, GstStructure * info) { GstSample *sample; sample = g_slice_new0 (GstSample); GST_LOG ("new %p", sample); gst_mini_object_init (GST_MINI_OBJECT_CAST (sample), 0, _gst_sample_type, (GstMiniObjectCopyFunction) _gst_sample_copy, NULL, (GstMiniObjectFreeFunction) _gst_sample_free); sample->buffer = buffer ? gst_buffer_ref (buffer) : NULL; sample->caps = caps ? gst_caps_ref (caps) : NULL; if (segment) gst_segment_copy_into (segment, &sample->segment); else gst_segment_init (&sample->segment, GST_FORMAT_TIME); if (info) { if (!gst_structure_set_parent_refcount (info, &sample->mini_object.refcount)) goto had_parent; sample->info = info; } return sample; /* ERRORS */ had_parent: { gst_sample_unref (sample); g_warning ("structure is already owned by another object"); return NULL; } }
/* sink and src pad event handlers */ static gboolean gst_ivtc_sink_event (GstBaseTransform * trans, GstEvent * event) { GstIvtc *ivtc = GST_IVTC (trans); GST_DEBUG_OBJECT (ivtc, "sink_event"); if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) { const GstSegment *seg; gst_ivtc_flush (ivtc); /* FIXME this should handle update events */ gst_event_parse_segment (event, &seg); gst_segment_copy_into (seg, &ivtc->segment); ivtc->current_ts = ivtc->segment.start; } return GST_BASE_TRANSFORM_CLASS (gst_ivtc_parent_class)->sink_event (trans, event); }
GstPadProbeReturn GStreamerReader::EventProbe(GstPad *aPad, GstEvent *aEvent) { GstElement* parent = GST_ELEMENT(gst_pad_get_parent(aPad)); LOG(PR_LOG_DEBUG, "event probe %s", GST_EVENT_TYPE_NAME (aEvent)); switch(GST_EVENT_TYPE(aEvent)) { case GST_EVENT_SEGMENT: { const GstSegment *newSegment; GstSegment* segment; /* Store the segments so we can convert timestamps to stream time, which * is what the upper layers sync on. */ ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); #if GST_VERSION_MINOR <= 1 && GST_VERSION_MICRO < 1 ResetDecode(); #endif gst_event_parse_segment(aEvent, &newSegment); if (parent == GST_ELEMENT(mVideoAppSink)) segment = &mVideoSegment; else segment = &mAudioSegment; gst_segment_copy_into (newSegment, segment); break; } case GST_EVENT_FLUSH_STOP: /* Reset on seeks */ ResetDecode(); break; default: break; } gst_object_unref(parent); return GST_PAD_PROBE_OK; }
static gboolean gst_video_rate_sink_event (GstBaseTransform * trans, GstEvent * event) { GstVideoRate *videorate; videorate = GST_VIDEO_RATE (trans); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT: { const GstSegment *segment; gst_event_parse_segment (event, &segment); if (segment->format != GST_FORMAT_TIME) goto format_error; GST_DEBUG_OBJECT (videorate, "handle NEWSEGMENT"); /* close up the previous segment, if appropriate */ if (videorate->prevbuf) { gint count = 0; GstFlowReturn res; res = GST_FLOW_OK; /* fill up to the end of current segment, * or only send out the stored buffer if there is no specific stop. * regardless, prevent going loopy in strange cases */ while (res == GST_FLOW_OK && count <= MAGIC_LIMIT && ((GST_CLOCK_TIME_IS_VALID (videorate->segment.stop) && videorate->next_ts - videorate->segment.base < videorate->segment.stop) || count < 1)) { res = gst_video_rate_flush_prev (videorate, count > 0); count++; } if (count > 1) { videorate->dup += count - 1; if (!videorate->silent) gst_video_rate_notify_duplicate (videorate); } else if (count == 0) { videorate->drop++; if (!videorate->silent) gst_video_rate_notify_drop (videorate); } /* clean up for the new one; _chain will resume from the new start */ gst_video_rate_swap_prev (videorate, NULL, 0); } videorate->base_ts = 0; videorate->out_frame_count = 0; videorate->next_ts = GST_CLOCK_TIME_NONE; /* We just want to update the accumulated stream_time */ gst_segment_copy_into (segment, &videorate->segment); GST_DEBUG_OBJECT (videorate, "updated segment: %" GST_SEGMENT_FORMAT, &videorate->segment); break; } case GST_EVENT_EOS:{ gint count = 0; GstFlowReturn res = GST_FLOW_OK; GST_DEBUG_OBJECT (videorate, "Got EOS"); /* If the segment has a stop position, fill the segment */ if (GST_CLOCK_TIME_IS_VALID (videorate->segment.stop)) { /* fill up to the end of current segment, * or only send out the stored buffer if there is no specific stop. * regardless, prevent going loopy in strange cases */ while (res == GST_FLOW_OK && count <= MAGIC_LIMIT && ((videorate->next_ts - videorate->segment.base < videorate->segment.stop) || count < 1)) { res = gst_video_rate_flush_prev (videorate, count > 0); count++; } } else if (videorate->prevbuf) { /* Output at least one frame but if the buffer duration is valid, output * enough frames to use the complete buffer duration */ if (GST_BUFFER_DURATION_IS_VALID (videorate->prevbuf)) { GstClockTime end_ts = videorate->next_ts + GST_BUFFER_DURATION (videorate->prevbuf); while (res == GST_FLOW_OK && count <= MAGIC_LIMIT && ((videorate->next_ts - videorate->segment.base < end_ts) || count < 1)) { res = gst_video_rate_flush_prev (videorate, count > 0); count++; } } else { res = gst_video_rate_flush_prev (videorate, FALSE); count = 1; } } if (count > 1) { videorate->dup += count - 1; if (!videorate->silent) gst_video_rate_notify_duplicate (videorate); } else if (count == 0) { videorate->drop++; if (!videorate->silent) gst_video_rate_notify_drop (videorate); } break; } case GST_EVENT_FLUSH_STOP: /* also resets the segment */ GST_DEBUG_OBJECT (videorate, "Got FLUSH_STOP"); gst_video_rate_reset (videorate); break; case GST_EVENT_GAP: /* no gaps after videorate, ignore the event */ gst_event_unref (event); return TRUE; default: break; } return GST_BASE_TRANSFORM_CLASS (parent_class)->sink_event (trans, event); /* ERRORS */ format_error: { GST_WARNING_OBJECT (videorate, "Got segment but doesn't have GST_FORMAT_TIME value"); return FALSE; } }
/* 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; }
/** * progress_buffer_enqueue_item() * * Add an item in the queue. Must be called in the locked context. Item may be event or data. */ static GstFlowReturn progress_buffer_enqueue_item(ProgressBuffer *element, GstMiniObject *item) { gboolean signal = FALSE; if (GST_IS_BUFFER (item)) { gdouble elapsed; // update sink segment position element->sink_segment.position = GST_BUFFER_OFFSET(GST_BUFFER(item)) + gst_buffer_get_size (GST_BUFFER(item)); if(element->sink_segment.stop < element->sink_segment.position) // This must never happen. return GST_FLOW_ERROR; cache_write_buffer(element->cache, GST_BUFFER(item)); elapsed = g_timer_elapsed(element->bandwidth_timer, NULL); element->subtotal += gst_buffer_get_size (GST_BUFFER(item)); if (elapsed > 1.0) { element->bandwidth = element->subtotal/elapsed; element->subtotal = 0; g_timer_start(element->bandwidth_timer); } // send buffer progress position up (used to track buffer fill, etc.) signal = send_position_message(element, signal); } else if (GST_IS_EVENT (item)) { GstEvent *event = GST_EVENT_CAST (item); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: element->eos_status.eos = TRUE; if (element->sink_segment.position < element->sink_segment.stop) element->sink_segment.stop = element->sink_segment.position; progress_buffer_set_pending_event(element, NULL); signal = send_position_message(element, TRUE); gst_event_unref(event); // INLINE - gst_event_unref() break; case GST_EVENT_SEGMENT: { GstSegment segment; element->unexpected = FALSE; gst_event_copy_segment (event, &segment); if (segment.format != GST_FORMAT_BYTES) { gst_element_message_full(GST_ELEMENT(element), GST_MESSAGE_ERROR, GST_STREAM_ERROR, GST_STREAM_ERROR_FORMAT, g_strdup("GST_FORMAT_BYTES buffers expected."), NULL, ("progressbuffer.c"), ("progress_buffer_enqueue_item"), 0); gst_event_unref(event); // INLINE - gst_event_unref() return GST_FLOW_ERROR; } if (segment.stop - segment.start <= 0) { gst_element_message_full(GST_ELEMENT(element), GST_MESSAGE_ERROR, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE, g_strdup("Only limited content is supported by progressbuffer."), NULL, ("progressbuffer.c"), ("progress_buffer_enqueue_item"), 0); gst_event_unref(event); // INLINE - gst_event_unref() return GST_FLOW_ERROR; } if ((segment.flags & GST_SEGMENT_FLAG_UPDATE) == GST_SEGMENT_FLAG_UPDATE) // Updating segments create new cache. { if (element->cache) destroy_cache(element->cache); element->cache = create_cache(); if (!element->cache) { gst_element_message_full(GST_ELEMENT(element), GST_MESSAGE_ERROR, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_OPEN_READ_WRITE, g_strdup("Couldn't create backing cache"), NULL, ("progressbuffer.c"), ("progress_buffer_enqueue_item"), 0); gst_event_unref(event); // INLINE - gst_event_unref() return GST_FLOW_ERROR; } } else { cache_set_write_position(element->cache, 0); cache_set_read_position(element->cache, 0); element->cache_read_offset = segment.start; } gst_segment_copy_into (&segment, &element->sink_segment); progress_buffer_set_pending_event(element, event); element->instant_seek = TRUE; signal = send_position_message(element, TRUE); break; } default: gst_event_unref(event); // INLINE - gst_event_unref() break; } } if (signal) g_cond_signal(&element->add_cond); return GST_FLOW_OK; }
static gboolean mpegts_demuxer_sink_event(GstPad *pad, GstObject *parent, GstEvent *event) { MpegTSDemuxer *demuxer = MPEGTS_DEMUXER(parent); gboolean result = TRUE; switch (GST_EVENT_TYPE(event)) { case GST_EVENT_EOS: #ifdef DEBUG_OUTPUT g_print("MpegTS sinkEvent: EOS\n"); #endif g_mutex_lock(&demuxer->lock); demuxer->is_eos = TRUE; g_cond_signal(&demuxer->add_cond); // Signal read callback to read up remaining data. g_mutex_unlock(&demuxer->lock); gst_event_unref(event); break; case GST_EVENT_FLUSH_START: #ifdef DEBUG_OUTPUT g_print("MpegTS sinkEvent: FLUSH_START ..."); #endif result = gst_pad_event_default(demuxer->sinkpad, parent, event); g_mutex_lock(&demuxer->lock); demuxer->is_flushing = TRUE; g_cond_signal(&demuxer->del_cond); // Signal _chain() and read callback to wake them up and update state. g_cond_signal(&demuxer->add_cond); g_mutex_unlock(&demuxer->lock); if (demuxer->reader_thread) { g_thread_join(demuxer->reader_thread); demuxer->reader_thread = NULL; } #ifdef DEBUG_OUTPUT g_print("done.\n"); #endif break; case GST_EVENT_FLUSH_STOP: // Stop flushing buffers. g_mutex_lock(&demuxer->lock); mpegts_demuxer_flush(demuxer); demuxer->is_flushing = FALSE; // Unset flag so chain function accepts buffers. g_mutex_unlock(&demuxer->lock); result = gst_pad_event_default(demuxer->sinkpad, parent, event); #ifdef DEBUG_OUTPUT g_print("MpegTS sinkEvent: FLUSH_STOP\n"); #endif break; case GST_EVENT_SEGMENT: { GstSegment segment; gst_event_copy_segment(event, &segment); gst_event_unref(event); g_mutex_lock(&demuxer->lock); if (!demuxer->is_closing) { #ifdef DEBUG_OUTPUT g_print("MpegTS sinkEvent: NEW_SEGMENT, time=%.2f\n", time != GST_CLOCK_TIME_NONE ? (double)time/GST_SECOND : -1.0); #endif if (segment.format == GST_FORMAT_TIME) { gst_segment_copy_into(&segment, &demuxer->audio.segment); gst_segment_copy_into(&segment, &demuxer->video.segment); } demuxer->audio.discont = demuxer->video.discont = TRUE; demuxer->is_eos = FALSE; demuxer->is_last_buffer_send = FALSE; demuxer->is_reading = TRUE; if (!demuxer->reader_thread) { demuxer->reader_thread = g_thread_new(NULL, mpegts_demuxer_process_input, demuxer); #ifdef DEBUG_OUTPUT g_print("MpegTS: Process_input thread created\n"); #endif } else post_message(demuxer, "Demuxer thread is not null", GST_MESSAGE_ERROR, GST_CORE_ERROR, GST_CORE_ERROR_THREAD); } g_mutex_unlock(&demuxer->lock); break; } default: result = mpegts_demuxer_push_to_sources(demuxer, event); break; } return result; }
static gboolean splitmux_part_pad_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstSplitMuxPartPad *part_pad = SPLITMUX_PART_PAD_CAST (pad); GstSplitMuxPartReader *reader = part_pad->reader; gboolean ret = TRUE; SplitMuxSrcPad *target; GstDataQueueItem *item; SPLITMUX_PART_LOCK (reader); target = gst_object_ref (part_pad->target); GST_LOG_OBJECT (reader, "Pad %" GST_PTR_FORMAT " event %" GST_PTR_FORMAT, pad, event); if (part_pad->flushing && GST_EVENT_TYPE (event) != GST_EVENT_FLUSH_STOP) goto drop_event; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEGMENT:{ GstSegment *seg = &part_pad->segment; GST_LOG_OBJECT (pad, "Received segment %" GST_PTR_FORMAT, event); gst_event_copy_segment (event, seg); gst_event_copy_segment (event, &part_pad->orig_segment); if (seg->format != GST_FORMAT_TIME) goto wrong_segment; /* Adjust segment */ /* Adjust start/stop so the overall file is 0 + start_offset based */ if (seg->stop != -1) { seg->stop -= seg->start; seg->stop += seg->time + reader->start_offset; } seg->start = seg->time + reader->start_offset; seg->time += reader->start_offset; seg->position += reader->start_offset; GST_LOG_OBJECT (pad, "Adjusted segment now %" GST_PTR_FORMAT, event); /* Replace event */ gst_event_unref (event); event = gst_event_new_segment (seg); if (reader->prep_state != PART_STATE_PREPARING_COLLECT_STREAMS && reader->prep_state != PART_STATE_PREPARING_MEASURE_STREAMS) break; /* Only do further stuff with segments during initial measuring */ /* Take the first segment from the first part */ if (target->segment.format == GST_FORMAT_UNDEFINED) { gst_segment_copy_into (seg, &target->segment); GST_DEBUG_OBJECT (reader, "Target pad segment now %" GST_SEGMENT_FORMAT, &target->segment); } if (seg->stop != -1 && target->segment.stop != -1) { GstClockTime stop = seg->base + seg->stop; if (stop > target->segment.stop) { target->segment.stop = stop; GST_DEBUG_OBJECT (reader, "Adjusting segment stop by %" GST_TIME_FORMAT " output now %" GST_SEGMENT_FORMAT, GST_TIME_ARGS (reader->start_offset), &target->segment); } } GST_LOG_OBJECT (pad, "Forwarding segment %" GST_PTR_FORMAT, event); break; } case GST_EVENT_EOS:{ GST_DEBUG_OBJECT (part_pad, "State %u EOS event. MaxTS seen %" GST_TIME_FORMAT, reader->prep_state, GST_TIME_ARGS (part_pad->max_ts)); if (reader->prep_state == PART_STATE_PREPARING_COLLECT_STREAMS || reader->prep_state == PART_STATE_PREPARING_MEASURE_STREAMS) { /* Mark this pad as EOS */ part_pad->is_eos = TRUE; if (splitmux_part_is_eos_locked (reader)) { /* Finished measuring things, set state and tell the state change func * so it can seek back to the start */ GST_LOG_OBJECT (reader, "EOS while measuring streams. Resetting for ready"); reader->prep_state = PART_STATE_PREPARING_RESET_FOR_READY; SPLITMUX_PART_BROADCAST (reader); } goto drop_event; } break; } case GST_EVENT_FLUSH_START: reader->flushing = TRUE; part_pad->flushing = TRUE; GST_LOG_OBJECT (reader, "Pad %" GST_PTR_FORMAT " flushing dataqueue", part_pad); gst_data_queue_set_flushing (part_pad->queue, TRUE); SPLITMUX_PART_BROADCAST (reader); break; case GST_EVENT_FLUSH_STOP:{ gst_data_queue_set_flushing (part_pad->queue, FALSE); gst_data_queue_flush (part_pad->queue); part_pad->seen_buffer = FALSE; part_pad->flushing = FALSE; part_pad->is_eos = FALSE; reader->flushing = splitmux_is_flushing (reader); GST_LOG_OBJECT (reader, "%s pad %" GST_PTR_FORMAT " flush_stop. Overall flushing=%d", reader->path, pad, reader->flushing); SPLITMUX_PART_BROADCAST (reader); break; } default: break; } /* Don't send events downstream while preparing */ if (reader->prep_state != PART_STATE_READY) goto drop_event; /* Don't pass flush events - those are done by the parent */ if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START || GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) goto drop_event; if (!block_until_can_push (reader)) { SPLITMUX_PART_UNLOCK (reader); gst_object_unref (target); gst_event_unref (event); return FALSE; } switch (GST_EVENT_TYPE (event)) { case GST_EVENT_GAP:{ /* FIXME: Drop initial gap (if any) in each segment, not all GAPs */ goto drop_event; } default: break; } /* We are active, and one queue is empty, place this buffer in * the dataqueue */ gst_object_ref (part_pad->queue); SPLITMUX_PART_UNLOCK (reader); GST_LOG_OBJECT (reader, "Enqueueing event %" GST_PTR_FORMAT, event); item = g_slice_new (GstDataQueueItem); item->destroy = (GDestroyNotify) splitmux_part_free_queue_item; item->object = GST_MINI_OBJECT (event); item->size = 0; item->duration = 0; if (item->duration == GST_CLOCK_TIME_NONE) item->duration = 0; item->visible = FALSE; if (!gst_data_queue_push (part_pad->queue, item)) { splitmux_part_free_queue_item (item); ret = FALSE; } gst_object_unref (part_pad->queue); gst_object_unref (target); return ret; wrong_segment: gst_event_unref (event); gst_object_unref (target); SPLITMUX_PART_UNLOCK (reader); GST_ELEMENT_ERROR (reader, STREAM, FAILED, (NULL), ("Received non-time segment - reader %s pad %" GST_PTR_FORMAT, reader->path, pad)); return FALSE; drop_event: GST_LOG_OBJECT (pad, "Dropping event %" GST_PTR_FORMAT " from %" GST_PTR_FORMAT " on %" GST_PTR_FORMAT, event, pad, target); gst_event_unref (event); gst_object_unref (target); SPLITMUX_PART_UNLOCK (reader); return TRUE; }