1 /*
2 * onviftimestamp.c
3 *
4 * Copyright (C) 2014 Axis Communications AB
5 * Author: Guillaume Desmottes <guillaume.desmottes@collabora.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <gst/check/gstcheck.h>
22 #include <gst/rtp/gstrtpbuffer.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 GstElement *element;
28 static GstPad *mysrcpad;
29 static GstPad *mysinkpad;
30 /* These are global mainly because they are used from the setup/cleanup
31 * fixture functions */
32 static gulong myprobe;
33 static GList *mypushedevents;
34 static GList *myreceivedevents;
35
36 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
37 GST_PAD_SINK,
38 GST_PAD_ALWAYS,
39 GST_STATIC_CAPS ("application/x-rtp")
40 );
41 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
42 GST_PAD_SRC,
43 GST_PAD_ALWAYS,
44 GST_STATIC_CAPS ("application/x-rtp")
45 );
46
47 #define NTP_OFFSET ((guint64) 1245)
48 #define TIMESTAMP ((GstClockTime)42)
49 #define CSEQ 0x78
50 #define COMPARE TRUE
51 #define NO_COMPARE FALSE
52
53 static GstPadProbeReturn
event_probe(GstPad * pad,GstPadProbeInfo * info,gpointer user_data)54 event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
55 {
56 GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
57
58 GST_INFO ("got %" GST_PTR_FORMAT, event);
59 myreceivedevents = g_list_append (myreceivedevents, gst_event_ref (event));
60
61 return GST_PAD_PROBE_OK;
62 }
63
64 static GstEvent *
create_ntp_offset_event(GstClockTime ntp_offset,gboolean discont)65 create_ntp_offset_event (GstClockTime ntp_offset, gboolean discont)
66 {
67 GstStructure *structure;
68
69 structure = gst_structure_new ("GstNtpOffset", "ntp-offset", G_TYPE_UINT64,
70 ntp_offset, "discont", G_TYPE_BOOLEAN, discont, NULL);
71
72 return gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
73 }
74
75 static GstEvent *
create_event(GstEventType type)76 create_event (GstEventType type)
77 {
78 GstEvent *event = NULL;
79
80 switch (type) {
81 case GST_EVENT_CUSTOM_DOWNSTREAM:
82 event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
83 gst_structure_new ("x-app/test", "test-field", G_TYPE_STRING,
84 "test-value", NULL));
85 break;
86 case GST_EVENT_CUSTOM_DOWNSTREAM_OOB:
87 event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB,
88 gst_structure_new ("x-app/test", "test-field", G_TYPE_STRING,
89 "test-value", NULL));
90 break;
91 case GST_EVENT_EOS:
92 event = gst_event_new_eos ();
93 break;
94 default:
95 g_assert_not_reached ();
96 break;
97 }
98
99 return event;
100 }
101
102 static void
create_and_push_event(GstEventType type)103 create_and_push_event (GstEventType type)
104 {
105 GstEvent *event = create_event (type);
106
107 mypushedevents = g_list_append (mypushedevents, event);
108 fail_unless (gst_pad_push_event (mysrcpad, event));
109 }
110
111 static void
check_and_clear_events(gint expected,gboolean compare)112 check_and_clear_events (gint expected, gboolean compare)
113 {
114 GList *p;
115 GList *r;
116
117 /* verify that there's as many queued events as expected */
118 fail_unless_equals_int (g_list_length (myreceivedevents), expected);
119
120 if (compare) {
121 fail_unless_equals_int (expected, g_list_length (mypushedevents));
122
123 /* verify that the events are queued in the expected order */
124 r = myreceivedevents;
125 p = mypushedevents;
126
127 while (p != NULL) {
128 fail_unless_equals_pointer (p->data, r->data);
129 p = g_list_next (p);
130 r = g_list_next (r);
131 }
132 }
133
134 g_list_free_full (myreceivedevents, (GDestroyNotify) gst_event_unref);
135 myreceivedevents = NULL;
136 g_list_free (mypushedevents);
137 mypushedevents = NULL;
138 }
139
140 static void
setup(void)141 setup (void)
142 {
143 element = gst_check_setup_element ("rtponviftimestamp");
144
145 mysinkpad = gst_check_setup_sink_pad (element, &sinktemplate);
146 gst_pad_set_active (mysinkpad, TRUE);
147
148 mysrcpad = gst_check_setup_src_pad (element, &srctemplate);
149 gst_pad_set_active (mysrcpad, TRUE);
150 }
151
152 static void
cleanup(void)153 cleanup (void)
154 {
155 gst_check_drop_buffers ();
156
157 gst_pad_set_active (mysrcpad, FALSE);
158 gst_check_teardown_src_pad (element);
159 mysrcpad = NULL;
160
161 gst_pad_set_active (mysinkpad, FALSE);
162 gst_check_teardown_sink_pad (element);
163 mysinkpad = NULL;
164
165 gst_check_teardown_element (element);
166 element = NULL;
167
168 gst_check_drop_buffers ();
169 }
170
171 static void
setup_with_event(void)172 setup_with_event (void)
173 {
174 setup ();
175
176 myprobe = gst_pad_add_probe (mysinkpad,
177 GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, event_probe, NULL, NULL);
178 myreceivedevents = NULL;
179 mypushedevents = NULL;
180 }
181
182 static void
cleanup_with_event(void)183 cleanup_with_event (void)
184 {
185 gst_pad_remove_probe (mysinkpad, myprobe);
186 myprobe = 0;
187 myreceivedevents = NULL;
188 mypushedevents = NULL;
189
190 cleanup ();
191 }
192
193 static void
check_buffer_equal(GstBuffer * buf,GstBuffer * expected)194 check_buffer_equal (GstBuffer * buf, GstBuffer * expected)
195 {
196 GstMapInfo info_buf, info_expected;
197
198 fail_if (buf == NULL);
199 fail_if (expected == NULL);
200
201 fail_unless (gst_buffer_map (buf, &info_buf, GST_MAP_READ));
202 fail_unless (gst_buffer_map (expected, &info_expected, GST_MAP_READ));
203
204 GST_LOG ("buffer: size %" G_GSIZE_FORMAT, info_buf.size);
205 GST_LOG ("expected: size %" G_GSIZE_FORMAT, info_expected.size);
206 GST_MEMDUMP ("buffer", info_buf.data, info_buf.size);
207 GST_MEMDUMP ("expected", info_expected.data, info_expected.size);
208
209 fail_unless_equals_uint64 (info_buf.size, info_expected.size);
210 fail_unless_equals_int (memcmp (info_buf.data, info_expected.data,
211 info_buf.size), 0);
212
213 gst_buffer_unmap (buf, &info_buf);
214 gst_buffer_unmap (expected, &info_expected);
215 }
216
217 /* Create a RTP buffer without the extension */
218 static GstBuffer *
create_rtp_buffer(GstClockTime timestamp,gboolean clean_point)219 create_rtp_buffer (GstClockTime timestamp, gboolean clean_point)
220 {
221 GstBuffer *buffer_in;
222 GstRTPBuffer rtpbuffer_in = GST_RTP_BUFFER_INIT;
223
224 buffer_in = gst_rtp_buffer_new_allocate (0, 0, 0);
225 GST_BUFFER_PTS (buffer_in) = timestamp;
226
227 if (!clean_point)
228 GST_BUFFER_FLAG_SET (buffer_in, GST_BUFFER_FLAG_DELTA_UNIT);
229
230 fail_unless (gst_rtp_buffer_map (buffer_in, GST_MAP_READ, &rtpbuffer_in));
231 fail_if (gst_rtp_buffer_get_extension (&rtpbuffer_in));
232 gst_rtp_buffer_unmap (&rtpbuffer_in);
233
234 return buffer_in;
235 }
236
237 static guint64
convert_to_ntp(GstClockTime t)238 convert_to_ntp (GstClockTime t)
239 {
240 guint64 ntptime;
241
242 /* convert to NTP time. upper 32 bits should contain the seconds
243 * and the lower 32 bits, the fractions of a second. */
244 ntptime = gst_util_uint64_scale (t, (G_GINT64_CONSTANT (1) << 32),
245 GST_SECOND);
246
247 return ntptime;
248 }
249
250 /* Create a copy of @buffer_in having the RTP extension */
251 static GstBuffer *
create_extension_buffer(GstBuffer * buffer_in,gboolean clean_point,gboolean end_contiguous,gboolean discont,guint64 ntp_offset,guint8 cseq,gboolean first_buffer)252 create_extension_buffer (GstBuffer * buffer_in, gboolean clean_point,
253 gboolean end_contiguous, gboolean discont, guint64 ntp_offset, guint8 cseq,
254 gboolean first_buffer)
255 {
256 GstBuffer *buffer_out;
257 GstRTPBuffer rtpbuffer_out = GST_RTP_BUFFER_INIT;
258 guint8 *data;
259 guint8 flags = 0;
260
261 buffer_out = gst_buffer_copy (buffer_in);
262
263 fail_unless (gst_rtp_buffer_map (buffer_out, GST_MAP_READWRITE,
264 &rtpbuffer_out));
265
266 /* extension */
267 fail_unless (gst_rtp_buffer_set_extension_data (&rtpbuffer_out, 0xABAC, 3));
268 fail_unless (gst_rtp_buffer_get_extension (&rtpbuffer_out));
269 fail_unless (gst_rtp_buffer_get_extension_data (&rtpbuffer_out, NULL,
270 (gpointer) & data, NULL));
271
272 /* NTP timestamp */
273 GST_WRITE_UINT64_BE (data, convert_to_ntp (GST_BUFFER_PTS (buffer_in) +
274 ntp_offset));
275
276 /* C E D mbz */
277 if (first_buffer)
278 flags |= (1 << 5);
279 if (clean_point)
280 flags |= (1 << 7);
281 if (end_contiguous)
282 flags |= (1 << 6);
283 if (discont)
284 flags |= (1 << 5);
285
286 GST_WRITE_UINT8 (data + 8, flags);
287
288 /* CSeq */
289 GST_WRITE_UINT8 (data + 9, cseq);
290
291 memset (data + 10, 0, 4);
292
293 gst_rtp_buffer_unmap (&rtpbuffer_out);
294
295 return buffer_out;
296 }
297
298 static void
do_one_buffer_test_apply(gboolean clean_point)299 do_one_buffer_test_apply (gboolean clean_point)
300 {
301 GstBuffer *buffer_in, *buffer_out;
302
303 g_object_set (element, "ntp-offset", NTP_OFFSET, "cseq", 0x12345678,
304 "set-e-bit", FALSE, NULL);
305
306 ASSERT_SET_STATE (element, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
307
308 buffer_in = create_rtp_buffer (TIMESTAMP, clean_point);
309 buffer_out = create_extension_buffer (buffer_in, clean_point, FALSE, FALSE,
310 NTP_OFFSET, CSEQ, TRUE);
311
312 /* push initial events */
313 gst_check_setup_events (mysrcpad, element, NULL, GST_FORMAT_TIME);
314
315 /* Push buffer */
316 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer_in), GST_FLOW_OK);
317
318 check_buffer_equal ((GstBuffer *) buffers->data, buffer_out);
319 gst_buffer_unref (buffer_out);
320
321 ASSERT_SET_STATE (element, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
322 }
323
324 static void
do_two_buffers_test_apply(gboolean end_contiguous)325 do_two_buffers_test_apply (gboolean end_contiguous)
326 {
327 GstBuffer *buffer_in, *buffer_out;
328 GList *node;
329
330 g_object_set (element, "ntp-offset", NTP_OFFSET, "cseq", 0x12345678,
331 "set-e-bit", TRUE, NULL);
332
333 ASSERT_SET_STATE (element, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
334
335 buffer_in = create_rtp_buffer (TIMESTAMP, FALSE);
336 buffer_out = create_extension_buffer (buffer_in, FALSE, end_contiguous,
337 FALSE, NTP_OFFSET, CSEQ, TRUE);
338
339 /* push initial events */
340 gst_check_setup_events (mysrcpad, element, NULL, GST_FORMAT_TIME);
341
342 /* Push buffer */
343 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer_in), GST_FLOW_OK);
344
345 /* The buffer hasn't been pushed it as the element is waiting for the next
346 * buffer. */
347 fail_unless_equals_int (g_list_length (buffers), 0);
348
349 /* push an ntp-offset event to trigger a discontinuty */
350 fail_unless (gst_pad_push_event (mysrcpad,
351 create_ntp_offset_event (NTP_OFFSET, end_contiguous)));
352
353 /* A second buffer is pushed */
354 buffer_in = create_rtp_buffer (TIMESTAMP + 1, FALSE);
355
356 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer_in), GST_FLOW_OK);
357
358 /* The first buffer has now been pushed out */
359 fail_unless_equals_int (g_list_length (buffers), 1);
360
361 node = g_list_last (buffers);
362 check_buffer_equal ((GstBuffer *) node->data, buffer_out);
363 gst_buffer_unref (buffer_out);
364
365 /* Push EOS */
366 fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
367
368 /* The second buffer has been pushed out */
369 fail_unless_equals_int (g_list_length (buffers), 2);
370
371 /* Last buffer always has the 'E' flag */
372 buffer_out = create_extension_buffer (buffer_in, FALSE, TRUE, end_contiguous,
373 NTP_OFFSET, CSEQ, FALSE);
374 node = g_list_last (buffers);
375 check_buffer_equal ((GstBuffer *) node->data, buffer_out);
376 gst_buffer_unref (buffer_out);
377
378 ASSERT_SET_STATE (element, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
379 }
380
GST_START_TEST(test_apply_clean_point)381 GST_START_TEST (test_apply_clean_point)
382 {
383 do_one_buffer_test_apply (TRUE);
384 }
385
386 GST_END_TEST;
387
GST_START_TEST(test_apply_no_e_bit)388 GST_START_TEST (test_apply_no_e_bit)
389 {
390 do_two_buffers_test_apply (FALSE);
391 }
392
393 GST_END_TEST;
394
GST_START_TEST(test_apply_e_bit)395 GST_START_TEST (test_apply_e_bit)
396 {
397 do_two_buffers_test_apply (TRUE);
398 }
399
400 GST_END_TEST;
401
GST_START_TEST(test_flushing)402 GST_START_TEST (test_flushing)
403 {
404 GstBuffer *buffer;
405
406 /* set the e-bit, so the element use caching */
407 g_object_set (element, "set-e-bit", TRUE, NULL);
408 /* set the ntp-offset, since no one will provide a clock */
409 g_object_set (element, "ntp-offset", NTP_OFFSET, NULL);
410
411 ASSERT_SET_STATE (element, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
412 gst_check_setup_events (mysrcpad, element, NULL, GST_FORMAT_TIME);
413
414 /* create and push the first buffer */
415 buffer = create_rtp_buffer (TIMESTAMP, TRUE);
416 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
417
418 /* no buffers should have made it through */
419 fail_unless_equals_int (g_list_length (buffers), 0);
420
421 /* flush the element */
422 fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_flush_start ()));
423 fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_flush_stop (FALSE)));
424
425 /* resend events */
426 gst_check_setup_events (mysrcpad, element, NULL, GST_FORMAT_TIME);
427
428 /* create and push a second buffer */
429 buffer = create_rtp_buffer (TIMESTAMP + 1, TRUE);
430 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
431
432 /* still no buffers should have made it through (the first one should have
433 * been dropped during flushing) */
434 fail_unless_equals_int (g_list_length (buffers), 0);
435
436 ASSERT_SET_STATE (element, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
437 }
438
439 GST_END_TEST;
440
GST_START_TEST(test_reusable_element_no_e_bit)441 GST_START_TEST (test_reusable_element_no_e_bit)
442 {
443 GstBuffer *buffer;
444
445 /* set the ntp-offset, since no one will provide a clock */
446 g_object_set (element, "ntp-offset", NTP_OFFSET, NULL);
447
448 ASSERT_SET_STATE (element, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
449 gst_check_setup_events (mysrcpad, element, NULL, GST_FORMAT_TIME);
450
451 /* create and push the first buffer */
452 buffer = create_rtp_buffer (TIMESTAMP, TRUE);
453 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
454
455 /* create and push a second buffer */
456 buffer = create_rtp_buffer (TIMESTAMP + 1, TRUE);
457 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
458
459 /* create and push a third buffer */
460 buffer = create_rtp_buffer (TIMESTAMP + 2, TRUE);
461 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
462
463 ASSERT_SET_STATE (element, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
464
465 fail_unless_equals_int (g_list_length (buffers), 3);
466
467 ASSERT_SET_STATE (element, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
468 gst_check_setup_events (mysrcpad, element, NULL, GST_FORMAT_TIME);
469
470 /* create and push the first buffer */
471 buffer = create_rtp_buffer (TIMESTAMP, TRUE);
472 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
473
474 /* create and push a second buffer */
475 buffer = create_rtp_buffer (TIMESTAMP + 1, TRUE);
476 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
477
478 /* create and push a third buffer */
479 buffer = create_rtp_buffer (TIMESTAMP + 2, TRUE);
480 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
481
482 ASSERT_SET_STATE (element, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
483
484 fail_unless_equals_int (g_list_length (buffers), 6);
485 }
486
487 GST_END_TEST;
488
GST_START_TEST(test_reusable_element_e_bit)489 GST_START_TEST (test_reusable_element_e_bit)
490 {
491 GstBuffer *buffer;
492
493 /* set the e-bit, so the element use caching */
494 g_object_set (element, "set-e-bit", TRUE, NULL);
495 /* set the ntp-offset, since no one will provide a clock */
496 g_object_set (element, "ntp-offset", NTP_OFFSET, NULL);
497
498 ASSERT_SET_STATE (element, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
499 gst_check_setup_events (mysrcpad, element, NULL, GST_FORMAT_TIME);
500
501 /* create and push the first buffer */
502 buffer = create_rtp_buffer (TIMESTAMP, TRUE);
503 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
504
505 /* create and push a second buffer */
506 buffer = create_rtp_buffer (TIMESTAMP + 1, TRUE);
507 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
508
509 /* create and push a third buffer */
510 buffer = create_rtp_buffer (TIMESTAMP + 2, TRUE);
511 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
512
513 ASSERT_SET_STATE (element, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
514
515 fail_unless_equals_int (g_list_length (buffers), 2);
516
517 ASSERT_SET_STATE (element, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
518 gst_check_setup_events (mysrcpad, element, NULL, GST_FORMAT_TIME);
519
520 /* create and push the first buffer */
521 buffer = create_rtp_buffer (TIMESTAMP, TRUE);
522 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
523
524 /* create and push a second buffer */
525 buffer = create_rtp_buffer (TIMESTAMP + 1, TRUE);
526 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
527
528 /* create and push a third buffer */
529 buffer = create_rtp_buffer (TIMESTAMP + 2, TRUE);
530 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
531
532 ASSERT_SET_STATE (element, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
533
534 fail_unless_equals_int (g_list_length (buffers), 4);
535 }
536
537 GST_END_TEST;
538
GST_START_TEST(test_ntp_offset_event)539 GST_START_TEST (test_ntp_offset_event)
540 {
541 GstBuffer *buffer_in, *buffer1_out, *buffer2_out;
542 GList *node;
543
544 /* set the e-bit, so the element use caching */
545 g_object_set (element, "set-e-bit", TRUE, NULL);
546
547 ASSERT_SET_STATE (element, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
548 gst_check_setup_events (mysrcpad, element, NULL, GST_FORMAT_TIME);
549
550 /* push an ntp-offset event */
551 fail_unless (gst_pad_push_event (mysrcpad,
552 create_ntp_offset_event (NTP_OFFSET, TRUE)));
553
554 /* create and push the first buffer */
555 buffer_in = create_rtp_buffer (TIMESTAMP, TRUE);
556 buffer1_out = create_extension_buffer (buffer_in, TRUE, TRUE, FALSE,
557 NTP_OFFSET, 0, TRUE);
558 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer_in), GST_FLOW_OK);
559
560 /* push a new ntp offset */
561 fail_unless (gst_pad_push_event (mysrcpad,
562 create_ntp_offset_event (2 * NTP_OFFSET, TRUE)));
563
564 /* create and push a second buffer (last) */
565 buffer_in = create_rtp_buffer (TIMESTAMP + 1, TRUE);
566 buffer2_out = create_extension_buffer (buffer_in, TRUE, TRUE, TRUE,
567 2 * NTP_OFFSET, 0, FALSE);
568 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer_in), GST_FLOW_OK);
569
570 /* the first buffer should have been pushed now */
571 fail_unless_equals_int (g_list_length (buffers), 1);
572 node = g_list_last (buffers);
573 check_buffer_equal ((GstBuffer *) node->data, buffer1_out);
574 gst_buffer_unref (buffer1_out);
575
576 /* push EOS */
577 fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
578
579 /* the second buffer has now been pushed */
580 fail_unless_equals_int (g_list_length (buffers), 2);
581 node = g_list_last (buffers);
582 check_buffer_equal ((GstBuffer *) node->data, buffer2_out);
583 gst_buffer_unref (buffer2_out);
584
585 ASSERT_SET_STATE (element, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
586 }
587
588 GST_END_TEST;
589
GST_START_TEST(test_serialized_events)590 GST_START_TEST (test_serialized_events)
591 {
592 GstBuffer *buffer;
593
594 /* we want the e-bit set so that buffers are cached */
595 g_object_set (element, "set-e-bit", TRUE, NULL);
596 g_object_set (element, "ntp-offset", NTP_OFFSET, NULL);
597
598 ASSERT_SET_STATE (element, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
599
600 /* send intitial events (stream-start and segment) */
601 gst_check_setup_events (mysrcpad, element, NULL, GST_FORMAT_TIME);
602 check_and_clear_events (2, NO_COMPARE);
603
604 /* events received while no buffer is cached should be forwarded */
605 create_and_push_event (GST_EVENT_CUSTOM_DOWNSTREAM);
606 check_and_clear_events (1, NO_COMPARE);
607
608 /* create and push the first buffer, which should be cached */
609 buffer = create_rtp_buffer (TIMESTAMP, TRUE);
610 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
611 fail_unless_equals_int (g_list_length (buffers), 0);
612 /* serialized events should be queued when there's a buffer cached */
613 create_and_push_event (GST_EVENT_CUSTOM_DOWNSTREAM);
614 fail_unless_equals_int (g_list_length (myreceivedevents), 0);
615 /* there's still a buffer cached... */
616 create_and_push_event (GST_EVENT_CUSTOM_DOWNSTREAM);
617 fail_unless_equals_int (g_list_length (myreceivedevents), 0);
618
619 /* receiving a new buffer should let the first through, along with the
620 * queued serialized events */
621 buffer = create_rtp_buffer (TIMESTAMP, TRUE);
622 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
623 fail_unless_equals_int (g_list_length (buffers), 1);
624 check_and_clear_events (2, COMPARE);
625
626 /* there's still a buffer cached, a new serialized event should be quueud */
627 create_and_push_event (GST_EVENT_CUSTOM_DOWNSTREAM);
628 fail_unless_equals_int (g_list_length (myreceivedevents), 0);
629
630 /* when receiving an EOS cached buffer and queued events should be forwarded */
631 create_and_push_event (GST_EVENT_EOS);
632 check_and_clear_events (2, COMPARE);
633 }
634
635 GST_END_TEST;
636
GST_START_TEST(test_non_serialized_events)637 GST_START_TEST (test_non_serialized_events)
638 {
639 GstEvent *event;
640 GstBuffer *buffer;
641
642 /* we want the e-bit set so that buffers are cached */
643 g_object_set (element, "set-e-bit", TRUE, NULL);
644 g_object_set (element, "ntp-offset", NTP_OFFSET, NULL);
645
646 ASSERT_SET_STATE (element, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
647
648 /* send intitial events (stream-start and segment) */
649 gst_check_setup_events (mysrcpad, element, NULL, GST_FORMAT_TIME);
650 fail_unless_equals_int (g_list_length (myreceivedevents), 2);
651 check_and_clear_events (2, NO_COMPARE);
652
653 /* events received while no buffer is cached should be forwarded */
654 create_and_push_event (GST_EVENT_CUSTOM_DOWNSTREAM_OOB);
655 check_and_clear_events (1, COMPARE);
656
657 /* create and push the first buffer, which should be cached */
658 buffer = create_rtp_buffer (TIMESTAMP, TRUE);
659 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
660 fail_unless_equals_int (g_list_length (buffers), 0);
661 /* non-serialized events should be forwarded regardless of whether
662 * there is a cached buffer */
663 create_and_push_event (GST_EVENT_CUSTOM_DOWNSTREAM_OOB);
664 check_and_clear_events (1, COMPARE);
665
666 /* there's still a buffer cached, push a serialized event and make sure
667 * it's queued */
668 create_and_push_event (GST_EVENT_CUSTOM_DOWNSTREAM);
669 fail_unless_equals_int (g_list_length (myreceivedevents), 0);
670 /* non-serialized events should be forwarded regardless of whether there
671 * are serialized events queued, thus the g_list_prepend below */
672 event = create_event (GST_EVENT_CUSTOM_DOWNSTREAM_OOB);
673 mypushedevents = g_list_prepend (mypushedevents, event);
674 fail_unless (gst_pad_push_event (mysrcpad, event));
675 fail_unless_equals_int (g_list_length (myreceivedevents), 1);
676
677 /* when receiving an EOS cached buffer and queued events should be forwarded */
678 create_and_push_event (GST_EVENT_EOS);
679 fail_unless_equals_int (g_list_length (buffers), 1);
680 check_and_clear_events (3, COMPARE);
681 }
682
683 GST_END_TEST;
684
685 static void
do_ntp_time(GstClockTime buffer_time,gint segment_start,gint segment_base)686 do_ntp_time (GstClockTime buffer_time, gint segment_start, gint segment_base)
687 {
688 GstSegment segment;
689 GstBuffer *buffer;
690 GstRTPBuffer rtpbuffer = GST_RTP_BUFFER_INIT;
691 guint8 *data;
692 guint64 expected_ntp_time;
693 guint64 timestamp;
694
695 /* create a segment that controls the behavior
696 * by changing segment.start and segment.base we affect the stream time and
697 * running time respectively */
698 gst_segment_init (&segment, GST_FORMAT_TIME);
699 segment.start = segment_start;
700 segment.base = segment_base;
701 gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment));
702
703 expected_ntp_time = gst_segment_to_stream_time (&segment, GST_FORMAT_TIME,
704 buffer_time);
705 expected_ntp_time += NTP_OFFSET;
706 expected_ntp_time = gst_util_uint64_scale (expected_ntp_time,
707 (G_GINT64_CONSTANT (1) << 32), GST_SECOND);
708
709 buffer = create_rtp_buffer (buffer_time, FALSE);
710 fail_unless_equals_int (gst_pad_push (mysrcpad, buffer), GST_FLOW_OK);
711 fail_unless_equals_int (g_list_length (buffers), 1);
712
713 buffer = g_list_last (buffers)->data;
714
715 /* get the extension header */
716 fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READWRITE, &rtpbuffer));
717 fail_unless (gst_rtp_buffer_get_extension_data (&rtpbuffer, NULL,
718 (gpointer) & data, NULL));
719
720 /* ...and read the NTP timestamp and verify that it's the expected one */
721 timestamp = GST_READ_UINT64_BE (data);
722 fail_unless_equals_uint64 (timestamp, expected_ntp_time);
723
724 gst_rtp_buffer_unmap (&rtpbuffer);
725 gst_check_drop_buffers ();
726 }
727
GST_START_TEST(test_ntp_time)728 GST_START_TEST (test_ntp_time)
729 {
730 /* we do not need buffer caching, so do not set the e-bit */
731 g_object_set (element, "set-e-bit", FALSE, NULL);
732 /* set an ntp offset suitable for testing */
733 g_object_set (element, "ntp-offset", NTP_OFFSET, NULL);
734
735 ASSERT_SET_STATE (element, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
736
737 /* push initial events */
738 gst_check_setup_events (mysrcpad, element, NULL, GST_FORMAT_TIME);
739
740 /* first test with a "clean" segment */
741 do_ntp_time (GST_MSECOND, 0, 0);
742 do_ntp_time (GST_SECOND + GST_MSECOND, 0, 0);
743
744 /* verify that changing the running time does not affect the ntp time stamps */
745 do_ntp_time (GST_MSECOND, 0, GST_SECOND);
746 do_ntp_time (GST_SECOND + GST_MSECOND, 0, GST_SECOND);
747
748 /* changing the segment.start affects the stream time, verify that the element
749 * handles it correctly */
750 do_ntp_time (GST_MSECOND, GST_MSECOND / 2, 0);
751 do_ntp_time (GST_SECOND + GST_MSECOND, GST_MSECOND / 2, 0);
752
753 /* and finally change both of them and verify that all's fine */
754 do_ntp_time (GST_MSECOND, GST_MSECOND / 2, GST_SECOND);
755 do_ntp_time (GST_SECOND + GST_MSECOND, GST_MSECOND / 2, GST_SECOND);
756 }
757
758 GST_END_TEST;
759
760 static Suite *
onviftimestamp_suite(void)761 onviftimestamp_suite (void)
762 {
763 Suite *s = suite_create ("onviftimestamp");
764 TCase *tc_general, *tc_events;
765
766 tc_general = tcase_create ("general");
767 suite_add_tcase (s, tc_general);
768 tcase_add_checked_fixture (tc_general, setup, cleanup);
769
770 tcase_add_test (tc_general, test_apply_clean_point);
771 tcase_add_test (tc_general, test_apply_no_e_bit);
772 tcase_add_test (tc_general, test_apply_e_bit);
773 tcase_add_test (tc_general, test_flushing);
774 tcase_add_test (tc_general, test_reusable_element_no_e_bit);
775 tcase_add_test (tc_general, test_reusable_element_e_bit);
776 tcase_add_test (tc_general, test_ntp_offset_event);
777 tcase_add_test (tc_general, test_ntp_time);
778
779 tc_events = tcase_create ("events");
780 suite_add_tcase (s, tc_events);
781 tcase_add_checked_fixture (tc_events, setup_with_event, cleanup_with_event);
782
783 tcase_add_test (tc_events, test_serialized_events);
784 tcase_add_test (tc_events, test_non_serialized_events);
785
786 return s;
787 }
788
789 GST_CHECK_MAIN (onviftimestamp);
790