📄 gstbasesrc.c
字号:
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 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. * 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 stream-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; 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 */ if (unlock) gst_base_src_unlock (src); /* grab streaming lock, this should eventually be possible, either * because the task is paused or our streaming thread stopped * because our peer is flushing. */ GST_PAD_STREAM_LOCK (src->srcpad); if (unlock) gst_base_src_unlock_stop (src); /* 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 */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)) { case GST_EVENT_FLUSH_START: case GST_EVENT_FLUSH_STOP: /* sending random flushes downstream can break stuff, * especially sync since all segment info will get flushed */ break; case GST_EVENT_EOS: /* FIXME, queue EOS and make sure the task or pull function * perform the EOS actions. */ break; case GST_EVENT_NEWSEGMENT: /* sending random NEWSEGMENT downstream can break sync. */ break; case GST_EVENT_TAG: case GST_EVENT_BUFFERSIZE: break; case GST_EVENT_QOS: break; case GST_EVENT_SEEK: { gboolean started; GST_OBJECT_LOCK (src->srcpad); if (GST_PAD_ACTIVATE_MODE (src->srcpad) == GST_ACTIVATE_PULL) goto wrong_mode; started = GST_PAD_ACTIVATE_MODE (src->srcpad) == GST_ACTIVATE_PUSH; GST_OBJECT_UNLOCK (src->srcpad); if (started) { /* when we are running in push mode, we can execute the * seek right now, we need to unlock. */ result = gst_base_src_perform_seek (src, event, TRUE); } else { GstEvent **event_p; /* else we store the event and execute the seek when we * get activated */ GST_OBJECT_LOCK (src); event_p = &src->data.ABI.pending_seek; gst_event_replace ((GstEvent **) event_p, event); GST_OBJECT_UNLOCK (src); /* assume the seek will work */ result = TRUE; } break; } case GST_EVENT_NAVIGATION: break; default: break; }done: gst_event_unref (event); return result; /* ERRORS */wrong_mode: { GST_DEBUG_OBJECT (src, "cannot perform seek when operating in pull mode"); GST_OBJECT_UNLOCK (src->srcpad); result = FALSE; goto done; }}static gbooleangst_base_src_default_event (GstBaseSrc * src, GstEvent * event){ gboolean result; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: /* is normally called when in push mode */ if (!src->seekable) goto not_seekable; result = gst_base_src_perform_seek (src, event, TRUE); break; case GST_EVENT_FLUSH_START: /* cancel any blocking getrange, is normally called * when in pull mode. */ result = gst_base_src_unlock (src); break; case GST_EVENT_FLUSH_STOP: result = gst_base_src_unlock_stop (src); break; default: result = TRUE; break; } return result; /* ERRORS */not_seekable: { GST_DEBUG_OBJECT (src, "is not seekable"); return FALSE; }}static gbooleangst_base_src_event_handler (GstPad * pad, GstEvent * event){ 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->event) { if (!(result = bclass->event (src, event))) goto subclass_failed; }done: gst_event_unref (event); gst_object_unref (src); return result; /* ERRORS */subclass_failed: { GST_DEBUG_OBJECT (src, "subclass refused event"); goto done; }}static voidgst_base_src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec){ GstBaseSrc *src; src = GST_BASE_SRC (object); switch (prop_id) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -