diff --git a/gst/videorate/gstvideorate.c b/gst/videorate/gstvideorate.c index a31055cecb..f432ecedb3 100644 --- a/gst/videorate/gstvideorate.c +++ b/gst/videorate/gstvideorate.c @@ -374,7 +374,17 @@ gst_video_rate_transform_caps (GstBaseTransform * trans, s2 = gst_structure_copy (s); s3 = NULL; - if (videorate->drop_only) { + if (videorate->updating_caps) { + GST_INFO_OBJECT (trans, + "Only updating caps %" GST_PTR_FORMAT " with framerate" " %d/%d", + caps, videorate->to_rate_numerator, videorate->to_rate_denominator); + + gst_structure_set (s1, "framerate", GST_TYPE_FRACTION, + videorate->to_rate_numerator, videorate->to_rate_denominator, NULL); + ret = gst_caps_merge_structure (ret, s1); + + continue; + } else if (videorate->drop_only) { gint min_num = 0, min_denom = 1; gint max_num = G_MAXINT, max_denom = 1; @@ -550,6 +560,7 @@ gst_video_rate_reset (GstVideoRate * videorate) videorate->last_ts = GST_CLOCK_TIME_NONE; videorate->discont = TRUE; videorate->average = 0; + videorate->force_variable_rate = FALSE; gst_video_rate_swap_prev (videorate, NULL, 0); gst_segment_init (&videorate->segment, GST_FORMAT_TIME); @@ -981,6 +992,54 @@ drop: return GST_BASE_TRANSFORM_FLOW_DROPPED; } +/* Check if downstream forces variable framerate (0/1) and if + * it is the case, use variable framerate ourself + * Otherwise compute the framerate from the 2 buffers that we + * have already received and make use of it as wanted framerate + */ +static void +gst_video_rate_check_variable_rate (GstVideoRate * videorate, + GstBuffer * buffer) +{ + GstStructure *st; + gint fps_d, fps_n; + GstCaps *srcpadcaps, *tmpcaps; + + GstPad *pad = NULL; + + srcpadcaps = + gst_pad_get_current_caps (GST_BASE_TRANSFORM_SRC_PAD (videorate)); + + gst_video_guess_framerate (GST_BUFFER_PTS (buffer) - + GST_BUFFER_PTS (videorate->prevbuf), &fps_n, &fps_d); + tmpcaps = gst_caps_copy (srcpadcaps); + st = gst_caps_get_structure (tmpcaps, 0); + gst_structure_set (st, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL); + gst_caps_unref (srcpadcaps); + + pad = gst_pad_get_peer (GST_BASE_TRANSFORM_SRC_PAD (videorate)); + if (pad && !gst_pad_query_accept_caps (pad, tmpcaps)) { + videorate->force_variable_rate = TRUE; + GST_DEBUG_OBJECT (videorate, "Downstream forces variable framerate" + " respecting it"); + + goto done; + } + + videorate->to_rate_numerator = fps_n; + videorate->to_rate_denominator = fps_d; + + GST_ERROR_OBJECT (videorate, "Computed framerate to %d/%d", + videorate->to_rate_numerator, videorate->to_rate_denominator); + + videorate->updating_caps = TRUE; + gst_base_transform_update_src_caps (GST_BASE_TRANSFORM (videorate), tmpcaps); + +done: + if (pad) + gst_object_unref (pad); +} + static GstFlowReturn gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) { @@ -997,6 +1056,11 @@ gst_video_rate_transform_ip (GstBaseTransform * trans, GstBuffer * buffer) videorate->to_rate_denominator == 0) goto not_negotiated; + if (videorate->to_rate_numerator == 0 && videorate->prevbuf && + !videorate->force_variable_rate) { + gst_video_rate_check_variable_rate (videorate, buffer); + } + GST_OBJECT_LOCK (videorate); avg_period = videorate->average_period_set; GST_OBJECT_UNLOCK (videorate); diff --git a/gst/videorate/gstvideorate.h b/gst/videorate/gstvideorate.h index 92c841cc42..d5e7c19ef0 100644 --- a/gst/videorate/gstvideorate.h +++ b/gst/videorate/gstvideorate.h @@ -65,6 +65,8 @@ struct _GstVideoRate guint64 average_period; GstClockTimeDiff wanted_diff; /* target average diff */ GstClockTimeDiff average; /* moving average period */ + gboolean force_variable_rate; + gboolean updating_caps; /* segment handling */ GstSegment segment; diff --git a/tests/check/elements/videorate.c b/tests/check/elements/videorate.c index f8a9e08490..ec57b70383 100644 --- a/tests/check/elements/videorate.c +++ b/tests/check/elements/videorate.c @@ -40,6 +40,10 @@ static GstPad *mysrcpad, *mysinkpad; "framerate = (fraction) 25/1 , " \ "format = (string) I420" +#define VIDEO_CAPS_FORCE_VARIABLE_FRAMERATE_STRING \ + "video/x-raw, " \ + "framerate = (fraction) 0/1" + #define VIDEO_CAPS_NO_FRAMERATE_STRING \ "video/x-raw, " \ "width = (int) 320, " \ @@ -77,6 +81,13 @@ static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS (VIDEO_CAPS_TEMPLATE_STRING) ); +static GstStaticPadTemplate force_variable_rate_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (VIDEO_CAPS_FORCE_VARIABLE_FRAMERATE_STRING) + ); + static void assert_videorate_stats (GstElement * videorate, const gchar * reason, guint64 xin, guint64 xout, guint64 xdropped, guint64 xduplicated) @@ -1015,8 +1026,10 @@ GST_START_TEST (test_caps_negotiation) videorate = setup_videorate_full (&srctemplate, &sinktemplate); caps = gst_caps_from_string (test->caps); + g_object_set_data_full (G_OBJECT (mysrcpad), "caps", gst_caps_ref (caps), (GDestroyNotify) gst_caps_unref); + g_object_set_data_full (G_OBJECT (mysinkpad), "caps", gst_caps_ref (caps), (GDestroyNotify) gst_caps_unref); gst_caps_unref (caps); @@ -1036,6 +1049,95 @@ GST_START_TEST (test_caps_negotiation) GST_END_TEST; +static void +videorate_send_buffers (GstElement * videorate, + const gchar * pre_push_caps, const gchar * post_push_caps) +{ + GstCaps *caps, *expected_caps; + GstBuffer *first; + GstBuffer *second; + GstBuffer *third; + + caps = gst_pad_get_current_caps (mysinkpad); + expected_caps = gst_caps_from_string (pre_push_caps); + gst_check_caps_equal (caps, expected_caps); + gst_caps_unref (caps); + gst_caps_unref (expected_caps); + + GST_DEBUG ("pushing first buffer"); + first = gst_buffer_new_and_alloc (4); + gst_buffer_memset (first, 0, 0, 4); + GST_BUFFER_TIMESTAMP (first) = 0; + fail_unless (gst_pad_push (mysrcpad, first) == GST_FLOW_OK); + + /* second buffer */ + second = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (second) = GST_SECOND / 25; + gst_buffer_memset (second, 0, 0, 4); + + fail_unless (gst_pad_push (mysrcpad, second) == GST_FLOW_OK); + + /* third buffer with new size */ + third = gst_buffer_new_and_alloc (4); + GST_BUFFER_TIMESTAMP (third) = 2 * GST_SECOND / 25; + gst_buffer_memset (third, 0, 0, 4); + + fail_unless (gst_pad_push (mysrcpad, third) == GST_FLOW_OK); + + caps = gst_pad_get_current_caps (mysinkpad); + expected_caps = gst_caps_from_string (post_push_caps); + gst_check_caps_equal (caps, expected_caps); + gst_caps_unref (caps); + gst_caps_unref (expected_caps); + +} + +GST_START_TEST (test_fixed_framerate) +{ + GstElement *videorate; + GstCaps *caps = gst_caps_from_string ("video/x-raw,framerate=0/1"); + + /* 1) if upstream caps contain a non-0/1 framerate, we should use that and pass + * it on downstream (if possible; otherwise fixate_to_nearest) + */ + videorate = setup_videorate_full (&srctemplate, &sinktemplate); + + caps = gst_caps_from_string ("video/x-raw,framerate=25/1"); + ASSERT_SET_STATE (videorate, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS); + gst_check_setup_events (mysrcpad, videorate, caps, GST_FORMAT_TIME); + gst_caps_unref (caps); + videorate_send_buffers (videorate, "video/x-raw,framerate=25/1", + "video/x-raw,framerate=25/1"); + cleanup_videorate (videorate); + + /* 2) if upstream framerate is 0/1 and downstream doesn't force a particular + * framerate, we try to guess based on buffer intervals and use that as output + * framerate */ + videorate = setup_videorate_full (&srctemplate, &sinktemplate); + ASSERT_SET_STATE (videorate, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS); + caps = gst_caps_from_string ("video/x-raw,framerate=0/1"); + gst_check_setup_events (mysrcpad, videorate, caps, GST_FORMAT_TIME); + gst_caps_unref (caps); + videorate_send_buffers (videorate, "video/x-raw,framerate=0/1", + "video/x-raw,framerate=25/1"); + cleanup_videorate (videorate); + + /* 3) if downstream force variable framerate, do that */ + videorate = + setup_videorate_full (&srctemplate, &force_variable_rate_template); + ASSERT_SET_STATE (videorate, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS); + caps = gst_caps_from_string ("video/x-raw,framerate=0/1"); + gst_check_setup_events (mysrcpad, videorate, caps, GST_FORMAT_TIME); + gst_caps_unref (caps); + videorate_send_buffers (videorate, "video/x-raw,framerate=0/1", + "video/x-raw,framerate=0/1"); + cleanup_videorate (videorate); + +} + +GST_END_TEST; + + static Suite * videorate_suite (void) { @@ -1054,6 +1156,7 @@ videorate_suite (void) tcase_add_test (tc_chain, test_selected_caps); tcase_add_loop_test (tc_chain, test_caps_negotiation, 0, G_N_ELEMENTS (caps_negotiation_tests)); + tcase_add_test (tc_chain, test_fixed_framerate); return s; }