• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer unit tests for streamsynchronizer
2  *
3  * Copyright (C) 2012 Edward Hervey <edward@collabora.com>, Collabora Ltd
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 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24 
25 #define GLIB_DISABLE_DEPRECATION_WARNINGS
26 
27 #undef GST_CAT_DEFAULT
28 #include <gst/check/gstcheck.h>
29 
30 static GstStaticPadTemplate mysinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
31     GST_PAD_SINK,
32     GST_PAD_ALWAYS,
33     GST_STATIC_CAPS_ANY);
34 static GstStaticPadTemplate mysrctemplate = GST_STATIC_PAD_TEMPLATE ("src",
35     GST_PAD_SRC,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS_ANY);
38 
39 typedef struct
40 {
41   GstPad *pad;
42   GList *to_push;
43 } MyPushInfo;
44 
45 GMutex push_mutex;
46 GCond push_cond;
47 
48 static GstPad *
get_other_pad(GstPad * pad)49 get_other_pad (GstPad * pad)
50 {
51   GstIterator *it;
52   GValue item = G_VALUE_INIT;
53   GstPad *otherpad;
54 
55   it = gst_pad_iterate_internal_links (pad);
56   fail_unless (gst_iterator_next (it, &item) == GST_ITERATOR_OK);
57   otherpad = g_value_dup_object (&item);
58   g_value_unset (&item);
59   gst_iterator_free (it);
60 
61   return otherpad;
62 }
63 
64 static GstFlowReturn
my_sink_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)65 my_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
66 {
67   GList **expected = GST_PAD_ELEMENT_PRIVATE (pad);
68   GList *next;
69   GstBuffer *exp;
70 
71   fail_if (*expected == NULL,
72       "streamsynchronizer pushed a buffer/event but we didn't expect any");
73 
74   next = (*expected)->next;
75 
76   fail_if (GST_IS_EVENT ((*expected)->data),
77       "Expected an event (%s) but got a buffer instead",
78       GST_EVENT_TYPE_NAME (GST_EVENT ((*expected)->data)));
79 
80   exp = GST_BUFFER ((*expected)->data);
81 
82   fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buf),
83       GST_BUFFER_TIMESTAMP (exp));
84 
85   GST_DEBUG ("Properly received expected buffer");
86   gst_buffer_unref (exp);
87   gst_buffer_unref (buf);
88 
89   g_list_free1 (*expected);
90   *expected = next;
91 
92   /* When done signal main thread */
93   if (next == NULL) {
94     g_mutex_lock (&push_mutex);
95     g_cond_signal (&push_cond);
96     g_mutex_unlock (&push_mutex);
97   }
98 
99   return GST_FLOW_OK;
100 }
101 
102 static gboolean
my_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)103 my_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
104 {
105   GList **expected = GST_PAD_ELEMENT_PRIVATE (pad);
106   GList *next;
107   GstEvent *exp;
108 
109   fail_if (*expected == NULL,
110       "streamsynchronizer pushed a buffer/event but we didn't expect any");
111 
112   next = (*expected)->next;
113 
114   fail_unless (GST_IS_EVENT ((*expected)->data),
115       "We were not expecting an event (But got an event of type %s)",
116       GST_EVENT_TYPE_NAME (event));
117   exp = GST_EVENT ((*expected)->data);
118   fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_TYPE (exp),
119       "Got event of type %s but expected of type %s",
120       GST_EVENT_TYPE_NAME (event), GST_EVENT_TYPE_NAME (exp));
121   fail_unless_equals_int (GST_EVENT_SEQNUM (event), GST_EVENT_SEQNUM (exp));
122   /* FIXME : Check more types */
123 
124   switch (GST_EVENT_TYPE (event)) {
125     case GST_EVENT_SEGMENT:
126     {
127       const GstSegment *recvseg, *expectseg;
128 
129       /* Compare segment values */
130       gst_event_parse_segment (event, &recvseg);
131       gst_event_parse_segment (exp, &expectseg);
132 
133       fail_unless_equals_int (recvseg->format, expectseg->format);
134       fail_unless_equals_uint64 (recvseg->base, expectseg->base);
135       fail_unless_equals_uint64 (recvseg->offset, expectseg->offset);
136       fail_unless_equals_uint64 (recvseg->start, expectseg->start);
137       fail_unless_equals_uint64 (recvseg->stop, expectseg->stop);
138       fail_unless_equals_uint64 (recvseg->time, expectseg->time);
139       fail_unless_equals_uint64 (recvseg->position, expectseg->position);
140       fail_unless_equals_uint64 (recvseg->duration, expectseg->duration);
141     }
142       break;
143     default:
144       break;
145   }
146 
147   GST_DEBUG ("Properly received expected event %s", GST_EVENT_TYPE_NAME (exp));
148 
149   gst_event_unref (exp);
150   gst_event_unref (event);
151 
152   g_list_free1 (*expected);
153   *expected = next;
154 
155   /* When done signal main thread */
156   if (next == NULL) {
157     g_mutex_lock (&push_mutex);
158     g_cond_signal (&push_cond);
159     g_mutex_unlock (&push_mutex);
160   }
161 
162   return TRUE;
163 }
164 
165 static gpointer
my_push_thread(MyPushInfo * pushinfo)166 my_push_thread (MyPushInfo * pushinfo)
167 {
168   GList *tmp;
169 
170   /* FIXME : Do this in a thread */
171   for (tmp = pushinfo->to_push; tmp; tmp = tmp->next) {
172     if (GST_IS_EVENT (tmp->data))
173       gst_pad_push_event (pushinfo->pad, GST_EVENT (tmp->data));
174     else
175       gst_pad_push (pushinfo->pad, GST_BUFFER (tmp->data));
176   }
177 
178   GST_INFO ("leaving thread");
179   return NULL;
180 }
181 
GST_START_TEST(test_basic)182 GST_START_TEST (test_basic)
183 {
184   GstElement *synchr;
185   GstPad *sinkpad, *srcpad;
186   GstPad *mysrcpad, *mysinkpad;
187   GList *to_push = NULL, *expected = NULL;
188   GstEvent *event;
189   GstBuffer *buf;
190   GThread *thread;
191   MyPushInfo pushinfo;
192   guint i;
193   GstSegment segment;
194   guint32 seqnum;
195 
196   synchr = gst_element_factory_make ("streamsynchronizer", NULL);
197 
198   /* Get sinkpad/srcpad */
199   sinkpad = gst_element_request_pad_simple (synchr, "sink_%u");
200   fail_unless (sinkpad != NULL);
201   srcpad = get_other_pad (sinkpad);
202   fail_unless (srcpad != NULL);
203 
204   gst_element_set_state (synchr, GST_STATE_PLAYING);
205 
206   mysrcpad = gst_pad_new_from_static_template (&mysrctemplate, "src");
207   fail_if (mysrcpad == NULL);
208   fail_unless (gst_pad_link (mysrcpad, sinkpad) == GST_PAD_LINK_OK);
209   fail_unless (gst_pad_set_active (mysrcpad, TRUE));
210 
211   mysinkpad = gst_pad_new_from_static_template (&mysinktemplate, "sink");
212   gst_pad_set_chain_function (mysinkpad, my_sink_chain);
213   gst_pad_set_event_function (mysinkpad, my_sink_event);
214   fail_if (mysinkpad == NULL);
215   fail_unless (gst_pad_link (srcpad, mysinkpad) == GST_PAD_LINK_OK);
216   fail_unless (gst_pad_set_active (mysinkpad, TRUE));
217   GST_PAD_ELEMENT_PRIVATE (mysinkpad) = &expected;
218 
219   /* Start with a stream START and a new segment */
220   event = gst_event_new_stream_start ("lala");
221   to_push = g_list_append (to_push, event);
222   expected = g_list_append (expected, gst_event_ref (event));
223 
224   gst_segment_init (&segment, GST_FORMAT_TIME);
225   event = gst_event_new_segment (&segment);
226   to_push = g_list_append (to_push, event);
227   expected = g_list_append (expected, gst_event_ref (event));
228 
229   /* Then 10 buffers */
230   for (i = 0; i < 10; i++) {
231     buf = gst_buffer_new ();
232     GST_BUFFER_TIMESTAMP (buf) = i * GST_SECOND;
233     GST_BUFFER_DURATION (buf) = GST_SECOND;
234     to_push = g_list_append (to_push, buf);
235     expected = g_list_append (expected, gst_buffer_ref (buf));
236   }
237 
238   /* Then a new stream start */
239   event = gst_event_new_stream_start ("lala again");
240   to_push = g_list_append (to_push, event);
241   expected = g_list_append (expected, gst_event_ref (event));
242 
243   /* This newsegment will be updated */
244   gst_segment_init (&segment, GST_FORMAT_TIME);
245   event = gst_event_new_segment (&segment);
246   seqnum = gst_event_get_seqnum (event);
247   to_push = g_list_append (to_push, event);
248   /* The received segment's base should be updated by streamsynchronizer to
249    * take into account the amount of data played before (i.e. 10s) */
250   segment.base = 10 * GST_SECOND;
251   event = gst_event_new_segment (&segment);
252   gst_event_set_seqnum (event, seqnum);
253   expected = g_list_append (expected, event);
254 
255   /* Then 10 buffers */
256   for (i = 0; i < 10; i++) {
257     buf = gst_buffer_new ();
258     GST_BUFFER_TIMESTAMP (buf) = i * GST_SECOND;
259     GST_BUFFER_DURATION (buf) = GST_SECOND;
260     to_push = g_list_append (to_push, buf);
261     expected = g_list_append (expected, gst_buffer_ref (buf));
262   }
263 
264   g_mutex_init (&push_mutex);
265   g_cond_init (&push_cond);
266 
267   pushinfo.pad = mysrcpad;
268   pushinfo.to_push = to_push;
269   g_mutex_lock (&push_mutex);
270   thread = g_thread_new ("pushthread", (GThreadFunc) my_push_thread, &pushinfo);
271   fail_unless (thread != NULL);
272 
273   g_cond_wait (&push_cond, &push_mutex);
274   g_mutex_unlock (&push_mutex);
275 
276   fail_if (expected != NULL);
277 
278   /* wait for thread to exit before freeing things */
279   g_thread_join (thread);
280 
281   /* Cleanup */
282   g_list_free (to_push);
283   gst_element_release_request_pad (synchr, sinkpad);
284   gst_object_unref (srcpad);
285   gst_object_unref (sinkpad);
286   gst_object_unref (mysinkpad);
287   gst_object_unref (mysrcpad);
288   gst_element_set_state (synchr, GST_STATE_NULL);
289   gst_object_unref (synchr);
290 }
291 
292 GST_END_TEST;
293 
294 static Suite *
streamsynchronizer_suite(void)295 streamsynchronizer_suite (void)
296 {
297   Suite *s = suite_create ("streamsynchronizer");
298   TCase *tc_chain = tcase_create ("general");
299 
300   suite_add_tcase (s, tc_chain);
301   tcase_add_test (tc_chain, test_basic);
302 
303   return s;
304 }
305 
306 GST_CHECK_MAIN (streamsynchronizer);
307