Merge remote-tracking branch 'origin/0.10'
[platform/upstream/gstreamer.git] / tests / check / elements / rtpjitterbuffer.c
1 /* GStreamer
2  *
3  * Copyright (C) 2009 Nokia Corporation and its subsidary(-ies)
4  *               contact: <stefan.kost@nokia.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include <gst/check/gstcheck.h>
23
24 /* For ease of programming we use globals to keep refs for our floating
25  * src and sink pads we create; otherwise we always have to do get_pad,
26  * get_peer, and then remove references in every test function */
27 static GstPad *mysrcpad, *mysinkpad;
28 /* we also have a list of src buffers */
29 static GList *inbuffers = NULL;
30 static gint num_dropped = 0;
31
32 #define RTP_CAPS_STRING    \
33     "application/x-rtp, "               \
34     "media = (string)audio, "           \
35     "payload = (int) 0, "               \
36     "clock-rate = (int) 8000, "         \
37     "encoding-name = (string)PCMU"
38
39 #define RTP_FRAME_SIZE 20
40
41 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
42     GST_PAD_SINK,
43     GST_PAD_ALWAYS,
44     GST_STATIC_CAPS ("application/x-rtp")
45     );
46 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
47     GST_PAD_SRC,
48     GST_PAD_ALWAYS,
49     GST_STATIC_CAPS ("application/x-rtp, "
50         "clock-rate = (int) [ 1, 2147483647 ]")
51     );
52
53 static void
54 buffer_dropped (gpointer data, GstMiniObject * obj)
55 {
56   GST_DEBUG ("dropping buffer %p", obj);
57   num_dropped++;
58 }
59
60 static GstElement *
61 setup_jitterbuffer (gint num_buffers)
62 {
63   GstElement *jitterbuffer;
64   GstClock *clock;
65   GstBuffer *buffer;
66   GstCaps *caps;
67   /* a 20 sample audio block (2,5 ms) generated with
68    * gst-launch audiotestsrc wave=silence blocksize=40 num-buffers=3 !
69    *    "audio/x-raw,channels=1,rate=8000" ! mulawenc ! rtppcmupay !
70    *     fakesink dump=1
71    */
72   guint8 in[] = {               /* first 4 bytes are rtp-header, next 4 bytes are timestamp */
73     0x80, 0x80, 0x1c, 0x24, 0x46, 0xcd, 0xb7, 0x11, 0x3c, 0x3a, 0x7c, 0x5b,
74     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
75     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
76   };
77   GstClockTime ts = G_GUINT64_CONSTANT (0);
78   GstClockTime tso = gst_util_uint64_scale (RTP_FRAME_SIZE, GST_SECOND, 8000);
79   /*guint latency = GST_TIME_AS_MSECONDS (num_buffers * tso); */
80   gint i;
81
82   GST_DEBUG ("setup_jitterbuffer");
83   jitterbuffer = gst_check_setup_element ("rtpjitterbuffer");
84   /* we need a clock here */
85   clock = gst_system_clock_obtain ();
86   gst_element_set_clock (jitterbuffer, clock);
87   gst_object_unref (clock);
88   /* setup latency */
89   /* latency would be 7 for 3 buffers here, default is 200
90      g_object_set (G_OBJECT (jitterbuffer), "latency", latency, NULL);
91      GST_INFO_OBJECT (jitterbuffer, "set latency to %u ms", latency);
92    */
93
94   mysrcpad = gst_check_setup_src_pad (jitterbuffer, &srctemplate);
95   mysinkpad = gst_check_setup_sink_pad (jitterbuffer, &sinktemplate);
96   gst_pad_set_active (mysrcpad, TRUE);
97   gst_pad_set_active (mysinkpad, TRUE);
98
99   /* create n buffers */
100   caps = gst_caps_from_string (RTP_CAPS_STRING);
101   gst_pad_set_caps (mysrcpad, caps);
102   gst_caps_unref (caps);
103
104   for (i = 0; i < num_buffers; i++) {
105     buffer = gst_buffer_new_and_alloc (sizeof (in));
106     gst_buffer_fill (buffer, 0, in, sizeof (in));
107     GST_BUFFER_TIMESTAMP (buffer) = ts;
108     GST_BUFFER_DURATION (buffer) = tso;
109     gst_mini_object_weak_ref (GST_MINI_OBJECT (buffer), buffer_dropped, NULL);
110     GST_DEBUG ("created buffer: %p", buffer);
111
112     if (!i)
113       GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
114
115     inbuffers = g_list_append (inbuffers, buffer);
116
117     /* hackish way to update the rtp header */
118     in[1] = 0x00;
119     in[3]++;                    /* seqnumber */
120     in[7] += RTP_FRAME_SIZE;    /* inc. timestamp with framesize */
121     ts += tso;
122   }
123   num_dropped = 0;
124
125   return jitterbuffer;
126 }
127
128 static GstStateChangeReturn
129 start_jitterbuffer (GstElement * jitterbuffer)
130 {
131   GstStateChangeReturn ret;
132   GstClockTime now;
133   GstClock *clock;
134
135   clock = gst_element_get_clock (jitterbuffer);
136   now = gst_clock_get_time (clock);
137   gst_object_unref (clock);
138
139   gst_element_set_base_time (jitterbuffer, now);
140   ret = gst_element_set_state (jitterbuffer, GST_STATE_PLAYING);
141
142   return ret;
143 }
144
145 static void
146 cleanup_jitterbuffer (GstElement * jitterbuffer)
147 {
148   GST_DEBUG ("cleanup_jitterbuffer");
149
150   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
151   g_list_free (buffers);
152   buffers = NULL;
153
154   g_list_free (inbuffers);
155   inbuffers = NULL;
156
157   gst_pad_set_active (mysrcpad, FALSE);
158   gst_pad_set_active (mysinkpad, FALSE);
159   gst_check_teardown_src_pad (jitterbuffer);
160   gst_check_teardown_sink_pad (jitterbuffer);
161   gst_check_teardown_element (jitterbuffer);
162 }
163
164 static void
165 check_jitterbuffer_results (GstElement * jitterbuffer, gint num_buffers)
166 {
167   GstBuffer *buffer;
168   GList *node;
169   GstClockTime ts = G_GUINT64_CONSTANT (0);
170   GstClockTime tso = gst_util_uint64_scale (RTP_FRAME_SIZE, GST_SECOND, 8000);
171   GstMapInfo map;
172   guint16 prev_sn = 0, cur_sn;
173   guint32 prev_ts = 0, cur_ts;
174
175   /* sleep for twice the latency */
176   g_usleep (400 * 1000);
177
178   GST_INFO ("of %d buffer %d/%d received/dropped", num_buffers,
179       g_list_length (buffers), num_dropped);
180   /* if this fails, not all buffers have been processed */
181   fail_unless_equals_int ((g_list_length (buffers) + num_dropped), num_buffers);
182
183   /* check the buffer list */
184   fail_unless_equals_int (g_list_length (buffers), num_buffers);
185   for (node = buffers; node; node = g_list_next (node)) {
186     fail_if ((buffer = (GstBuffer *) node->data) == NULL);
187     fail_if (GST_BUFFER_TIMESTAMP (buffer) != ts);
188     gst_buffer_map (buffer, &map, GST_MAP_READ);
189     cur_sn = ((guint16) map.data[2] << 8) | map.data[3];
190     cur_ts = ((guint32) map.data[4] << 24) | ((guint32) map.data[5] << 16) |
191         ((guint32) map.data[6] << 8) | map.data[7];
192     gst_buffer_unmap (buffer, &map);
193
194     if (node != buffers) {
195       fail_unless (cur_sn > prev_sn);
196       fail_unless (cur_ts > prev_ts);
197
198       prev_sn = cur_sn;
199       prev_ts = cur_ts;
200     }
201     ts += tso;
202   }
203 }
204
205 GST_START_TEST (test_push_forward_seq)
206 {
207   GstElement *jitterbuffer;
208   const guint num_buffers = 3;
209   GstBuffer *buffer;
210   GList *node;
211
212   jitterbuffer = setup_jitterbuffer (num_buffers);
213   fail_unless (start_jitterbuffer (jitterbuffer)
214       == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
215
216   /* push buffers: 0,1,2, */
217   for (node = inbuffers; node; node = g_list_next (node)) {
218     buffer = (GstBuffer *) node->data;
219     fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
220   }
221
222   /* check the buffer list */
223   check_jitterbuffer_results (jitterbuffer, num_buffers);
224
225   /* cleanup */
226   cleanup_jitterbuffer (jitterbuffer);
227 }
228
229 GST_END_TEST;
230
231 GST_START_TEST (test_push_backward_seq)
232 {
233   GstElement *jitterbuffer;
234   const guint num_buffers = 4;
235   GstBuffer *buffer;
236   GList *node;
237
238   jitterbuffer = setup_jitterbuffer (num_buffers);
239   fail_unless (start_jitterbuffer (jitterbuffer)
240       == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
241
242   /* push buffers: 0,3,2,1 */
243   buffer = (GstBuffer *) inbuffers->data;
244   fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
245   for (node = g_list_last (inbuffers); node != inbuffers;
246       node = g_list_previous (node)) {
247     buffer = (GstBuffer *) node->data;
248     fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
249   }
250
251   /* check the buffer list */
252   check_jitterbuffer_results (jitterbuffer, num_buffers);
253
254   /* cleanup */
255   cleanup_jitterbuffer (jitterbuffer);
256 }
257
258 GST_END_TEST;
259
260 GST_START_TEST (test_push_unordered)
261 {
262   GstElement *jitterbuffer;
263   const guint num_buffers = 4;
264   GstBuffer *buffer;
265
266   jitterbuffer = setup_jitterbuffer (num_buffers);
267   fail_unless (start_jitterbuffer (jitterbuffer)
268       == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
269
270   /* push buffers; 0,2,1,3 */
271   buffer = (GstBuffer *) inbuffers->data;
272   fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
273   buffer = g_list_nth_data (inbuffers, 2);
274   fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
275   buffer = g_list_nth_data (inbuffers, 1);
276   fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
277   buffer = g_list_nth_data (inbuffers, 3);
278   fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
279
280   /* check the buffer list */
281   check_jitterbuffer_results (jitterbuffer, num_buffers);
282
283   /* cleanup */
284   cleanup_jitterbuffer (jitterbuffer);
285 }
286
287 GST_END_TEST;
288
289 GST_START_TEST (test_basetime)
290 {
291   GstElement *jitterbuffer;
292   const guint num_buffers = 3;
293   GstBuffer *buffer;
294   GList *node;
295   GstClockTime tso = gst_util_uint64_scale (RTP_FRAME_SIZE, GST_SECOND, 8000);
296
297   jitterbuffer = setup_jitterbuffer (num_buffers);
298   fail_unless (start_jitterbuffer (jitterbuffer)
299       == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
300
301   /* push buffers: 2,1,0 */
302   for (node = g_list_last (inbuffers); node; node = g_list_previous (node)) {
303     buffer = (GstBuffer *) node->data;
304     fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
305   }
306
307   /* sleep for twice the latency */
308   g_usleep (400 * 1000);
309
310   /* if this fails, not all buffers have been processed */
311   fail_unless_equals_int ((g_list_length (buffers) + num_dropped), num_buffers);
312
313   buffer = (GstBuffer *) buffers->data;
314   fail_unless (GST_BUFFER_TIMESTAMP (buffer) != (num_buffers * tso));
315
316   /* cleanup */
317   cleanup_jitterbuffer (jitterbuffer);
318 }
319
320 GST_END_TEST;
321
322
323 static Suite *
324 rtpjitterbuffer_suite (void)
325 {
326   Suite *s = suite_create ("rtpjitterbuffer");
327   TCase *tc_chain = tcase_create ("general");
328
329   suite_add_tcase (s, tc_chain);
330   tcase_add_test (tc_chain, test_push_forward_seq);
331   tcase_add_test (tc_chain, test_push_backward_seq);
332   tcase_add_test (tc_chain, test_push_unordered);
333   tcase_add_test (tc_chain, test_basetime);
334
335   /* FIXME: test buffer lists */
336
337   return s;
338 }
339
340 GST_CHECK_MAIN (rtpjitterbuffer);