1 /* GStreamer
2 *
3 * Unit tests for glimagesink
4 *
5 * Copyright (C) 2014 Julien Isorce <j.isorce@samsung.com>
6 * Copyright (C) 2016 Matthew Waters <matthew@centricular.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <gst/gst.h>
30 #include <gst/video/video.h>
31 #include <gst/check/gstcheck.h>
32
33 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
34 GST_PAD_SRC,
35 GST_PAD_ALWAYS,
36 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA"))
37 );
38
39 static GstElement *sinkelement = NULL;
40 static GstPad *srcpad = NULL;
41
42 #define PAD_FUNC(name, type, param, check) \
43 static gpointer do_##name##_func (type * param) { \
44 fail_unless (gst_pad_##name (srcpad, param) == check); \
45 return NULL; \
46 }
47
48 /* *INDENT-OFF* */
49 PAD_FUNC (peer_query, GstQuery, query, TRUE)
50 PAD_FUNC (push, GstBuffer, buf, GST_FLOW_OK)
51 /* *INDENT-ON* */
52
53 /* On OSX it's required to have a main loop running in the
54 * main thread while using the display connection.
55 * So the call that use this display connection needs to be
56 * done in another thread.
57 * On other platforms a direct call can be done. */
58 #ifdef __APPLE__
59 static GMainLoop *loop;
60 static GThread *thread;
61 static GMutex lock;
62 static GCond cond;
63
64 static gboolean
_unlock_mutex(gpointer * unused)65 _unlock_mutex (gpointer * unused)
66 {
67 g_cond_broadcast (&cond);
68 g_mutex_unlock (&lock);
69
70 return G_SOURCE_REMOVE;
71 }
72
73 static gpointer
_thread_main(gpointer * unused)74 _thread_main (gpointer * unused)
75 {
76 g_mutex_lock (&lock);
77 g_idle_add ((GSourceFunc) _unlock_mutex, NULL);
78
79 g_main_loop_run (loop);
80
81 g_main_loop_unref (loop);
82 loop = NULL;
83
84 return NULL;
85 }
86
87 static void
_start_thread(void)88 _start_thread (void)
89 {
90 loop = g_main_loop_new (NULL, FALSE);
91 g_mutex_init (&lock);
92 g_cond_init (&cond);
93
94 thread = g_thread_new ("GLOSXTestThread", (GThreadFunc) _thread_main, NULL);
95
96 g_mutex_lock (&lock);
97 while (!loop) {
98 g_cond_wait (&cond, &lock);
99 }
100 g_mutex_unlock (&lock);
101 }
102
103 static void
_stop_thread(void)104 _stop_thread (void)
105 {
106 g_main_loop_quit (loop);
107 g_thread_join (thread);
108
109 g_mutex_clear (&lock);
110 g_cond_clear (&cond);
111 }
112 #endif
113
114 static void
setup_glimagesink(void)115 setup_glimagesink (void)
116 {
117 GstCaps *caps = NULL;
118
119 sinkelement = gst_check_setup_element ("glimagesink");
120 srcpad = gst_check_setup_src_pad (sinkelement, &srctemplate);
121 gst_pad_set_active (srcpad, TRUE);
122
123 caps =
124 gst_caps_from_string
125 ("video/x-raw, width=320, height=240, format=RGBA, framerate=30/1");
126 gst_check_setup_events (srcpad, sinkelement, caps, GST_FORMAT_TIME);
127 gst_caps_unref (caps);
128 }
129
130 static void
cleanup_glimagesink(void)131 cleanup_glimagesink (void)
132 {
133 gst_element_set_state (sinkelement, GST_STATE_NULL);
134 gst_element_get_state (sinkelement, NULL, NULL, GST_CLOCK_TIME_NONE);
135 gst_pad_set_active (srcpad, FALSE);
136 gst_check_teardown_src_pad (sinkelement);
137 gst_check_teardown_element (sinkelement);
138 }
139
140 /* Verify that glimagesink releases the buffers it currently
141 * owns, upon a drain query. */
GST_START_TEST(test_query_drain)142 GST_START_TEST (test_query_drain)
143 {
144 GstBuffer *buf = NULL;
145 GstBufferPool *originpool = NULL;
146 GstBufferPool *pool = NULL;
147 GstCaps *caps = NULL;
148 GstQuery *query = NULL;
149 GstStructure *config = NULL;
150 gint i = 0;
151 guint min = 0;
152 guint max = 0;
153 guint size = 0;
154 const gint maxbuffers = 4;
155
156 #ifdef __APPLE__
157 _start_thread ();
158 #endif
159
160 /* GstBaseSink handles the drain query as well. */
161 g_object_set (sinkelement, "enable-last-sample", TRUE, NULL);
162
163 ASSERT_SET_STATE (sinkelement, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
164
165 caps = gst_pad_get_current_caps (srcpad);
166 fail_unless (gst_caps_is_fixed (caps));
167
168 /* Let's retrieve the GstGLBufferPool to change its min
169 * and max nb buffers. For that just send an allocation
170 * query and change the pool config. */
171 query = gst_query_new_allocation (caps, TRUE);
172 do_peer_query_func (query);
173
174 fail_unless (gst_query_get_n_allocation_pools (query) == 1);
175
176 gst_query_parse_nth_allocation_pool (query, 0, &originpool, &size, &min,
177 &max);
178 fail_unless (originpool != NULL);
179 gst_query_unref (query);
180
181 config = gst_buffer_pool_get_config (originpool);
182 gst_buffer_pool_config_set_params (config, caps, size, maxbuffers,
183 maxbuffers);
184 fail_unless (gst_buffer_pool_set_config (originpool, config));
185
186 /* The gl pool is setup and ready to be activated. */
187 fail_unless (gst_buffer_pool_set_active (originpool, TRUE));
188
189 /* Let's build an upstream pool that will be feed with
190 * gl buffers. */
191 pool = gst_buffer_pool_new ();
192 config = gst_buffer_pool_get_config (pool);
193 gst_buffer_pool_config_set_params (config, caps, size, maxbuffers,
194 maxbuffers);
195 fail_unless (gst_buffer_pool_set_config (pool, config));
196 gst_caps_unref (caps);
197
198 fail_unless (gst_buffer_pool_set_active (pool, TRUE));
199
200 /* Unpopulate the pool and forget about its initial buffers
201 * It is necessary because the pool has to know there are
202 * N outstanding buffers. */
203 for (i = 0; i < maxbuffers; ++i) {
204 fail_unless (gst_buffer_pool_acquire_buffer (pool, &buf,
205 NULL) == GST_FLOW_OK);
206 gst_object_replace ((GstObject **) & buf->pool, NULL);
207 gst_buffer_unref (buf);
208 }
209
210 /* Transfer buffers from the gl pool to the upstream pool. */
211 for (i = 0; i < maxbuffers; ++i) {
212 fail_unless (gst_buffer_pool_acquire_buffer (originpool, &buf,
213 NULL) == GST_FLOW_OK);
214 gst_object_replace ((GstObject **) & buf->pool, (GstObject *) pool);
215 gst_buffer_unref (buf);
216 }
217
218 /* Push a lot of buffers like if a real pipeline was running. */
219 for (i = 0; i < 10 * maxbuffers; ++i) {
220 fail_unless (gst_buffer_pool_acquire_buffer (pool, &buf,
221 NULL) == GST_FLOW_OK);
222 do_push_func (buf);
223 }
224
225 /* Claim back buffers to the upstream pool. This is the point
226 * of this unit test, i.e. this test checks that glimagesink
227 * releases the buffers it currently owns, upon drain query. */
228 query = gst_query_new_drain ();
229 do_peer_query_func (query);
230 gst_query_unref (query);
231
232 /* Transfer buffers back to the downstream pool to be release
233 * properly. This also make sure that all buffers are returned.
234 * Indeed gst_buffer_pool_acquire_buffer is blocking here and
235 * we have set a maximum. */
236 for (i = 0; i < maxbuffers; ++i) {
237 fail_unless (gst_buffer_pool_acquire_buffer (pool, &buf,
238 NULL) == GST_FLOW_OK);
239 gst_object_replace ((GstObject **) & buf->pool, (GstObject *) originpool);
240 gst_buffer_unref (buf);
241 }
242
243 fail_unless (gst_buffer_pool_set_active (originpool, FALSE));
244 gst_object_unref (originpool);
245
246 /* At this point the gl pool contains all its buffers. We can
247 * deactivate it to release the textures. Note that only the gl
248 * pool can release the textures properly because it has a
249 * reference on the gl context. */
250 fail_unless (gst_buffer_pool_set_active (pool, FALSE));
251 gst_object_unref (pool);
252
253 #ifdef __APPLE__
254 _stop_thread ();
255 #endif
256 }
257
258 GST_END_TEST;
259
260 static Suite *
glimagesink_suite(void)261 glimagesink_suite (void)
262 {
263 Suite *s = suite_create ("glimagesink");
264 TCase *tc = tcase_create ("general");
265
266 tcase_set_timeout (tc, 5);
267
268 tcase_add_checked_fixture (tc, setup_glimagesink, cleanup_glimagesink);
269 tcase_add_test (tc, test_query_drain);
270 suite_add_tcase (s, tc);
271
272 return s;
273 }
274
275 int
main(int argc,char ** argv)276 main (int argc, char **argv)
277 {
278 Suite *s;
279 g_setenv ("GST_GL_XINITTHREADS", "1", TRUE);
280 gst_check_init (&argc, &argv);
281 s = glimagesink_suite ();
282 return gst_check_run_suite (s, "glimagesink", __FILE__);
283 }
284