videorate: Detect framerate if not forced to variable downstream

In case upstream does not provide videorate with framerate information,
it will detect the current framerate from the buffer it received,
but if downstream forces the use of variable framerate (most probably
through the use of a caps filter with framerate = 0 / 1), videorate will
respect that.

And add some unit tests

https://bugzilla.gnome.org/show_bug.cgi?id=734424
This commit is contained in:
Thibault Saunier 2014-12-09 13:18:42 +01:00 committed by Nicolas Dufresne
parent 1cda538e00
commit ae86dec9ca
3 changed files with 170 additions and 1 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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;
}