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 static GstBuffer *
create_video_frame(void)55 create_video_frame (void)
56 {
57 GstBuffer *buffer;
58 GstMapInfo map;
59 guint i;
60
61 buffer = gst_buffer_new_and_alloc (VIDEO_WIDTH * VIDEO_HEIGHT * 4);
62 gst_buffer_add_video_meta (buffer, GST_VIDEO_FRAME_FLAG_NONE, VIDEO_FORMAT,
63 VIDEO_WIDTH, VIDEO_HEIGHT);
64
65 gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
66 for (i = 0; i < map.size; i += 4)
67 GST_WRITE_UINT32_LE (map.data + i, 0xff000000);
68 gst_buffer_unmap (buffer, &map);
69
70 return buffer;
71 }
72
73 static GstBuffer *
create_overlay_frame(guint32 color)74 create_overlay_frame (guint32 color)
75 {
76 GstBuffer *buffer;
77 GstMapInfo map;
78 guint i;
79
80 buffer = gst_buffer_new_and_alloc (VIDEO_WIDTH * VIDEO_HEIGHT * 4);
81 gst_buffer_add_video_meta (buffer, GST_VIDEO_FRAME_FLAG_NONE, VIDEO_FORMAT,
82 VIDEO_WIDTH, VIDEO_HEIGHT);
83
84 gst_buffer_map (buffer, &map, GST_MAP_READWRITE);
85 for (i = 0; i < map.size; i += 4)
86 GST_WRITE_UINT32_LE (map.data + i, color);
87 gst_buffer_unmap (buffer, &map);
88
89 return buffer;
90 }
91
92 typedef struct
93 {
94 gboolean valid;
95 GstVideoInfo info;
96
97 guint expected_window_width, expected_window_height;
98
99 GstVideoOverlayComposition *comp;
100 } State;
101
102 static void
on_caps_changed(GstElement * element,GstCaps * caps,guint window_width,guint window_height,State * s)103 on_caps_changed (GstElement * element, GstCaps * caps, guint window_width,
104 guint window_height, State * s)
105 {
106 fail_unless (gst_video_info_from_caps (&s->info, caps));
107 s->valid = TRUE;
108
109 fail_unless_equals_int (s->expected_window_width, window_width);
110 fail_unless_equals_int (s->expected_window_height, window_height);
111 }
112
113 static GstVideoOverlayComposition *
on_draw(GstElement * element,GstSample * sample,State * s)114 on_draw (GstElement * element, GstSample * sample, State * s)
115 {
116 fail_unless (s->valid);
117 fail_unless (GST_IS_SAMPLE (sample));
118
119 return gst_video_overlay_composition_ref (s->comp);
120 }
121
GST_START_TEST(render_fallback)122 GST_START_TEST (render_fallback)
123 {
124 GstHarness *h;
125 GstVideoOverlayComposition *comp;
126 GstVideoOverlayRectangle *rect;
127 GstBuffer *buffer, *overlay;
128 State s = { 0, };
129 GstMapInfo map;
130 guint x, y;
131
132 h = gst_harness_new ("overlaycomposition");
133
134 g_signal_connect (h->element, "draw", G_CALLBACK (on_draw), &s);
135 g_signal_connect (h->element, "caps-changed", G_CALLBACK (on_caps_changed),
136 &s);
137
138 buffer = create_video_frame ();
139 overlay = create_overlay_frame (0x80ffffff);
140 rect =
141 gst_video_overlay_rectangle_new_raw (overlay, 32, 32, OVERLAY_WIDTH,
142 OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
143 gst_buffer_unref (overlay);
144 comp = gst_video_overlay_composition_new (rect);
145 gst_video_overlay_rectangle_unref (rect);
146
147 s.comp = comp;
148 s.expected_window_width = VIDEO_WIDTH;
149 s.expected_window_height = VIDEO_HEIGHT;
150
151 gst_harness_set_src_caps_str (h, VIDEO_CAPS);
152
153 buffer = gst_harness_push_and_pull (h, buffer);
154
155 gst_buffer_map (buffer, &map, GST_MAP_READ);
156 fail_unless_equals_int (map.size, VIDEO_WIDTH * VIDEO_HEIGHT * 4);
157
158 for (y = 0; y < VIDEO_HEIGHT; y++) {
159 for (x = 0; x < VIDEO_WIDTH; x++) {
160 guint32 val = GST_READ_UINT32_LE (map.data + y * VIDEO_WIDTH * 4 + x * 4);
161 guint32 expected_val;
162
163 if ((x >= 32 && x < 48) && (y >= 32 && y < 48)) {
164 expected_val = 0xff808080;
165 } else {
166 expected_val = 0xff000000;
167 }
168
169 fail_unless (val == expected_val, "Expected %08x but got %08x at (%u,%u)",
170 expected_val, val, x, y);
171 }
172 }
173
174 gst_buffer_unmap (buffer, &map);
175 gst_buffer_unref (buffer);
176
177 gst_video_overlay_composition_unref (s.comp);
178 gst_harness_teardown (h);
179 }
180
181 GST_END_TEST;
182
GST_START_TEST(render_fallback_2)183 GST_START_TEST (render_fallback_2)
184 {
185 GstHarness *h;
186 GstVideoOverlayComposition *comp;
187 GstVideoOverlayRectangle *rect;
188 GstBuffer *buffer, *overlay;
189 State s = { 0, };
190 GstMapInfo map;
191 guint x, y;
192
193 h = gst_harness_new ("overlaycomposition");
194
195 g_signal_connect (h->element, "draw", G_CALLBACK (on_draw), &s);
196 g_signal_connect (h->element, "caps-changed", G_CALLBACK (on_caps_changed),
197 &s);
198
199 overlay = create_overlay_frame (0xffff0000);
200 rect =
201 gst_video_overlay_rectangle_new_raw (overlay, 32, 32, OVERLAY_WIDTH,
202 OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
203 gst_buffer_unref (overlay);
204 comp = gst_video_overlay_composition_new (rect);
205 gst_video_overlay_rectangle_unref (rect);
206
207 overlay = create_overlay_frame (0xff0000ff);
208 rect =
209 gst_video_overlay_rectangle_new_raw (overlay, 64, 64, OVERLAY_WIDTH,
210 OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
211 gst_buffer_unref (overlay);
212 gst_video_overlay_composition_add_rectangle (comp, rect);
213 gst_video_overlay_rectangle_unref (rect);
214
215 s.comp = comp;
216 s.expected_window_width = VIDEO_WIDTH;
217 s.expected_window_height = VIDEO_HEIGHT;
218
219 gst_harness_set_src_caps_str (h, VIDEO_CAPS);
220
221 buffer = create_video_frame ();
222 buffer = gst_harness_push_and_pull (h, buffer);
223
224 gst_buffer_map (buffer, &map, GST_MAP_READ);
225 fail_unless_equals_int (map.size, VIDEO_WIDTH * VIDEO_HEIGHT * 4);
226
227 for (y = 0; y < VIDEO_HEIGHT; y++) {
228 for (x = 0; x < VIDEO_WIDTH; x++) {
229 guint32 val = GST_READ_UINT32_LE (map.data + y * VIDEO_WIDTH * 4 + x * 4);
230 guint32 expected_val;
231
232 if ((x >= 32 && x < 48) && (y >= 32 && y < 48)) {
233 expected_val = 0xffff0000;
234 } else if ((x >= 64 && x < 80) && (y >= 64 && y < 80)) {
235 expected_val = 0xff0000ff;
236 } else {
237 expected_val = 0xff000000;
238 }
239
240 fail_unless (val == expected_val, "Expected %08x but got %08x at (%u,%u)",
241 expected_val, val, x, y);
242 }
243 }
244
245 gst_buffer_unmap (buffer, &map);
246 gst_buffer_unref (buffer);
247
248 gst_video_overlay_composition_unref (s.comp);
249 gst_harness_teardown (h);
250 }
251
252 GST_END_TEST;
253
GST_START_TEST(render_meta)254 GST_START_TEST (render_meta)
255 {
256 GstHarness *h;
257 GstVideoOverlayComposition *comp;
258 GstVideoOverlayRectangle *rect;
259 GstBuffer *buffer, *overlay;
260 State s = { 0, };
261 GstMapInfo map;
262 guint x, y;
263 GstVideoOverlayCompositionMeta *meta;
264
265 h = gst_harness_new ("overlaycomposition");
266
267 g_signal_connect (h->element, "draw", G_CALLBACK (on_draw), &s);
268 g_signal_connect (h->element, "caps-changed", G_CALLBACK (on_caps_changed),
269 &s);
270
271 overlay = create_overlay_frame (0xffff0000);
272 rect =
273 gst_video_overlay_rectangle_new_raw (overlay, 32, 32, OVERLAY_WIDTH,
274 OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
275 gst_buffer_unref (overlay);
276 comp = gst_video_overlay_composition_new (rect);
277 gst_video_overlay_rectangle_unref (rect);
278
279 overlay = create_overlay_frame (0xff0000ff);
280 rect =
281 gst_video_overlay_rectangle_new_raw (overlay, 64, 64, OVERLAY_WIDTH,
282 OVERLAY_HEIGHT, GST_VIDEO_OVERLAY_FORMAT_FLAG_NONE);
283 gst_buffer_unref (overlay);
284 gst_video_overlay_composition_add_rectangle (comp, rect);
285 gst_video_overlay_rectangle_unref (rect);
286
287 s.comp = comp;
288 s.expected_window_width = VIDEO_WIDTH;
289 s.expected_window_height = VIDEO_HEIGHT;
290
291 gst_harness_add_propose_allocation_meta (h,
292 GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
293 gst_harness_set_src_caps_str (h, VIDEO_CAPS);
294
295 buffer = create_video_frame ();
296 buffer = gst_harness_push_and_pull (h, buffer);
297
298 gst_buffer_map (buffer, &map, GST_MAP_READ);
299 fail_unless_equals_int (map.size, VIDEO_WIDTH * VIDEO_HEIGHT * 4);
300
301 for (y = 0; y < VIDEO_HEIGHT; y++) {
302 for (x = 0; x < VIDEO_WIDTH; x++) {
303 guint32 val = GST_READ_UINT32_LE (map.data + y * VIDEO_WIDTH * 4 + x * 4);
304 guint32 expected_val = 0xff000000;
305
306 fail_unless (val == expected_val, "Expected %08x but got %08x at (%u,%u)",
307 expected_val, val, x, y);
308 }
309 }
310
311 gst_buffer_unmap (buffer, &map);
312
313 meta = gst_buffer_get_video_overlay_composition_meta (buffer);
314 fail_unless (meta);
315 fail_unless (meta->overlay == s.comp);
316 gst_buffer_unref (buffer);
317
318 gst_video_overlay_composition_unref (s.comp);
319 gst_harness_teardown (h);
320 }
321
322 GST_END_TEST;
323
324 static Suite *
overlaycomposition_suite(void)325 overlaycomposition_suite (void)
326 {
327 Suite *s = suite_create ("overlaycomposition");
328 TCase *tc = tcase_create ("general");
329
330 suite_add_tcase (s, tc);
331
332 tcase_add_test (tc, render_fallback);
333 tcase_add_test (tc, render_fallback_2);
334 tcase_add_test (tc, render_meta);
335
336 return s;
337 }
338
339 GST_CHECK_MAIN (overlaycomposition);
340