67c8c4758e5008f9dc8744c39b14cb133a65e3e8
[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   //FIXME:
236   //fail_if_can_read ("first client", pfd1[0]);
237
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);
243
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);
248
249   /* now add the second client */
250   g_signal_emit_by_name (sink, "add", pfd2[1]);
251   //FIXME:
252   //fail_if_can_read ("second client", pfd2[0]);
253
254   /* now push another buffer, which will trigger streamheader for second
255    * client */
256   buf = gst_buffer_new_and_alloc (4);
257   memcpy (GST_BUFFER_DATA (buf), "deaf", 4);
258   gst_pad_push (mysrcpad, buf);
259
260   fail_unless_read ("first client", pfd1[0], 4, "deaf");
261
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);
267
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);
273 }
274
275 GST_END_TEST;
276
277 /* this tests changing of streamheaders
278  * - set streamheader caps on the pad
279  * - pushes the IN_CAPS buffers
280  * - pushes a buffer
281  * - add a first client
282  * - verifies that this first client receives the first streamheader caps,
283  *   plus a new buffer
284  * - change streamheader caps
285  * - verify that the first client receives the new streamheader buffers as well
286  */
287 GST_START_TEST (test_change_streamheader)
288 {
289   GstElement *sink;
290   GstBuffer *hbuf1, *hbuf2, *buf;
291   GstCaps *caps;
292   GstStructure *structure;
293   int pfd1[2], pfd2[2];
294   guint8 data[12];
295   GValue array = { 0 };
296   GValue value = { 0 };
297   guint64 bytes_served;
298   int avail;
299
300   sink = setup_multifdsink ();
301
302   fail_if (pipe (pfd1) == -1);
303   fail_if (pipe (pfd2) == -1);
304
305   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
306
307   /* create caps with streamheader, set the caps, and push the IN_CAPS
308    * buffers */
309   gst_multifdsink_create_streamheader ("babe", "deadbeef", &hbuf1, &hbuf2,
310       &caps);
311   fail_unless (gst_pad_set_caps (mysrcpad, caps));
312   gst_caps_unref (caps);
313
314   fail_unless (gst_pad_push (mysrcpad, hbuf1) == GST_FLOW_OK);
315   fail_unless (gst_pad_push (mysrcpad, hbuf2) == GST_FLOW_OK);
316
317   /* add the first client */
318   g_signal_emit_by_name (sink, "add", pfd1[1]);
319
320   /* verify this hasn't triggered a write yet */
321   /* FIXME: possibly racy, since if it would write, we may not get it
322    * immediately ? */
323   fail_if_can_read ("first client, no buffer", pfd1[0]);
324
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);
329
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);
334
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]);
338
339   /* change the streamheader */
340   gst_buffer_unref (hbuf1);
341   gst_buffer_unref (hbuf2);
342   gst_multifdsink_create_streamheader ("beef", "deadbabe", &hbuf1, &hbuf2,
343       &caps);
344   fail_unless (gst_pad_set_caps (mysrcpad, caps));
345   gst_caps_unref (caps);
346
347   fail_unless (gst_pad_push (mysrcpad, hbuf1) == GST_FLOW_OK);
348   fail_unless (gst_pad_push (mysrcpad, hbuf2) == GST_FLOW_OK);
349
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]);
353
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);
359
360   /* FIXME: here's a bug - the first client does not get new streamheaders */
361   fail_unless_read ("first client", pfd1[0], 4, "deaf");
362
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);
369
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);
375 }
376
377 GST_END_TEST;
378
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
385  */
386
387 Suite *
388 multifdsink_suite (void)
389 {
390   Suite *s = suite_create ("multifdsink");
391   TCase *tc_chain = tcase_create ("general");
392
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);
398
399   return s;
400 }
401
402 int
403 main (int argc, char **argv)
404 {
405   int nf;
406
407   Suite *s = multifdsink_suite ();
408   SRunner *sr = srunner_create (s);
409
410   gst_check_init (&argc, &argv);
411
412   srunner_run_all (sr, CK_NORMAL);
413   nf = srunner_ntests_failed (sr);
414   srunner_free (sr);
415
416   return nf;
417 }