mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-17 03:35:21 +00:00
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:
parent
1cda538e00
commit
ae86dec9ca
3 changed files with 170 additions and 1 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue