macros show the correct line
[platform/upstream/gstreamer.git] / tests / check / elements / multifdsink.c
1 /* GStreamer
2  *
3  * Copyright (C) 2006 Thomas Vander Stichele <thomas at apestaart dot org>
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., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <unistd.h>
22 #include <sys/ioctl.h>
23 #ifdef HAVE_FIONREAD_IN_SYS_FILIO
24 #include <sys/filio.h>
25 #endif
26
27 #include <gst/check/gstcheck.h>
28
29 GstPad *mysrcpad;
30
31 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
32     GST_PAD_SRC,
33     GST_PAD_ALWAYS,
34     GST_STATIC_CAPS ("application/x-gdp")
35     );
36
37 GstElement *
38 setup_multifdsink ()
39 {
40   GstElement *multifdsink;
41
42   GST_DEBUG ("setup_multifdsink");
43   multifdsink = gst_check_setup_element ("multifdsink");
44   mysrcpad = gst_check_setup_src_pad (multifdsink, &srctemplate, NULL);
45
46   return multifdsink;
47 }
48
49 void
50 cleanup_multifdsink (GstElement * multifdsink)
51 {
52   GST_DEBUG ("cleanup_multifdsink");
53
54   gst_check_teardown_src_pad (multifdsink);
55   gst_check_teardown_element (multifdsink);
56 }
57
58 static void
59 wait_bytes_served (GstElement * sink, guint64 bytes)
60 {
61   guint64 bytes_served = 0;
62
63   while (bytes_served != bytes) {
64     g_object_get (sink, "bytes-served", &bytes_served, NULL);
65   }
66 }
67
68 /* FIXME: possibly racy, since if it would write, we may not get it
69  * immediately ? */
70 #define fail_if_can_read(msg,fd) \
71 G_STMT_START { \
72   long avail; \
73 \
74   fail_if (ioctl (fd, FIONREAD, &avail) < 0, "%s: could not ioctl", msg); \
75   fail_if (avail > 0, "%s: has bytes available to read"); \
76 } G_STMT_END;
77
78
79 GST_START_TEST (test_no_clients)
80 {
81   GstElement *sink;
82   GstBuffer *buffer;
83
84   sink = setup_multifdsink ();
85
86   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
87
88   buffer = gst_buffer_new_and_alloc (4);
89   fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
90
91   GST_DEBUG ("cleaning up multifdsink");
92   ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
93   cleanup_multifdsink (sink);
94 }
95
96 GST_END_TEST;
97
98 GST_START_TEST (test_add_client)
99 {
100   GstElement *sink;
101   GstBuffer *buffer;
102   int pfd[2];
103   gchar data[4];
104   guint64 bytes_served;
105
106   sink = setup_multifdsink ();
107
108   fail_if (pipe (pfd) == -1);
109
110   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
111
112   /* add the client */
113   g_signal_emit_by_name (sink, "add", pfd[1]);
114
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);
118
119   fail_if (read (pfd[0], data, 4) < 4);
120   fail_unless (strncmp (data, "dead", 4) == 0);
121   wait_bytes_served (sink, 4);
122
123   GST_DEBUG ("cleaning up multifdsink");
124   ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
125   cleanup_multifdsink (sink);
126 }
127
128 GST_END_TEST;
129
130 #define fail_unless_read(msg,fd,size,ref) \
131 G_STMT_START { \
132   char data[size + 1]; \
133   int nbytes; \
134 \
135   GST_DEBUG ("%s: reading %d bytes", msg, size); \
136   nbytes = read (fd, data, size); \
137   data[size] = 0; \
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); \
142 } G_STMT_END;
143
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 */
147 static void
148 gst_multifdsink_create_streamheader (const gchar * data1,
149     const gchar * data2, GstBuffer ** hbuf1, GstBuffer ** hbuf2,
150     GstCaps ** caps)
151 {
152   GValue array = { 0 };
153   GValue value = { 0 };
154   GstStructure *structure;
155   guint size1 = strlen (data1);
156   guint size2 = strlen (data2);
157
158   fail_if (hbuf1 == NULL);
159   fail_if (hbuf2 == NULL);
160   fail_if (caps == NULL);
161
162   /* create caps with streamheader, set the caps, and push the IN_CAPS
163    * buffers */
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);
173
174   g_value_init (&array, GST_TYPE_ARRAY);
175
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);
180
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);
185
186   *caps = gst_caps_from_string ("application/x-gst-check");
187   structure = gst_caps_get_structure (*caps, 0);
188
189   gst_structure_set_value (structure, "streamheader", &array);
190   g_value_unset (&array);
191 }
192
193
194 /* this test:
195  * - adds a first client
196  * - sets streamheader caps on the pad
197  * - pushes the IN_CAPS buffers
198  * - pushes a buffer
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
202  * - the new buffer
203  */
204 GST_START_TEST (test_streamheader)
205 {
206   GstElement *sink;
207   GstBuffer *hbuf1, *hbuf2, *buf;
208   GstCaps *caps;
209   GstStructure *structure;
210   int pfd1[2], pfd2[2];
211   guint8 data[12];
212   guint64 bytes_served;
213   int avail;
214
215   sink = setup_multifdsink ();
216
217   fail_if (pipe (pfd1) == -1);
218   fail_if (pipe (pfd2) == -1);
219
220   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
221
222   /* add the first client */
223   g_signal_emit_by_name (sink, "add", pfd1[1]);
224
225   /* create caps with streamheader, set the caps, and push the IN_CAPS
226    * buffers */
227   gst_multifdsink_create_streamheader ("babe", "deadbeef", &hbuf1, &hbuf2,
228       &caps);
229   fail_unless (gst_pad_set_caps (mysrcpad, caps));
230   gst_caps_unref (caps);
231
232   fail_unless (gst_pad_push (mysrcpad, hbuf1) == GST_FLOW_OK);
233   fail_unless (gst_pad_push (mysrcpad, hbuf2) == GST_FLOW_OK);
234
235   fail_if_can_read ("first client", pfd1[0]);
236
237   /* push a non-IN_CAPS buffer, this should trigger the client receiving the
238    * first three buffers */
239   buf = gst_buffer_new_and_alloc (4);
240   memcpy (GST_BUFFER_DATA (buf), "f00d", 4);
241   gst_pad_push (mysrcpad, buf);
242
243   fail_unless_read ("first client", pfd1[0], 4, "babe");
244   fail_unless_read ("first client", pfd1[0], 8, "deadbeef");
245   fail_unless_read ("first client", pfd1[0], 4, "f00d");
246   wait_bytes_served (sink, 16);
247
248   /* now add the second client */
249   g_signal_emit_by_name (sink, "add", pfd2[1]);
250   fail_if_can_read ("second client", pfd2[0]);
251
252   /* now push another buffer, which will trigger streamheader for second
253    * client */
254   buf = gst_buffer_new_and_alloc (4);
255   memcpy (GST_BUFFER_DATA (buf), "deaf", 4);
256   gst_pad_push (mysrcpad, buf);
257
258   fail_unless_read ("first client", pfd1[0], 4, "deaf");
259
260   fail_unless_read ("second client", pfd2[0], 4, "babe");
261   fail_unless_read ("second client", pfd2[0], 8, "deadbeef");
262   /* we missed the f00d buffer */
263   fail_unless_read ("second client", pfd2[0], 4, "deaf");
264   wait_bytes_served (sink, 36);
265
266   gst_buffer_unref (hbuf1);
267   gst_buffer_unref (hbuf2);
268   GST_DEBUG ("cleaning up multifdsink");
269   ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
270   cleanup_multifdsink (sink);
271 }
272
273 GST_END_TEST;
274
275 /* this tests changing of streamheaders
276  * - set streamheader caps on the pad
277  * - pushes the IN_CAPS buffers
278  * - pushes a buffer
279  * - add a first client
280  * - verifies that this first client receives the first streamheader caps,
281  *   plus a new buffer
282  * - change streamheader caps
283  * - verify that the first client receives the new streamheader buffers as well
284  */
285 GST_START_TEST (test_change_streamheader)
286 {
287   GstElement *sink;
288   GstBuffer *hbuf1, *hbuf2, *buf;
289   GstCaps *caps;
290   GstStructure *structure;
291   int pfd1[2], pfd2[2];
292   guint8 data[12];
293   GValue array = { 0 };
294   GValue value = { 0 };
295   guint64 bytes_served;
296   int avail;
297
298   sink = setup_multifdsink ();
299
300   fail_if (pipe (pfd1) == -1);
301   fail_if (pipe (pfd2) == -1);
302
303   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
304
305   /* create caps with streamheader, set the caps, and push the IN_CAPS
306    * buffers */
307   gst_multifdsink_create_streamheader ("babe", "deadbeef", &hbuf1, &hbuf2,
308       &caps);
309   fail_unless (gst_pad_set_caps (mysrcpad, caps));
310   gst_caps_unref (caps);
311
312   fail_unless (gst_pad_push (mysrcpad, hbuf1) == GST_FLOW_OK);
313   fail_unless (gst_pad_push (mysrcpad, hbuf2) == GST_FLOW_OK);
314
315   /* add the first client */
316   g_signal_emit_by_name (sink, "add", pfd1[1]);
317
318   /* verify this hasn't triggered a write yet */
319   /* FIXME: possibly racy, since if it would write, we may not get it
320    * immediately ? */
321   fail_if_can_read ("first client, no buffer", pfd1[0]);
322
323   /* now push a buffer and read */
324   buf = gst_buffer_new_and_alloc (4);
325   memcpy (GST_BUFFER_DATA (buf), "f00d", 4);
326   gst_pad_push (mysrcpad, buf);
327
328   fail_unless_read ("change: first client", pfd1[0], 4, "babe");
329   fail_unless_read ("change: first client", pfd1[0], 8, "deadbeef");
330   fail_unless_read ("change: first client", pfd1[0], 4, "f00d");
331   wait_bytes_served (sink, 16);
332
333   /* now add the second client */
334   g_signal_emit_by_name (sink, "add", pfd2[1]);
335   fail_if_can_read ("second client, no buffer", pfd2[0]);
336
337   /* change the streamheader */
338   gst_buffer_unref (hbuf1);
339   gst_buffer_unref (hbuf2);
340   gst_multifdsink_create_streamheader ("beef", "deadbabe", &hbuf1, &hbuf2,
341       &caps);
342   fail_unless (gst_pad_set_caps (mysrcpad, caps));
343   gst_caps_unref (caps);
344
345   fail_unless (gst_pad_push (mysrcpad, hbuf1) == GST_FLOW_OK);
346   fail_unless (gst_pad_push (mysrcpad, hbuf2) == GST_FLOW_OK);
347
348   /* verify neither client has new data available to read */
349   fail_if_can_read ("first client, changed streamheader", pfd1[0]);
350   fail_if_can_read ("second client, changed streamheader", pfd2[0]);
351
352   /* now push another buffer, which will trigger streamheader for second
353    * client, but should also send new streamheaders to first client */
354   buf = gst_buffer_new_and_alloc (4);
355   memcpy (GST_BUFFER_DATA (buf), "deaf", 4);
356   gst_pad_push (mysrcpad, buf);
357
358   /* FIXME: here's a bug - the first client does not get new streamheaders */
359   fail_unless_read ("first client", pfd1[0], 4, "deaf");
360
361   /* new streamheader data */
362   fail_unless_read ("second client", pfd2[0], 4, "beef");
363   fail_unless_read ("second client", pfd2[0], 8, "deadbabe");
364   /* we missed the f00d buffer */
365   fail_unless_read ("second client", pfd2[0], 4, "deaf");
366   wait_bytes_served (sink, 36);
367
368   gst_buffer_unref (hbuf1);
369   gst_buffer_unref (hbuf2);
370   GST_DEBUG ("cleaning up multifdsink");
371   ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
372   cleanup_multifdsink (sink);
373 }
374
375 GST_END_TEST;
376
377 /* FIXME: add test simulating chained oggs where:
378  * sync-method is burst-on-connect
379  * (when multifdsink actually does burst-on-connect based on byte size, not
380    "last keyframe" which any frame for audio :))
381  * an old client still needs to read from before the new streamheaders
382  * a new client gets the new streamheaders
383  */
384
385 Suite *
386 multifdsink_suite (void)
387 {
388   Suite *s = suite_create ("multifdsink");
389   TCase *tc_chain = tcase_create ("general");
390
391   suite_add_tcase (s, tc_chain);
392   tcase_add_test (tc_chain, test_no_clients);
393   tcase_add_test (tc_chain, test_add_client);
394   tcase_add_test (tc_chain, test_streamheader);
395   tcase_add_test (tc_chain, test_change_streamheader);
396
397   return s;
398 }
399
400 int
401 main (int argc, char **argv)
402 {
403   int nf;
404
405   Suite *s = multifdsink_suite ();
406   SRunner *sr = srunner_create (s);
407
408   gst_check_init (&argc, &argv);
409
410   srunner_run_all (sr, CK_NORMAL);
411   nf = srunner_ntests_failed (sr);
412   srunner_free (sr);
413
414   return nf;
415 }