📄 gstbasesrc.c
字号:
res = TRUE; } else res = FALSE; gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); break; } case GST_QUERY_LATENCY: { GstClockTime min, max; gboolean live; /* Subclasses should override and implement something usefull */ res = gst_base_src_query_latency (src, &live, &min, &max); GST_LOG_OBJECT (src, "report latency: live %d, min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT, live, GST_TIME_ARGS (min), GST_TIME_ARGS (max)); gst_query_set_latency (query, live, min, max); break; } case GST_QUERY_JITTER: case GST_QUERY_RATE: default: res = FALSE; break; } GST_DEBUG_OBJECT (src, "query %s returns %d", GST_QUERY_TYPE_NAME (query), res); return res;}static gbooleangst_base_src_query (GstPad * pad, GstQuery * query){ GstBaseSrc *src; GstBaseSrcClass *bclass; gboolean result = FALSE; src = GST_BASE_SRC (gst_pad_get_parent (pad)); bclass = GST_BASE_SRC_GET_CLASS (src); if (bclass->query) result = bclass->query (src, query); else result = gst_pad_query_default (pad, query); gst_object_unref (src); return result;}static gbooleangst_base_src_default_do_seek (GstBaseSrc * src, GstSegment * segment){ gboolean res = TRUE; /* update our offset if the start/stop position was updated */ if (segment->format == GST_FORMAT_BYTES) { segment->last_stop = segment->start; segment->time = segment->start; } else if (segment->start == 0) { /* seek to start, we can implement a default for this. */ segment->last_stop = 0; segment->time = 0; res = TRUE; } else res = FALSE; return res;}static gbooleangst_base_src_do_seek (GstBaseSrc * src, GstSegment * segment){ GstBaseSrcClass *bclass; gboolean result = FALSE; bclass = GST_BASE_SRC_GET_CLASS (src); if (bclass->do_seek) result = bclass->do_seek (src, segment); return result;}#define SEEK_TYPE_IS_RELATIVE(t) (((t) != GST_SEEK_TYPE_NONE) && ((t) != GST_SEEK_TYPE_SET))static gbooleangst_base_src_default_prepare_seek_segment (GstBaseSrc * src, GstEvent * event, GstSegment * segment){ /* By default, we try one of 2 things: * - For absolute seek positions, convert the requested position to our * configured processing format and place it in the output segment \ * - For relative seek positions, convert our current (input) values to the * seek format, adjust by the relative seek offset and then convert back to * the processing format */ GstSeekType cur_type, stop_type; gint64 cur, stop; GstSeekFlags flags; GstFormat seek_format, dest_format; gdouble rate; gboolean update; gboolean res = TRUE; gst_event_parse_seek (event, &rate, &seek_format, &flags, &cur_type, &cur, &stop_type, &stop); dest_format = segment->format; if (seek_format == dest_format) { gst_segment_set_seek (segment, rate, seek_format, flags, cur_type, cur, stop_type, stop, &update); return TRUE; } if (cur_type != GST_SEEK_TYPE_NONE) { /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */ res = gst_pad_query_convert (src->srcpad, seek_format, cur, &dest_format, &cur); cur_type = GST_SEEK_TYPE_SET; } if (res && stop_type != GST_SEEK_TYPE_NONE) { /* FIXME: Handle seek_cur & seek_end by converting the input segment vals */ res = gst_pad_query_convert (src->srcpad, seek_format, stop, &dest_format, &stop); stop_type = GST_SEEK_TYPE_SET; } /* And finally, configure our output segment in the desired format */ gst_segment_set_seek (segment, rate, dest_format, flags, cur_type, cur, stop_type, stop, &update); if (!res) goto no_format; return res;no_format: { GST_DEBUG_OBJECT (src, "undefined format given, seek aborted."); return FALSE; }}static gbooleangst_base_src_prepare_seek_segment (GstBaseSrc * src, GstEvent * event, GstSegment * seeksegment){ GstBaseSrcClass *bclass; gboolean result = FALSE; bclass = GST_BASE_SRC_GET_CLASS (src); if (bclass->prepare_seek_segment) result = bclass->prepare_seek_segment (src, event, seeksegment); return result;}/* this code implements the seeking. It is a good example * handling all cases. * * A seek updates the currently configured segment.start * and segment.stop values based on the SEEK_TYPE. If the * segment.start value is updated, a seek to this new position * should be performed. * * The seek can only be executed when we are not currently * streaming any data, to make sure that this is the case, we * acquire the STREAM_LOCK which is taken when we are in the * _loop() function or when a getrange() is called. Normally * we will not receive a seek if we are operating in pull mode * though. When we operate as a live source we might block on the live * cond, which does not release the STREAM_LOCK. Therefore we will try * to grab the LIVE_LOCK instead of the STREAM_LOCK to make sure it is * safe to perform the seek. * * When we are in the loop() function, we might be in the middle * of pushing a buffer, which might block in a sink. To make sure * that the push gets unblocked we push out a FLUSH_START event. * Our loop function will get a WRONG_STATE return value from * the push and will pause, effectively releasing the STREAM_LOCK. * * For a non-flushing seek, we pause the task, which might eventually * release the STREAM_LOCK. We say eventually because when the sink * blocks on the sample we might wait a very long time until the sink * unblocks the sample. In any case we acquire the STREAM_LOCK and * can continue the seek. A non-flushing seek is normally done in a * running pipeline to perform seamless playback, this means that the sink is * PLAYING and will return from its chain function. * In the case of a non-flushing seek we need to make sure that the * data we output after the seek is continuous with the previous data, * this is because a non-flushing seek does not reset the running-time * to 0. We do this by closing the currently running segment, ie. sending * a new_segment event with the stop position set to the last processed * position. * * After updating the segment.start/stop values, we prepare for * streaming again. We push out a FLUSH_STOP to make the peer pad * accept data again and we start our task again. * * A segment seek posts a message on the bus saying that the playback * of the segment started. We store the segment flag internally because * when we reach the segment.stop we have to post a segment.done * instead of EOS when doing a segment seek. *//* FIXME (0.11), we have the unlock gboolean here because most current * implementations (fdsrc, -base/gst/tcp/, ...) unconditionally unlock, even when * the streaming thread isn't running, resulting in bogus unlocks later when it * starts. This is fixed by adding unlock_stop, but we should still avoid unlocking * unnecessarily for backwards compatibility. Ergo, the unlock variable stays * until 0.11 */static gbooleangst_base_src_perform_seek (GstBaseSrc * src, GstEvent * event, gboolean unlock){ gboolean res = TRUE; gdouble rate; GstFormat seek_format, dest_format; GstSeekFlags flags; GstSeekType cur_type, stop_type; gint64 cur, stop; gboolean flush, playing; gboolean update; gboolean relative_seek = FALSE; gboolean seekseg_configured = FALSE; GstSegment seeksegment; GST_DEBUG_OBJECT (src, "doing seek"); dest_format = src->segment.format; if (event) { gst_event_parse_seek (event, &rate, &seek_format, &flags, &cur_type, &cur, &stop_type, &stop); relative_seek = SEEK_TYPE_IS_RELATIVE (cur_type) || SEEK_TYPE_IS_RELATIVE (stop_type); if (dest_format != seek_format && !relative_seek) { /* If we have an ABSOLUTE position (SEEK_SET only), we can convert it * here before taking the stream lock, otherwise we must convert it later, * once we have the stream lock and can read the current position */ gst_segment_init (&seeksegment, dest_format); if (!gst_base_src_prepare_seek_segment (src, event, &seeksegment)) goto prepare_failed; seekseg_configured = TRUE; } flush = flags & GST_SEEK_FLAG_FLUSH; } else { flush = FALSE; } /* send flush start */ if (flush) gst_pad_push_event (src->srcpad, gst_event_new_flush_start ()); else gst_pad_pause_task (src->srcpad); /* unblock streaming thread. */ gst_base_src_set_flushing (src, TRUE, FALSE, unlock, &playing); /* 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 (src->srcpad); gst_base_src_set_flushing (src, FALSE, playing, unlock, NULL); /* If we configured the seeksegment above, don't overwrite it now. Otherwise * 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 suceeds. */ if (!seekseg_configured) { memcpy (&seeksegment, &src->segment, sizeof (GstSegment)); /* now configure the final seek segment */ if (event) { if (src->segment.format != seek_format) { /* OK, here's where we give the subclass a chance to convert the relative * seek into an absolute one in the processing format. We set up any * absolute seek above, before taking the stream lock. */ if (!gst_base_src_prepare_seek_segment (src, event, &seeksegment)) { GST_DEBUG_OBJECT (src, "Preparing the seek failed after flushing. " "Aborting seek"); res = FALSE; } } else { /* The seek format matches our processing format, no need to ask the * the subclass to configure the segment. */ gst_segment_set_seek (&seeksegment, rate, seek_format, flags, cur_type, cur, stop_type, stop, &update); } } /* Else, no seek event passed, so we're just (re)starting the current segment. */ } if (res) { GST_DEBUG_OBJECT (src, "segment configured from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT, seeksegment.start, seeksegment.stop, seeksegment.last_stop); /* do the seek, segment.last_stop contains the new position. */ res = gst_base_src_do_seek (src, &seeksegment); } /* and prepare to continue streaming */ if (flush) { /* 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 (src->srcpad, gst_event_new_flush_stop ()); } else if (res && src->data.ABI.running) { /* we are running the current segment and doing a non-flushing seek, * close the segment first based on the last_stop. */ GST_DEBUG_OBJECT (src, "closing running segment %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT, src->segment.start, src->segment.last_stop); /* queue the segment for sending in the stream thread */ if (src->priv->close_segment) gst_event_unref (src->priv->close_segment); src->priv->close_segment = gst_event_new_new_segment_full (TRUE, src->segment.rate, src->segment.applied_rate, src->segment.format, src->segment.start, src->segment.last_stop, src->segment.time); } /* The subclass must have converted the segment to the processing format * by now */ if (res && seeksegment.format != dest_format) { GST_DEBUG_OBJECT (src, "Subclass failed to prepare a seek segment " "in the correct format. Aborting seek."); res = FALSE; } /* if successfull seek, we update our real segment and push * out the new segment. */ if (res) { memcpy (&src->segment, &seeksegment, sizeof (GstSegment)); if (src->segment.flags & GST_SEEK_FLAG_SEGMENT) { gst_element_post_message (GST_ELEMENT (src), gst_message_new_segment_start (GST_OBJECT (src), src->segment.format, src->segment.last_stop)); } /* for deriving a stop position for the playback segment form the seek * segment, we must take the duration when the stop is not set */ if ((stop = src->segment.stop) == -1) stop = src->segment.duration; GST_DEBUG_OBJECT (src, "Sending newsegment from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT, src->segment.start, stop); /* now replace the old segment so that we send it in the stream thread the * next time it is scheduled. */ if (src->priv->start_segment) gst_event_unref (src->priv->start_segment); src->priv->start_segment = gst_event_new_new_segment_full (FALSE, src->segment.rate, src->segment.applied_rate, src->segment.format, src->segment.last_stop, stop, src->segment.time); } src->priv->discont = TRUE; src->data.ABI.running = TRUE; /* and restart the task in case it got paused explicitely or by * the FLUSH_START event we pushed out. */ gst_pad_start_task (src->srcpad, (GstTaskFunction) gst_base_src_loop, src->srcpad); /* and release the lock again so we can continue streaming */ GST_PAD_STREAM_UNLOCK (src->srcpad); return res; /* ERROR */prepare_failed: GST_DEBUG_OBJECT (src, "Preparing the seek failed before flushing. " "Aborting seek"); return FALSE;}static const GstQueryType *gst_base_src_get_query_types (GstElement * element){ static const GstQueryType query_types[] = { GST_QUERY_DURATION, GST_QUERY_POSITION, GST_QUERY_SEEKING, GST_QUERY_SEGMENT, GST_QUERY_FORMATS, GST_QUERY_LATENCY, GST_QUERY_JITTER, GST_QUERY_RATE, GST_QUERY_CONVERT, 0 }; return query_types;}/* all events send to this element directly. This is mainly done from the * application. */static gbooleangst_base_src_send_event (GstElement * element, GstEvent * event){ GstBaseSrc *src; gboolean result = FALSE; src = GST_BASE_SRC (element); switch (GST_EVENT_TYPE (event)) { /* bidirectional events */ case GST_EVENT_FLUSH_START: case GST_EVENT_FLUSH_STOP: /* sending random flushes downstream can break stuff,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -