• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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