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., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <gst/check/gstcheck.h>
26 #include <gst/video/video-overlay-composition.h>
27 
28 #define I420_Y_ROWSTRIDE(width) (GST_ROUND_UP_4(width))
29 #define I420_U_ROWSTRIDE(width) (GST_ROUND_UP_8(width)/2)
30 #define I420_V_ROWSTRIDE(width) ((GST_ROUND_UP_8(I420_Y_ROWSTRIDE(width)))/2)
31 
32 #define I420_Y_OFFSET(w,h) (0)
33 #define I420_U_OFFSET(w,h) (I420_Y_OFFSET(w,h)+(I420_Y_ROWSTRIDE(w)*GST_ROUND_UP_2(h)))
34 #define I420_V_OFFSET(w,h) (I420_U_OFFSET(w,h)+(I420_U_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
35 
36 #define I420_SIZE(w,h)     (I420_V_OFFSET(w,h)+(I420_V_ROWSTRIDE(w)*GST_ROUND_UP_2(h)/2))
37 
38 #define WIDTH 240
39 #define HEIGHT 120
40 
41 /* For ease of programming we use globals to keep refs for our floating
42  * src and sink pads we create; otherwise we always have to do get_pad,
43  * get_peer, and then remove references in every test function */
44 static GstPad *myvideosrcpad, *mytextsrcpad, *mysinkpad;
45 
46 #define VIDEO_CAPS_STRING               \
47     "video/x-raw, "                 \
48     "format = (string) I420, "          \
49     "framerate = (fraction) 1/1, "      \
50     "width = (int) 240, "               \
51     "height = (int) 120"
52 
53 #define VIDEO_CAPS_TEMPLATE_STRING      \
54     "video/x-raw, "                 \
55     "format = (string) I420"
56 
57 #define VIDEO_CAPS_TEMPLATE_WITH_FEATURE_STRING                              \
58     "video/x-raw(" GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY ", "                \
59     GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION "), "                \
60     "format = (string) I420;"                                                \
61     "video/x-raw, "                                                          \
62     "format = (string) I420;"
63 
64 #define UNSUPPORTED_VIDEO_CAPS_STRING                                            \
65     "video/x-raw(" GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META "), "  \
66     "format = (string) I420, "                                                   \
67     "framerate = (fraction) 1/1, "                                               \
68     "width = (int) 240, "                                                        \
69     "height = (int) 120"
70 
71 #define UNSUPPORTED_VIDEO_CAPS_TEMPLATE_STRING                                  \
72     "video/x-raw(" GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META "), " \
73     "format = (string) I420"
74 
75 #define UNSUPPORTED_VIDEO_CAPS_TEMPLATE_WITH_FEATURE_STRING                     \
76     "video/x-raw(" GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META ","   \
77                    GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION "), "    \
78     "format = (string) I420;"                                                   \
79     "video/x-raw(" GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META "), " \
80     "format = (string) I420"
81 
82 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
83     GST_PAD_SINK,
84     GST_PAD_ALWAYS,
85     GST_STATIC_CAPS (VIDEO_CAPS_TEMPLATE_STRING)
86     );
87 
88 static GstStaticPadTemplate sinktemplate_with_features =
89 GST_STATIC_PAD_TEMPLATE ("sink",
90     GST_PAD_SINK,
91     GST_PAD_ALWAYS,
92     GST_STATIC_CAPS (VIDEO_CAPS_TEMPLATE_WITH_FEATURE_STRING)
93     );
94 
95 static GstStaticPadTemplate text_srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
96     GST_PAD_SRC,
97     GST_PAD_ALWAYS,
98     GST_STATIC_CAPS ("text/x-raw, format=utf8")
99     );
100 
101 static GstStaticPadTemplate video_srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
102     GST_PAD_SRC,
103     GST_PAD_ALWAYS,
104     GST_STATIC_CAPS (VIDEO_CAPS_TEMPLATE_STRING)
105     );
106 
107 static GstStaticPadTemplate unsupported_sinktemplate_with_features =
108 GST_STATIC_PAD_TEMPLATE ("sink",
109     GST_PAD_SINK,
110     GST_PAD_ALWAYS,
111     GST_STATIC_CAPS (UNSUPPORTED_VIDEO_CAPS_TEMPLATE_WITH_FEATURE_STRING)
112     );
113 
114 static GstStaticPadTemplate sinktemplate_any = GST_STATIC_PAD_TEMPLATE ("sink",
115     GST_PAD_SINK,
116     GST_PAD_ALWAYS,
117     GST_STATIC_CAPS_ANY);
118 
119 static GstStaticPadTemplate unsupported_video_srctemplate =
120 GST_STATIC_PAD_TEMPLATE ("src",
121     GST_PAD_SRC,
122     GST_PAD_ALWAYS,
123     GST_STATIC_CAPS (UNSUPPORTED_VIDEO_CAPS_TEMPLATE_STRING)
124     );
125 
126 static void
gst_check_setup_events_textoverlay(GstPad * srcpad,GstElement * element,GstCaps * caps,GstFormat format,const gchar * stream_id)127 gst_check_setup_events_textoverlay (GstPad * srcpad, GstElement * element,
128     GstCaps * caps, GstFormat format, const gchar * stream_id)
129 {
130   GstSegment segment;
131 
132   gst_segment_init (&segment, format);
133 
134   fail_unless (gst_pad_push_event (srcpad,
135           gst_event_new_stream_start (stream_id)));
136   if (caps)
137     fail_unless (gst_pad_push_event (srcpad, gst_event_new_caps (caps)));
138   fail_unless (gst_pad_push_event (srcpad, gst_event_new_segment (&segment)));
139 }
140 
141 
142 static gboolean
sink_query_handler(GstPad * pad,GstObject * parent,GstQuery * query)143 sink_query_handler (GstPad * pad, GstObject * parent, GstQuery * query)
144 {
145   gboolean ret = FALSE;
146   GstQueryType type = GST_QUERY_TYPE (query);
147 
148   switch (type) {
149     case GST_QUERY_ALLOCATION:{
150       gst_query_add_allocation_meta (query,
151           GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
152 
153       ret = TRUE;
154 
155       break;
156     }
157     default:{
158       ret = gst_pad_query_default (pad, parent, query);
159       break;
160     }
161   }
162   return ret;
163 }
164 
165 /* much like gst_check_setup_src_pad(), but with possibility to give a hint
166  * which sink template of the element to use, if there are multiple ones */
167 static GstPad *
notgst_check_setup_src_pad2(GstElement * element,GstStaticPadTemplate * template,GstCaps * caps,const gchar * sink_template_name)168 notgst_check_setup_src_pad2 (GstElement * element,
169     GstStaticPadTemplate * template, GstCaps * caps,
170     const gchar * sink_template_name)
171 {
172   GstPad *srcpad, *sinkpad;
173 
174   if (sink_template_name == NULL)
175     sink_template_name = "sink";
176 
177   /* sending pad */
178   srcpad = gst_pad_new_from_static_template (template, "src");
179   GST_DEBUG_OBJECT (element, "setting up sending pad %p", srcpad);
180   fail_if (srcpad == NULL, "Could not create a srcpad");
181   ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1);
182 
183   if (!(sinkpad = gst_element_get_static_pad (element, sink_template_name)))
184     sinkpad = gst_element_get_request_pad (element, sink_template_name);
185   fail_if (sinkpad == NULL, "Could not get sink pad from %s",
186       GST_ELEMENT_NAME (element));
187   ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
188   if (caps)
189     fail_unless (gst_pad_set_caps (srcpad, caps));
190   fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
191       "Could not link source and %s sink pads", GST_ELEMENT_NAME (element));
192   gst_object_unref (sinkpad);   /* because we got it higher up */
193   ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 1);
194 
195   return srcpad;
196 }
197 
198 static void
notgst_check_teardown_src_pad2(GstElement * element,const gchar * sink_template_name)199 notgst_check_teardown_src_pad2 (GstElement * element,
200     const gchar * sink_template_name)
201 {
202   GstPad *srcpad, *sinkpad;
203 
204   if (sink_template_name == NULL)
205     sink_template_name = "sink";
206 
207   /* clean up floating src pad */
208   if (!(sinkpad = gst_element_get_static_pad (element, sink_template_name)))
209     sinkpad = gst_element_get_request_pad (element, sink_template_name);
210   ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
211   srcpad = gst_pad_get_peer (sinkpad);
212 
213   gst_pad_unlink (srcpad, sinkpad);
214 
215   /* pad refs held by both creator and this function (through _get) */
216   ASSERT_OBJECT_REFCOUNT (sinkpad, "element sinkpad", 2);
217   gst_object_unref (sinkpad);
218   /* one more ref is held by element itself */
219 
220   /* pad refs held by both creator and this function (through _get_peer) */
221   ASSERT_OBJECT_REFCOUNT (srcpad, "check srcpad", 2);
222   gst_object_unref (srcpad);
223   gst_object_unref (srcpad);
224 }
225 
226 static GstElement *
setup_textoverlay_with_templates(GstStaticPadTemplate * srcpad_template,GstStaticPadTemplate * textpad_template,GstStaticPadTemplate * sinkpad_template,gboolean enable_allocation_query)227 setup_textoverlay_with_templates (GstStaticPadTemplate * srcpad_template,
228     GstStaticPadTemplate * textpad_template,
229     GstStaticPadTemplate * sinkpad_template, gboolean enable_allocation_query)
230 {
231   GstElement *textoverlay;
232 
233   GST_DEBUG ("setup_textoverlay");
234   textoverlay = gst_check_setup_element ("textoverlay");
235   mysinkpad = gst_check_setup_sink_pad (textoverlay, sinkpad_template);
236 
237   if (enable_allocation_query) {
238     GST_PAD_SET_PROXY_ALLOCATION (mysinkpad);
239     gst_pad_set_query_function (mysinkpad, sink_query_handler);
240   }
241 
242   myvideosrcpad =
243       notgst_check_setup_src_pad2 (textoverlay, srcpad_template, NULL,
244       "video_sink");
245 
246   if (textpad_template) {
247     mytextsrcpad =
248         notgst_check_setup_src_pad2 (textoverlay, textpad_template, NULL,
249         "text_sink");
250     gst_pad_set_active (mytextsrcpad, TRUE);
251   } else {
252     mytextsrcpad = NULL;
253   }
254 
255   gst_pad_set_active (myvideosrcpad, TRUE);
256   gst_pad_set_active (mysinkpad, TRUE);
257 
258   return textoverlay;
259 }
260 
261 static GstElement *
setup_textoverlay(gboolean video_only_no_text)262 setup_textoverlay (gboolean video_only_no_text)
263 {
264   GstStaticPadTemplate *srcpad_template = NULL;
265   GstStaticPadTemplate *textpad_template = NULL;
266   GstStaticPadTemplate *sinkpad_template = NULL;
267 
268   srcpad_template = &video_srctemplate;
269   if (!video_only_no_text)
270     textpad_template = &text_srctemplate;
271   sinkpad_template = &sinktemplate;
272 
273   return setup_textoverlay_with_templates (srcpad_template,
274       textpad_template, sinkpad_template, FALSE);
275 }
276 
277 static gboolean
buffer_is_all_black(GstBuffer * buf,GstCaps * caps)278 buffer_is_all_black (GstBuffer * buf, GstCaps * caps)
279 {
280   GstStructure *s;
281   gint x, y, w, h;
282   GstMapInfo map;
283 
284   fail_unless (buf != NULL);
285   fail_unless (caps != NULL);
286   s = gst_caps_get_structure (caps, 0);
287   fail_unless (s != NULL);
288   fail_unless (gst_structure_get_int (s, "width", &w));
289   fail_unless (gst_structure_get_int (s, "height", &h));
290 
291   gst_buffer_map (buf, &map, GST_MAP_READ);
292   for (y = 0; y < h; ++y) {
293     guint8 *ptr = map.data + (y * GST_ROUND_UP_4 (w));
294 
295     for (x = 0; x < w; ++x) {
296       if (ptr[x] != 0x00) {
297         GST_LOG ("non-black pixel (%d) at (x,y) %d,%d", ptr[x], x, y);
298         gst_buffer_unmap (buf, &map);
299         return FALSE;
300       }
301     }
302   }
303   gst_buffer_unmap (buf, &map);
304 
305   return TRUE;
306 }
307 
308 static GstCaps *
create_video_caps(const gchar * caps_string)309 create_video_caps (const gchar * caps_string)
310 {
311   GstCaps *caps;
312 
313   caps = gst_caps_from_string (caps_string);
314   fail_unless (caps != NULL);
315   fail_unless (gst_caps_is_fixed (caps));
316 
317   return caps;
318 }
319 
320 static GstBuffer *
create_black_buffer(GstCaps * caps)321 create_black_buffer (GstCaps * caps)
322 {
323   GstStructure *s;
324   GstBuffer *buffer;
325   gint w, h, size;
326 
327   fail_unless (caps != NULL);
328 
329   s = gst_caps_get_structure (caps, 0);
330   fail_unless (gst_structure_get_int (s, "width", &w));
331   fail_unless (gst_structure_get_int (s, "height", &h));
332 
333   GST_LOG ("creating buffer (%dx%d)", w, h);
334 
335   size = I420_SIZE (w, h);
336   buffer = gst_buffer_new_and_alloc (size);
337   /* we're only checking the Y plane later, so just zero it all out,
338    * even if it's not the blackest black there is */
339   gst_buffer_memset (buffer, 0, 0, size);
340 
341   /* double check to make sure it's been created right */
342   fail_unless (buffer_is_all_black (buffer, caps));
343 
344   return buffer;
345 }
346 
347 static GstBuffer *
create_text_buffer(const gchar * txt,GstClockTime ts,GstClockTime duration)348 create_text_buffer (const gchar * txt, GstClockTime ts, GstClockTime duration)
349 {
350   GstBuffer *buffer;
351   guint txt_len;
352 
353   fail_unless (txt != NULL);
354 
355   txt_len = strlen (txt);
356 
357   buffer = gst_buffer_new_and_alloc (txt_len);
358   gst_buffer_fill (buffer, 0, txt, txt_len);
359 
360   GST_BUFFER_TIMESTAMP (buffer) = ts;
361   GST_BUFFER_DURATION (buffer) = duration;
362 
363   return buffer;
364 }
365 
366 static gboolean
_test_textoverlay_check_caps_has_feature(GstElement * textoverlay,const gchar * padname,const gchar * feature)367 _test_textoverlay_check_caps_has_feature (GstElement * textoverlay,
368     const gchar * padname, const gchar * feature)
369 {
370   GstPad *pad;
371   GstCaps *caps;
372   GstCapsFeatures *f;
373   gboolean ret;
374 
375   pad = gst_element_get_static_pad (textoverlay, padname);
376   fail_unless (pad != NULL);
377 
378   caps = gst_pad_get_current_caps (pad);
379   fail_unless (caps != NULL);
380 
381   gst_object_unref (pad);
382 
383   f = gst_caps_get_features (caps, 0);
384   if (f != NULL) {
385     ret = gst_caps_features_contains (f, feature);
386   } else {
387     ret = FALSE;
388   }
389 
390   gst_caps_unref (caps);
391   return ret;
392 }
393 
394 static void
cleanup_textoverlay(GstElement * textoverlay)395 cleanup_textoverlay (GstElement * textoverlay)
396 {
397   GST_DEBUG ("cleanup_textoverlay");
398 
399   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
400   g_list_free (buffers);
401   buffers = NULL;
402 
403   gst_element_set_state (textoverlay, GST_STATE_NULL);
404   gst_element_get_state (textoverlay, NULL, NULL, GST_CLOCK_TIME_NONE);
405   gst_pad_set_active (myvideosrcpad, FALSE);
406   gst_pad_set_active (mysinkpad, FALSE);
407   notgst_check_teardown_src_pad2 (textoverlay, "video_sink");
408   if (mytextsrcpad) {
409     notgst_check_teardown_src_pad2 (textoverlay, "text_sink");
410   }
411   gst_check_teardown_sink_pad (textoverlay);
412   gst_check_teardown_element (textoverlay);
413 }
414 
GST_START_TEST(test_video_passthrough)415 GST_START_TEST (test_video_passthrough)
416 {
417   GstElement *textoverlay;
418   GstBuffer *inbuffer, *outbuffer;
419   GstCaps *incaps, *outcaps;
420   GstSegment segment;
421 
422   textoverlay = setup_textoverlay (TRUE);
423   fail_unless (gst_element_set_state (textoverlay,
424           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
425       "could not set to playing");
426 
427   incaps = create_video_caps (VIDEO_CAPS_STRING);
428   gst_check_setup_events_textoverlay (myvideosrcpad, textoverlay, incaps,
429       GST_FORMAT_TIME, "video");
430   inbuffer = create_black_buffer (incaps);
431   gst_caps_unref (incaps);
432 
433   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
434 
435   /* ========== (1) video buffer without timestamp => should be dropped ==== */
436 
437   /* take additional ref to keep it alive */
438   gst_buffer_ref (inbuffer);
439   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
440 
441   /* pushing gives away one of the two references we have ... */
442   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
443 
444   /* should have been discarded as out-of-segment since it has no timestamp */
445   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
446   fail_unless_equals_int (g_list_length (buffers), 0);
447 
448   /* ========== (2) buffer with 0 timestamp => simple passthrough ========== */
449 
450   /* now try again, this time with timestamp (segment defaults to 0 start) */
451   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
452   GST_BUFFER_DURATION (inbuffer) = GST_CLOCK_TIME_NONE;
453 
454   /* take additional ref to keep it alive */
455   gst_buffer_ref (inbuffer);
456   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
457 
458   /* pushing gives away one of the two references we have ... */
459   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
460 
461   /* text pad is not linked, timestamp is in segment, no static text to
462    * render, should have gone through right away without modification */
463   fail_unless_equals_int (g_list_length (buffers), 1);
464   outbuffer = GST_BUFFER_CAST (buffers->data);
465   fail_unless (outbuffer == inbuffer);
466   outcaps = gst_pad_get_current_caps (mysinkpad);
467   fail_unless (buffer_is_all_black (outbuffer, outcaps));
468   gst_caps_unref (outcaps);
469   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
470 
471   /* and clean up */
472   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
473   g_list_free (buffers);
474   buffers = NULL;
475   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
476 
477   /* ========== (3) buffer with 0 timestamp and no duration, with the
478    *                segment starting from 1sec => should be discarded */
479 
480   gst_segment_init (&segment, GST_FORMAT_TIME);
481   segment.start = 1 * GST_SECOND;
482   segment.stop = -1;
483   segment.time = 0;
484   gst_pad_push_event (myvideosrcpad, gst_event_new_segment (&segment));
485 
486   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
487   GST_BUFFER_DURATION (inbuffer) = GST_CLOCK_TIME_NONE;
488 
489   /* take additional ref to keep it alive */
490   gst_buffer_ref (inbuffer);
491   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
492 
493   /* pushing gives away one of the two references we have ... */
494   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
495 
496   /* should have been discarded as out-of-segment */
497   fail_unless_equals_int (g_list_length (buffers), 0);
498   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
499 
500   /* ========== (4) buffer with 0 timestamp and small defined duration, with
501    *                segment starting from 1sec => should be discarded */
502 
503   gst_pad_push_event (myvideosrcpad, gst_event_new_segment (&segment));
504 
505   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 10;
506 
507   /* take additional ref to keep it alive */
508   gst_buffer_ref (inbuffer);
509   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
510 
511   /* pushing gives away one of the two references we have ... */
512   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
513 
514   /* should have been discarded as out-of-segment since it has no timestamp */
515   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
516   fail_unless_equals_int (g_list_length (buffers), 0);
517 
518   /* ========== (5) buffer partially overlapping into the segment => should
519    *                be pushed through, but with adjusted stamp values */
520 
521   gst_pad_push_event (myvideosrcpad, gst_event_new_segment (&segment));
522 
523   GST_BUFFER_TIMESTAMP (inbuffer) = GST_SECOND / 4;
524   GST_BUFFER_DURATION (inbuffer) = GST_SECOND;
525 
526   /* take additional ref to keep it alive */
527   gst_buffer_ref (inbuffer);
528   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
529 
530   /* pushing gives away one of the two references we have ... */
531   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
532 
533   /* should be a new buffer for the stamp fix-up */
534   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
535   fail_unless_equals_int (g_list_length (buffers), 1);
536   outbuffer = GST_BUFFER_CAST (buffers->data);
537   outcaps = gst_pad_get_current_caps (mysinkpad);
538   fail_unless (outbuffer != inbuffer);
539   fail_unless (GST_BUFFER_TIMESTAMP (outbuffer) == GST_SECOND);
540   fail_unless (GST_BUFFER_DURATION (outbuffer) == (GST_SECOND / 4));
541   fail_unless (buffer_is_all_black (outbuffer, outcaps));
542   gst_caps_unref (outcaps);
543   /* and clean up */
544   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
545   g_list_free (buffers);
546   buffers = NULL;
547   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
548 
549   /* cleanup */
550   cleanup_textoverlay (textoverlay);
551   gst_buffer_unref (inbuffer);
552 }
553 
554 GST_END_TEST;
555 
GST_START_TEST(test_video_passthrough_with_feature)556 GST_START_TEST (test_video_passthrough_with_feature)
557 {
558   GstElement *textoverlay;
559   GstBuffer *inbuffer, *outbuffer;
560   GstCaps *incaps, *outcaps;
561   GstVideoOverlayCompositionMeta *comp_meta;
562 
563   textoverlay = setup_textoverlay_with_templates (&video_srctemplate,
564       NULL, &sinktemplate_with_features, TRUE);
565 
566   /* set static text to render */
567   g_object_set (textoverlay, "text", "XLX", NULL);
568 
569   fail_unless (gst_element_set_state (textoverlay,
570           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
571       "could not set to playing");
572 
573   incaps = create_video_caps (VIDEO_CAPS_STRING);
574   gst_check_setup_events_textoverlay (myvideosrcpad, textoverlay, incaps,
575       GST_FORMAT_TIME, "video");
576   inbuffer = create_black_buffer (incaps);
577   gst_caps_unref (incaps);
578   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
579 
580   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
581   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 10;
582 
583   /* take additional ref to keep it alive */
584   gst_buffer_ref (inbuffer);
585   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
586 
587   /* pushing gives away one of the two references we have ... */
588   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
589 
590   /* should have been dropped in favour of a new writable buffer */
591   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
592   fail_unless_equals_int (g_list_length (buffers), 1);
593   outbuffer = GST_BUFFER_CAST (buffers->data);
594   outcaps = gst_pad_get_current_caps (mysinkpad);
595   fail_unless (outbuffer != inbuffer);
596 
597   /* output buffer should be black */
598   fail_unless (buffer_is_all_black (outbuffer, outcaps) == TRUE);
599   gst_caps_unref (outcaps);
600 
601   /* output buffer should have the composition meta */
602   comp_meta = gst_buffer_get_video_overlay_composition_meta (outbuffer);
603   fail_unless (comp_meta != NULL);
604 
605   fail_unless (GST_BUFFER_TIMESTAMP (outbuffer) == 0);
606   fail_unless (GST_BUFFER_DURATION (outbuffer) == (GST_SECOND / 10));
607 
608   /* and clean up */
609   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
610   g_list_free (buffers);
611   buffers = NULL;
612   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
613 
614   /* cleanup */
615   cleanup_textoverlay (textoverlay);
616   gst_buffer_unref (inbuffer);
617 }
618 
619 GST_END_TEST;
620 
GST_START_TEST(test_video_passthrough_with_feature_and_unsupported_caps)621 GST_START_TEST (test_video_passthrough_with_feature_and_unsupported_caps)
622 {
623   GstElement *textoverlay;
624   GstBuffer *inbuffer, *outbuffer;
625   GstCaps *incaps, *outcaps;
626   GstVideoOverlayCompositionMeta *comp_meta;
627 
628   textoverlay =
629       setup_textoverlay_with_templates (&unsupported_video_srctemplate, NULL,
630       &unsupported_sinktemplate_with_features, TRUE);
631 
632   /* set static text to render */
633   g_object_set (textoverlay, "text", "XLX", NULL);
634 
635   fail_unless (gst_element_set_state (textoverlay,
636           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
637       "could not set to playing");
638 
639   incaps = create_video_caps (UNSUPPORTED_VIDEO_CAPS_STRING);
640   gst_check_setup_events_textoverlay (myvideosrcpad, textoverlay, incaps,
641       GST_FORMAT_TIME, "video");
642   inbuffer = create_black_buffer (incaps);
643   gst_caps_unref (incaps);
644   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
645 
646   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
647   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 10;
648 
649   /* take additional ref to keep it alive */
650   gst_buffer_ref (inbuffer);
651   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
652 
653   /* pushing gives away one of the two references we have ... */
654   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
655 
656   /* should have been dropped in favour of a new writable buffer */
657   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
658   fail_unless_equals_int (g_list_length (buffers), 1);
659   outbuffer = GST_BUFFER_CAST (buffers->data);
660   outcaps = gst_pad_get_current_caps (mysinkpad);
661   fail_unless (outbuffer != inbuffer);
662 
663   /* output buffer should be black */
664   fail_unless (buffer_is_all_black (outbuffer, outcaps) == TRUE);
665   gst_caps_unref (outcaps);
666 
667   /* output buffer should have the composition meta */
668   comp_meta = gst_buffer_get_video_overlay_composition_meta (outbuffer);
669   fail_unless (comp_meta != NULL);
670 
671   fail_unless (GST_BUFFER_TIMESTAMP (outbuffer) == 0);
672   fail_unless (GST_BUFFER_DURATION (outbuffer) == (GST_SECOND / 10));
673 
674   /* and clean up */
675   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
676   g_list_free (buffers);
677   buffers = NULL;
678   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
679 
680   /* cleanup */
681   cleanup_textoverlay (textoverlay);
682   gst_buffer_unref (inbuffer);
683 }
684 
685 GST_END_TEST;
686 
687 
GST_START_TEST(test_video_render_with_any_features_and_no_allocation_meta)688 GST_START_TEST (test_video_render_with_any_features_and_no_allocation_meta)
689 {
690   GstElement *textoverlay;
691   GstBuffer *inbuffer, *outbuffer;
692   GstCaps *incaps, *outcaps;
693   GstVideoOverlayCompositionMeta *comp_meta;
694 
695   textoverlay =
696       setup_textoverlay_with_templates (&video_srctemplate,
697       NULL, &sinktemplate_any, FALSE);
698 
699   /* set static text to render */
700   g_object_set (textoverlay, "text", "XLX", NULL);
701 
702   fail_unless (gst_element_set_state (textoverlay,
703           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
704       "could not set to playing");
705 
706   incaps = create_video_caps (VIDEO_CAPS_STRING);
707   gst_check_setup_events_textoverlay (myvideosrcpad, textoverlay, incaps,
708       GST_FORMAT_TIME, "video");
709   inbuffer = create_black_buffer (incaps);
710   gst_caps_unref (incaps);
711   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
712 
713   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
714   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 10;
715 
716   /* take additional ref to keep it alive */
717   gst_buffer_ref (inbuffer);
718   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
719 
720   /* pushing gives away one of the two references we have ... */
721   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
722 
723   /* should have been dropped in favour of a new writable buffer */
724   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
725   fail_unless_equals_int (g_list_length (buffers), 1);
726   outbuffer = GST_BUFFER_CAST (buffers->data);
727   outcaps = gst_pad_get_current_caps (mysinkpad);
728   fail_unless (outbuffer != inbuffer);
729 
730   /* output buffer should have rendered text */
731   fail_if (buffer_is_all_black (outbuffer, outcaps));
732   gst_caps_unref (outcaps);
733 
734   /* output buffer should not have the composition meta */
735   comp_meta = gst_buffer_get_video_overlay_composition_meta (outbuffer);
736   fail_unless (comp_meta == NULL);
737 
738   fail_unless (GST_BUFFER_TIMESTAMP (outbuffer) == 0);
739   fail_unless (GST_BUFFER_DURATION (outbuffer) == (GST_SECOND / 10));
740 
741   /* output caps shouldn't have the composition meta */
742   fail_if (_test_textoverlay_check_caps_has_feature (textoverlay, "src",
743           GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION));
744 
745   /* and clean up */
746   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
747   g_list_free (buffers);
748   buffers = NULL;
749   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
750 
751   /* cleanup */
752   cleanup_textoverlay (textoverlay);
753   gst_buffer_unref (inbuffer);
754 }
755 
756 GST_END_TEST;
757 
758 
GST_START_TEST(test_video_render_static_text)759 GST_START_TEST (test_video_render_static_text)
760 {
761   GstElement *textoverlay;
762   GstBuffer *inbuffer, *outbuffer;
763   GstCaps *incaps, *outcaps;
764 
765   textoverlay = setup_textoverlay (TRUE);
766 
767   /* set static text to render */
768   g_object_set (textoverlay, "text", "XLX", NULL);
769 
770   fail_unless (gst_element_set_state (textoverlay,
771           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
772       "could not set to playing");
773 
774   incaps = create_video_caps (VIDEO_CAPS_STRING);
775   gst_check_setup_events_textoverlay (myvideosrcpad, textoverlay, incaps,
776       GST_FORMAT_TIME, "video");
777   inbuffer = create_black_buffer (incaps);
778   gst_caps_unref (incaps);
779   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
780 
781   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
782   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 10;
783 
784   /* take additional ref to keep it alive */
785   gst_buffer_ref (inbuffer);
786   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 2);
787 
788   /* pushing gives away one of the two references we have ... */
789   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
790 
791   /* should have been dropped in favour of a new writable buffer */
792   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
793   fail_unless_equals_int (g_list_length (buffers), 1);
794   outbuffer = GST_BUFFER_CAST (buffers->data);
795   outcaps = gst_pad_get_current_caps (mysinkpad);
796   fail_unless (outbuffer != inbuffer);
797 
798   /* there should be text rendered */
799   fail_unless (buffer_is_all_black (outbuffer, outcaps) == FALSE);
800   gst_caps_unref (outcaps);
801 
802   fail_unless (GST_BUFFER_TIMESTAMP (outbuffer) == 0);
803   fail_unless (GST_BUFFER_DURATION (outbuffer) == (GST_SECOND / 10));
804 
805   /* and clean up */
806   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
807   g_list_free (buffers);
808   buffers = NULL;
809   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
810 
811   /* cleanup */
812   cleanup_textoverlay (textoverlay);
813   gst_buffer_unref (inbuffer);
814 }
815 
816 GST_END_TEST;
817 
818 static gpointer
test_video_waits_for_text_send_text_newsegment_thread(gpointer data)819 test_video_waits_for_text_send_text_newsegment_thread (gpointer data)
820 {
821   GstSegment segment;
822 
823   g_usleep (1 * G_USEC_PER_SEC);
824 
825   /* send an update newsegment; the video buffer should now be pushed through
826    * even though there is no text buffer queued at the moment */
827   GST_INFO ("Sending newsegment update on text pad");
828   gst_segment_init (&segment, GST_FORMAT_TIME);
829   segment.base = 35 * GST_SECOND;
830   segment.start = 35 * GST_SECOND;
831   segment.time = 35 * GST_SECOND;
832   gst_pad_push_event (mytextsrcpad, gst_event_new_segment (&segment));
833 
834   return NULL;
835 }
836 
837 static gpointer
test_video_waits_for_text_shutdown_element(gpointer data)838 test_video_waits_for_text_shutdown_element (gpointer data)
839 {
840   g_usleep (1 * G_USEC_PER_SEC);
841 
842   GST_INFO ("Trying to shut down textoverlay element ...");
843   /* set to NULL state to make sure we can shut it down while it's
844    * blocking in the video chain function waiting for a text buffer */
845   gst_element_set_state (GST_ELEMENT (data), GST_STATE_NULL);
846   GST_INFO ("Done.");
847 
848   return NULL;
849 }
850 
GST_START_TEST(test_video_waits_for_text)851 GST_START_TEST (test_video_waits_for_text)
852 {
853   GstElement *textoverlay;
854   GstBuffer *inbuffer, *outbuffer, *tbuf;
855   GstCaps *caps, *incaps, *outcaps;
856   GThread *thread;
857 
858   textoverlay = setup_textoverlay (FALSE);
859 
860   fail_unless (gst_element_set_state (textoverlay,
861           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
862       "could not set to playing");
863 
864   caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING, "utf8",
865       NULL);
866   gst_check_setup_events_textoverlay (mytextsrcpad, textoverlay, caps,
867       GST_FORMAT_TIME, "text");
868   gst_caps_unref (caps);
869 
870   tbuf = create_text_buffer ("XLX", 1 * GST_SECOND, 5 * GST_SECOND);
871   gst_buffer_ref (tbuf);
872   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 2);
873 
874   GST_LOG ("pushing text buffer");
875   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
876 
877   /* it should be stuck in textoverlay until it gets a text buffer or a
878    * newsegment event that indicates it's not needed any longer */
879   fail_unless_equals_int (g_list_length (buffers), 0);
880 
881   incaps = create_video_caps (VIDEO_CAPS_STRING);
882   gst_check_setup_events_textoverlay (myvideosrcpad, textoverlay, incaps,
883       GST_FORMAT_TIME, "video");
884   inbuffer = create_black_buffer (incaps);
885   gst_caps_unref (incaps);
886   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
887 
888   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
889   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 2;
890 
891   /* pushing gives away one of the two references we have ... */
892   GST_LOG ("pushing video buffer 1");
893   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
894 
895   /* video buffer should have gone through untainted, since the text is later */
896   fail_unless_equals_int (g_list_length (buffers), 1);
897 
898   /* text should still be stuck in textoverlay */
899   ASSERT_MINI_OBJECT_REFCOUNT (gst_buffer_peek_memory (tbuf, 0), "tbuf-mem", 2);
900 
901   /* there should be no text rendered */
902   outbuffer = GST_BUFFER_CAST (buffers->data);
903   ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
904   outcaps = gst_pad_get_current_caps (mysinkpad);
905   fail_unless (buffer_is_all_black (outbuffer, outcaps));
906   gst_caps_unref (outcaps);
907 
908   /* now, another video buffer */
909   inbuffer = create_black_buffer (incaps);
910   GST_BUFFER_TIMESTAMP (inbuffer) = GST_SECOND;
911   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 2;
912   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
913 
914   /* pushing gives away one of the two references we have ... */
915   GST_LOG ("pushing video buffer 2");
916   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
917 
918   /* video buffer should have gone right away, with text rendered on it */
919   fail_unless_equals_int (g_list_length (buffers), 2);
920 
921   /* text should still be stuck in textoverlay */
922   ASSERT_MINI_OBJECT_REFCOUNT (gst_buffer_peek_memory (tbuf, 0), "tbuf-mem", 2);
923 
924   /* there should be text rendered */
925   outbuffer = GST_BUFFER_CAST (buffers->next->data);
926   ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
927   outcaps = gst_pad_get_current_caps (mysinkpad);
928   fail_unless (buffer_is_all_black (outbuffer, outcaps) == FALSE);
929   gst_caps_unref (outcaps);
930 
931   /* a third video buffer */
932   inbuffer = create_black_buffer (incaps);
933   GST_BUFFER_TIMESTAMP (inbuffer) = 30 * GST_SECOND;
934   GST_BUFFER_DURATION (inbuffer) = GST_SECOND / 2;
935 
936   /* video buffer #3: should not go through, it should discard the current
937    * text buffer as too old and then wait for the next text buffer (or a
938    * newsegment event to arrive); we spawn a background thread to send such
939    * a newsegment event after a second or so so we get back control */
940   thread =
941       g_thread_try_new ("gst-check",
942       test_video_waits_for_text_send_text_newsegment_thread, NULL, NULL);
943   fail_unless (thread != NULL);
944   g_thread_unref (thread);
945 
946   GST_LOG ("pushing video buffer 3");
947   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_OK);
948 
949   /* but the text should no longer be stuck in textoverlay */
950   ASSERT_MINI_OBJECT_REFCOUNT (gst_buffer_peek_memory (tbuf, 0), "tbuf-mem", 1);
951 
952   /* video buffer should have gone through after newsegment event */
953   fail_unless_equals_int (g_list_length (buffers), 3);
954 
955   /* ... and there should not be any text rendered on it */
956   outbuffer = GST_BUFFER_CAST (buffers->next->next->data);
957   ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
958   outcaps = gst_pad_get_current_caps (mysinkpad);
959   fail_unless (buffer_is_all_black (outbuffer, outcaps));
960   gst_caps_unref (outcaps);
961 
962   /* a fourth video buffer */
963   inbuffer = create_black_buffer (incaps);
964   GST_BUFFER_TIMESTAMP (inbuffer) = 35 * GST_SECOND;
965   GST_BUFFER_DURATION (inbuffer) = GST_SECOND;
966 
967   /* video buffer #4: should not go through, it should wait for the next
968    * text buffer (or a newsegment event) to arrive; we spawn a background
969    * thread to shut down the element while it's waiting to make sure that
970    * works ok */
971   thread = g_thread_try_new ("gst-check",
972       test_video_waits_for_text_shutdown_element, textoverlay, NULL);
973   fail_unless (thread != NULL);
974   g_thread_unref (thread);
975 
976   GST_LOG ("pushing video buffer 4");
977   fail_unless (gst_pad_push (myvideosrcpad, inbuffer) == GST_FLOW_FLUSHING);
978 
979   /* and clean up */
980   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
981   g_list_free (buffers);
982   buffers = NULL;
983 
984   /* cleanup */
985   cleanup_textoverlay (textoverlay);
986 
987   /* give up our ref, textoverlay should've cleared its queued buffer by now */
988   ASSERT_BUFFER_REFCOUNT (tbuf, "tbuf", 1);
989   gst_buffer_unref (tbuf);
990 }
991 
992 GST_END_TEST;
993 
994 static gpointer
test_render_continuity_push_video_buffers_thread(gpointer data)995 test_render_continuity_push_video_buffers_thread (gpointer data)
996 {
997   /* push video buffers at 1fps */
998   guint frame_count = 0;
999   GstCaps *vcaps;
1000 
1001   vcaps = create_video_caps (VIDEO_CAPS_STRING);
1002   gst_check_setup_events_textoverlay (myvideosrcpad, data, vcaps,
1003       GST_FORMAT_TIME, "video");
1004 
1005   do {
1006     GstBuffer *vbuf;
1007 
1008     vbuf = create_black_buffer (vcaps);
1009     ASSERT_BUFFER_REFCOUNT (vbuf, "vbuf", 1);
1010 
1011     GST_BUFFER_TIMESTAMP (vbuf) = frame_count * GST_SECOND;
1012     GST_BUFFER_DURATION (vbuf) = GST_SECOND;
1013 
1014     /* pushing gives away one of the two references we have ... */
1015     GST_LOG ("pushing video buffer %u @ %" GST_TIME_FORMAT, frame_count,
1016         GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (vbuf)));
1017     fail_unless (gst_pad_push (myvideosrcpad, vbuf) == GST_FLOW_OK);
1018 
1019     ++frame_count;
1020   } while (frame_count < 15);
1021 
1022   gst_caps_unref (vcaps);
1023 
1024   return NULL;
1025 }
1026 
GST_START_TEST(test_render_continuity)1027 GST_START_TEST (test_render_continuity)
1028 {
1029   GThread *thread;
1030   GstElement *textoverlay;
1031   GstBuffer *tbuf;
1032   GstCaps *caps, *outcaps;
1033 
1034   textoverlay = setup_textoverlay (FALSE);
1035 
1036   fail_unless (gst_element_set_state (textoverlay,
1037           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
1038       "could not set to playing");
1039 
1040   thread = g_thread_try_new ("gst-check",
1041       test_render_continuity_push_video_buffers_thread, textoverlay, NULL);
1042   fail_unless (thread != NULL);
1043   g_thread_unref (thread);
1044 
1045   caps = gst_caps_new_simple ("text/x-raw", "format", G_TYPE_STRING, "utf8",
1046       NULL);
1047   gst_check_setup_events_textoverlay (mytextsrcpad, textoverlay, caps,
1048       GST_FORMAT_TIME, "text");
1049   gst_caps_unref (caps);
1050 
1051   tbuf = create_text_buffer ("XLX", 2 * GST_SECOND, GST_SECOND);
1052   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
1053       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
1054   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
1055 
1056   tbuf = create_text_buffer ("XLX", 3 * GST_SECOND, 2 * GST_SECOND);
1057   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
1058       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
1059   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
1060 
1061   tbuf = create_text_buffer ("XLX", 7 * GST_SECOND, GST_SECOND);
1062   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
1063       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
1064   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
1065 
1066   tbuf = create_text_buffer ("XLX", 8 * GST_SECOND, GST_SECOND);
1067   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
1068       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
1069   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
1070 
1071   tbuf = create_text_buffer ("XLX", 9 * GST_SECOND, GST_SECOND);
1072   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
1073       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
1074   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
1075 
1076   tbuf = create_text_buffer ("XLX", 10 * GST_SECOND, 30 * GST_SECOND);
1077   GST_LOG ("pushing text buffer @ %" GST_TIME_FORMAT,
1078       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (tbuf)));
1079   fail_unless (gst_pad_push (mytextsrcpad, tbuf) == GST_FLOW_OK);
1080 
1081   GST_LOG ("give the other thread some time to push through the remaining"
1082       "video buffers");
1083   g_usleep (G_USEC_PER_SEC);
1084   GST_LOG ("done");
1085 
1086   /* we should have 15 buffers each with one second length now */
1087   fail_unless_equals_int (g_list_length (buffers), 15);
1088 
1089   outcaps = gst_pad_get_current_caps (mysinkpad);
1090 
1091   /* buffers 0 + 1 should be black */
1092   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 0)),
1093           outcaps));
1094   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 1)),
1095           outcaps));
1096 
1097   /* buffers 2 - 4 should have text */
1098   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
1099                   2)), outcaps) == FALSE);
1100   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
1101                   3)), outcaps) == FALSE);
1102   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
1103                   4)), outcaps) == FALSE);
1104 
1105   /* buffers 5 + 6 should be black */
1106   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 5)),
1107           outcaps));
1108   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers, 6)),
1109           outcaps));
1110 
1111   /* buffers 7 - last should have text */
1112   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
1113                   7)), outcaps) == FALSE);
1114   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
1115                   8)), outcaps) == FALSE);
1116   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
1117                   9)), outcaps) == FALSE);
1118   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
1119                   10)), outcaps) == FALSE);
1120   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
1121                   11)), outcaps) == FALSE);
1122   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
1123                   12)), outcaps) == FALSE);
1124   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
1125                   13)), outcaps) == FALSE);
1126   fail_unless (buffer_is_all_black (GST_BUFFER (g_list_nth_data (buffers,
1127                   14)), outcaps) == FALSE);
1128   gst_caps_unref (outcaps);
1129 
1130   /* and clean up */
1131   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
1132   g_list_free (buffers);
1133   buffers = NULL;
1134 
1135   /* cleanup */
1136   cleanup_textoverlay (textoverlay);
1137 }
1138 
1139 GST_END_TEST;
1140 
1141 static Suite *
textoverlay_suite(void)1142 textoverlay_suite (void)
1143 {
1144   Suite *s = suite_create ("textoverlay");
1145   TCase *tc_chain = tcase_create ("general");
1146 
1147   suite_add_tcase (s, tc_chain);
1148 
1149   tcase_add_test (tc_chain, test_video_passthrough);
1150   tcase_add_test (tc_chain, test_video_passthrough_with_feature);
1151   tcase_add_test (tc_chain,
1152       test_video_passthrough_with_feature_and_unsupported_caps);
1153   tcase_add_test (tc_chain,
1154       test_video_render_with_any_features_and_no_allocation_meta);
1155   tcase_add_test (tc_chain, test_video_render_static_text);
1156   tcase_add_test (tc_chain, test_render_continuity);
1157   tcase_add_test (tc_chain, test_video_waits_for_text);
1158 
1159   return s;
1160 }
1161 
1162 GST_CHECK_MAIN (textoverlay);
1163