1 /* GStreamer
2 *
3 * Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 #include <gst/gst.h>
25 #include <gst/check/gstcheck.h>
26 #include <gst/check/gstharness.h>
27 #include <gst/video/video.h>
28
29 #define VIDEO_WIDTH 320
30 #define VIDEO_HEIGHT 240
31 #define OVERLAY_WIDTH 16
32 #define OVERLAY_HEIGHT 16
33
34 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
35 #define VIDEO_FORMAT_STR "BGRA"
36 #define VIDEO_FORMAT GST_VIDEO_FORMAT_BGRA
37 #else
38 #define VIDEO_FORMAT_STR "ARGB"
39 #define VIDEO_FORMAT GST_VIDEO_FORMAT_ARGB
40 #endif
41
42 #define VIDEO_CAPS "video/x-raw, " \
43 "format = (string) " VIDEO_FORMAT_STR ", " \
44 "width = (int) 320, " \
45 "height = (int) 240, " \
46 "framerate = (fraction) 30/1"
47
48 #define VIDEO_CAPS_WITH_META "video/x-raw(" GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION "), " \
49 "format = (string) " VIDEO_FORMAT_STR ", " \
50 "width = (int) 320, " \
51 "height = (int) 240, " \
52 "framerate = (fraction) 30/1"
53
54 #if (G_BYTE_ORDER == G_BIG_ENDIAN)
55 #define GST_READ_UINT32_NATIVE(data) GST_READ_UINT32_BE(data)
56 #define GST_WRITE_UINT32_NATIVE(data,val) GST_WRITE_UINT32_BE(data,val)
57 #else
58 #define GST_READ_UINT32_NATIVE(data) GST_READ_UINT32_LE(data)
59 #define GST_WRITE_UINT32_NATIVE(data,val) GST_WRITE_UINT32_LE(data,val)
60 #endif
61
62 static GstBuffer *
create_video_frame(void)63 create_video_frame (void)
64 {
65 GstBuffer *buffer;
66 GstMapInfo map;
67 guint i;
68
69 buffer = gst_buffer_new_and_alloc (VIDEO_WIDTH * VIDEO_HEIGHT * 4);
70 gst_buffer_add_video_meta (buffer, GST_VIDEO_FRAME_FLAG_NONE, VIDEO_FORMAT,
71 VIDEO_WIDTH, VIDEO_HEIGHT);
72
73 gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
74 for (i = 0; i < map.size; i += 4)
75 GST_WRITE_UINT32_NATIVE (map.data + i, 0xff000000);
76 gst_buffer_unmap (buffer, &map);
77
78 return buffer;
79 }
80
81 static GstBuffer *
create_overlay_frame(guint32 color)82 create_overlay_frame (guint32 color)
83 {
84 GstBuffer *buffer;
85 GstMapInfo map;
86 guint i;
87
88 buffer = gst_buffer_new_and_alloc (VIDEO_WIDTH * VIDEO_HEIGHT * 4);
89 gst_buffer_add_video_meta (buffer, GST_VIDEO_FRAME_FLAG_NONE, VIDEO_FORMAT,
90 VIDEO_WIDTH, VIDEO_HEIGHT);
91
92 gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
93 for (i = 0; i < map.size; i += 4)
94 GST_WRITE_UINT32_NATIVE (map.data + i, color);
95 gst_buffer_unmap (buffer, &map);
96
97 return buffer;
98 }
99
100 typedef struct
101 {
102 gboolean valid;
103 GstVideoInfo info;
104
105 guint expected_window_width, expected_window_height;
106
107 GstVideoOverlayComposition *comp;
108 } State;
109
110 static void
on_caps_changed(GstElement * element,GstCaps * caps,guint window_width,guint window_height,State * s)111 on_caps_changed (GstElement * element, GstCaps * caps, guint window_width,
112 guint window_height, State * s)
113 {
114 fail_unless (gst_video_info_from_caps (&s->info, caps));
115 s->valid = TRUE;
116
117 fail_unless_equals_int (s->expected_window_width, window_width);
118 fail_unless_equals_int (s->expected_window_height, window_height);
119 }
120
121 static GstVideoOverlayComposition *
on_draw(GstElement * element,GstSample * sample,State * s)122 on_draw (GstElement * element, GstSample * sample, State * s)
123 {
124 fail_unless (s->valid);
125 fail_unless (GST_IS_SAMPLE (sample));
126
127 return gst_video_overlay_composition_ref (s->comp);
128 }
129
GST_START_TEST(render_fallback)130 GST_START_TEST (render_fallback)
131 {
132 GstHarness *h;
133 GstVideoOverlayComposition *comp;
134 GstVideoOverlayRectangle *rect;
135 GstBuffer *buffer, *overlay;
136 State s = { 0, };
137 GstMapInfo map;
138 guint x, y;
139
140 h = gst_harness_new ("overlaycomposition");
141
142 g_signal_connect (h->element, "draw", G_CALLBACK (on_draw), &s);
143 g_signal_connect (h->element, "caps-changed", G_CALLBACK (on_caps_changed),
144 &s);
145
146 buffer = create_video_frame ();
147 overlay = create_overlay_frame (0x80ffffff);
148 rect =
149 gst_video_overlay_rectangle_new_raw (overlay, 32, 32, OVERLAY_WIDTH,
150 OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
151 gst_buffer_unref (overlay);
152 comp = gst_video_overlay_composition_new (rect);
153 gst_video_overlay_rectangle_unref (rect);
154
155 s.comp = comp;
156 s.expected_window_width = VIDEO_WIDTH;
157 s.expected_window_height = VIDEO_HEIGHT;
158
159 gst_harness_set_src_caps_str (h, VIDEO_CAPS);
160
161 buffer = gst_harness_push_and_pull (h, buffer);
162
163 gst_buffer_map (buffer, &map, GST_MAP_READ);
164 fail_unless_equals_int (map.size, VIDEO_WIDTH * VIDEO_HEIGHT * 4);
165
166 for (y = 0; y < VIDEO_HEIGHT; y++) {
167 for (x = 0; x < VIDEO_WIDTH; x++) {
168 guint32 val =
169 GST_READ_UINT32_NATIVE (map.data + y * VIDEO_WIDTH * 4 + x * 4);
170 guint32 expected_val;
171
172 if ((x >= 32 && x < 48) && (y >= 32 && y < 48)) {
173 expected_val = 0xff808080;
174 } else {
175 expected_val = 0xff000000;
176 }
177
178 fail_unless (val == expected_val, "Expected %08x but got %08x at (%u,%u)",
179 expected_val, val, x, y);
180 }
181 }
182
183 gst_buffer_unmap (buffer, &map);
184 gst_buffer_unref (buffer);
185
186 gst_video_overlay_composition_unref (s.comp);
187 gst_harness_teardown (h);
188 }
189
190 GST_END_TEST;
191
GST_START_TEST(render_fallback_2)192 GST_START_TEST (render_fallback_2)
193 {
194 GstHarness *h;
195 GstVideoOverlayComposition *comp;
196 GstVideoOverlayRectangle *rect;
197 GstBuffer *buffer, *overlay;
198 State s = { 0, };
199 GstMapInfo map;
200 guint x, y;
201
202 h = gst_harness_new ("overlaycomposition");
203
204 g_signal_connect (h->element, "draw", G_CALLBACK (on_draw), &s);
205 g_signal_connect (h->element, "caps-changed", G_CALLBACK (on_caps_changed),
206 &s);
207
208 overlay = create_overlay_frame (0xffff0000);
209 rect =
210 gst_video_overlay_rectangle_new_raw (overlay, 32, 32, OVERLAY_WIDTH,
211 OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
212 gst_buffer_unref (overlay);
213 comp = gst_video_overlay_composition_new (rect);
214 gst_video_overlay_rectangle_unref (rect);
215
216 overlay = create_overlay_frame (0xff0000ff);
217 rect =
218 gst_video_overlay_rectangle_new_raw (overlay, 64, 64, OVERLAY_WIDTH,
219 OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
220 gst_buffer_unref (overlay);
221 gst_video_overlay_composition_add_rectangle (comp, rect);
222 gst_video_overlay_rectangle_unref (rect);
223
224 s.comp = comp;
225 s.expected_window_width = VIDEO_WIDTH;
226 s.expected_window_height = VIDEO_HEIGHT;
227
228 gst_harness_set_src_caps_str (h, VIDEO_CAPS);
229
230 buffer = create_video_frame ();
231 buffer = gst_harness_push_and_pull (h, buffer);
232
233 gst_buffer_map (buffer, &map, GST_MAP_READ);
234 fail_unless_equals_int (map.size, VIDEO_WIDTH * VIDEO_HEIGHT * 4);
235
236 for (y = 0; y < VIDEO_HEIGHT; y++) {
237 for (x = 0; x < VIDEO_WIDTH; x++) {
238 guint32 val =
239 GST_READ_UINT32_NATIVE (map.data + y * VIDEO_WIDTH * 4 + x * 4);
240 guint32 expected_val;
241
242 if ((x >= 32 && x < 48) && (y >= 32 && y < 48)) {
243 expected_val = 0xffff0000;
244 } else if ((x >= 64 && x < 80) && (y >= 64 && y < 80)) {
245 expected_val = 0xff0000ff;
246 } else {
247 expected_val = 0xff000000;
248 }
249
250 fail_unless (val == expected_val, "Expected %08x but got %08x at (%u,%u)",
251 expected_val, val, x, y);
252 }
253 }
254
255 gst_buffer_unmap (buffer, &map);
256 gst_buffer_unref (buffer);
257
258 gst_video_overlay_composition_unref (s.comp);
259 gst_harness_teardown (h);
260 }
261
262 GST_END_TEST;
263
GST_START_TEST(render_meta)264 GST_START_TEST (render_meta)
265 {
266 GstHarness *h;
267 GstVideoOverlayComposition *comp;
268 GstVideoOverlayRectangle *rect;
269 GstBuffer *buffer, *overlay;
270 State s = { 0, };
271 GstMapInfo map;
272 guint x, y;
273 GstVideoOverlayCompositionMeta *meta;
274
275 h = gst_harness_new ("overlaycomposition");
276
277 g_signal_connect (h->element, "draw", G_CALLBACK (on_draw), &s);
278 g_signal_connect (h->element, "caps-changed", G_CALLBACK (on_caps_changed),
279 &s);
280
281 overlay = create_overlay_frame (0xffff0000);
282 rect =
283 gst_video_overlay_rectangle_new_raw (overlay, 32, 32, OVERLAY_WIDTH,
284 OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
285 gst_buffer_unref (overlay);
286 comp = gst_video_overlay_composition_new (rect);
287 gst_video_overlay_rectangle_unref (rect);
288
289 overlay = create_overlay_frame (0xff0000ff);
290 rect =
291 gst_video_overlay_rectangle_new_raw (overlay, 64, 64, OVERLAY_WIDTH,
292 OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
293 gst_buffer_unref (overlay);
294 gst_video_overlay_composition_add_rectangle (comp, rect);
295 gst_video_overlay_rectangle_unref (rect);
296
297 s.comp = comp;
298 s.expected_window_width = VIDEO_WIDTH;
299 s.expected_window_height = VIDEO_HEIGHT;
300
301 gst_harness_add_propose_allocation_meta (h,
302 GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
303 gst_harness_set_src_caps_str (h, VIDEO_CAPS);
304
305 buffer = create_video_frame ();
306 buffer = gst_harness_push_and_pull (h, buffer);
307
308 gst_buffer_map (buffer, &map, GST_MAP_READ);
309 fail_unless_equals_int (map.size, VIDEO_WIDTH * VIDEO_HEIGHT * 4);
310
311 for (y = 0; y < VIDEO_HEIGHT; y++) {
312 for (x = 0; x < VIDEO_WIDTH; x++) {
313 guint32 val =
314 GST_READ_UINT32_NATIVE (map.data + y * VIDEO_WIDTH * 4 + x * 4);
315 guint32 expected_val = 0xff000000;
316
317 fail_unless (val == expected_val, "Expected %08x but got %08x at (%u,%u)",
318 expected_val, val, x, y);
319 }
320 }
321
322 gst_buffer_unmap (buffer, &map);
323
324 meta = gst_buffer_get_video_overlay_composition_meta (buffer);
325 fail_unless (meta);
326 fail_unless (meta->overlay == s.comp);
327 gst_buffer_unref (buffer);
328
329 gst_video_overlay_composition_unref (s.comp);
330 gst_harness_teardown (h);
331 }
332
333 GST_END_TEST;
334
335 static Suite *
overlaycomposition_suite(void)336 overlaycomposition_suite (void)
337 {
338 Suite *s = suite_create ("overlaycomposition");
339 TCase *tc = tcase_create ("general");
340
341 suite_add_tcase (s, tc);
342
343 tcase_add_test (tc, render_fallback);
344 tcase_add_test (tc, render_fallback_2);
345 tcase_add_test (tc, render_meta);
346
347 return s;
348 }
349
350 GST_CHECK_MAIN (overlaycomposition);
351