1 /* GStreamer
2  *
3  * Copyright (C) 2014 Matthew Waters <ystreet00@gmail.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 
25 #include <gst/check/gstcheck.h>
26 #include <gst/gl/gstglfuncs.h>
27 
28 #include <gst/gl/gl.h>
29 
30 #include <stdio.h>
31 
32 static GstGLDisplay *display;
33 static GstGLContext *context;
34 static GstGLWindow *window;
35 static GstGLUpload *upload;
36 static guint tex_id;
37 static GstGLShader *shader;
38 static GLint shader_attr_position_loc;
39 static GLint shader_attr_texture_loc;
40 static guint vbo, vbo_indices, vao;
41 static GstGLFramebuffer *fbo;
42 static GstGLMemory *fbo_tex;
43 
44 static const GLfloat vertices[] = {
45   1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
46   -1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
47   -1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
48   1.0f, -1.0f, 0.0f, 1.0f, 1.0f
49 };
50 
51 static GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
52 
53 #define FORMAT GST_GL_RGBA
54 #define WIDTH 10
55 #define HEIGHT 10
56 #define RED 0xff, 0x00, 0x00, 0xff
57 #define GREEN 0x00, 0xff, 0x00, 0xff
58 #define BLUE 0x00, 0x00, 0xff, 0xff
59 
60 static gchar rgba_data[] =
61     { RED, GREEN, BLUE, RED, GREEN, BLUE, RED, GREEN, BLUE, RED,
62   GREEN, BLUE, RED, GREEN, BLUE, RED, GREEN, BLUE, RED, GREEN,
63   BLUE, RED, GREEN, BLUE, RED, GREEN, BLUE, RED, GREEN, BLUE,
64   RED, RED, RED, RED, RED, RED, RED, RED, RED, RED,
65   GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN, GREEN,
66   BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE, BLUE,
67   RED, GREEN, BLUE, RED, GREEN, BLUE, RED, GREEN, BLUE, RED,
68   RED, GREEN, BLUE, RED, GREEN, BLUE, RED, GREEN, BLUE, RED,
69   RED, GREEN, BLUE, RED, GREEN, BLUE, RED, GREEN, BLUE, RED,
70   RED, GREEN, BLUE, RED, GREEN, BLUE, RED, GREEN, BLUE, RED
71 };
72 
73 static void
setup(void)74 setup (void)
75 {
76   GError *error = NULL;
77 
78   display = gst_gl_display_new ();
79   context = gst_gl_context_new (display);
80 
81   gst_gl_context_create (context, 0, &error);
82   window = gst_gl_context_get_window (context);
83 
84   fail_if (error != NULL, "Error creating context: %s\n",
85       error ? error->message : "Unknown Error");
86 
87   upload = gst_gl_upload_new (context);
88 }
89 
90 static void
_check_gl_error(GstGLContext * context,gpointer data)91 _check_gl_error (GstGLContext * context, gpointer data)
92 {
93   GLuint error = context->gl_vtable->GetError ();
94   fail_if (error != GL_NONE, "GL error 0x%x encountered during processing\n",
95       error);
96 }
97 
98 static void
teardown(void)99 teardown (void)
100 {
101   gst_object_unref (upload);
102   gst_object_unref (window);
103 
104   gst_gl_context_thread_add (context, (GstGLContextThreadFunc) _check_gl_error,
105       NULL);
106   gst_object_unref (context);
107   gst_object_unref (display);
108   if (shader)
109     gst_object_unref (shader);
110 }
111 
112 static void
_bind_buffer(GstGLContext * context)113 _bind_buffer (GstGLContext * context)
114 {
115   const GstGLFuncs *gl = context->gl_vtable;
116 
117   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, vbo_indices);
118   gl->BindBuffer (GL_ARRAY_BUFFER, vbo);
119 
120   /* Load the vertex position */
121   gl->VertexAttribPointer (shader_attr_position_loc, 3, GL_FLOAT, GL_FALSE,
122       5 * sizeof (GLfloat), (void *) 0);
123 
124   /* Load the texture coordinate */
125   gl->VertexAttribPointer (shader_attr_texture_loc, 2, GL_FLOAT, GL_FALSE,
126       5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
127 
128   gl->EnableVertexAttribArray (shader_attr_position_loc);
129   gl->EnableVertexAttribArray (shader_attr_texture_loc);
130 }
131 
132 static void
_unbind_buffer(GstGLContext * context)133 _unbind_buffer (GstGLContext * context)
134 {
135   const GstGLFuncs *gl = context->gl_vtable;
136 
137   gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
138   gl->BindBuffer (GL_ARRAY_BUFFER, 0);
139 
140   gl->DisableVertexAttribArray (shader_attr_position_loc);
141   gl->DisableVertexAttribArray (shader_attr_texture_loc);
142 }
143 
144 static void
init(gpointer data)145 init (gpointer data)
146 {
147   const GstGLFuncs *gl = context->gl_vtable;
148   GError *error = NULL;
149 
150   shader = gst_gl_shader_new_default (context, &error);
151   fail_if (shader == NULL, "failed to create shader object %s", error->message);
152 
153   shader_attr_position_loc =
154       gst_gl_shader_get_attribute_location (shader, "a_position");
155   shader_attr_texture_loc =
156       gst_gl_shader_get_attribute_location (shader, "a_texcoord");
157 
158   fbo = gst_gl_framebuffer_new_with_default_depth (context, WIDTH, HEIGHT);
159 
160   {
161     GstGLMemoryAllocator *allocator;
162     GstGLVideoAllocationParams *params;
163     GstVideoInfo v_info;
164 
165     allocator = gst_gl_memory_allocator_get_default (context);
166     gst_video_info_set_format (&v_info, GST_VIDEO_FORMAT_RGBA, WIDTH, HEIGHT);
167     params =
168         gst_gl_video_allocation_params_new (context, NULL, &v_info, 0, NULL,
169         GST_GL_TEXTURE_TARGET_2D, FORMAT);
170     fbo_tex =
171         (GstGLMemory *) gst_gl_base_memory_alloc ((GstGLBaseMemoryAllocator *)
172         allocator, (GstGLAllocationParams *) params);
173     gst_object_unref (allocator);
174     gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
175   }
176 
177   if (!vbo) {
178     if (gl->GenVertexArrays) {
179       gl->GenVertexArrays (1, &vao);
180       gl->BindVertexArray (vao);
181     }
182 
183     gl->GenBuffers (1, &vbo);
184     gl->BindBuffer (GL_ARRAY_BUFFER, vbo);
185     gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
186         GL_STATIC_DRAW);
187 
188     gl->GenBuffers (1, &vbo_indices);
189     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, vbo_indices);
190     gl->BufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (indices), indices,
191         GL_STATIC_DRAW);
192 
193     if (gl->GenVertexArrays) {
194       _bind_buffer (context);
195       gl->BindVertexArray (0);
196     }
197 
198     gl->BindBuffer (GL_ARRAY_BUFFER, 0);
199     gl->BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);
200   }
201 }
202 
203 static void
deinit(gpointer data)204 deinit (gpointer data)
205 {
206   GstGLContext *context = data;
207   const GstGLFuncs *gl = context->gl_vtable;
208 
209   if (vbo)
210     gl->DeleteBuffers (1, &vbo);
211   vbo = 0;
212   if (vbo_indices)
213     gl->DeleteBuffers (1, &vbo_indices);
214   vbo_indices = 0;
215   if (vao)
216     gl->DeleteVertexArrays (1, &vao);
217   vao = 0;
218 
219   if (fbo)
220     gst_object_unref (fbo);
221   fbo = NULL;
222 
223   if (fbo_tex)
224     gst_memory_unref (GST_MEMORY_CAST (fbo_tex));
225   fbo_tex = NULL;
226 }
227 
228 static gboolean
blit_tex(gpointer data)229 blit_tex (gpointer data)
230 {
231   GstGLContext *context = data;
232   const GstGLFuncs *gl = context->gl_vtable;
233 
234   gl->Clear (GL_COLOR_BUFFER_BIT);
235 
236   gst_gl_shader_use (shader);
237 
238   if (gl->GenVertexArrays)
239     gl->BindVertexArray (vao);
240   _bind_buffer (context);
241 
242   gl->ActiveTexture (GL_TEXTURE0);
243   gl->BindTexture (GL_TEXTURE_2D, tex_id);
244   gst_gl_shader_set_uniform_1i (shader, "s_texture", 0);
245 
246   gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
247 
248   if (gl->GenVertexArrays)
249     gl->BindVertexArray (0);
250   else
251     _unbind_buffer (context);
252 
253   return TRUE;
254 }
255 
256 static void
draw_render(gpointer data)257 draw_render (gpointer data)
258 {
259   gst_gl_framebuffer_draw_to_texture (fbo, fbo_tex,
260       (GstGLFramebufferFunc) blit_tex, data);
261 }
262 
GST_START_TEST(test_upload_data)263 GST_START_TEST (test_upload_data)
264 {
265   GstCaps *in_caps, *out_caps;
266   GstBuffer *inbuf, *outbuf;
267   GstMapInfo map_info;
268   gint res;
269   gint i = 0;
270 
271   in_caps = gst_caps_from_string ("video/x-raw,format=RGBA,"
272       "width=10,height=10");
273   out_caps = gst_caps_from_string ("video/x-raw(memory:GLMemory),"
274       "format=RGBA,width=10,height=10");
275 
276   gst_gl_upload_set_caps (upload, in_caps, out_caps);
277 
278   inbuf = gst_buffer_new_wrapped_full (0, rgba_data, WIDTH * HEIGHT * 4,
279       0, WIDTH * HEIGHT * 4, NULL, NULL);
280 
281   res = gst_gl_upload_perform_with_buffer (upload, inbuf, &outbuf);
282   fail_unless (res == GST_GL_UPLOAD_DONE, "Failed to upload buffer");
283   fail_unless (GST_IS_BUFFER (outbuf));
284   fail_unless (gst_buffer_get_video_meta (outbuf));
285   fail_unless (gst_buffer_get_gl_sync_meta (outbuf));
286 
287   res = gst_buffer_map (outbuf, &map_info, GST_MAP_READ | GST_MAP_GL);
288   fail_if (res == FALSE, "Failed to map gl memory");
289 
290   tex_id = *(guint *) map_info.data;
291 
292   gst_buffer_unmap (outbuf, &map_info);
293 
294   gst_gl_window_set_preferred_size (window, WIDTH, HEIGHT);
295   gst_gl_window_draw (window);
296 
297   gst_gl_window_send_message (window, GST_GL_WINDOW_CB (init), context);
298 
299   while (i < 2) {
300     gst_gl_window_send_message (window, GST_GL_WINDOW_CB (draw_render),
301         context);
302     i++;
303   }
304   gst_gl_window_send_message (window, GST_GL_WINDOW_CB (deinit), context);
305 
306   gst_caps_unref (in_caps);
307   gst_caps_unref (out_caps);
308   gst_buffer_unref (inbuf);
309   gst_buffer_unref (outbuf);
310 }
311 
312 GST_END_TEST;
313 
GST_START_TEST(test_upload_gl_memory)314 GST_START_TEST (test_upload_gl_memory)
315 {
316   GstGLBaseMemoryAllocator *base_mem_alloc;
317   GstGLVideoAllocationParams *params;
318   GstBuffer *buffer, *outbuf;
319   GstGLMemory *gl_mem;
320   GstCaps *in_caps, *out_caps;
321   GstStructure *out_s;
322   GstVideoInfo in_info;
323   GstMapInfo map_info;
324   gint i = 0;
325   gint res;
326 
327   base_mem_alloc =
328       GST_GL_BASE_MEMORY_ALLOCATOR (gst_allocator_find
329       (GST_GL_MEMORY_ALLOCATOR_NAME));
330 
331   in_caps = gst_caps_from_string ("video/x-raw,format=RGBA,width=10,height=10");
332   gst_video_info_from_caps (&in_info, in_caps);
333 
334   /* create GL buffer */
335   buffer = gst_buffer_new ();
336   params = gst_gl_video_allocation_params_new_wrapped_data (context, NULL,
337       &in_info, 0, NULL, GST_GL_TEXTURE_TARGET_2D,
338       GST_GL_RGBA, rgba_data, NULL, NULL);
339   gl_mem = (GstGLMemory *) gst_gl_base_memory_alloc (base_mem_alloc,
340       (GstGLAllocationParams *) params);
341   gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
342 
343   res =
344       gst_memory_map ((GstMemory *) gl_mem, &map_info,
345       GST_MAP_READ | GST_MAP_GL);
346   fail_if (res == FALSE, "Failed to map gl memory\n");
347   tex_id = *(guint *) map_info.data;
348   gst_memory_unmap ((GstMemory *) gl_mem, &map_info);
349 
350   gst_buffer_append_memory (buffer, (GstMemory *) gl_mem);
351 
352   /* at this point glupload hasn't received any buffers so can output anything */
353   out_caps = gst_gl_upload_transform_caps (upload, context,
354       GST_PAD_SINK, in_caps, NULL);
355   out_s = gst_caps_get_structure (out_caps, 0);
356   fail_unless (gst_structure_has_field_typed (out_s, "texture-target",
357           GST_TYPE_LIST));
358   gst_caps_unref (out_caps);
359 
360   /* set some output caps without setting texture-target: this should trigger RECONFIGURE */
361   out_caps = gst_caps_from_string ("video/x-raw(memory:GLMemory),"
362       "format=RGBA,width=10,height=10");
363 
364   /* set caps with texture-target not fixed. This should trigger RECONFIGURE. */
365   gst_gl_upload_set_caps (upload, in_caps, out_caps);
366   gst_caps_unref (out_caps);
367 
368   /* push a texture-target=2D buffer */
369   res = gst_gl_upload_perform_with_buffer (upload, buffer, &outbuf);
370   fail_unless (res == GST_GL_UPLOAD_RECONFIGURE);
371   fail_if (outbuf);
372 
373   /* now glupload has seen a 2D buffer and so wants to transform to that */
374   out_caps = gst_gl_upload_transform_caps (upload, context,
375       GST_PAD_SINK, in_caps, NULL);
376   out_s = gst_caps_get_structure (out_caps, 0);
377   fail_unless_equals_string (gst_structure_get_string (out_s, "texture-target"),
378       "2D");
379   gst_caps_unref (out_caps);
380 
381   /* try setting the wrong type first tho */
382   out_caps = gst_caps_from_string ("video/x-raw(memory:GLMemory),"
383       "format=RGBA,width=10,height=10,texture-target=RECTANGLE");
384   gst_gl_upload_set_caps (upload, in_caps, out_caps);
385   gst_caps_unref (out_caps);
386 
387   res = gst_gl_upload_perform_with_buffer (upload, buffer, &outbuf);
388   fail_unless (res == GST_GL_UPLOAD_RECONFIGURE);
389   fail_if (outbuf);
390 
391   /* finally do set the correct texture-target */
392   out_caps = gst_caps_from_string ("video/x-raw(memory:GLMemory),"
393       "format=RGBA,width=10,height=10,texture-target=2D");
394   gst_gl_upload_set_caps (upload, in_caps, out_caps);
395   gst_caps_unref (out_caps);
396 
397   res = gst_gl_upload_perform_with_buffer (upload, buffer, &outbuf);
398   fail_unless (res == GST_GL_UPLOAD_DONE, "Failed to upload buffer");
399   fail_unless (GST_IS_BUFFER (outbuf));
400 
401   gst_gl_window_set_preferred_size (window, WIDTH, HEIGHT);
402   gst_gl_window_draw (window);
403   gst_gl_window_send_message (window, GST_GL_WINDOW_CB (init), context);
404 
405   while (i < 2) {
406     gst_gl_window_send_message (window, GST_GL_WINDOW_CB (draw_render),
407         context);
408     i++;
409   }
410   gst_gl_window_send_message (window, GST_GL_WINDOW_CB (deinit), context);
411 
412   gst_caps_unref (in_caps);
413   gst_buffer_unref (buffer);
414   gst_buffer_unref (outbuf);
415   gst_object_unref (base_mem_alloc);
416 }
417 
418 GST_END_TEST;
419 
420 
421 static Suite *
gst_gl_upload_suite(void)422 gst_gl_upload_suite (void)
423 {
424   Suite *s = suite_create ("GstGLUpload");
425   TCase *tc_chain = tcase_create ("upload");
426 
427   suite_add_tcase (s, tc_chain);
428   tcase_add_checked_fixture (tc_chain, setup, teardown);
429   tcase_add_test (tc_chain, test_upload_data);
430   tcase_add_test (tc_chain, test_upload_gl_memory);
431 
432   return s;
433 }
434 
435 GST_CHECK_MAIN (gst_gl_upload);
436