appsink: add support for buffer lists
[platform/upstream/gstreamer.git] / tests / check / elements / appsink.c
1 /* GStreamer
2  *
3  * Copyright (C) 2009, Axis Communications AB, LUND, SWEDEN
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., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include <gst/check/gstcheck.h>
22 #include <gst/app/gstappsink.h>
23
24 gint global_testdata;
25
26 static GstPad *mysrcpad;
27
28 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
29     GST_PAD_SRC,
30     GST_PAD_ALWAYS,
31     GST_STATIC_CAPS ("application/x-gst-check")
32     );
33
34 static GstElement *
35 setup_appsink (void)
36 {
37   GstElement *appsink;
38   GstCaps *caps;
39
40   GST_DEBUG ("setup_appsink");
41   appsink = gst_check_setup_element ("appsink");
42   mysrcpad = gst_check_setup_src_pad (appsink, &srctemplate);
43   gst_pad_set_active (mysrcpad, TRUE);
44
45   caps = gst_caps_new_empty_simple ("application/x-gst-check");
46   gst_check_setup_events (mysrcpad, appsink, caps, GST_FORMAT_TIME);
47   gst_caps_unref (caps);
48
49   return appsink;
50 }
51
52 static void
53 cleanup_appsink (GstElement * appsink)
54 {
55   GST_DEBUG ("cleanup_appsink");
56
57   gst_check_teardown_src_pad (appsink);
58   gst_check_teardown_element (appsink);
59 }
60
61 /* This function does an operation to it's indata argument and returns it.
62  * The exact operation performed doesn't matter. Currently it multiplies with
63  * two, but it could do anything. The idea is to use the function to verify
64  * that the code calling it gets run. */
65 static gint
66 operate_on_data (gint indata)
67 {
68   return indata * 2;
69 }
70
71 static GstFlowReturn
72 callback_function (GstAppSink * appsink, gpointer callback_data)
73 {
74   global_testdata = operate_on_data (*((gint *) callback_data));
75
76   return GST_FLOW_OK;
77 }
78
79 static void
80 notify_function (gpointer callback_data)
81 {
82   global_testdata = operate_on_data (*((gint *) callback_data));
83 }
84
85 GST_START_TEST (test_non_clients)
86 {
87   GstElement *sink;
88   GstBuffer *buffer;
89
90   sink = setup_appsink ();
91
92   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
93
94   buffer = gst_buffer_new_and_alloc (4);
95   fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
96
97   GST_DEBUG ("cleaning up appsink");
98   ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
99   cleanup_appsink (sink);
100 }
101
102 GST_END_TEST;
103
104 /* Verifies that the handoff callback gets run one time when passing a buffer */
105 GST_START_TEST (test_handoff_callback)
106 {
107   GstElement *sink;
108   GstBuffer *buffer;
109   gint testdata;
110   GstAppSinkCallbacks callbacks = { NULL };
111
112   sink = setup_appsink ();
113
114   global_testdata = 0;
115   testdata = 5;                 /* Arbitrary value */
116
117   callbacks.new_sample = callback_function;
118
119   gst_app_sink_set_callbacks (GST_APP_SINK (sink), &callbacks, &testdata, NULL);
120
121   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
122
123   buffer = gst_buffer_new_and_alloc (4);
124   /* Pushing a buffer should run our callback */
125   fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
126
127   testdata = operate_on_data (testdata);
128
129   /* If both test_data & global_testdata have been operated on, we're happy. */
130   fail_unless (testdata == global_testdata);
131
132   GST_DEBUG ("cleaning up appsink");
133   ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
134   cleanup_appsink (sink);
135 }
136
137 GST_END_TEST;
138
139 /* Verifies that the notify function gets executed when the sink is destroyed */
140 GST_START_TEST (test_notify0)
141 {
142   GstElement *sink;
143   gint testdata;
144   GstAppSinkCallbacks callbacks = { NULL };
145
146   sink = gst_element_factory_make ("appsink", NULL);
147
148   global_testdata = 0;
149   testdata = 17;                /* Arbitrary value */
150
151   gst_app_sink_set_callbacks (GST_APP_SINK (sink), &callbacks,
152       &testdata, (*notify_function));
153
154   GST_DEBUG ("cleaning up appsink");
155   /* Destroying sink should call our notify_function */
156   gst_object_unref (sink);
157
158   testdata = operate_on_data (testdata);
159
160   /* If both test_data & global_testdata have been operated on, we're happy. */
161   fail_unless (testdata == global_testdata);
162 }
163
164 GST_END_TEST;
165
166
167 /* Verifies that the notify function gets executed when
168  * gst_app_sink_set_callbacks () gets called */
169 GST_START_TEST (test_notify1)
170 {
171   GstElement *sink;
172   gint testdata;
173   GstAppSinkCallbacks callbacks = { NULL };
174
175   sink = gst_element_factory_make ("appsink", NULL);
176
177   global_testdata = 0;
178   testdata = 42;                /* Arbitrary value */
179
180   gst_app_sink_set_callbacks (GST_APP_SINK (sink), &callbacks,
181       &testdata, (*notify_function));
182   /* Setting new callbacks should trigger the destroy of the old data */
183   gst_app_sink_set_callbacks (GST_APP_SINK (sink), &callbacks, &testdata, NULL);
184
185   testdata = operate_on_data (testdata);
186
187   /* If both test_data & global_testdata have been operated on, we're happy. */
188   fail_unless (testdata == global_testdata);
189
190   GST_DEBUG ("cleaning up appsink");
191   gst_object_unref (sink);
192 }
193
194 GST_END_TEST;
195
196 static const gint values[] = { 1, 2, 4 };
197
198 static GstBufferList *
199 create_buffer_list (void)
200 {
201   guint len;
202   GstBuffer *buffer;
203   GstBufferList *mylist;
204
205   mylist = gst_buffer_list_new ();
206   fail_if (mylist == NULL);
207
208   len = gst_buffer_list_length (mylist);
209   fail_if (len != 0);
210
211   buffer = gst_buffer_new_and_alloc (sizeof (gint));
212   gst_buffer_fill (buffer, 0, &values[0], sizeof (gint));
213   gst_buffer_list_add (mylist, buffer);
214
215   buffer = gst_buffer_new_and_alloc (sizeof (gint));
216   gst_buffer_fill (buffer, 0, &values[1], sizeof (gint));
217   gst_buffer_list_add (mylist, buffer);
218
219   buffer = gst_buffer_new_and_alloc (sizeof (gint));
220   gst_buffer_fill (buffer, 0, &values[2], sizeof (gint));
221   gst_buffer_list_add (mylist, buffer);
222
223   return mylist;
224 }
225
226 static GstFlowReturn
227 callback_function_sample_fallback (GstAppSink * appsink, gpointer p_counter)
228 {
229   GstSample *sample;
230   GstBuffer *buf;
231   gint *p_int_counter = p_counter;
232
233   sample = gst_app_sink_pull_sample (appsink);
234   buf = gst_sample_get_buffer (sample);
235   fail_unless (GST_IS_BUFFER (buf));
236
237   /* buffer list has 3 buffers in two groups */
238   switch (*p_int_counter) {
239     case 0:
240       fail_unless_equals_int (gst_buffer_get_size (buf), sizeof (gint));
241       gst_check_buffer_data (buf, &values[0], sizeof (gint));
242       break;
243     case 1:
244       fail_unless_equals_int (gst_buffer_get_size (buf), sizeof (gint));
245       gst_check_buffer_data (buf, &values[1], sizeof (gint));
246       break;
247     case 2:
248       fail_unless_equals_int (gst_buffer_get_size (buf), sizeof (gint));
249       gst_check_buffer_data (buf, &values[2], sizeof (gint));
250       break;
251     default:
252       g_warn_if_reached ();
253       break;
254   }
255
256   gst_sample_unref (sample);
257
258   *p_int_counter += 1;
259
260   return GST_FLOW_OK;
261 }
262
263 static GstFlowReturn
264 callback_function_sample (GstAppSink * appsink, gpointer p_counter)
265 {
266   GstSample *sample;
267   GstBufferList *list;
268   gint *p_int_counter = p_counter;
269   guint len;
270   gint i;
271
272   sample = gst_app_sink_pull_sample (appsink);
273   list = gst_sample_get_buffer_list (sample);
274   fail_unless (GST_IS_BUFFER_LIST (list));
275   len = gst_buffer_list_length (list);
276   fail_unless_equals_int (len, 3);
277
278   for (i = 0; i < len; i++) {
279     GstBuffer *buf = gst_buffer_list_get (list, i);
280     fail_unless_equals_int (gst_buffer_get_size (buf), sizeof (gint));
281     gst_check_buffer_data (buf, &values[i], sizeof (gint));
282   }
283
284   gst_sample_unref (sample);
285
286   *p_int_counter += 1;
287
288   return GST_FLOW_OK;
289 }
290
291 GST_START_TEST (test_buffer_list_fallback)
292 {
293   GstElement *sink;
294   GstBufferList *list;
295   GstAppSinkCallbacks callbacks = { NULL };
296   gint counter = 0;
297   gboolean buffer_list_support;
298
299   sink = setup_appsink ();
300
301   /* verify that the buffer list support is disabled per default */
302   g_object_get (sink, "buffer-list", &buffer_list_support, NULL);
303   fail_unless (buffer_list_support == FALSE);
304
305
306   callbacks.new_sample = callback_function_sample_fallback;
307
308   gst_app_sink_set_callbacks (GST_APP_SINK (sink), &callbacks, &counter, NULL);
309
310   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
311
312   list = create_buffer_list ();
313   fail_unless (gst_pad_push_list (mysrcpad, list) == GST_FLOW_OK);
314
315   fail_unless_equals_int (counter, 3);
316
317   ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
318   cleanup_appsink (sink);
319 }
320
321 GST_END_TEST;
322
323 GST_START_TEST (test_buffer_list_support)
324 {
325   GstElement *sink;
326   GstBufferList *list;
327   GstAppSinkCallbacks callbacks = { NULL };
328   gint counter = 0;
329
330   sink = setup_appsink ();
331
332   /* enable buffer list support */
333   g_object_set (sink, "buffer-list", TRUE, NULL);
334
335   callbacks.new_sample = callback_function_sample;
336
337   gst_app_sink_set_callbacks (GST_APP_SINK (sink), &callbacks, &counter, NULL);
338
339   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
340
341   list = create_buffer_list ();
342   fail_unless (gst_pad_push_list (mysrcpad, list) == GST_FLOW_OK);
343
344   fail_unless_equals_int (counter, 1);
345
346   ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
347   cleanup_appsink (sink);
348 }
349
350 GST_END_TEST;
351
352 GST_START_TEST (test_buffer_list_fallback_signal)
353 {
354   GstElement *sink;
355   GstBufferList *list;
356   gint counter = 0;
357
358   sink = setup_appsink ();
359
360   /* C calling convention to the rescue.. */
361   g_signal_connect (sink, "new-sample",
362       G_CALLBACK (callback_function_sample_fallback), &counter);
363
364   g_object_set (sink, "emit-signals", TRUE, NULL);
365
366   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
367
368   list = create_buffer_list ();
369   fail_unless (gst_pad_push_list (mysrcpad, list) == GST_FLOW_OK);
370
371   fail_unless_equals_int (counter, 3);
372
373   ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
374   cleanup_appsink (sink);
375 }
376
377 GST_END_TEST;
378
379 GST_START_TEST (test_buffer_list_signal)
380 {
381   GstElement *sink;
382   GstBufferList *list;
383   gint counter = 0;
384
385   sink = setup_appsink ();
386
387   /* enable buffer list support */
388   g_object_set (sink, "buffer-list", TRUE, NULL);
389
390   /* C calling convention to the rescue.. */
391   g_signal_connect (sink, "new-sample", G_CALLBACK (callback_function_sample),
392       &counter);
393
394   g_object_set (sink, "emit-signals", TRUE, NULL);
395
396   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
397
398   list = create_buffer_list ();
399   fail_unless (gst_pad_push_list (mysrcpad, list) == GST_FLOW_OK);
400
401   fail_unless_equals_int (counter, 1);
402
403   ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
404   cleanup_appsink (sink);
405 }
406
407 GST_END_TEST;
408
409 GST_START_TEST (test_segment)
410 {
411   GstElement *sink;
412   GstSegment segment;
413   GstBuffer *buffer;
414   GstSample *pulled_preroll;
415   GstSample *pulled_sample;
416
417   sink = setup_appsink ();
418
419   gst_segment_init (&segment, GST_FORMAT_TIME);
420   segment.start = 2 * GST_SECOND;
421   fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
422
423   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
424
425   buffer = gst_buffer_new_and_alloc (4);
426   fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
427
428   g_signal_emit_by_name (sink, "pull-preroll", &pulled_preroll);
429   fail_unless (gst_segment_is_equal (&segment,
430           gst_sample_get_segment (pulled_preroll)));
431   gst_sample_unref (pulled_preroll);
432
433   g_signal_emit_by_name (sink, "pull-sample", &pulled_sample);
434   fail_unless (gst_segment_is_equal (&segment,
435           gst_sample_get_segment (pulled_sample)));
436   gst_sample_unref (pulled_sample);
437
438   ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
439   cleanup_appsink (sink);
440 }
441
442 GST_END_TEST;
443
444 GST_START_TEST (test_pull_with_timeout)
445 {
446   GstElement *sink;
447   GstBuffer *buffer;
448   GstSample *s;
449   guint64 t1, tdiff;
450
451   sink = setup_appsink ();
452
453   ASSERT_SET_STATE (sink, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
454
455   /* Check that it actually waits for a bit */
456   t1 = gst_util_get_timestamp ();
457   s = gst_app_sink_try_pull_preroll (GST_APP_SINK (sink), GST_SECOND / 20);
458   tdiff = gst_util_get_timestamp () - t1;
459   GST_LOG ("tdiff: %" GST_TIME_FORMAT, GST_TIME_ARGS (tdiff));
460   fail_unless (s == NULL);
461   fail_unless (tdiff > (GST_SECOND / (20 * 2)));
462
463   buffer = gst_buffer_new_and_alloc (4);
464   fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
465
466   s = gst_app_sink_try_pull_preroll (GST_APP_SINK (sink), GST_SECOND / 20);
467   fail_unless (s != NULL);
468   gst_sample_unref (s);
469
470   s = gst_app_sink_try_pull_sample (GST_APP_SINK (sink), 500 * GST_SECOND);
471   fail_unless (s != NULL);
472   gst_sample_unref (s);
473
474   /* No waiting */
475   s = gst_app_sink_try_pull_sample (GST_APP_SINK (sink), 0);
476   fail_unless (s == NULL);
477
478   /* Check that it actually waits for a bit */
479   t1 = gst_util_get_timestamp ();
480   s = gst_app_sink_try_pull_sample (GST_APP_SINK (sink), GST_SECOND / 20);
481   tdiff = gst_util_get_timestamp () - t1;
482   GST_LOG ("tdiff: %" GST_TIME_FORMAT, GST_TIME_ARGS (tdiff));
483   fail_unless (s == NULL);
484   fail_unless (tdiff > (GST_SECOND / (20 * 2)));
485
486   /* No waiting, with buffer pending */
487   buffer = gst_buffer_new_and_alloc (5);
488   fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
489   s = gst_app_sink_try_pull_sample (GST_APP_SINK (sink), 0);
490   fail_unless (s != NULL);
491   gst_sample_unref (s);
492
493   /* With timeout, with buffer pending */
494   buffer = gst_buffer_new_and_alloc (6);
495   fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
496   s = gst_app_sink_try_pull_sample (GST_APP_SINK (sink), GST_SECOND / 20);
497   fail_unless (s != NULL);
498   gst_sample_unref (s);
499
500   ASSERT_SET_STATE (sink, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
501   cleanup_appsink (sink);
502 }
503
504 GST_END_TEST;
505
506 static Suite *
507 appsink_suite (void)
508 {
509   Suite *s = suite_create ("appsink");
510   TCase *tc_chain = tcase_create ("general");
511
512   suite_add_tcase (s, tc_chain);
513   tcase_add_test (tc_chain, test_non_clients);
514   tcase_add_test (tc_chain, test_handoff_callback);
515   tcase_add_test (tc_chain, test_notify0);
516   tcase_add_test (tc_chain, test_notify1);
517   tcase_add_test (tc_chain, test_buffer_list_fallback);
518   tcase_add_test (tc_chain, test_buffer_list_support);
519   tcase_add_test (tc_chain, test_buffer_list_fallback_signal);
520   tcase_add_test (tc_chain, test_buffer_list_signal);
521   tcase_add_test (tc_chain, test_segment);
522   tcase_add_test (tc_chain, test_pull_with_timeout);
523
524   return s;
525 }
526
527 GST_CHECK_MAIN (appsink);