basesrc: Add public gst_base_src_negotiate () function

This is useful for when format changes occur mid-stream.
This commit is contained in:
Carlos Rafael Giani 2019-06-24 14:35:16 +02:00
parent 27fbaf9d44
commit 6412988975
3 changed files with 115 additions and 3 deletions

View file

@ -360,7 +360,7 @@ static GstFlowReturn gst_base_src_getrange (GstPad * pad, GstObject * parent,
static GstFlowReturn gst_base_src_get_range (GstBaseSrc * src, guint64 offset, static GstFlowReturn gst_base_src_get_range (GstBaseSrc * src, guint64 offset,
guint length, GstBuffer ** buf); guint length, GstBuffer ** buf);
static gboolean gst_base_src_seekable (GstBaseSrc * src); static gboolean gst_base_src_seekable (GstBaseSrc * src);
static gboolean gst_base_src_negotiate (GstBaseSrc * basesrc); static gboolean gst_base_src_negotiate_unlocked (GstBaseSrc * basesrc);
static gboolean gst_base_src_update_length (GstBaseSrc * src, guint64 offset, static gboolean gst_base_src_update_length (GstBaseSrc * src, guint64 offset,
guint * length, gboolean force); guint * length, gboolean force);
@ -2804,7 +2804,7 @@ gst_base_src_loop (GstPad * pad)
/* check if we need to renegotiate */ /* check if we need to renegotiate */
if (gst_pad_check_reconfigure (pad)) { if (gst_pad_check_reconfigure (pad)) {
if (!gst_base_src_negotiate (src)) { if (!gst_base_src_negotiate_unlocked (src)) {
gst_pad_mark_reconfigure (pad); gst_pad_mark_reconfigure (pad);
if (GST_PAD_IS_FLUSHING (pad)) { if (GST_PAD_IS_FLUSHING (pad)) {
GST_LIVE_LOCK (src); GST_LIVE_LOCK (src);
@ -3374,7 +3374,7 @@ no_caps:
} }
static gboolean static gboolean
gst_base_src_negotiate (GstBaseSrc * basesrc) gst_base_src_negotiate_unlocked (GstBaseSrc * basesrc)
{ {
GstBaseSrcClass *bclass; GstBaseSrcClass *bclass;
gboolean result; gboolean result;
@ -3401,6 +3401,39 @@ gst_base_src_negotiate (GstBaseSrc * basesrc)
return result; return result;
} }
/**
* gst_base_src_negotiate:
* @basesrc: base source instance
*
* Negotiates src pad caps with downstream elements.
* Unmarks GST_PAD_FLAG_NEED_RECONFIGURE in any case. But marks it again
* if #GstBaseSrcClass.negotiate() fails.
*
* Do not call this in the #GstBaseSrcClass.fill() vmethod. Call this in
* #GstBaseSrcClass.create() or in #GstBaseSrcClass.alloc(), _before_ any
* buffer is allocated.
*
* Returns: %TRUE if the negotiation succeeded, else %FALSE.
*
* Since: 1.18
*/
gboolean
gst_base_src_negotiate (GstBaseSrc * src)
{
gboolean ret = TRUE;
g_return_val_if_fail (GST_IS_BASE_SRC (src), FALSE);
GST_PAD_STREAM_LOCK (src->srcpad);
gst_pad_check_reconfigure (src->srcpad);
ret = gst_base_src_negotiate_unlocked (src);
if (!ret)
gst_pad_mark_reconfigure (src->srcpad);
GST_PAD_STREAM_UNLOCK (src->srcpad);
return ret;
}
static gboolean static gboolean
gst_base_src_start (GstBaseSrc * basesrc) gst_base_src_start (GstBaseSrc * basesrc)
{ {

View file

@ -275,6 +275,9 @@ void gst_base_src_set_async (GstBaseSrc *src, gboolean async);
GST_BASE_API GST_BASE_API
gboolean gst_base_src_is_async (GstBaseSrc *src); gboolean gst_base_src_is_async (GstBaseSrc *src);
GST_BASE_API
gboolean gst_base_src_negotiate (GstBaseSrc *src);
GST_BASE_API GST_BASE_API
void gst_base_src_start_complete (GstBaseSrc * basesrc, GstFlowReturn ret); void gst_base_src_start_complete (GstBaseSrc * basesrc, GstFlowReturn ret);

View file

@ -909,6 +909,8 @@ typedef struct
GstBaseSrc parent; GstBaseSrc parent;
GstSegment *segment; GstSegment *segment;
gboolean n_output_buffers; gboolean n_output_buffers;
gsize num_times_negotiate_called;
gboolean do_renegotiate;
} TimeSrc; } TimeSrc;
typedef GstBaseSrcClass TimeSrcClass; typedef GstBaseSrcClass TimeSrcClass;
@ -923,6 +925,8 @@ time_src_init (TimeSrc * src)
gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
gst_base_src_set_automatic_eos (GST_BASE_SRC (src), FALSE); gst_base_src_set_automatic_eos (GST_BASE_SRC (src), FALSE);
src->n_output_buffers = 0; src->n_output_buffers = 0;
src->num_times_negotiate_called = 0;
src->do_renegotiate = FALSE;
} }
/* This test src outputs a compressed format, with a single GOP /* This test src outputs a compressed format, with a single GOP
@ -941,6 +945,11 @@ time_src_create (GstBaseSrc * bsrc, guint64 offset, guint size,
if (src->segment->position >= src->segment->stop) if (src->segment->position >= src->segment->stop)
return GST_FLOW_EOS; return GST_FLOW_EOS;
if (src->do_renegotiate) {
gst_base_src_negotiate (bsrc);
src->do_renegotiate = FALSE;
}
*p_buf = gst_buffer_new (); *p_buf = gst_buffer_new ();
GST_BUFFER_PTS (*p_buf) = src->segment->position; GST_BUFFER_PTS (*p_buf) = src->segment->position;
GST_BUFFER_DURATION (*p_buf) = GST_SECOND; GST_BUFFER_DURATION (*p_buf) = GST_SECOND;
@ -973,6 +982,14 @@ time_src_is_seekable (GstBaseSrc * bsrc)
return TRUE; return TRUE;
} }
static gboolean
time_src_negotiate (GstBaseSrc * bsrc)
{
TimeSrc *src = (TimeSrc *) bsrc;
src->num_times_negotiate_called++;
return GST_BASE_SRC_CLASS (time_src_parent_class)->negotiate (bsrc);
}
static void static void
time_src_class_init (TimeSrcClass * klass) time_src_class_init (TimeSrcClass * klass)
{ {
@ -984,6 +1001,7 @@ time_src_class_init (TimeSrcClass * klass)
gstbasesrc_class->create = time_src_create; gstbasesrc_class->create = time_src_create;
gstbasesrc_class->do_seek = time_src_do_seek; gstbasesrc_class->do_seek = time_src_do_seek;
gstbasesrc_class->is_seekable = time_src_is_seekable; gstbasesrc_class->is_seekable = time_src_is_seekable;
gstbasesrc_class->negotiate = time_src_negotiate;
} }
GST_START_TEST (basesrc_time_automatic_eos) GST_START_TEST (basesrc_time_automatic_eos)
@ -1033,6 +1051,63 @@ GST_START_TEST (basesrc_time_automatic_eos)
GST_END_TEST; GST_END_TEST;
GST_START_TEST (basesrc_negotiate)
{
GstElement *src, *sink;
GstElement *pipe;
GstSegment seg;
src = g_object_new (time_src_get_type (), NULL);
sink = gst_element_factory_make ("fakesink", NULL);
pipe = gst_pipeline_new (NULL);
gst_bin_add (GST_BIN (pipe), src);
gst_bin_add (GST_BIN (pipe), sink);
gst_element_link (src, sink);
/* Use some default segment to get the stream going. */
gst_segment_init (&seg, GST_FORMAT_TIME);
((TimeSrc *) src)->segment = gst_segment_copy (&seg);
/* Check that gst_base_src_negotiate () actually ends up calling
* the negotiate () vmethod by first running the test pipeline
* normally, and then running it with a gst_base_src_negotiate ()
* call, and checking how many times negotiate () was called in
* both cases. */
/* Run pipeline, keep do_renegotiate at FALSE, so
* gst_base_src_negotiate () won't be called. negotiate () still
* will be called once, as part of the regular caps negotiation
* sequence. */
fail_unless (((TimeSrc *) src)->num_times_negotiate_called == 0);
gst_element_set_state (pipe, GST_STATE_PLAYING);
gst_element_get_state (pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
fail_unless (((TimeSrc *) src)->num_times_negotiate_called == 1);
gst_element_set_state (pipe, GST_STATE_NULL);
((TimeSrc *) src)->num_times_negotiate_called = 0;
/* Run pipeline, set do_renegotiate to TRUE. This will cause
* negotiate () to be called twice: Once during caps negotiation,
* and once by the gst_base_src_negotiate () call in the
* time_src_create () function. */
((TimeSrc *) src)->do_renegotiate = TRUE;
fail_unless (((TimeSrc *) src)->num_times_negotiate_called == 0);
gst_element_set_state (pipe, GST_STATE_PLAYING);
gst_element_get_state (pipe, NULL, NULL, GST_CLOCK_TIME_NONE);
fail_unless (((TimeSrc *) src)->num_times_negotiate_called == 2);
gst_element_set_state (pipe, GST_STATE_NULL);
if (((TimeSrc *) src)->segment)
gst_segment_free (((TimeSrc *) src)->segment);
gst_object_unref (pipe);
}
GST_END_TEST;
static Suite * static Suite *
gst_basesrc_suite (void) gst_basesrc_suite (void)
{ {
@ -1050,6 +1125,7 @@ gst_basesrc_suite (void)
tcase_add_test (tc, basesrc_seek_on_last_buffer); tcase_add_test (tc, basesrc_seek_on_last_buffer);
tcase_add_test (tc, basesrc_create_bufferlist); tcase_add_test (tc, basesrc_create_bufferlist);
tcase_add_test (tc, basesrc_time_automatic_eos); tcase_add_test (tc, basesrc_time_automatic_eos);
tcase_add_test (tc, basesrc_negotiate);
return s; return s;
} }