fe06b42958a7bef8b950d0478a06dc4e5ce6be6a
[platform/upstream/gstreamer.git] / tests / check / elements / textoverlay.c
1 /* GStreamer unit tests for textoverlay
2  *
3  * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
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
23 #include <gst/check/gstcheck.h>
24
25 #define I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
26 #define I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
27 #define I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(I420_Y_ROWSTRIDE(width)))/2)
28
29 #define I420_Y_OFFSET(w,h) (0)
30 #define I420_U_OFFSET(w,h) (I420_Y_OFFSET(w,h)+(I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
31 #define I420_V_OFFSET(w,h) (I420_U_OFFSET(w,h)+(I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
32
33 #define I420_SIZE(w,h)     (I420_V_OFFSET(w,h)+(I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
34
35 #define WIDTH 240
36 #define HEIGHT 120
37
38 /* For ease of programming we use globals to keep refs for our floating
39  * src and sink pads we create; otherwise we always have to do get_pad,
40  * get_peer, and then remove references in every test function */
41 static GstPad *myvideosrcpad, *mytextsrcpad, *mysinkpad;
42
43 #define VIDEO_CAPS_STRING               \
44     "video/x-raw, "                 \
45     "format = (string) I420, "          \
46     "framerate = (fraction) 1/1, "      \
47     "width = (int) 240, "               \
48     "height = (int) 120"
49
50 #define VIDEO_CAPS_TEMPLATE_STRING      \
51     "video/x-raw, "                 \
52     "format = (string) I420"
53
54 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
55     GST_PAD_SINK,
56     GST_PAD_ALWAYS,
57     GST_STATIC_CAPS (VIDEO_CAPS_TEMPLATE_STRING)
58     );
59 static GstStaticPadTemplate text_srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
60     GST_PAD_SRC,
61     GST_PAD_ALWAYS,
62     GST_STATIC_CAPS ("text/x-raw, format=utf8")
63     );
64
65 static GstStaticPadTemplate video_srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
66     GST_PAD_SRC,
67     GST_PAD_ALWAYS,
68     GST_STATIC_CAPS (VIDEO_CAPS_TEMPLATE_STRING)
69     );
70
71 /* much like gst_check_setup_src_pad(), but with possibility to give a hint
72  * which sink template of the element to use, if there are multiple ones */
73 static GstPad *
74 notgst_check_setup_src_pad2 (GstElement * element,
75     GstStaticPadTemplate * template, GstCaps * caps,
76     const gchar * sink_template_name)
77 {
78   GstPad *srcpad, *sinkpad;
79
80   if (sink_template_name == NULL)
81     sink_template_name = "sink";
82
83   /* sending pad */
84   srcpad = gst_pad_new_from_static_template (template, "src");
85   GST_DEBUG_OBJECT (element, "setting up sending pad %p", srcpad);
86   fail_if (srcpad == NULL, "Could not create a srcpad");
87   ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1);
88
89   if (!(sinkpad = gst_element_get_static_pad (element, sink_template_name)))
90     sinkpad = gst_element_get_request_pad (element, sink_template_name);
91   fail_if (sinkpad == NULL, "Could not get sink pad from %s",
92       GST_ELEMENT_NAME (element));
93   ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
94   if (caps)
95     fail_unless (gst_pad_set_caps (srcpad, caps));
96   fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
97       "Could not link source and %s sink pads", GST_ELEMENT_NAME (element));
98   gst_object_unref (sinkpad);   /* because we got it higher up */
99   ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 1);
100
101   return srcpad;
102 }
103
104 static void
105 notgst_check_teardown_src_pad2 (GstElement * element,
106     const gchar * sink_template_name)
107 {
108   GstPad *srcpad, *sinkpad;
109
110   if (sink_template_name == NULL)
111     sink_template_name = "sink";
112
113   /* clean up floating src pad */
114   if (!(sinkpad = gst_element_get_static_pad (element, sink_template_name)))
115     sinkpad = gst_element_get_request_pad (element, sink_template_name);
116   ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
117   srcpad = gst_pad_get_peer (sinkpad);
118
119   gst_pad_unlink (srcpad, sinkpad);
120
121   /* pad refs held by both creator and this function (through _get) */
122   ASSERT_OBJECT_REFCOUNT (sinkpad, "element sinkpad", 2);
123   gst_object_unref (sinkpad);
124   /* one more ref is held by element itself */
125
126   /* pad refs held by both creator and this function (through _get_peer) */
127   ASSERT_OBJECT_REFCOUNT (srcpad, "check srcpad", 2);
128   gst_object_unref (srcpad);
129   gst_object_unref (srcpad);
130 }
131
132 static GstElement *
133 setup_textoverlay (gboolean video_only_no_text)
134 {
135   GstElement *textoverlay;
136
137   GST_DEBUG ("setup_textoverlay");
138   textoverlay = gst_check_setup_element ("textoverlay");
139   mysinkpad = gst_check_setup_sink_pad (textoverlay, &sinktemplate);
140   myvideosrcpad =
141       notgst_check_setup_src_pad2 (textoverlay, &video_srctemplate, NULL,
142       "video_sink");
143
144   if (!video_only_no_text) {
145     mytextsrcpad =
146         notgst_check_setup_src_pad2 (textoverlay, &text_srctemplate, NULL,
147         "text_sink");
148     gst_pad_set_active (mytextsrcpad, TRUE);
149   } else {
150     mytextsrcpad = NULL;
151   }
152
153   gst_pad_set_active (myvideosrcpad, TRUE);
154   gst_pad_set_active (mysinkpad, TRUE);
155
156   return textoverlay;
157 }
158
159 static gboolean
160 buffer_is_all_black (GstBuffer * buf, GstCaps * caps)
161 {
162   GstStructure *s;
163   gint x, y, w, h;
164   GstMapInfo map;
165
166   fail_unless (buf != NULL);
167   fail_unless (caps != NULL);
168   s = gst_caps_get_structure (caps, 0);
169   fail_unless (s != NULL);
170   fail_unless (gst_structure_get_int (s, "width", &w));
171   fail_unless (gst_structure_get_int (s, "height", &h));
172
173   gst_buffer_map (buf, &map, GST_MAP_READ);
174   for (y = 0; y < h; ++y) {
175     guint8 *ptr = map.data + (y * GST_ROUND_UP_4 (w));
176
177     for (x = 0; x < w; ++x) {
178       if (ptr[x] != 0x00) {
179         GST_LOG ("non-black pixel (%d) at (x,y) %d,%d", ptr[x], x, y);
180         gst_buffer_unmap (buf, &map);
181         return FALSE;
182       }
183     }
184   }
185   gst_buffer_unmap (buf, &map);
186
187   return TRUE;
188 }
189
190 static GstCaps *
191 create_video_caps (const gchar * caps_string)
192 {
193   GstCaps *caps;
194
195   caps = gst_caps_from_string (caps_string);
196   fail_unless (caps != NULL);
197   fail_unless (gst_caps_is_fixed (caps));
198
199   return caps;
200 }
201
202 static GstBuffer *
203 create_black_buffer (GstCaps * caps)
204 {
205   GstStructure *s;
206   GstBuffer *buffer;
207   gint w, h, size;
208
209   fail_unless (caps != NULL);
210
211   s = gst_caps_get_structure (caps, 0);
212   fail_unless (gst_structure_get_int (s, "width", &w));
213   fail_unless (gst_structure_get_int (s, "height", &h));
214
215   GST_LOG ("creating buffer (%dx%d)", w, h);
216
217   size = I420_SIZE (w, h);
218   buffer = gst_buffer_new_and_alloc (size);
219   /* we're only checking the Y plane later, so just zero it all out,
220    * even if it's not the blackest black there is */
221   gst_buffer_memset (buffer, 0, 0, size);
222
223   /* double check to make sure it's been created right */
224   fail_unless (buffer_is_all_black (buffer, caps));
225
226   return buffer;
227 }
228
229 static GstBuffer *
230 create_text_buffer (const gchar * txt, GstClockTime ts, GstClockTime duration)
231 {
232   GstBuffer *buffer;
233   guint txt_len;
234
235   fail_unless (txt != NULL);
236
237   txt_len = strlen (txt);
238
239   buffer = gst_buffer_new_and_alloc (txt_len);
240   gst_buffer_fill (buffer, 0, txt, txt_len);
241
242   GST_BUFFER_TIMESTAMP (buffer) = ts;
243   GST_BUFFER_DURATION (buffer) = duration;
244
245   return buffer;
246 }
247
248 static void
249 cleanup_textoverlay (GstElement * textoverlay)
250 {
251   GST_DEBUG ("cleanup_textoverlay");
252
253   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
254   g_list_free (buffers);
255   buffers = NULL;
256
257   gst_element_set_state (textoverlay, GST_STATE_NULL);
258   gst_element_get_state (textoverlay, NULL, NULL, GST_CLOCK_TIME_NONE);
259   gst_pad_set_active (myvideosrcpad, FALSE);
260   gst_pad_set_active (mysinkpad, FALSE);
261   notgst_check_teardown_src_pad2 (textoverlay, "video_sink");
262   if (mytextsrcpad) {
263     notgst_check_teardown_src_pad2 (textoverlay, "text_sink");
264   }
265   gst_check_teardown_sink_pad (textoverlay);
266   gst_check_teardown_element (textoverlay);
267 }
268
269 GST_START_TEST (test_video_passthrough)
270 {
271   GstElement *textoverlay;
272   GstBuffer *inbuffer, *outbuffer;
273   GstCaps *incaps, *outcaps;
274   GstSegment segment;
275
276   textoverlay = setup_textoverlay (TRUE);
277   fail_unless (gst_element_set_state (textoverlay,
278           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
279       "could not set to playing");
280
281   incaps = create_video_caps (VIDEO_CAPS_STRING);
282   gst_pad_set_caps (myvideosrcpad, incaps);
283   inbuffer = create_black_buffer (incaps);
284   gst_caps_unref (incaps);
285
286   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
287
288   /* ========== (1) video buffer without timestamp => should be dropped ==== */
289
290   /* take additional ref to keep it alive */
291   gst_buffer_ref (inbuffer);
292   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
293
294   /* pushing gives away one of the two references we have ... */
295   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
296
297   /* should have been discarded as out-of-segment since it has no timestamp */
298   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
299   fail_unless_equals_int (g_list_length (buffers), 0);
300
301   /* ========== (2) buffer with 0 timestamp => simple passthrough ========== */
302
303   /* now try again, this time with timestamp (segment defaults to 0 start) */
304   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
305   GST_BUFFER_DURATION (inbuffer) = GST_CLOCK_TIME_NONE;
306
307   /* take additional ref to keep it alive */
308   gst_buffer_ref (inbuffer);
309   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
310
311   /* pushing gives away one of the two references we have ... */
312   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
313
314   /* text pad is not linked, timestamp is in segment, no static text to
315    * render, should have gone through right away without modification */
316   fail_unless_equals_int (g_list_length (buffers), 1);
317   outbuffer = GST_BUFFER_CAST (buffers->data);
318   fail_unless (outbuffer == inbuffer);
319   outcaps = gst_pad_get_current_caps (mysinkpad);
320   fail_unless (buffer_is_all_black (outbuffer, outcaps));
321   gst_caps_unref (outcaps);
322   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
323
324   /* and clean up */
325   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
326   g_list_free (buffers);
327   buffers = NULL;
328   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
329
330   /* ========== (3) buffer with 0 timestamp and no duration, with the
331    *                segment starting from 1sec => should be discarded */
332
333   gst_segment_init (&segment, GST_FORMAT_TIME);
334   segment.start = 1 * GST_SECOND;
335   segment.stop = -1;
336   segment.time = 0;
337   gst_pad_push_event (myvideosrcpad, gst_event_new_segment (&segment));
338
339   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
340   GST_BUFFER_DURATION (inbuffer) = GST_CLOCK_TIME_NONE;
341
342   /* take additional ref to keep it alive */
343   gst_buffer_ref (inbuffer);
344   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
345
346   /* pushing gives away one of the two references we have ... */
347   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
348
349   /* should have been discarded as out-of-segment */
350   fail_unless_equals_int (g_list_length (buffers), 0);
351   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
352
353   /* ========== (4) buffer with 0 timestamp and small defined duration, with
354    *                segment starting from 1sec => should be discarded */
355
356   gst_pad_push_event (myvideosrcpad, gst_event_new_segment (&segment));
357
358   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 10;
359
360   /* take additional ref to keep it alive */
361   gst_buffer_ref (inbuffer);
362   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
363
364   /* pushing gives away one of the two references we have ... */
365   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
366
367   /* should have been discarded as out-of-segment since it has no timestamp */
368   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
369   fail_unless_equals_int (g_list_length (buffers), 0);
370
371   /* ========== (5) buffer partially overlapping into the segment => should
372    *                be pushed through, but with adjusted stamp values */
373
374   gst_pad_push_event (myvideosrcpad, gst_event_new_segment (&segment));
375
376   GST_BUFFER_TIMESTAMP (inbuffer) = GST_SECOND / 4;
377   GST_BUFFER_DURATION (inbuffer) = GST_SECOND;
378
379   /* take additional ref to keep it alive */
380   gst_buffer_ref (inbuffer);
381   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
382
383   /* pushing gives away one of the two references we have ... */
384   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
385
386   /* should be a new buffer for the stamp fix-up */
387   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
388   fail_unless_equals_int (g_list_length (buffers), 1);
389   outbuffer = GST_BUFFER_CAST (buffers->data);
390   outcaps = gst_pad_get_current_caps (mysinkpad);
391   fail_unless (outbuffer != inbuffer);
392   fail_unless (GST_BUFFER_TIMESTAMP (outbuffer) == GST_SECOND);
393   fail_unless (GST_BUFFER_DURATION (outbuffer) == (GST_SECOND / 4));
394   fail_unless (buffer_is_all_black (outbuffer, outcaps));
395   gst_caps_unref (outcaps);
396   /* and clean up */
397   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
398   g_list_free (buffers);
399   buffers = NULL;
400   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
401
402   /* cleanup */
403   cleanup_textoverlay (textoverlay);
404   gst_buffer_unref (inbuffer);
405 }
406
407 GST_END_TEST;
408
409 GST_START_TEST (test_video_render_static_text)
410 {
411   GstElement *textoverlay;
412   GstBuffer *inbuffer, *outbuffer;
413   GstCaps *incaps, *outcaps;
414
415   textoverlay = setup_textoverlay (TRUE);
416
417   /* set static text to render */
418   g_object_set (textoverlay, "text", "XLX", NULL);
419
420   fail_unless (gst_element_set_state (textoverlay,
421           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
422       "could not set to playing");
423
424   incaps = create_video_caps (VIDEO_CAPS_STRING);
425   gst_pad_set_caps (myvideosrcpad, incaps);
426   inbuffer = create_black_buffer (incaps);
427   gst_caps_unref (incaps);
428   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
429
430   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
431   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 10;
432
433   /* take additional ref to keep it alive */
434   gst_buffer_ref (inbuffer);
435   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
436
437   /* pushing gives away one of the two references we have ... */
438   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
439
440   /* should have been dropped in favour of a new writable buffer */
441   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
442   fail_unless_equals_int (g_list_length (buffers), 1);
443   outbuffer = GST_BUFFER_CAST (buffers->data);
444   outcaps = gst_pad_get_current_caps (mysinkpad);
445   fail_unless (outbuffer != inbuffer);
446
447   /* there should be text rendered */
448   fail_unless (buffer_is_all_black (outbuffer, outcaps) == FALSE);
449   gst_caps_unref (outcaps);
450
451   fail_unless (GST_BUFFER_TIMESTAMP (outbuffer) == 0);
452   fail_unless (GST_BUFFER_DURATION (outbuffer) == (GST_SECOND / 10));
453
454   /* and clean up */
455   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
456   g_list_free (buffers);
457   buffers = NULL;
458   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
459
460   /* cleanup */
461   cleanup_textoverlay (textoverlay);
462   gst_buffer_unref (inbuffer);
463 }
464
465 GST_END_TEST;
466
467 static gpointer
468 test_video_waits_for_text_send_text_newsegment_thread (gpointer data)
469 {
470   GstSegment segment;
471
472   g_usleep (1 * G_USEC_PER_SEC);
473
474   /* send an update newsegment; the video buffer should now be pushed through 
475    * even though there is no text buffer queued at the moment */
476   GST_INFO ("Sending newsegment update on text pad");
477   gst_segment_init (&segment, GST_FORMAT_TIME);
478   segment.base = 35 * GST_SECOND;
479   segment.start = 35 * GST_SECOND;
480   segment.time = 35 * GST_SECOND;
481   gst_pad_push_event (mytextsrcpad, gst_event_new_segment (&segment));
482
483   return NULL;
484 }
485
486 static gpointer
487 test_video_waits_for_text_shutdown_element (gpointer data)
488 {
489   g_usleep (1 * G_USEC_PER_SEC);
490
491   GST_INFO ("Trying to shut down textoverlay element ...");
492   /* set to NULL state to make sure we can shut it down while it's
493    * blocking in the video chain function waiting for a text buffer */
494   gst_element_set_state (GST_ELEMENT (data), GST_STATE_NULL);
495   GST_INFO ("Done.");
496
497   return NULL;
498 }
499
500 GST_START_TEST (test_video_waits_for_text)
501 {
502   GstElement *textoverlay;
503   GstBuffer *inbuffer, *outbuffer, *tbuf;
504   GstCaps *caps, *incaps, *outcaps;
505   GThread *thread;
506
507   textoverlay = setup_textoverlay (FALSE);
508
509   fail_unless (gst_element_set_state (textoverlay,
510           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
511       "could not set to playing");
512
513   caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING, "utf8",
514       NULL);
515   gst_pad_set_caps (mytextsrcpad, caps);
516   gst_caps_unref (caps);
517
518   tbuf = create_text_buffer ("XLX", 1 * GST_SECOND, 5 * GST_SECOND);
519   gst_buffer_ref (tbuf);
520   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 2);
521
522   GST_LOG ("pushing text buffer");
523   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
524
525   /* it should be stuck in textoverlay until it gets a text buffer or a
526    * newsegment event that indicates it's not needed any longer */
527   fail_unless_equals_int (g_list_length (buffers), 0);
528
529   incaps = create_video_caps (VIDEO_CAPS_STRING);
530   gst_pad_set_caps (myvideosrcpad, incaps);
531   inbuffer = create_black_buffer (incaps);
532   gst_caps_unref (incaps);
533   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
534
535   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
536   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 2;
537
538   /* pushing gives away one of the two references we have ... */
539   GST_LOG ("pushing video buffer 1");
540   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
541
542   /* video buffer should have gone through untainted, since the text is later */
543   fail_unless_equals_int (g_list_length (buffers), 1);
544
545   /* text should still be stuck in textoverlay */
546   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 2);
547
548   /* there should be no text rendered */
549   outbuffer = GST_BUFFER_CAST (buffers->data);
550   ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
551   outcaps = gst_pad_get_current_caps (mysinkpad);
552   fail_unless (buffer_is_all_black (outbuffer, outcaps));
553   gst_caps_unref (outcaps);
554
555   /* now, another video buffer */
556   inbuffer = create_black_buffer (incaps);
557   GST_BUFFER_TIMESTAMP (inbuffer) = GST_SECOND;
558   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 2;
559   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
560
561   /* pushing gives away one of the two references we have ... */
562   GST_LOG ("pushing video buffer 2");
563   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
564
565   /* video buffer should have gone right away, with text rendered on it */
566   fail_unless_equals_int (g_list_length (buffers), 2);
567
568   /* text should still be stuck in textoverlay */
569   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 2);
570
571   /* there should be text rendered */
572   outbuffer = GST_BUFFER_CAST (buffers->next->data);
573   ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
574   outcaps = gst_pad_get_current_caps (mysinkpad);
575   fail_unless (buffer_is_all_black (outbuffer, outcaps) == FALSE);
576   gst_caps_unref (outcaps);
577
578   /* a third video buffer */
579   inbuffer = create_black_buffer (incaps);
580   GST_BUFFER_TIMESTAMP (inbuffer) = 30 * GST_SECOND;
581   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 2;
582
583   /* video buffer #3: should not go through, it should discard the current
584    * text buffer as too old and then wait for the next text buffer (or a
585    * newsegment event to arrive); we spawn a background thread to send such
586    * a newsegment event after a second or so so we get back control */
587   thread =
588       g_thread_try_new ("gst-check",
589       test_video_waits_for_text_send_text_newsegment_thread, NULL, NULL);
590   fail_unless (thread != NULL);
591   g_thread_unref (thread);
592
593   GST_LOG ("pushing video buffer 3");
594   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
595
596   /* but the text should no longer be stuck in textoverlay */
597   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 1);
598
599   /* video buffer should have gone through after newsegment event */
600   fail_unless_equals_int (g_list_length (buffers), 3);
601
602   /* ... and there should not be any text rendered on it */
603   outbuffer = GST_BUFFER_CAST (buffers->next->next->data);
604   ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
605   outcaps = gst_pad_get_current_caps (mysinkpad);
606   fail_unless (buffer_is_all_black (outbuffer, outcaps));
607   gst_caps_unref (outcaps);
608
609   /* a fourth video buffer */
610   inbuffer = create_black_buffer (incaps);
611   GST_BUFFER_TIMESTAMP (inbuffer) = 35 * GST_SECOND;
612   GST_BUFFER_DURATION (inbuffer) = GST_SECOND;
613
614   /* video buffer #4: should not go through, it should wait for the next
615    * text buffer (or a newsegment event) to arrive; we spawn a background
616    * thread to shut down the element while it's waiting to make sure that
617    * works ok */
618   thread = g_thread_try_new ("gst-check",
619       test_video_waits_for_text_shutdown_element, textoverlay, NULL);
620   fail_unless (thread != NULL);
621   g_thread_unref (thread);
622
623   GST_LOG ("pushing video buffer 4");
624   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_FLUSHING);
625
626   /* and clean up */
627   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
628   g_list_free (buffers);
629   buffers = NULL;
630
631   /* cleanup */
632   cleanup_textoverlay (textoverlay);
633
634   /* give up our ref, textoverlay should've cleared its queued buffer by now */
635   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 1);
636   gst_buffer_unref (tbuf);
637 }
638
639 GST_END_TEST;
640
641 static gpointer
642 test_render_continuity_push_video_buffers_thread (gpointer data)
643 {
644   /* push video buffers at 1fps */
645   guint frame_count = 0;
646   GstCaps *vcaps;
647
648   vcaps = create_video_caps (VIDEO_CAPS_STRING);
649   gst_pad_set_caps (myvideosrcpad, vcaps);
650
651   do {
652     GstBuffer *vbuf;
653
654     vbuf = create_black_buffer (vcaps);
655     ASSERT_BUFFER_REFCOUNT (vbuf, "vbuf", 1);
656
657     GST_BUFFER_TIMESTAMP (vbuf) = frame_count * GST_SECOND;
658     GST_BUFFER_DURATION (vbuf) = GST_SECOND;
659
660     /* pushing gives away one of the two references we have ... */
661     GST_LOG ("pushing video buffer %u @ %" GST_TIME_FORMAT, frame_count,
662         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (vbuf)));
663     fail_unless (gst_pad_push (myvideosrcpad, vbuf) == GST_FLOW_OK);
664
665     ++frame_count;
666   } while (frame_count < 15);
667
668   gst_caps_unref (vcaps);
669
670   return NULL;
671 }
672
673 GST_START_TEST (test_render_continuity)
674 {
675   GThread *thread;
676   GstElement *textoverlay;
677   GstBuffer *tbuf;
678   GstCaps *caps, *outcaps;
679
680   textoverlay = setup_textoverlay (FALSE);
681
682   fail_unless (gst_element_set_state (textoverlay,
683           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
684       "could not set to playing");
685
686   thread = g_thread_try_new ("gst-check",
687       test_render_continuity_push_video_buffers_thread, NULL, NULL);
688   fail_unless (thread != NULL);
689   g_thread_unref (thread);
690
691   caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING, "utf8",
692       NULL);
693   gst_pad_set_caps (mytextsrcpad, caps);
694   gst_caps_unref (caps);
695
696   tbuf = create_text_buffer ("XLX", 2 * GST_SECOND, GST_SECOND);
697   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
698       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
699   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
700
701   tbuf = create_text_buffer ("XLX", 3 * GST_SECOND, 2 * GST_SECOND);
702   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
703       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
704   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
705
706   tbuf = create_text_buffer ("XLX", 7 * GST_SECOND, GST_SECOND);
707   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
708       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
709   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
710
711   tbuf = create_text_buffer ("XLX", 8 * GST_SECOND, GST_SECOND);
712   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
713       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
714   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
715
716   tbuf = create_text_buffer ("XLX", 9 * GST_SECOND, GST_SECOND);
717   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
718       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
719   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
720
721   tbuf = create_text_buffer ("XLX", 10 * GST_SECOND, 30 * GST_SECOND);
722   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
723       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
724   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
725
726   GST_LOG ("give the other thread some time to push through the remaining"
727       "video buffers");
728   g_usleep (G_USEC_PER_SEC);
729   GST_LOG ("done");
730
731   /* we should have 15 buffers each with one second length now */
732   fail_unless_equals_int (g_list_length (buffers), 15);
733
734   outcaps = gst_pad_get_current_caps (mysinkpad);
735
736   /* buffers 0 + 1 should be black */
737   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 0)),
738           outcaps));
739   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 1)),
740           outcaps));
741
742   /* buffers 2 - 4 should have text */
743   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
744                   2)), outcaps) == FALSE);
745   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
746                   3)), outcaps) == FALSE);
747   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
748                   4)), outcaps) == FALSE);
749
750   /* buffers 5 + 6 should be black */
751   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 5)),
752           outcaps));
753   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 6)),
754           outcaps));
755
756   /* buffers 7 - last should have text */
757   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
758                   7)), outcaps) == FALSE);
759   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
760                   8)), outcaps) == FALSE);
761   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
762                   9)), outcaps) == FALSE);
763   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
764                   10)), outcaps) == FALSE);
765   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
766                   11)), outcaps) == FALSE);
767   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
768                   12)), outcaps) == FALSE);
769   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
770                   13)), outcaps) == FALSE);
771   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
772                   14)), outcaps) == FALSE);
773   gst_caps_unref (outcaps);
774
775   /* and clean up */
776   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
777   g_list_free (buffers);
778   buffers = NULL;
779
780   /* cleanup */
781   cleanup_textoverlay (textoverlay);
782 }
783
784 GST_END_TEST;
785
786 static Suite *
787 textoverlay_suite (void)
788 {
789   Suite *s = suite_create ("textoverlay");
790   TCase *tc_chain = tcase_create ("general");
791
792   suite_add_tcase (s, tc_chain);
793
794   tcase_add_test (tc_chain, test_video_passthrough);
795   tcase_add_test (tc_chain, test_video_render_static_text);
796   tcase_add_test (tc_chain, test_render_continuity);
797   tcase_add_test (tc_chain, test_video_waits_for_text);
798
799   return s;
800 }
801
802 GST_CHECK_MAIN (textoverlay);