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