mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
350 lines
9.9 KiB
C
350 lines
9.9 KiB
C
/* 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);
|