videotestsrc: Don't re-render every frame when it's not needed.

When the pattern being rendered by videotestsrc doesn't have motion,
cache a rendered buffer and output it repeatedly with updated
metadata.

Based on a patch by Edward Hervey <edward@centricular.com>

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/issues/10

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1293>
This commit is contained in:
Jan Schmidt 2020-07-17 14:37:14 +10:00 committed by Guillaume Desmottes
parent b3c71584e0
commit 1b22ffdfbd
2 changed files with 142 additions and 21 deletions

View file

@ -521,6 +521,59 @@ gst_video_test_src_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
return caps;
}
static gboolean
gst_video_test_src_is_static_pattern (GstVideoTestSrc * videotestsrc)
{
switch (videotestsrc->pattern_type) {
case GST_VIDEO_TEST_SRC_SMPTE:
case GST_VIDEO_TEST_SRC_SNOW:
case GST_VIDEO_TEST_SRC_BLINK:
case GST_VIDEO_TEST_SRC_BALL:
return FALSE;
case GST_VIDEO_TEST_SRC_ZONE_PLATE:
case GST_VIDEO_TEST_SRC_CHROMA_ZONE_PLATE:
if (videotestsrc->kxt != 0 || videotestsrc->kyt != 0 ||
videotestsrc->kt != 0 || videotestsrc->kt2 != 0) {
return FALSE;
}
break;
default:
break;
}
switch (videotestsrc->pattern_type) {
/* Any pattern that's not a solid color is non-static
* with horizontal motion */
case GST_VIDEO_TEST_SRC_SMPTE:
case GST_VIDEO_TEST_SRC_SNOW:
case GST_VIDEO_TEST_SRC_CHECKERS1:
case GST_VIDEO_TEST_SRC_CHECKERS2:
case GST_VIDEO_TEST_SRC_CHECKERS4:
case GST_VIDEO_TEST_SRC_CHECKERS8:
case GST_VIDEO_TEST_SRC_CIRCULAR:
case GST_VIDEO_TEST_SRC_BLINK:
case GST_VIDEO_TEST_SRC_SMPTE75:
case GST_VIDEO_TEST_SRC_ZONE_PLATE:
case GST_VIDEO_TEST_SRC_GAMUT:
case GST_VIDEO_TEST_SRC_CHROMA_ZONE_PLATE:
case GST_VIDEO_TEST_SRC_BALL:
case GST_VIDEO_TEST_SRC_SMPTE100:
case GST_VIDEO_TEST_SRC_BAR:
case GST_VIDEO_TEST_SRC_PINWHEEL:
case GST_VIDEO_TEST_SRC_SPOKES:
case GST_VIDEO_TEST_SRC_GRADIENT:
case GST_VIDEO_TEST_SRC_COLORS:
if (videotestsrc->horizontal_speed)
return FALSE;
break;
default:
break;
}
return TRUE;
}
static void
gst_video_test_src_set_pattern (GstVideoTestSrc * videotestsrc,
int pattern_type)
@ -618,6 +671,7 @@ gst_video_test_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstVideoTestSrc *src = GST_VIDEO_TEST_SRC (object);
gboolean invalidate = TRUE;
switch (prop_id) {
case PROP_PATTERN:
@ -625,9 +679,11 @@ gst_video_test_src_set_property (GObject * object, guint prop_id,
break;
case PROP_TIMESTAMP_OFFSET:
src->timestamp_offset = g_value_get_int64 (value);
invalidate = FALSE;
break;
case PROP_IS_LIVE:
gst_base_src_set_live (GST_BASE_SRC (src), g_value_get_boolean (value));
invalidate = FALSE;
break;
case PROP_K0:
src->k0 = g_value_get_int (value);
@ -686,6 +742,12 @@ gst_video_test_src_set_property (GObject * object, guint prop_id,
default:
break;
}
if (invalidate) {
/* Property change invalidated the current pattern - check if it's static now or not */
src->have_static_pattern = gst_video_test_src_is_static_pattern (src);
gst_clear_buffer (&src->cached);
}
}
static void
@ -964,6 +1026,8 @@ gst_video_test_src_setcaps (GstBaseSrc * bsrc, GstCaps * caps)
videotestsrc->running_time = 0;
videotestsrc->n_frames = 0;
gst_clear_buffer (&videotestsrc->cached);
GST_OBJECT_UNLOCK (videotestsrc);
return TRUE;
@ -1132,10 +1196,9 @@ gst_video_test_src_is_seekable (GstBaseSrc * psrc)
}
static GstFlowReturn
gst_video_test_src_fill (GstPushSrc * psrc, GstBuffer * buffer)
fill_image (GstPushSrc * psrc, GstBuffer * buffer)
{
GstVideoTestSrc *src;
GstClockTime next_time;
GstVideoFrame frame;
gconstpointer pal;
gsize palsize;
@ -1155,18 +1218,9 @@ gst_video_test_src_fill (GstPushSrc * psrc, GstBuffer * buffer)
goto eos;
}
GST_LOG_OBJECT (src,
"creating buffer from pool for frame %" G_GINT64_FORMAT, src->n_frames);
if (!gst_video_frame_map (&frame, &src->info, buffer, GST_MAP_WRITE))
goto invalid_frame;
GST_BUFFER_PTS (buffer) =
src->accum_rtime + src->timestamp_offset + src->running_time;
GST_BUFFER_DTS (buffer) = GST_CLOCK_TIME_NONE;
gst_object_sync_values (GST_OBJECT (psrc), GST_BUFFER_PTS (buffer));
src->make_image (src, GST_BUFFER_PTS (buffer), &frame);
if ((pal = gst_video_format_get_palette (GST_VIDEO_FRAME_FORMAT (&frame),
@ -1175,6 +1229,69 @@ gst_video_test_src_fill (GstPushSrc * psrc, GstBuffer * buffer)
}
gst_video_frame_unmap (&frame);
return GST_FLOW_OK;
not_negotiated:
{
return GST_FLOW_NOT_NEGOTIATED;
}
eos:
{
GST_DEBUG_OBJECT (src, "eos: 0 framerate, frame %d", (gint) src->n_frames);
return GST_FLOW_EOS;
}
invalid_frame:
{
GST_DEBUG_OBJECT (src, "invalid frame");
return GST_FLOW_OK;
}
}
static GstFlowReturn
gst_video_test_src_fill (GstPushSrc * psrc, GstBuffer * buffer)
{
GstVideoTestSrc *src = GST_VIDEO_TEST_SRC (psrc);
GstClockTime next_time;
GstFlowReturn ret;
GstClockTime pts =
src->accum_rtime + src->timestamp_offset + src->running_time;
gst_object_sync_values (GST_OBJECT (src), pts);
if (src->have_static_pattern) {
GstVideoFrame sframe, dframe;
if (src->cached == NULL) {
src->cached = gst_buffer_new_allocate (NULL, src->info.size, NULL);
ret = fill_image (GST_PUSH_SRC (src), src->cached);
if (G_UNLIKELY (ret != GST_FLOW_OK))
goto fill_failed;
} else {
GST_LOG_OBJECT (src, "Reusing cached pattern buffer");
}
/* Do a memory copy instead of just passing a reference to this buffer to
* be consistent with other sources. This should make things clear for
* cases where downstream cannot queue the same buffer twice (such as v4l2)
*/
gst_video_frame_map (&sframe, &src->info, src->cached, GST_MAP_READ);
gst_video_frame_map (&dframe, &src->info, buffer, GST_MAP_WRITE);
if (!gst_video_frame_copy (&dframe, &sframe))
goto copy_failed;
gst_video_frame_unmap (&sframe);
gst_video_frame_unmap (&dframe);
} else {
ret = fill_image (GST_PUSH_SRC (src), buffer);
if (G_UNLIKELY (ret != GST_FLOW_OK))
goto fill_failed;
}
GST_BUFFER_PTS (buffer) = pts;
GST_BUFFER_DTS (buffer) = GST_CLOCK_TIME_NONE;
GST_DEBUG_OBJECT (src, "Timestamp: %" GST_TIME_FORMAT " = accumulated %"
GST_TIME_FORMAT " + offset: %"
@ -1211,22 +1328,20 @@ gst_video_test_src_fill (GstPushSrc * psrc, GstBuffer * buffer)
return GST_FLOW_OK;
not_negotiated:
fill_failed:
{
return GST_FLOW_NOT_NEGOTIATED;
GST_DEBUG_OBJECT (src, "fill returned %d (%s)", ret,
gst_flow_get_name (ret));
return ret;
}
eos:
copy_failed:
{
GST_DEBUG_OBJECT (src, "eos: 0 framerate, frame %d", (gint) src->n_frames);
return GST_FLOW_EOS;
}
invalid_frame:
{
GST_DEBUG_OBJECT (src, "invalid frame");
return GST_FLOW_OK;
GST_DEBUG_OBJECT (src, "Failed to copy cached buffer");
return GST_FLOW_ERROR;
}
}
static gboolean
gst_video_test_src_start (GstBaseSrc * basesrc)
{
@ -1268,6 +1383,8 @@ gst_video_test_src_stop (GstBaseSrc * basesrc)
src->n_lines = 0;
src->lines = NULL;
gst_clear_buffer (&src->cached);
return TRUE;
}

View file

@ -206,6 +206,10 @@ struct _GstVideoTestSrc {
guint n_lines;
gint offset;
gpointer *lines;
/* cached buffer used for static patterns that don't change */
GstBuffer *cached;
gboolean have_static_pattern;
};
GST_ELEMENT_REGISTER_DECLARE (videotestsrc);