tizen 2.3 release
[framework/multimedia/gst-plugins-base0.10.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-yuv, "                 \
45     "format = (fourcc) 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-yuv, "                 \
52     "format = (fourcc) 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   /* caps could have been set, make sure they get unset */
122   gst_pad_set_caps (srcpad, NULL);
123
124   /* pad refs held by both creator and this function (through _get) */
125   ASSERT_OBJECT_REFCOUNT (sinkpad, "element sinkpad", 2);
126   gst_object_unref (sinkpad);
127   /* one more ref is held by element itself */
128
129   /* pad refs held by both creator and this function (through _get_peer) */
130   ASSERT_OBJECT_REFCOUNT (srcpad, "check srcpad", 2);
131   gst_object_unref (srcpad);
132   gst_object_unref (srcpad);
133 }
134
135 static GstElement *
136 setup_textoverlay (gboolean video_only_no_text)
137 {
138   GstElement *textoverlay;
139
140   GST_DEBUG ("setup_textoverlay");
141   textoverlay = gst_check_setup_element ("textoverlay");
142   mysinkpad = gst_check_setup_sink_pad (textoverlay, &sinktemplate, NULL);
143   myvideosrcpad =
144       notgst_check_setup_src_pad2 (textoverlay, &video_srctemplate, NULL,
145       "video_sink");
146
147   if (!video_only_no_text) {
148     mytextsrcpad =
149         notgst_check_setup_src_pad2 (textoverlay, &text_srctemplate, NULL,
150         "text_sink");
151     gst_pad_set_active (mytextsrcpad, TRUE);
152   } else {
153     mytextsrcpad = NULL;
154   }
155
156   gst_pad_set_active (myvideosrcpad, TRUE);
157   gst_pad_set_active (mysinkpad, TRUE);
158
159   return textoverlay;
160 }
161
162 static gboolean
163 buffer_is_all_black (GstBuffer * buf)
164 {
165   GstStructure *s;
166   gint x, y, w, h;
167
168   fail_unless (buf != NULL);
169   fail_unless (GST_BUFFER_CAPS (buf) != NULL);
170   s = gst_caps_get_structure (GST_BUFFER_CAPS (buf), 0);
171   fail_unless (s != NULL);
172   fail_unless (gst_structure_get_int (s, "width", &w));
173   fail_unless (gst_structure_get_int (s, "height", &h));
174
175   for (y = 0; y < h; ++y) {
176     guint8 *data = GST_BUFFER_DATA (buf) + (y * GST_ROUND_UP_4 (w));
177
178     for (x = 0; x < w; ++x) {
179       if (data[x] != 0x00) {
180         GST_LOG ("non-black pixel at (x,y) %d,%d", x, y);
181         return FALSE;
182       }
183     }
184   }
185
186   return TRUE;
187 }
188
189 static GstBuffer *
190 create_black_buffer (const gchar * caps_string)
191 {
192   GstStructure *s;
193   GstBuffer *buffer;
194   GstCaps *caps;
195   gint w, h, size;
196
197   fail_unless (caps_string != NULL);
198
199   caps = gst_caps_from_string (caps_string);
200   fail_unless (caps != NULL);
201   fail_unless (gst_caps_is_fixed (caps));
202
203   s = gst_caps_get_structure (caps, 0);
204   fail_unless (gst_structure_get_int (s, "width", &w));
205   fail_unless (gst_structure_get_int (s, "height", &h));
206
207   GST_LOG ("creating buffer (%dx%d)", w, h);
208   size = I420_SIZE (w, h);
209   buffer = gst_buffer_new_and_alloc (size);
210   /* we're only checking the Y plane later, so just zero it all out,
211    * even if it's not the blackest black there is */
212   memset (GST_BUFFER_DATA (buffer), 0, size);
213
214   gst_buffer_set_caps (buffer, caps);
215   gst_caps_unref (caps);
216
217   /* double check to make sure it's been created right */
218   fail_unless (buffer_is_all_black (buffer));
219
220   return buffer;
221 }
222
223 static GstBuffer *
224 create_text_buffer (const gchar * txt, GstClockTime ts, GstClockTime duration)
225 {
226   GstBuffer *buffer;
227   GstCaps *caps;
228   guint txt_len;
229
230   fail_unless (txt != NULL);
231
232   txt_len = strlen (txt);
233
234   buffer = gst_buffer_new_and_alloc (txt_len);
235   memcpy (GST_BUFFER_DATA (buffer), txt, txt_len);
236
237   GST_BUFFER_TIMESTAMP (buffer) = ts;
238   GST_BUFFER_DURATION (buffer) = duration;
239
240   caps = gst_caps_new_simple ("text/plain", NULL);
241   gst_buffer_set_caps (buffer, caps);
242   gst_caps_unref (caps);
243
244   return buffer;
245 }
246
247 static void
248 cleanup_textoverlay (GstElement * textoverlay)
249 {
250   GST_DEBUG ("cleanup_textoverlay");
251
252   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
253   g_list_free (buffers);
254   buffers = NULL;
255
256   gst_element_set_state (textoverlay, GST_STATE_NULL);
257   gst_element_get_state (textoverlay, NULL, NULL, GST_CLOCK_TIME_NONE);
258   gst_pad_set_active (myvideosrcpad, FALSE);
259   gst_pad_set_active (mysinkpad, FALSE);
260   notgst_check_teardown_src_pad2 (textoverlay, "video_sink");
261   if (mytextsrcpad) {
262     notgst_check_teardown_src_pad2 (textoverlay, "text_sink");
263   }
264   gst_check_teardown_sink_pad (textoverlay);
265   gst_check_teardown_element (textoverlay);
266 }
267
268 GST_START_TEST (test_video_passthrough)
269 {
270   GstElement *textoverlay;
271   GstBuffer *inbuffer;
272
273   textoverlay = setup_textoverlay (TRUE);
274   fail_unless (gst_element_set_state (textoverlay,
275           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
276       "could not set to playing");
277
278   inbuffer = create_black_buffer (VIDEO_CAPS_STRING);
279   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
280
281   /* ========== (1) video buffer without timestamp => should be dropped ==== */
282
283   /* take additional ref to keep it alive */
284   gst_buffer_ref (inbuffer);
285   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
286
287   /* pushing gives away one of the two references we have ... */
288   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
289
290   /* should have been discarded as out-of-segment since it has no timestamp */
291   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
292   fail_unless_equals_int (g_list_length (buffers), 0);
293
294   /* ========== (2) buffer with 0 timestamp => simple passthrough ========== */
295
296   /* now try again, this time with timestamp (segment defaults to 0 start) */
297   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
298   GST_BUFFER_DURATION (inbuffer) = GST_CLOCK_TIME_NONE;
299
300   /* take additional ref to keep it alive */
301   gst_buffer_ref (inbuffer);
302   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
303
304   /* pushing gives away one of the two references we have ... */
305   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
306
307   /* text pad is not linked, timestamp is in segment, no static text to
308    * render, should have gone through right away without modification */
309   fail_unless_equals_int (g_list_length (buffers), 1);
310   fail_unless (GST_BUFFER_CAST (buffers->data) == inbuffer);
311   fail_unless (buffer_is_all_black (inbuffer));
312   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
313
314   /* and clean up */
315   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
316   g_list_free (buffers);
317   buffers = NULL;
318   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
319
320   /* ========== (3) buffer with 0 timestamp and no duration, with the
321    *                segment starting from 1sec => should be discarded */
322
323   gst_pad_push_event (myvideosrcpad,
324       gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 1 * GST_SECOND,
325           -1, 0));
326
327   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
328   GST_BUFFER_DURATION (inbuffer) = GST_CLOCK_TIME_NONE;
329
330   /* take additional ref to keep it alive */
331   gst_buffer_ref (inbuffer);
332   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
333
334   /* pushing gives away one of the two references we have ... */
335   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
336
337   /* should have been discarded as out-of-segment */
338   fail_unless_equals_int (g_list_length (buffers), 0);
339   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
340
341   /* ========== (4) buffer with 0 timestamp and small defined duration, with
342    *                segment starting from 1sec => should be discarded */
343
344   gst_pad_push_event (myvideosrcpad,
345       gst_event_new_new_segment (FALSE, 1.0, 1 * GST_FORMAT_TIME, GST_SECOND,
346           -1, 0));
347
348   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 10;
349
350   /* take additional ref to keep it alive */
351   gst_buffer_ref (inbuffer);
352   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
353
354   /* pushing gives away one of the two references we have ... */
355   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
356
357   /* should have been discarded as out-of-segment since it has no timestamp */
358   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
359   fail_unless_equals_int (g_list_length (buffers), 0);
360
361   /* ========== (5) buffer partially overlapping into the segment => should
362    *                be pushed through, but with adjusted stamp values */
363
364   gst_pad_push_event (myvideosrcpad,
365       gst_event_new_new_segment (FALSE, 1.0, 1 * GST_FORMAT_TIME, GST_SECOND,
366           -1, 0));
367
368   GST_BUFFER_TIMESTAMP (inbuffer) = GST_SECOND / 4;
369   GST_BUFFER_DURATION (inbuffer) = GST_SECOND;
370
371   /* take additional ref to keep it alive */
372   gst_buffer_ref (inbuffer);
373   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
374
375   /* pushing gives away one of the two references we have ... */
376   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
377
378   /* should be the parent for a new subbuffer for the stamp fix-up */
379   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
380   fail_unless_equals_int (g_list_length (buffers), 1);
381   fail_unless (GST_BUFFER_CAST (buffers->data) != inbuffer);
382   fail_unless (GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (buffers->data)) ==
383       GST_SECOND);
384   fail_unless (GST_BUFFER_DURATION (GST_BUFFER_CAST (buffers->data)) ==
385       (GST_SECOND / 4));
386   fail_unless (buffer_is_all_black (GST_BUFFER_CAST (buffers->data)));
387   /* and clean up */
388   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
389   g_list_free (buffers);
390   buffers = NULL;
391   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
392
393   /* cleanup */
394   cleanup_textoverlay (textoverlay);
395   gst_buffer_unref (inbuffer);
396 }
397
398 GST_END_TEST;
399
400 GST_START_TEST (test_video_render_static_text)
401 {
402   GstElement *textoverlay;
403   GstBuffer *inbuffer;
404
405   textoverlay = setup_textoverlay (TRUE);
406
407   /* set static text to render */
408   g_object_set (textoverlay, "text", "XLX", NULL);
409
410   fail_unless (gst_element_set_state (textoverlay,
411           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
412       "could not set to playing");
413
414   inbuffer = create_black_buffer (VIDEO_CAPS_STRING);
415   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
416
417   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
418   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 10;
419
420   /* take additional ref to keep it alive */
421   gst_buffer_ref (inbuffer);
422   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
423
424   /* pushing gives away one of the two references we have ... */
425   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
426
427   /* should have been dropped in favour of a new writable buffer */
428   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
429   fail_unless_equals_int (g_list_length (buffers), 1);
430   fail_unless (GST_BUFFER_CAST (buffers->data) != inbuffer);
431
432   /* there should be text rendered */
433   fail_unless (buffer_is_all_black (GST_BUFFER_CAST (buffers->data)) == FALSE);
434
435   fail_unless (GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (buffers->data)) == 0);
436   fail_unless (GST_BUFFER_DURATION (GST_BUFFER_CAST (buffers->data)) ==
437       (GST_SECOND / 10));
438
439   /* and clean up */
440   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
441   g_list_free (buffers);
442   buffers = NULL;
443   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
444
445   /* cleanup */
446   cleanup_textoverlay (textoverlay);
447   gst_buffer_unref (inbuffer);
448 }
449
450 GST_END_TEST;
451
452 static gpointer
453 test_video_waits_for_text_send_text_newsegment_thread (gpointer data)
454 {
455   g_usleep (1 * G_USEC_PER_SEC);
456
457   /* send an update newsegment; the video buffer should now be pushed through 
458    * even though there is no text buffer queued at the moment */
459   GST_INFO ("Sending newsegment update on text pad");
460   gst_pad_push_event (mytextsrcpad,
461       gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_TIME,
462           35 * GST_SECOND, -1, 35 * GST_SECOND));
463
464   return NULL;
465 }
466
467 static gpointer
468 test_video_waits_for_text_shutdown_element (gpointer data)
469 {
470   g_usleep (1 * G_USEC_PER_SEC);
471
472   GST_INFO ("Trying to shut down textoverlay element ...");
473   /* set to NULL state to make sure we can shut it down while it's
474    * blocking in the video chain function waiting for a text buffer */
475   gst_element_set_state (GST_ELEMENT (data), GST_STATE_NULL);
476   GST_INFO ("Done.");
477
478   return NULL;
479 }
480
481 GST_START_TEST (test_video_waits_for_text)
482 {
483   GstElement *textoverlay;
484   GstBuffer *inbuffer, *tbuf;
485   GThread *thread;
486
487   textoverlay = setup_textoverlay (FALSE);
488
489   fail_unless (gst_element_set_state (textoverlay,
490           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
491       "could not set to playing");
492
493   tbuf = create_text_buffer ("XLX", 1 * GST_SECOND, 5 * GST_SECOND);
494   gst_buffer_ref (tbuf);
495   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 2);
496
497   GST_LOG ("pushing text buffer");
498   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
499
500   /* it should be stuck in textoverlay until it gets a text buffer or a
501    * newsegment event that indicates it's not needed any longer */
502   fail_unless_equals_int (g_list_length (buffers), 0);
503
504   inbuffer = create_black_buffer (VIDEO_CAPS_STRING);
505   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
506
507   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
508   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 2;
509
510   /* take additional ref to keep it alive */
511   gst_buffer_ref (inbuffer);
512   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
513
514   /* pushing gives away one of the two references we have ... */
515   GST_LOG ("pushing video buffer 1");
516   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
517
518   /* video buffer should have gone through untainted, since the text is later */
519   fail_unless_equals_int (g_list_length (buffers), 1);
520
521   /* text should still be stuck in textoverlay */
522   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 2);
523
524   /* there should be no text rendered */
525   fail_unless (buffer_is_all_black (GST_BUFFER_CAST (buffers->data)));
526
527   /* now, another video buffer */
528   inbuffer = gst_buffer_make_metadata_writable (inbuffer);
529   GST_BUFFER_TIMESTAMP (inbuffer) = GST_SECOND;
530   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 2;
531
532   /* pushing gives away one of the two references we have ... */
533   GST_LOG ("pushing video buffer 2");
534   gst_buffer_ref (inbuffer);
535   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
536
537   /* video buffer should have gone right away, with text rendered on it */
538   fail_unless_equals_int (g_list_length (buffers), 2);
539
540   /* text should still be stuck in textoverlay */
541   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 2);
542
543   /* there should be text rendered */
544   fail_unless (buffer_is_all_black (GST_BUFFER_CAST (buffers->next->data)) ==
545       FALSE);
546
547   /* a third video buffer */
548   inbuffer = gst_buffer_make_metadata_writable (inbuffer);
549   GST_BUFFER_TIMESTAMP (inbuffer) = 30 * GST_SECOND;
550   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 2;
551
552   /* video buffer #3: should not go through, it should discard the current
553    * text buffer as too old and then wait for the next text buffer (or a
554    * newsegment event to arrive); we spawn a background thread to send such
555    * a newsegment event after a second or so so we get back control */
556   thread =
557       g_thread_create (test_video_waits_for_text_send_text_newsegment_thread,
558       NULL, FALSE, NULL);
559   fail_unless (thread != NULL);
560
561   GST_LOG ("pushing video buffer 3");
562   gst_buffer_ref (inbuffer);
563   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
564
565   /* but the text should no longer be stuck in textoverlay */
566   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 1);
567
568   /* video buffer should have gone through after newsegment event */
569   fail_unless_equals_int (g_list_length (buffers), 3);
570
571   /* ... and there should not be any text rendered on it */
572   fail_unless (buffer_is_all_black (GST_BUFFER_CAST (buffers->next->
573               next->data)));
574
575   /* a fourth video buffer */
576   inbuffer = gst_buffer_make_metadata_writable (inbuffer);
577   GST_BUFFER_TIMESTAMP (inbuffer) = 35 * GST_SECOND;
578   GST_BUFFER_DURATION (inbuffer) = GST_SECOND;
579
580   /* video buffer #4: should not go through, it should wait for the next
581    * text buffer (or a newsegment event) to arrive; we spawn a background
582    * thread to shut down the element while it's waiting to make sure that
583    * works ok */
584   thread = g_thread_create (test_video_waits_for_text_shutdown_element,
585       textoverlay, FALSE, NULL);
586   fail_unless (thread != NULL);
587
588   GST_LOG ("pushing video buffer 4");
589   gst_buffer_ref (inbuffer);
590   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_WRONG_STATE);
591
592   /* and clean up */
593   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
594   g_list_free (buffers);
595   buffers = NULL;
596   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
597
598   /* cleanup */
599   cleanup_textoverlay (textoverlay);
600   gst_buffer_unref (inbuffer);
601
602   /* give up our ref, textoverlay should've cleared its queued buffer by now */
603   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 1);
604   gst_buffer_unref (tbuf);
605 }
606
607 GST_END_TEST;
608
609 static gpointer
610 test_render_continuity_push_video_buffers_thread (gpointer data)
611 {
612   /* push video buffers at 1fps */
613   guint frame_count = 0;
614
615   do {
616     GstBuffer *vbuf;
617
618     vbuf = create_black_buffer (VIDEO_CAPS_STRING);
619     ASSERT_BUFFER_REFCOUNT (vbuf, "vbuf", 1);
620
621     GST_BUFFER_TIMESTAMP (vbuf) = frame_count * GST_SECOND;
622     GST_BUFFER_DURATION (vbuf) = GST_SECOND;
623
624     /* pushing gives away one of the two references we have ... */
625     GST_LOG ("pushing video buffer %u @ %" GST_TIME_FORMAT, frame_count,
626         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (vbuf)));
627     fail_unless (gst_pad_push (myvideosrcpad, vbuf) == GST_FLOW_OK);
628
629     ++frame_count;
630   } while (frame_count < 15);
631
632   return NULL;
633 }
634
635
636 GST_START_TEST (test_render_continuity)
637 {
638   GThread *thread;
639   GstElement *textoverlay;
640   GstBuffer *tbuf;
641
642   textoverlay = setup_textoverlay (FALSE);
643
644   fail_unless (gst_element_set_state (textoverlay,
645           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
646       "could not set to playing");
647
648   thread = g_thread_create (test_render_continuity_push_video_buffers_thread,
649       NULL, FALSE, NULL);
650   fail_unless (thread != NULL);
651
652   tbuf = create_text_buffer ("XLX", 2 * GST_SECOND, GST_SECOND);
653   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
654       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
655   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
656
657   tbuf = create_text_buffer ("XLX", 3 * GST_SECOND, 2 * GST_SECOND);
658   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
659       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
660   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
661
662   tbuf = create_text_buffer ("XLX", 7 * GST_SECOND, GST_SECOND);
663   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
664       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
665   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
666
667   tbuf = create_text_buffer ("XLX", 8 * GST_SECOND, GST_SECOND);
668   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
669       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
670   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
671
672   tbuf = create_text_buffer ("XLX", 9 * GST_SECOND, GST_SECOND);
673   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
674       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
675   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
676
677   tbuf = create_text_buffer ("XLX", 10 * GST_SECOND, 30 * GST_SECOND);
678   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
679       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
680   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
681
682   GST_LOG ("give the other thread some time to push through the remaining"
683       "video buffers");
684   g_usleep (G_USEC_PER_SEC);
685   GST_LOG ("done");
686
687   /* we should have 15 buffers each with one second length now */
688   fail_unless_equals_int (g_list_length (buffers), 15);
689
690   /* buffers 0 + 1 should be black */
691   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 0))));
692   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 1))));
693
694   /* buffers 2 - 4 should have text */
695   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
696                   2))) == FALSE);
697   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
698                   3))) == FALSE);
699   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
700                   4))) == FALSE);
701
702   /* buffers 5 + 6 should be black */
703   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 5))));
704   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 6))));
705
706   /* buffers 7 - last should have text */
707   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
708                   7))) == FALSE);
709   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
710                   8))) == FALSE);
711   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
712                   9))) == FALSE);
713   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
714                   10))) == FALSE);
715   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
716                   11))) == FALSE);
717   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
718                   12))) == FALSE);
719   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
720                   13))) == FALSE);
721   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
722                   14))) == FALSE);
723
724   /* and clean up */
725   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
726   g_list_free (buffers);
727   buffers = NULL;
728
729   /* cleanup */
730   cleanup_textoverlay (textoverlay);
731 }
732
733 GST_END_TEST;
734
735 static Suite *
736 textoverlay_suite (void)
737 {
738   Suite *s = suite_create ("textoverlay");
739   TCase *tc_chain = tcase_create ("general");
740
741   suite_add_tcase (s, tc_chain);
742
743   tcase_add_test (tc_chain, test_video_passthrough);
744   tcase_add_test (tc_chain, test_video_render_static_text);
745   tcase_add_test (tc_chain, test_render_continuity);
746   tcase_add_test (tc_chain, test_video_waits_for_text);
747
748   return s;
749 }
750
751 GST_CHECK_MAIN (textoverlay);