3 * Copyright (C) 2006 Thomas Vander Stichele <thomas at apestaart dot org>
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.
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.
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., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 #include <sys/ioctl.h>
23 #ifdef HAVE_FIONREAD_IN_SYS_FILIO
24 #include <sys/filio.h>
27 #include <gst/check/gstcheck.h>
31 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
34 GST_STATIC_CAPS ("application/x-gst-check")
40 GstElement *multifdsink;
42 GST_DEBUG ("setup_multifdsink");
43 multifdsink = gst_check_setup_element ("multifdsink");
44 mysrcpad = gst_check_setup_src_pad (multifdsink, &srctemplate, NULL);
50 cleanup_multifdsink (GstElement * multifdsink)
52 GST_DEBUG ("cleanup_multifdsink");
54 gst_check_teardown_src_pad (multifdsink);
55 gst_check_teardown_element (multifdsink);
59 wait_bytes_served (GstElement * sink, guint64 bytes)
61 guint64 bytes_served = 0;
63 while (bytes_served != bytes) {
64 g_object_get (sink, "bytes-served", &bytes_served, NULL);
68 /* FIXME: possibly racy, since if it would write, we may not get it
70 #define fail_if_can_read(msg,fd) \
74 fail_if (ioctl (fd, FIONREAD, &avail) < 0, "%s: could not ioctl", msg); \
75 fail_if (avail > 0, "%s: has bytes available to read"); \
79 GST_START_TEST (test_no_clients)
85 sink = setup_multifdsink ();
87 ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
89 caps = gst_caps_from_string ("application/x-gst-check");
90 buffer = gst_buffer_new_and_alloc (4);
91 gst_buffer_set_caps (buffer, caps);
92 gst_caps_unref (caps);
93 fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
95 GST_DEBUG ("cleaning up multifdsink");
96 ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
97 cleanup_multifdsink (sink);
102 GST_START_TEST (test_add_client)
109 guint64 bytes_served;
111 sink = setup_multifdsink ();
113 fail_if (pipe (pfd) == -1);
115 ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
118 g_signal_emit_by_name (sink, "add", pfd[1]);
120 caps = gst_caps_from_string ("application/x-gst-check");
121 buffer = gst_buffer_new_and_alloc (4);
122 gst_buffer_set_caps (buffer, caps);
123 gst_caps_unref (caps);
124 memcpy (GST_BUFFER_DATA (buffer), "dead", 4);
125 fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
127 fail_if (read (pfd[0], data, 4) < 4);
128 fail_unless (strncmp (data, "dead", 4) == 0);
129 wait_bytes_served (sink, 4);
131 GST_DEBUG ("cleaning up multifdsink");
132 ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
133 cleanup_multifdsink (sink);
138 #define fail_unless_read(msg,fd,size,ref) \
140 char data[size + 1]; \
143 GST_DEBUG ("%s: reading %d bytes", msg, size); \
144 nbytes = read (fd, data, size); \
146 GST_DEBUG ("%s: read %d bytes", msg, nbytes); \
147 fail_if (nbytes < size); \
148 fail_unless (memcmp (data, ref, size) == 0, \
149 "data read '%s' differs from '%s'", data, ref); \
152 /* from the given two data buffers, create two streamheader buffers and
153 * some caps that match it, and store them in the given pointers
154 * returns buffers and caps with a refcount of 1 */
156 gst_multifdsink_create_streamheader (const gchar * data1,
157 const gchar * data2, GstBuffer ** hbuf1, GstBuffer ** hbuf2,
160 GValue array = { 0 };
161 GValue value = { 0 };
162 GstStructure *structure;
163 guint size1 = strlen (data1);
164 guint size2 = strlen (data2);
166 fail_if (hbuf1 == NULL);
167 fail_if (hbuf2 == NULL);
168 fail_if (caps == NULL);
170 /* create caps with streamheader, set the caps, and push the IN_CAPS
172 *hbuf1 = gst_buffer_new_and_alloc (size1);
173 GST_BUFFER_FLAG_SET (*hbuf1, GST_BUFFER_FLAG_IN_CAPS);
174 memcpy (GST_BUFFER_DATA (*hbuf1), data1, size1);
175 *hbuf2 = gst_buffer_new_and_alloc (size2);
176 GST_BUFFER_FLAG_SET (*hbuf2, GST_BUFFER_FLAG_IN_CAPS);
177 memcpy (GST_BUFFER_DATA (*hbuf2), data2, size2);
178 /* we want to keep them around for the tests */
179 gst_buffer_ref (*hbuf1);
180 gst_buffer_ref (*hbuf2);
182 g_value_init (&array, GST_TYPE_ARRAY);
184 g_value_init (&value, GST_TYPE_BUFFER);
185 gst_value_set_buffer (&value, *hbuf1);
186 gst_value_array_append_value (&array, &value);
187 g_value_unset (&value);
189 g_value_init (&value, GST_TYPE_BUFFER);
190 gst_value_set_buffer (&value, *hbuf2);
191 gst_value_array_append_value (&array, &value);
192 g_value_unset (&value);
194 *caps = gst_caps_from_string ("application/x-gst-check");
195 structure = gst_caps_get_structure (*caps, 0);
197 gst_structure_set_value (structure, "streamheader", &array);
198 g_value_unset (&array);
200 /* set our streamheadery caps on the buffers */
201 gst_buffer_set_caps (*hbuf1, *caps);
202 gst_buffer_set_caps (*hbuf2, *caps);
204 GST_DEBUG ("created streamheader caps %" GST_PTR_FORMAT, *caps);
209 * - adds a first client
210 * - sets streamheader caps on the pad
211 * - pushes the IN_CAPS buffers
213 * - verifies that the client received all the data correctly
214 * - adds a second client
215 * - verifies that this second client receives the streamheader caps too, plus
218 GST_START_TEST (test_streamheader)
221 GstBuffer *hbuf1, *hbuf2, *buf;
223 GstStructure *structure;
224 int pfd1[2], pfd2[2];
226 guint64 bytes_served;
229 sink = setup_multifdsink ();
231 fail_if (pipe (pfd1) == -1);
232 fail_if (pipe (pfd2) == -1);
234 ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
236 /* add the first client */
237 g_signal_emit_by_name (sink, "add", pfd1[1]);
239 /* create caps with streamheader, set the caps, and push the IN_CAPS
241 gst_multifdsink_create_streamheader ("babe", "deadbeef", &hbuf1, &hbuf2,
243 fail_unless (gst_pad_set_caps (mysrcpad, caps));
244 gst_caps_unref (caps);
246 fail_unless (gst_pad_push (mysrcpad, hbuf1) == GST_FLOW_OK);
247 fail_unless (gst_pad_push (mysrcpad, hbuf2) == GST_FLOW_OK);
250 //fail_if_can_read ("first client", pfd1[0]);
252 /* push a non-IN_CAPS buffer, this should trigger the client receiving the
253 * first three buffers */
254 buf = gst_buffer_new_and_alloc (4);
255 memcpy (GST_BUFFER_DATA (buf), "f00d", 4);
256 gst_pad_push (mysrcpad, buf);
258 fail_unless_read ("first client", pfd1[0], 4, "babe");
259 fail_unless_read ("first client", pfd1[0], 8, "deadbeef");
260 fail_unless_read ("first client", pfd1[0], 4, "f00d");
261 wait_bytes_served (sink, 16);
263 /* now add the second client */
264 g_signal_emit_by_name (sink, "add", pfd2[1]);
266 //fail_if_can_read ("second client", pfd2[0]);
268 /* now push another buffer, which will trigger streamheader for second
270 buf = gst_buffer_new_and_alloc (4);
271 memcpy (GST_BUFFER_DATA (buf), "deaf", 4);
272 gst_pad_push (mysrcpad, buf);
274 fail_unless_read ("first client", pfd1[0], 4, "deaf");
276 fail_unless_read ("second client", pfd2[0], 4, "babe");
277 fail_unless_read ("second client", pfd2[0], 8, "deadbeef");
278 /* we missed the f00d buffer */
279 fail_unless_read ("second client", pfd2[0], 4, "deaf");
280 wait_bytes_served (sink, 36);
282 gst_buffer_unref (hbuf1);
283 gst_buffer_unref (hbuf2);
284 GST_DEBUG ("cleaning up multifdsink");
285 ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
286 cleanup_multifdsink (sink);
291 /* this tests changing of streamheaders
292 * - set streamheader caps on the pad
293 * - pushes the IN_CAPS buffers
295 * - add a first client
296 * - verifies that this first client receives the first streamheader caps,
298 * - change streamheader caps
299 * - verify that the first client receives the new streamheader buffers as well
301 GST_START_TEST (test_change_streamheader)
304 GstBuffer *hbuf1, *hbuf2, *buf;
306 GstStructure *structure;
307 int pfd1[2], pfd2[2];
309 GValue array = { 0 };
310 GValue value = { 0 };
311 guint64 bytes_served;
314 sink = setup_multifdsink ();
316 fail_if (pipe (pfd1) == -1);
317 fail_if (pipe (pfd2) == -1);
319 ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
321 /* create caps with streamheader, set the caps, and push the IN_CAPS
323 gst_multifdsink_create_streamheader ("first", "header", &hbuf1, &hbuf2,
325 fail_unless (gst_pad_set_caps (mysrcpad, caps));
326 gst_caps_unref (caps);
328 fail_unless (gst_pad_push (mysrcpad, hbuf1) == GST_FLOW_OK);
329 fail_unless (gst_pad_push (mysrcpad, hbuf2) == GST_FLOW_OK);
331 /* add the first client */
332 g_signal_emit_by_name (sink, "add", pfd1[1]);
334 /* verify this hasn't triggered a write yet */
335 /* FIXME: possibly racy, since if it would write, we may not get it
337 fail_if_can_read ("first client, no buffer", pfd1[0]);
339 /* now push a buffer and read */
340 buf = gst_buffer_new_and_alloc (4);
341 memcpy (GST_BUFFER_DATA (buf), "f00d", 4);
342 gst_pad_push (mysrcpad, buf);
344 fail_unless_read ("change: first client", pfd1[0], 5, "first");
345 fail_unless_read ("change: first client", pfd1[0], 6, "header");
346 fail_unless_read ("change: first client", pfd1[0], 4, "f00d");
347 //wait_bytes_served (sink, 16);
349 /* now add the second client */
350 g_signal_emit_by_name (sink, "add", pfd2[1]);
351 fail_if_can_read ("second client, no buffer", pfd2[0]);
353 /* change the streamheader */
354 gst_buffer_unref (hbuf1);
355 gst_buffer_unref (hbuf2);
356 gst_multifdsink_create_streamheader ("second", "header", &hbuf1, &hbuf2,
358 fail_unless (gst_pad_set_caps (mysrcpad, caps));
359 gst_caps_unref (caps);
361 fail_unless (gst_pad_push (mysrcpad, hbuf1) == GST_FLOW_OK);
362 fail_unless (gst_pad_push (mysrcpad, hbuf2) == GST_FLOW_OK);
364 /* verify neither client has new data available to read */
365 fail_if_can_read ("first client, changed streamheader", pfd1[0]);
366 fail_if_can_read ("second client, changed streamheader", pfd2[0]);
368 /* now push another buffer, which will trigger streamheader for second
369 * client, but should also send new streamheaders to first client */
370 buf = gst_buffer_new_and_alloc (8);
371 memcpy (GST_BUFFER_DATA (buf), "deadbabe", 8);
372 gst_pad_push (mysrcpad, buf);
374 fail_unless_read ("first client", pfd1[0], 6, "second");
375 fail_unless_read ("first client", pfd1[0], 6, "header");
376 fail_unless_read ("first client", pfd1[0], 8, "deadbabe");
378 /* new streamheader data */
379 fail_unless_read ("second client", pfd2[0], 6, "second");
380 fail_unless_read ("second client", pfd2[0], 6, "header");
381 /* we missed the f00d buffer */
382 fail_unless_read ("second client", pfd2[0], 8, "deadbabe");
383 //wait_bytes_served (sink, 36);
385 gst_buffer_unref (hbuf1);
386 gst_buffer_unref (hbuf2);
387 GST_DEBUG ("cleaning up multifdsink");
388 ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
389 cleanup_multifdsink (sink);
394 /* FIXME: add test simulating chained oggs where:
395 * sync-method is burst-on-connect
396 * (when multifdsink actually does burst-on-connect based on byte size, not
397 "last keyframe" which any frame for audio :))
398 * an old client still needs to read from before the new streamheaders
399 * a new client gets the new streamheaders
403 multifdsink_suite (void)
405 Suite *s = suite_create ("multifdsink");
406 TCase *tc_chain = tcase_create ("general");
408 suite_add_tcase (s, tc_chain);
409 // tcase_add_test (tc_chain, test_no_clients);
410 // tcase_add_test (tc_chain, test_add_client);
411 // tcase_add_test (tc_chain, test_streamheader);
412 tcase_add_test (tc_chain, test_change_streamheader);
418 main (int argc, char **argv)
422 Suite *s = multifdsink_suite ();
423 SRunner *sr = srunner_create (s);
425 gst_check_init (&argc, &argv);
427 srunner_run_all (sr, CK_NORMAL);
428 nf = srunner_ntests_failed (sr);