tests: videorate: remove obsolete color-matrix caps field
[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/plain")
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_empty_simple ("text/plain");
514   gst_pad_set_caps (mytextsrcpad, caps);
515   gst_caps_unref (caps);
516
517   tbuf = create_text_buffer ("XLX", 1 * GST_SECOND, 5 * GST_SECOND);
518   gst_buffer_ref (tbuf);
519   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 2);
520
521   GST_LOG ("pushing text buffer");
522   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
523
524   /* it should be stuck in textoverlay until it gets a text buffer or a
525    * newsegment event that indicates it's not needed any longer */
526   fail_unless_equals_int (g_list_length (buffers), 0);
527
528   incaps = create_video_caps (VIDEO_CAPS_STRING);
529   gst_pad_set_caps (myvideosrcpad, incaps);
530   inbuffer = create_black_buffer (incaps);
531   gst_caps_unref (incaps);
532   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
533
534   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
535   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 2;
536
537   /* pushing gives away one of the two references we have ... */
538   GST_LOG ("pushing video buffer 1");
539   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
540
541   /* video buffer should have gone through untainted, since the text is later */
542   fail_unless_equals_int (g_list_length (buffers), 1);
543
544   /* text should still be stuck in textoverlay */
545   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 2);
546
547   /* there should be no text rendered */
548   outbuffer = GST_BUFFER_CAST (buffers->data);
549   ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
550   outcaps = gst_pad_get_current_caps (mysinkpad);
551   fail_unless (buffer_is_all_black (outbuffer, outcaps));
552   gst_caps_unref (outcaps);
553
554   /* now, another video buffer */
555   inbuffer = create_black_buffer (incaps);
556   GST_BUFFER_TIMESTAMP (inbuffer) = GST_SECOND;
557   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 2;
558   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
559
560   /* pushing gives away one of the two references we have ... */
561   GST_LOG ("pushing video buffer 2");
562   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
563
564   /* video buffer should have gone right away, with text rendered on it */
565   fail_unless_equals_int (g_list_length (buffers), 2);
566
567   /* text should still be stuck in textoverlay */
568   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 2);
569
570   /* there should be text rendered */
571   outbuffer = GST_BUFFER_CAST (buffers->next->data);
572   ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
573   outcaps = gst_pad_get_current_caps (mysinkpad);
574   fail_unless (buffer_is_all_black (outbuffer, outcaps) == FALSE);
575   gst_caps_unref (outcaps);
576
577   /* a third video buffer */
578   inbuffer = create_black_buffer (incaps);
579   GST_BUFFER_TIMESTAMP (inbuffer) = 30 * GST_SECOND;
580   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 2;
581
582   /* video buffer #3: should not go through, it should discard the current
583    * text buffer as too old and then wait for the next text buffer (or a
584    * newsegment event to arrive); we spawn a background thread to send such
585    * a newsegment event after a second or so so we get back control */
586   thread =
587       g_thread_create (test_video_waits_for_text_send_text_newsegment_thread,
588       NULL, FALSE, NULL);
589   fail_unless (thread != NULL);
590
591   GST_LOG ("pushing video buffer 3");
592   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
593
594   /* but the text should no longer be stuck in textoverlay */
595   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 1);
596
597   /* video buffer should have gone through after newsegment event */
598   fail_unless_equals_int (g_list_length (buffers), 3);
599
600   /* ... and there should not be any text rendered on it */
601   outbuffer = GST_BUFFER_CAST (buffers->next->next->data);
602   ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
603   outcaps = gst_pad_get_current_caps (mysinkpad);
604   fail_unless (buffer_is_all_black (outbuffer, outcaps));
605   gst_caps_unref (outcaps);
606
607   /* a fourth video buffer */
608   inbuffer = create_black_buffer (incaps);
609   GST_BUFFER_TIMESTAMP (inbuffer) = 35 * GST_SECOND;
610   GST_BUFFER_DURATION (inbuffer) = GST_SECOND;
611
612   /* video buffer #4: should not go through, it should wait for the next
613    * text buffer (or a newsegment event) to arrive; we spawn a background
614    * thread to shut down the element while it's waiting to make sure that
615    * works ok */
616   thread = g_thread_create (test_video_waits_for_text_shutdown_element,
617       textoverlay, FALSE, NULL);
618   fail_unless (thread != NULL);
619
620   GST_LOG ("pushing video buffer 4");
621   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_FLUSHING);
622
623   /* and clean up */
624   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
625   g_list_free (buffers);
626   buffers = NULL;
627
628   /* cleanup */
629   cleanup_textoverlay (textoverlay);
630
631   /* give up our ref, textoverlay should've cleared its queued buffer by now */
632   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 1);
633   gst_buffer_unref (tbuf);
634 }
635
636 GST_END_TEST;
637
638 static gpointer
639 test_render_continuity_push_video_buffers_thread (gpointer data)
640 {
641   /* push video buffers at 1fps */
642   guint frame_count = 0;
643   GstCaps *vcaps;
644
645   vcaps = create_video_caps (VIDEO_CAPS_STRING);
646   gst_pad_set_caps (myvideosrcpad, vcaps);
647
648   do {
649     GstBuffer *vbuf;
650
651     vbuf = create_black_buffer (vcaps);
652     ASSERT_BUFFER_REFCOUNT (vbuf, "vbuf", 1);
653
654     GST_BUFFER_TIMESTAMP (vbuf) = frame_count * GST_SECOND;
655     GST_BUFFER_DURATION (vbuf) = GST_SECOND;
656
657     /* pushing gives away one of the two references we have ... */
658     GST_LOG ("pushing video buffer %u @ %" GST_TIME_FORMAT, frame_count,
659         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (vbuf)));
660     fail_unless (gst_pad_push (myvideosrcpad, vbuf) == GST_FLOW_OK);
661
662     ++frame_count;
663   } while (frame_count < 15);
664
665   gst_caps_unref (vcaps);
666
667   return NULL;
668 }
669
670 GST_START_TEST (test_render_continuity)
671 {
672   GThread *thread;
673   GstElement *textoverlay;
674   GstBuffer *tbuf;
675   GstCaps *caps, *outcaps;
676
677   textoverlay = setup_textoverlay (FALSE);
678
679   fail_unless (gst_element_set_state (textoverlay,
680           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
681       "could not set to playing");
682
683   thread = g_thread_create (test_render_continuity_push_video_buffers_thread,
684       NULL, FALSE, NULL);
685   fail_unless (thread != NULL);
686
687   caps = gst_caps_new_empty_simple ("text/plain");
688   gst_pad_set_caps (mytextsrcpad, caps);
689   gst_caps_unref (caps);
690
691   tbuf = create_text_buffer ("XLX", 2 * GST_SECOND, GST_SECOND);
692   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
693       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
694   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
695
696   tbuf = create_text_buffer ("XLX", 3 * GST_SECOND, 2 * 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", 7 * GST_SECOND, 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", 8 * 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", 9 * 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", 10 * GST_SECOND, 30 * 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   GST_LOG ("give the other thread some time to push through the remaining"
722       "video buffers");
723   g_usleep (G_USEC_PER_SEC);
724   GST_LOG ("done");
725
726   /* we should have 15 buffers each with one second length now */
727   fail_unless_equals_int (g_list_length (buffers), 15);
728
729   outcaps = gst_pad_get_current_caps (mysinkpad);
730
731   /* buffers 0 + 1 should be black */
732   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 0)),
733           outcaps));
734   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 1)),
735           outcaps));
736
737   /* buffers 2 - 4 should have text */
738   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
739                   2)), outcaps) == FALSE);
740   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
741                   3)), outcaps) == FALSE);
742   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
743                   4)), outcaps) == FALSE);
744
745   /* buffers 5 + 6 should be black */
746   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 5)),
747           outcaps));
748   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 6)),
749           outcaps));
750
751   /* buffers 7 - last should have text */
752   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
753                   7)), outcaps) == FALSE);
754   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
755                   8)), outcaps) == FALSE);
756   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
757                   9)), outcaps) == FALSE);
758   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
759                   10)), outcaps) == FALSE);
760   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
761                   11)), outcaps) == FALSE);
762   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
763                   12)), outcaps) == FALSE);
764   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
765                   13)), outcaps) == FALSE);
766   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
767                   14)), outcaps) == FALSE);
768   gst_caps_unref (outcaps);
769
770   /* and clean up */
771   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
772   g_list_free (buffers);
773   buffers = NULL;
774
775   /* cleanup */
776   cleanup_textoverlay (textoverlay);
777 }
778
779 GST_END_TEST;
780
781 static Suite *
782 textoverlay_suite (void)
783 {
784   Suite *s = suite_create ("textoverlay");
785   TCase *tc_chain = tcase_create ("general");
786
787   suite_add_tcase (s, tc_chain);
788
789   tcase_add_test (tc_chain, test_video_passthrough);
790   tcase_add_test (tc_chain, test_video_render_static_text);
791   tcase_add_test (tc_chain, test_render_continuity);
792   tcase_add_test (tc_chain, test_video_waits_for_text);
793
794   return s;
795 }
796
797 GST_CHECK_MAIN (textoverlay);