gstreamer/subprojects/gst-plugins-base/tests/check/elements/overlaycomposition.c

351 lines
9.9 KiB
C
Raw Normal View History

/* GStreamer
*
* Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <gst/check/gstcheck.h>
#include <gst/check/gstharness.h>
#include <gst/video/video.h>
#define VIDEO_WIDTH 320
#define VIDEO_HEIGHT 240
#define OVERLAY_WIDTH 16
#define OVERLAY_HEIGHT 16
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define VIDEO_FORMAT_STR "BGRA"
#define VIDEO_FORMAT GST_VIDEO_FORMAT_BGRA
#else
#define VIDEO_FORMAT_STR "ARGB"
#define VIDEO_FORMAT GST_VIDEO_FORMAT_ARGB
#endif
#define VIDEO_CAPS "video/x-raw, " \
"format = (string) " VIDEO_FORMAT_STR ", " \
"width = (int) 320, " \
"height = (int) 240, " \
"framerate = (fraction) 30/1"
#define VIDEO_CAPS_WITH_META "video/x-raw(" GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION "), " \
"format = (string) " VIDEO_FORMAT_STR ", " \
"width = (int) 320, " \
"height = (int) 240, " \
"framerate = (fraction) 30/1"
#if (G_BYTE_ORDER == G_BIG_ENDIAN)
#define GST_READ_UINT32_NATIVE(data) GST_READ_UINT32_BE(data)
#define GST_WRITE_UINT32_NATIVE(data,val) GST_WRITE_UINT32_BE(data,val)
#else
#define GST_READ_UINT32_NATIVE(data) GST_READ_UINT32_LE(data)
#define GST_WRITE_UINT32_NATIVE(data,val) GST_WRITE_UINT32_LE(data,val)
#endif
static GstBuffer *
create_video_frame (void)
{
GstBuffer *buffer;
GstMapInfo map;
guint i;
buffer = gst_buffer_new_and_alloc (VIDEO_WIDTH * VIDEO_HEIGHT * 4);
gst_buffer_add_video_meta (buffer, GST_VIDEO_FRAME_FLAG_NONE, VIDEO_FORMAT,
VIDEO_WIDTH, VIDEO_HEIGHT);
gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
for (i = 0; i < map.size; i += 4)
GST_WRITE_UINT32_NATIVE (map.data + i, 0xff000000);
gst_buffer_unmap (buffer, &map);
return buffer;
}
static GstBuffer *
create_overlay_frame (guint32 color)
{
GstBuffer *buffer;
GstMapInfo map;
guint i;
buffer = gst_buffer_new_and_alloc (VIDEO_WIDTH * VIDEO_HEIGHT * 4);
gst_buffer_add_video_meta (buffer, GST_VIDEO_FRAME_FLAG_NONE, VIDEO_FORMAT,
VIDEO_WIDTH, VIDEO_HEIGHT);
gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
for (i = 0; i < map.size; i += 4)
GST_WRITE_UINT32_NATIVE (map.data + i, color);
gst_buffer_unmap (buffer, &map);
return buffer;
}
typedef struct
{
gboolean valid;
GstVideoInfo info;
guint expected_window_width, expected_window_height;
GstVideoOverlayComposition *comp;
} State;
static void
on_caps_changed (GstElement * element, GstCaps * caps, guint window_width,
guint window_height, State * s)
{
fail_unless (gst_video_info_from_caps (&s->info, caps));
s->valid = TRUE;
fail_unless_equals_int (s->expected_window_width, window_width);
fail_unless_equals_int (s->expected_window_height, window_height);
}
static GstVideoOverlayComposition *
on_draw (GstElement * element, GstSample * sample, State * s)
{
fail_unless (s->valid);
fail_unless (GST_IS_SAMPLE (sample));
return gst_video_overlay_composition_ref (s->comp);
}
GST_START_TEST (render_fallback)
{
GstHarness *h;
GstVideoOverlayComposition *comp;
GstVideoOverlayRectangle *rect;
GstBuffer *buffer, *overlay;
State s = { 0, };
GstMapInfo map;
guint x, y;
h = gst_harness_new ("overlaycomposition");
g_signal_connect (h->element, "draw", G_CALLBACK (on_draw), &s);
g_signal_connect (h->element, "caps-changed", G_CALLBACK (on_caps_changed),
&s);
buffer = create_video_frame ();
overlay = create_overlay_frame (0x80ffffff);
rect =
gst_video_overlay_rectangle_new_raw (overlay, 32, 32, OVERLAY_WIDTH,
OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
gst_buffer_unref (overlay);
comp = gst_video_overlay_composition_new (rect);
gst_video_overlay_rectangle_unref (rect);
s.comp = comp;
s.expected_window_width = VIDEO_WIDTH;
s.expected_window_height = VIDEO_HEIGHT;
gst_harness_set_src_caps_str (h, VIDEO_CAPS);
buffer = gst_harness_push_and_pull (h, buffer);
gst_buffer_map (buffer, &map, GST_MAP_READ);
fail_unless_equals_int (map.size, VIDEO_WIDTH * VIDEO_HEIGHT * 4);
for (y = 0; y < VIDEO_HEIGHT; y++) {
for (x = 0; x < VIDEO_WIDTH; x++) {
guint32 val =
GST_READ_UINT32_NATIVE (map.data + y * VIDEO_WIDTH * 4 + x * 4);
guint32 expected_val;
if ((x >= 32 && x < 48) && (y >= 32 && y < 48)) {
expected_val = 0xff808080;
} else {
expected_val = 0xff000000;
}
fail_unless (val == expected_val, "Expected %08x but got %08x at (%u,%u)",
expected_val, val, x, y);
}
}
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
gst_video_overlay_composition_unref (s.comp);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (render_fallback_2)
{
GstHarness *h;
GstVideoOverlayComposition *comp;
GstVideoOverlayRectangle *rect;
GstBuffer *buffer, *overlay;
State s = { 0, };
GstMapInfo map;
guint x, y;
h = gst_harness_new ("overlaycomposition");
g_signal_connect (h->element, "draw", G_CALLBACK (on_draw), &s);
g_signal_connect (h->element, "caps-changed", G_CALLBACK (on_caps_changed),
&s);
overlay = create_overlay_frame (0xffff0000);
rect =
gst_video_overlay_rectangle_new_raw (overlay, 32, 32, OVERLAY_WIDTH,
OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
gst_buffer_unref (overlay);
comp = gst_video_overlay_composition_new (rect);
gst_video_overlay_rectangle_unref (rect);
overlay = create_overlay_frame (0xff0000ff);
rect =
gst_video_overlay_rectangle_new_raw (overlay, 64, 64, OVERLAY_WIDTH,
OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
gst_buffer_unref (overlay);
gst_video_overlay_composition_add_rectangle (comp, rect);
gst_video_overlay_rectangle_unref (rect);
s.comp = comp;
s.expected_window_width = VIDEO_WIDTH;
s.expected_window_height = VIDEO_HEIGHT;
gst_harness_set_src_caps_str (h, VIDEO_CAPS);
buffer = create_video_frame ();
buffer = gst_harness_push_and_pull (h, buffer);
gst_buffer_map (buffer, &map, GST_MAP_READ);
fail_unless_equals_int (map.size, VIDEO_WIDTH * VIDEO_HEIGHT * 4);
for (y = 0; y < VIDEO_HEIGHT; y++) {
for (x = 0; x < VIDEO_WIDTH; x++) {
guint32 val =
GST_READ_UINT32_NATIVE (map.data + y * VIDEO_WIDTH * 4 + x * 4);
guint32 expected_val;
if ((x >= 32 && x < 48) && (y >= 32 && y < 48)) {
expected_val = 0xffff0000;
} else if ((x >= 64 && x < 80) && (y >= 64 && y < 80)) {
expected_val = 0xff0000ff;
} else {
expected_val = 0xff000000;
}
fail_unless (val == expected_val, "Expected %08x but got %08x at (%u,%u)",
expected_val, val, x, y);
}
}
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
gst_video_overlay_composition_unref (s.comp);
gst_harness_teardown (h);
}
GST_END_TEST;
GST_START_TEST (render_meta)
{
GstHarness *h;
GstVideoOverlayComposition *comp;
GstVideoOverlayRectangle *rect;
GstBuffer *buffer, *overlay;
State s = { 0, };
GstMapInfo map;
guint x, y;
GstVideoOverlayCompositionMeta *meta;
h = gst_harness_new ("overlaycomposition");
g_signal_connect (h->element, "draw", G_CALLBACK (on_draw), &s);
g_signal_connect (h->element, "caps-changed", G_CALLBACK (on_caps_changed),
&s);
overlay = create_overlay_frame (0xffff0000);
rect =
gst_video_overlay_rectangle_new_raw (overlay, 32, 32, OVERLAY_WIDTH,
OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
gst_buffer_unref (overlay);
comp = gst_video_overlay_composition_new (rect);
gst_video_overlay_rectangle_unref (rect);
overlay = create_overlay_frame (0xff0000ff);
rect =
gst_video_overlay_rectangle_new_raw (overlay, 64, 64, OVERLAY_WIDTH,
OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
gst_buffer_unref (overlay);
gst_video_overlay_composition_add_rectangle (comp, rect);
gst_video_overlay_rectangle_unref (rect);
s.comp = comp;
s.expected_window_width = VIDEO_WIDTH;
s.expected_window_height = VIDEO_HEIGHT;
gst_harness_add_propose_allocation_meta (h,
GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
gst_harness_set_src_caps_str (h, VIDEO_CAPS);
buffer = create_video_frame ();
buffer = gst_harness_push_and_pull (h, buffer);
gst_buffer_map (buffer, &map, GST_MAP_READ);
fail_unless_equals_int (map.size, VIDEO_WIDTH * VIDEO_HEIGHT * 4);
for (y = 0; y < VIDEO_HEIGHT; y++) {
for (x = 0; x < VIDEO_WIDTH; x++) {
guint32 val =
GST_READ_UINT32_NATIVE (map.data + y * VIDEO_WIDTH * 4 + x * 4);
guint32 expected_val = 0xff000000;
fail_unless (val == expected_val, "Expected %08x but got %08x at (%u,%u)",
expected_val, val, x, y);
}
}
gst_buffer_unmap (buffer, &map);
meta = gst_buffer_get_video_overlay_composition_meta (buffer);
fail_unless (meta);
fail_unless (meta->overlay == s.comp);
gst_buffer_unref (buffer);
gst_video_overlay_composition_unref (s.comp);
gst_harness_teardown (h);
}
GST_END_TEST;
static Suite *
overlaycomposition_suite (void)
{
Suite *s = suite_create ("overlaycomposition");
TCase *tc = tcase_create ("general");
suite_add_tcase (s, tc);
tcase_add_test (tc, render_fallback);
tcase_add_test (tc, render_fallback_2);
tcase_add_test (tc, render_meta);
return s;
}
GST_CHECK_MAIN (overlaycomposition);