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-gdp")
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)
84 sink = setup_multifdsink ();
86 ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
88 buffer = gst_buffer_new_and_alloc (4);
89 fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
91 GST_DEBUG ("cleaning up multifdsink");
92 ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
93 cleanup_multifdsink (sink);
98 GST_START_TEST (test_add_client)
104 guint64 bytes_served;
106 sink = setup_multifdsink ();
108 fail_if (pipe (pfd) == -1);
110 ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
113 g_signal_emit_by_name (sink, "add", pfd[1]);
115 buffer = gst_buffer_new_and_alloc (4);
116 memcpy (GST_BUFFER_DATA (buffer), "dead", 4);
117 fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
119 fail_if (read (pfd[0], data, 4) < 4);
120 fail_unless (strncmp (data, "dead", 4) == 0);
121 wait_bytes_served (sink, 4);
123 GST_DEBUG ("cleaning up multifdsink");
124 ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
125 cleanup_multifdsink (sink);
130 #define fail_unless_read(msg,fd,size,ref) \
132 char data[size + 1]; \
135 GST_DEBUG ("%s: reading %d bytes", msg, size); \
136 nbytes = read (fd, data, size); \
138 GST_DEBUG ("%s: read %d bytes", msg, nbytes); \
139 fail_if (nbytes < size); \
140 fail_unless (memcmp (data, ref, size) == 0, \
141 "data read '%s' differs from '%s'", data, ref); \
144 /* from the given two data buffers, create two streamheader buffers and
145 * some caps that match it, and store them in the given pointers
146 * returns buffers and caps with a refcount of 1 */
148 gst_multifdsink_create_streamheader (const gchar * data1,
149 const gchar * data2, GstBuffer ** hbuf1, GstBuffer ** hbuf2,
152 GValue array = { 0 };
153 GValue value = { 0 };
154 GstStructure *structure;
155 guint size1 = strlen (data1);
156 guint size2 = strlen (data2);
158 fail_if (hbuf1 == NULL);
159 fail_if (hbuf2 == NULL);
160 fail_if (caps == NULL);
162 /* create caps with streamheader, set the caps, and push the IN_CAPS
164 *hbuf1 = gst_buffer_new_and_alloc (size1);
165 GST_BUFFER_FLAG_SET (*hbuf1, GST_BUFFER_FLAG_IN_CAPS);
166 memcpy (GST_BUFFER_DATA (*hbuf1), data1, size1);
167 *hbuf2 = gst_buffer_new_and_alloc (size2);
168 GST_BUFFER_FLAG_SET (*hbuf2, GST_BUFFER_FLAG_IN_CAPS);
169 memcpy (GST_BUFFER_DATA (*hbuf2), data2, size2);
170 /* we want to keep them around for the tests */
171 gst_buffer_ref (*hbuf1);
172 gst_buffer_ref (*hbuf2);
174 g_value_init (&array, GST_TYPE_ARRAY);
176 g_value_init (&value, GST_TYPE_BUFFER);
177 gst_value_set_buffer (&value, *hbuf1);
178 gst_value_array_append_value (&array, &value);
179 g_value_unset (&value);
181 g_value_init (&value, GST_TYPE_BUFFER);
182 gst_value_set_buffer (&value, *hbuf2);
183 gst_value_array_append_value (&array, &value);
184 g_value_unset (&value);
186 *caps = gst_caps_from_string ("application/x-gst-check");
187 structure = gst_caps_get_structure (*caps, 0);
189 gst_structure_set_value (structure, "streamheader", &array);
190 g_value_unset (&array);
195 * - adds a first client
196 * - sets streamheader caps on the pad
197 * - pushes the IN_CAPS buffers
199 * - verifies that the client received all the data correctly
200 * - adds a second client
201 * - verifies that this second client receives the streamheader caps too, plus
204 GST_START_TEST (test_streamheader)
207 GstBuffer *hbuf1, *hbuf2, *buf;
209 GstStructure *structure;
210 int pfd1[2], pfd2[2];
212 guint64 bytes_served;
215 sink = setup_multifdsink ();
217 fail_if (pipe (pfd1) == -1);
218 fail_if (pipe (pfd2) == -1);
220 ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
222 /* add the first client */
223 g_signal_emit_by_name (sink, "add", pfd1[1]);
225 /* create caps with streamheader, set the caps, and push the IN_CAPS
227 gst_multifdsink_create_streamheader ("babe", "deadbeef", &hbuf1, &hbuf2,
229 fail_unless (gst_pad_set_caps (mysrcpad, caps));
230 gst_caps_unref (caps);
232 fail_unless (gst_pad_push (mysrcpad, hbuf1) == GST_FLOW_OK);
233 fail_unless (gst_pad_push (mysrcpad, hbuf2) == GST_FLOW_OK);
236 //fail_if_can_read ("first client", pfd1[0]);
238 /* push a non-IN_CAPS buffer, this should trigger the client receiving the
239 * first three buffers */
240 buf = gst_buffer_new_and_alloc (4);
241 memcpy (GST_BUFFER_DATA (buf), "f00d", 4);
242 gst_pad_push (mysrcpad, buf);
244 fail_unless_read ("first client", pfd1[0], 4, "babe");
245 fail_unless_read ("first client", pfd1[0], 8, "deadbeef");
246 fail_unless_read ("first client", pfd1[0], 4, "f00d");
247 wait_bytes_served (sink, 16);
249 /* now add the second client */
250 g_signal_emit_by_name (sink, "add", pfd2[1]);
252 //fail_if_can_read ("second client", pfd2[0]);
254 /* now push another buffer, which will trigger streamheader for second
256 buf = gst_buffer_new_and_alloc (4);
257 memcpy (GST_BUFFER_DATA (buf), "deaf", 4);
258 gst_pad_push (mysrcpad, buf);
260 fail_unless_read ("first client", pfd1[0], 4, "deaf");
262 fail_unless_read ("second client", pfd2[0], 4, "babe");
263 fail_unless_read ("second client", pfd2[0], 8, "deadbeef");
264 /* we missed the f00d buffer */
265 fail_unless_read ("second client", pfd2[0], 4, "deaf");
266 wait_bytes_served (sink, 36);
268 gst_buffer_unref (hbuf1);
269 gst_buffer_unref (hbuf2);
270 GST_DEBUG ("cleaning up multifdsink");
271 ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
272 cleanup_multifdsink (sink);
277 /* this tests changing of streamheaders
278 * - set streamheader caps on the pad
279 * - pushes the IN_CAPS buffers
281 * - add a first client
282 * - verifies that this first client receives the first streamheader caps,
284 * - change streamheader caps
285 * - verify that the first client receives the new streamheader buffers as well
287 GST_START_TEST (test_change_streamheader)
290 GstBuffer *hbuf1, *hbuf2, *buf;
292 GstStructure *structure;
293 int pfd1[2], pfd2[2];
295 GValue array = { 0 };
296 GValue value = { 0 };
297 guint64 bytes_served;
300 sink = setup_multifdsink ();
302 fail_if (pipe (pfd1) == -1);
303 fail_if (pipe (pfd2) == -1);
305 ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
307 /* create caps with streamheader, set the caps, and push the IN_CAPS
309 gst_multifdsink_create_streamheader ("babe", "deadbeef", &hbuf1, &hbuf2,
311 fail_unless (gst_pad_set_caps (mysrcpad, caps));
312 gst_caps_unref (caps);
314 fail_unless (gst_pad_push (mysrcpad, hbuf1) == GST_FLOW_OK);
315 fail_unless (gst_pad_push (mysrcpad, hbuf2) == GST_FLOW_OK);
317 /* add the first client */
318 g_signal_emit_by_name (sink, "add", pfd1[1]);
320 /* verify this hasn't triggered a write yet */
321 /* FIXME: possibly racy, since if it would write, we may not get it
323 fail_if_can_read ("first client, no buffer", pfd1[0]);
325 /* now push a buffer and read */
326 buf = gst_buffer_new_and_alloc (4);
327 memcpy (GST_BUFFER_DATA (buf), "f00d", 4);
328 gst_pad_push (mysrcpad, buf);
330 fail_unless_read ("change: first client", pfd1[0], 4, "babe");
331 fail_unless_read ("change: first client", pfd1[0], 8, "deadbeef");
332 fail_unless_read ("change: first client", pfd1[0], 4, "f00d");
333 wait_bytes_served (sink, 16);
335 /* now add the second client */
336 g_signal_emit_by_name (sink, "add", pfd2[1]);
337 fail_if_can_read ("second client, no buffer", pfd2[0]);
339 /* change the streamheader */
340 gst_buffer_unref (hbuf1);
341 gst_buffer_unref (hbuf2);
342 gst_multifdsink_create_streamheader ("beef", "deadbabe", &hbuf1, &hbuf2,
344 fail_unless (gst_pad_set_caps (mysrcpad, caps));
345 gst_caps_unref (caps);
347 fail_unless (gst_pad_push (mysrcpad, hbuf1) == GST_FLOW_OK);
348 fail_unless (gst_pad_push (mysrcpad, hbuf2) == GST_FLOW_OK);
350 /* verify neither client has new data available to read */
351 fail_if_can_read ("first client, changed streamheader", pfd1[0]);
352 fail_if_can_read ("second client, changed streamheader", pfd2[0]);
354 /* now push another buffer, which will trigger streamheader for second
355 * client, but should also send new streamheaders to first client */
356 buf = gst_buffer_new_and_alloc (4);
357 memcpy (GST_BUFFER_DATA (buf), "deaf", 4);
358 gst_pad_push (mysrcpad, buf);
360 /* FIXME: here's a bug - the first client does not get new streamheaders */
361 fail_unless_read ("first client", pfd1[0], 4, "deaf");
363 /* new streamheader data */
364 fail_unless_read ("second client", pfd2[0], 4, "beef");
365 fail_unless_read ("second client", pfd2[0], 8, "deadbabe");
366 /* we missed the f00d buffer */
367 fail_unless_read ("second client", pfd2[0], 4, "deaf");
368 wait_bytes_served (sink, 36);
370 gst_buffer_unref (hbuf1);
371 gst_buffer_unref (hbuf2);
372 GST_DEBUG ("cleaning up multifdsink");
373 ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
374 cleanup_multifdsink (sink);
379 /* FIXME: add test simulating chained oggs where:
380 * sync-method is burst-on-connect
381 * (when multifdsink actually does burst-on-connect based on byte size, not
382 "last keyframe" which any frame for audio :))
383 * an old client still needs to read from before the new streamheaders
384 * a new client gets the new streamheaders
388 multifdsink_suite (void)
390 Suite *s = suite_create ("multifdsink");
391 TCase *tc_chain = tcase_create ("general");
393 suite_add_tcase (s, tc_chain);
394 tcase_add_test (tc_chain, test_no_clients);
395 tcase_add_test (tc_chain, test_add_client);
396 tcase_add_test (tc_chain, test_streamheader);
397 tcase_add_test (tc_chain, test_change_streamheader);
403 main (int argc, char **argv)
407 Suite *s = multifdsink_suite ();
408 SRunner *sr = srunner_create (s);
410 gst_check_init (&argc, &argv);
412 srunner_run_all (sr, CK_NORMAL);
413 nf = srunner_ntests_failed (sr);