1 /* GStreamer
2  *
3  * unit test for rawvideoparse
4  *
5  * Copyright (C) <2016> Carlos Rafael Giani <dv at pseudoterminal dot org>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <gst/check/gstcheck.h>
27 #include <gst/video/video.h>
28 
29 /* The checks use as test data an 8x8 Y444 image, with 25 Hz framerate. In the
30  * sink caps configuration, the stride is 8 bytes, and the frames are tightly
31  * packed together. In the properties configuration, the stride is 10 bytes, the
32  * planes aren't tightly packed (there are 20 bytes between the planes), and the
33  * frames overall have padding between them (the overall frame size is
34  * stride (10) * height (8) * num-planes (3) + bytes-between-planes (20) * 2
35  * = 280 bytes, and the frame stride is 500 bytes, so there are 220 bytes of
36  * extra padding between frames).
37  *
38  * In the test 8x8 frame, the pixels are all set to #000000, except for two
39  * pixels: (xofs+1 yofs+0) is set to #8899AA, (xofs+0 yofs+1) is set to #112233.
40  * The first frame uses the offsets xofs=0 yofs=0. The second frame uses
41  * xofs=1 yofs=0 etc. For each configuration, there is a separate set of frames,
42  * each stored in the GstAdapter in the Context struct.
43  *
44  * During the tests, as part of the checks, the pixels are verified to have the
45  * right values. The pattern of the pixels was chosen to easily detect stride
46  * errors, incorrect plane offsets etc.
47  */
48 
49 #define TEST_WIDTH 8
50 #define TEST_HEIGHT 8
51 #define TEST_FRAMERATE_N 25
52 #define TEST_FRAMERATE_D 1
53 #define TEST_FRAME_FORMAT GST_VIDEO_FORMAT_Y444
54 #define NUM_TEST_PLANES 3
55 
56 #define PROP_CTX_PLANE_STRIDE 10
57 #define PROP_CTX_FRAME_SIZE 500
58 #define PROP_CTX_PLANE_PADDING 20
59 #define PROP_CTX_PLANE_SIZE (PROP_CTX_PLANE_STRIDE * TEST_HEIGHT + PROP_CTX_PLANE_PADDING)
60 
61 GstElement *rawvideoparse;
62 
63 /* For ease of programming we use globals to keep refs for our floating
64  * src and sink pads we create; otherwise we always have to do get_pad,
65  * get_peer, and then remove references in every test function */
66 static GstPad *mysrcpad, *mysinkpad;
67 
68 typedef struct
69 {
70   GstAdapter *data;
71   guint plane_stride;
72   guint plane_size;
73 }
74 Context;
75 
76 static Context properties_ctx, sinkcaps_ctx;
77 
78 static void
set_pixel(Context const * ctx,guint8 * pixels,guint x,guint y,guint32 color)79 set_pixel (Context const *ctx, guint8 * pixels, guint x, guint y, guint32 color)
80 {
81   guint i;
82   guint ofs = y * ctx->plane_stride + x;
83   for (i = 0; i < NUM_TEST_PLANES; ++i)
84     pixels[ctx->plane_size * i + ofs] =
85         (color >> ((NUM_TEST_PLANES - 1 - i) * 8)) & 0xFF;
86 }
87 
88 static guint32
get_pixel(Context const * ctx,const guint8 * pixels,guint x,guint y)89 get_pixel (Context const *ctx, const guint8 * pixels, guint x, guint y)
90 {
91   guint i;
92   guint ofs = y * ctx->plane_stride + x;
93   guint32 color = 0;
94   for (i = 0; i < NUM_TEST_PLANES; ++i)
95     color |=
96         ((guint32) (pixels[ctx->plane_size * i + ofs])) << ((NUM_TEST_PLANES -
97             1 - i) * 8);
98   return color;
99 }
100 
101 static void
fill_test_pattern(Context const * ctx,GstBuffer * buffer,guint xofs,guint yofs)102 fill_test_pattern (Context const *ctx, GstBuffer * buffer, guint xofs,
103     guint yofs)
104 {
105   guint8 *pixels;
106   GstMapInfo map_info;
107 
108   gst_buffer_map (buffer, &map_info, GST_MAP_WRITE);
109   pixels = map_info.data;
110 
111   memset (pixels, 0, ctx->plane_size * NUM_TEST_PLANES);
112   set_pixel (ctx, pixels, 1 + xofs, 0 + yofs, 0x8899AA);
113   set_pixel (ctx, pixels, 0 + xofs, 1 + yofs, 0x112233);
114 
115   gst_buffer_unmap (buffer, &map_info);
116 }
117 
118 static void
check_test_pattern(Context const * ctx,GstBuffer * buffer,guint xofs,guint yofs)119 check_test_pattern (Context const *ctx, GstBuffer * buffer, guint xofs,
120     guint yofs)
121 {
122   guint x, y;
123   guint8 *pixels;
124   GstMapInfo map_info;
125 
126   gst_buffer_map (buffer, &map_info, GST_MAP_READ);
127   pixels = map_info.data;
128 
129   fail_unless_equals_uint64_hex (get_pixel (ctx, pixels, 1 + xofs, 0 + yofs),
130       0x8899AA);
131   fail_unless_equals_uint64_hex (get_pixel (ctx, pixels, 0 + xofs, 1 + yofs),
132       0x112233);
133 
134   for (y = 0; y < TEST_HEIGHT; ++y) {
135     for (x = 0; x < TEST_WIDTH; ++x) {
136       if ((x == (1 + xofs) && y == (0 + yofs)) || (x == (0 + xofs)
137               && y == (1 + yofs)))
138         continue;
139 
140       fail_unless_equals_uint64_hex (get_pixel (ctx, pixels, x, y), 0x000000);
141     }
142   }
143 
144   gst_buffer_unmap (buffer, &map_info);
145 }
146 
147 
148 static void
setup_rawvideoparse(gboolean use_sink_caps,gboolean set_properties,GstCaps * incaps,GstFormat format)149 setup_rawvideoparse (gboolean use_sink_caps,
150     gboolean set_properties, GstCaps * incaps, GstFormat format)
151 {
152   guint i;
153 
154 
155   /* Setup the rawvideoparse element and the pads */
156 
157   static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
158       GST_PAD_SINK,
159       GST_PAD_ALWAYS,
160       GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL))
161       );
162   static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
163       GST_PAD_SRC,
164       GST_PAD_ALWAYS,
165       GST_STATIC_CAPS_ANY);
166 
167   rawvideoparse = gst_check_setup_element ("rawvideoparse");
168 
169   properties_ctx.plane_stride = PROP_CTX_PLANE_STRIDE;
170   properties_ctx.plane_size = PROP_CTX_PLANE_SIZE;
171   properties_ctx.data = gst_adapter_new ();
172 
173   sinkcaps_ctx.plane_stride = TEST_WIDTH;
174   sinkcaps_ctx.plane_size = TEST_WIDTH * TEST_HEIGHT;
175   sinkcaps_ctx.data = gst_adapter_new ();
176 
177   g_object_set (G_OBJECT (rawvideoparse), "use-sink-caps", use_sink_caps, NULL);
178   if (set_properties) {
179     GValue plane_offsets = G_VALUE_INIT;
180     GValue plane_strides = G_VALUE_INIT;
181     GValue val = G_VALUE_INIT;
182 
183     g_value_init (&val, G_TYPE_INT);
184     g_value_init (&plane_offsets, GST_TYPE_ARRAY);
185     g_value_init (&plane_strides, GST_TYPE_ARRAY);
186 
187     for (i = 0; i < NUM_TEST_PLANES; ++i) {
188       g_value_set_int (&val, properties_ctx.plane_size * i);
189       gst_value_array_append_value (&plane_offsets, &val);
190     }
191 
192     for (i = 0; i < NUM_TEST_PLANES; ++i) {
193       g_value_set_int (&val, properties_ctx.plane_stride);
194       gst_value_array_append_value (&plane_strides, &val);
195     }
196 
197     g_value_unset (&val);
198 
199     g_object_set (G_OBJECT (rawvideoparse), "width", TEST_WIDTH, "height",
200         TEST_HEIGHT, "frame-size", PROP_CTX_FRAME_SIZE, "framerate",
201         TEST_FRAMERATE_N, TEST_FRAMERATE_D, "format", TEST_FRAME_FORMAT, NULL);
202     g_object_set_property (G_OBJECT (rawvideoparse), "plane-offsets",
203         &plane_offsets);
204     g_object_set_property (G_OBJECT (rawvideoparse), "plane-strides",
205         &plane_strides);
206 
207     g_value_unset (&plane_offsets);
208     g_value_unset (&plane_strides);
209   }
210 
211   /* Check that the plane stride/offset values are correct */
212   {
213     GValue plane_offsets_array = G_VALUE_INIT;
214     GValue plane_strides_array = G_VALUE_INIT;
215 
216     /* By default, 320x240 i420 is used as format */
217     guint plane_offsets[3] = { 0, 76800, 96000 };
218     guint plane_strides[3] = { 320, 160, 160 };
219 
220     g_value_init (&plane_offsets_array, GST_TYPE_ARRAY);
221     g_value_init (&plane_strides_array, GST_TYPE_ARRAY);
222 
223     if (set_properties) {
224       /* When properties are explicitely set, we use Y444 as video format,
225        * so in that case, plane stride values are all the same */
226       plane_offsets[0] = properties_ctx.plane_size * 0;
227       plane_offsets[1] = properties_ctx.plane_size * 1;
228       plane_offsets[2] = properties_ctx.plane_size * 2;
229       plane_strides[0] = plane_strides[1] = plane_strides[2] =
230           properties_ctx.plane_stride;
231     }
232 
233     g_object_get_property (G_OBJECT (rawvideoparse), "plane-offsets",
234         &plane_offsets_array);
235     g_object_get_property (G_OBJECT (rawvideoparse), "plane-strides",
236         &plane_strides_array);
237 
238     fail_unless (gst_value_array_get_size (&plane_offsets_array) ==
239         gst_value_array_get_size (&plane_strides_array));
240 
241     for (i = 0; i < gst_value_array_get_size (&plane_offsets_array); ++i) {
242       const GValue *gvalue;
243 
244       gvalue = gst_value_array_get_value (&plane_offsets_array, i);
245       fail_unless (gvalue != NULL);
246       fail_unless_equals_uint64 (plane_offsets[i], g_value_get_int (gvalue));
247 
248       gvalue = gst_value_array_get_value (&plane_strides_array, i);
249       fail_unless (gvalue != NULL);
250       fail_unless_equals_uint64 (plane_strides[i], g_value_get_int (gvalue));
251     }
252 
253     g_value_unset (&plane_offsets_array);
254     g_value_unset (&plane_strides_array);
255   }
256 
257   fail_unless (gst_element_set_state (rawvideoparse,
258           GST_STATE_PAUSED) == GST_STATE_CHANGE_SUCCESS,
259       "could not set to paused");
260 
261   mysrcpad = gst_check_setup_src_pad (rawvideoparse, &srctemplate);
262   mysinkpad = gst_check_setup_sink_pad (rawvideoparse, &sinktemplate);
263 
264   gst_pad_set_active (mysrcpad, TRUE);
265   gst_pad_set_active (mysinkpad, TRUE);
266 
267   gst_check_setup_events (mysrcpad, rawvideoparse, incaps, format);
268   if (incaps)
269     gst_caps_unref (incaps);
270 
271 
272   /* Fill the adapters with test frames */
273 
274   for (i = 0; i < 10; ++i) {
275     GstBuffer *buffer =
276         gst_buffer_new_allocate (NULL, PROP_CTX_FRAME_SIZE, NULL);
277     gst_buffer_memset (buffer, 0, 0xCC, gst_buffer_get_size (buffer));
278     fill_test_pattern (&properties_ctx, buffer, i, 0);
279     gst_adapter_push (properties_ctx.data, buffer);
280   }
281 
282   for (i = 0; i < 10; ++i) {
283     GstBuffer *buffer =
284         gst_buffer_new_allocate (NULL, sinkcaps_ctx.plane_size * 3, NULL);
285     gst_buffer_memset (buffer, 0, 0xCC, gst_buffer_get_size (buffer));
286     fill_test_pattern (&sinkcaps_ctx, buffer, i, 0);
287     gst_adapter_push (sinkcaps_ctx.data, buffer);
288   }
289 }
290 
291 static void
cleanup_rawvideoparse(void)292 cleanup_rawvideoparse (void)
293 {
294   int num_buffers, i;
295 
296   gst_pad_set_active (mysrcpad, FALSE);
297   gst_pad_set_active (mysinkpad, FALSE);
298   gst_check_teardown_src_pad (rawvideoparse);
299   gst_check_teardown_sink_pad (rawvideoparse);
300   gst_check_teardown_element (rawvideoparse);
301 
302   g_object_unref (G_OBJECT (properties_ctx.data));
303   g_object_unref (G_OBJECT (sinkcaps_ctx.data));
304 
305   if (buffers != NULL) {
306     num_buffers = g_list_length (buffers);
307     for (i = 0; i < num_buffers; ++i) {
308       GstBuffer *buf = GST_BUFFER (buffers->data);
309       buffers = g_list_remove (buffers, buf);
310       gst_buffer_unref (buf);
311     }
312 
313     g_list_free (buffers);
314     buffers = NULL;
315   }
316 }
317 
318 static void
push_data_and_check_output(Context * ctx,gsize num_in_bytes,gsize expected_num_out_bytes,gint64 expected_pts,gint64 expected_dur,guint expected_num_buffers_in_list,guint buf_idx,guint xofs,guint yofs)319 push_data_and_check_output (Context * ctx, gsize num_in_bytes,
320     gsize expected_num_out_bytes, gint64 expected_pts, gint64 expected_dur,
321     guint expected_num_buffers_in_list, guint buf_idx, guint xofs, guint yofs)
322 {
323   GstBuffer *inbuf, *outbuf;
324   guint num_buffers;
325 
326   /* Simulate upstream input by taking num_in_bytes bytes from the adapter */
327   inbuf = gst_adapter_take_buffer (ctx->data, num_in_bytes);
328   fail_unless (inbuf != NULL);
329 
330   /* Push the input data and check that the output buffers list grew as
331    * expected */
332   fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK);
333   num_buffers = g_list_length (buffers);
334   fail_unless_equals_int (num_buffers, expected_num_buffers_in_list);
335 
336   /* Take the output buffer */
337   outbuf = g_list_nth_data (buffers, buf_idx);
338   fail_unless (outbuf != NULL);
339 
340   /* Verify size, PTS, duration of the output buffer */
341   fail_unless_equals_uint64 (expected_num_out_bytes,
342       gst_buffer_get_size (outbuf));
343   fail_unless_equals_uint64 (expected_pts, GST_BUFFER_PTS (outbuf));
344   fail_unless_equals_uint64 (expected_dur, GST_BUFFER_DURATION (outbuf));
345 
346   /* Check that the pixels have the correct values */
347   check_test_pattern (ctx, outbuf, xofs, yofs);
348 }
349 
350 
GST_START_TEST(test_push_unaligned_data_properties_config)351 GST_START_TEST (test_push_unaligned_data_properties_config)
352 {
353   setup_rawvideoparse (FALSE, TRUE, NULL, GST_FORMAT_BYTES);
354 
355   /* Send in data buffers that are not aligned to multiples of the
356    * frame size (= sample size * num_channels). This tests if rawvideoparse
357    * aligns output data properly.
358    *
359    * The second line sends a buffer with multiple frames inside.
360    * rawvideoparse will then parse this buffer repeatedly (and prepend
361    * leftover data from the earlier parse iteration), explaining why
362    * all of a sudden there are 4 output buffers, compared to just one
363    * earlier. The output data is expected to be 280 bytes large, since this
364    * is the size of the actual frame, without extra padding at the end.
365    */
366   push_data_and_check_output (&properties_ctx, 511, 280, GST_MSECOND * 0,
367       GST_MSECOND * 40, 1, 0, 0, 0);
368   push_data_and_check_output (&properties_ctx, 1940, 280, GST_MSECOND * 40,
369       GST_MSECOND * 40, 4, 1, 1, 0);
370   push_data_and_check_output (&properties_ctx, 10, 280, GST_MSECOND * 80,
371       GST_MSECOND * 40, 4, 2, 2, 0);
372 
373   cleanup_rawvideoparse ();
374 }
375 
376 GST_END_TEST;
377 
GST_START_TEST(test_push_unaligned_data_sink_caps_config)378 GST_START_TEST (test_push_unaligned_data_sink_caps_config)
379 {
380   GstVideoInfo vinfo;
381   GstCaps *caps;
382 
383   /* This test is essentially the same as test_push_unaligned_data_properties_config,
384    * except that rawvideoparse uses the sink caps config instead of the property config.
385    * Also, the input sizes are different, since the sink caps config does not use extra
386    * padding between planes and does use a stride that directly corresponds to the width,
387    * resulting in smaller frame size (192 bytes vs 280 bytes). */
388 
389   gst_video_info_set_format (&vinfo, TEST_FRAME_FORMAT, TEST_WIDTH,
390       TEST_HEIGHT);
391   GST_VIDEO_INFO_FPS_N (&vinfo) = 25;
392   GST_VIDEO_INFO_FPS_D (&vinfo) = 1;
393   caps = gst_video_info_to_caps (&vinfo);
394 
395   setup_rawvideoparse (TRUE, FALSE, caps, GST_FORMAT_BYTES);
396 
397   push_data_and_check_output (&sinkcaps_ctx, 250, 192, GST_MSECOND * 0,
398       GST_MSECOND * 40, 1, 0, 0, 0);
399   push_data_and_check_output (&sinkcaps_ctx, 811, 192, GST_MSECOND * 40,
400       GST_MSECOND * 40, 5, 1, 1, 0);
401   push_data_and_check_output (&sinkcaps_ctx, 10, 192, GST_MSECOND * 80,
402       GST_MSECOND * 40, 5, 2, 2, 0);
403 
404   cleanup_rawvideoparse ();
405 }
406 
407 GST_END_TEST;
408 
GST_START_TEST(test_config_switch)409 GST_START_TEST (test_config_switch)
410 {
411   GstVideoInfo vinfo;
412   GstCaps *caps;
413 
414   /* Start processing with the properties config active, then mid-stream switch to
415    * the sink caps config. Since the sink caps config does not use padding, its
416    * frame size is smaller. The buffer duration stays the same (since it only depends
417    * on the framerate), but the expected output buffer size is different). */
418 
419   gst_video_info_set_format (&vinfo, TEST_FRAME_FORMAT, TEST_WIDTH,
420       TEST_HEIGHT);
421   GST_VIDEO_INFO_FPS_N (&vinfo) = 25;
422   GST_VIDEO_INFO_FPS_D (&vinfo) = 1;
423   caps = gst_video_info_to_caps (&vinfo);
424 
425   setup_rawvideoparse (FALSE, TRUE, caps, GST_FORMAT_BYTES);
426 
427   /* Push in data with properties config active */
428   push_data_and_check_output (&properties_ctx, 500, 280, GST_MSECOND * 0,
429       GST_MSECOND * 40, 1, 0, 0, 0);
430   push_data_and_check_output (&properties_ctx, 500, 280, GST_MSECOND * 40,
431       GST_MSECOND * 40, 2, 1, 1, 0);
432 
433   /* Perform the switch */
434   g_object_set (G_OBJECT (rawvideoparse), "use-sink-caps", TRUE, NULL);
435 
436   /* Push in data with sink caps config active, expecting a different frame size */
437   push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 80,
438       GST_MSECOND * 40, 3, 2, 0, 0);
439 
440   cleanup_rawvideoparse ();
441 }
442 
443 GST_END_TEST;
444 
GST_START_TEST(test_push_with_no_framerate)445 GST_START_TEST (test_push_with_no_framerate)
446 {
447   /* Test the special case when no framerate is set. The parser is expected to
448    * still work then, but without setting duration or PTS/DTS (it cannot do that,
449    * because these require a nonzero framerate). The output buffers have PTS 0,
450    * all subsequent ones have no set PTS. */
451 
452   setup_rawvideoparse (FALSE, TRUE, NULL, GST_FORMAT_BYTES);
453   g_object_set (G_OBJECT (rawvideoparse), "framerate", 0, 1, NULL);
454 
455   push_data_and_check_output (&properties_ctx, 500, 280, 0, GST_CLOCK_TIME_NONE,
456       1, 0, 0, 0);
457   push_data_and_check_output (&properties_ctx, 500, 280, GST_CLOCK_TIME_NONE,
458       GST_CLOCK_TIME_NONE, 2, 1, 1, 0);
459 
460   cleanup_rawvideoparse ();
461 }
462 
463 GST_END_TEST;
464 
GST_START_TEST(test_computed_plane_strides)465 GST_START_TEST (test_computed_plane_strides)
466 {
467   /* Test how plane strides & offsets are (re)computed if custom offsets/strides
468    * are disabled, and how they are preserved if they are enabled. */
469 
470   GValue plane_offsets_array = G_VALUE_INIT;
471   GValue plane_strides_array = G_VALUE_INIT;
472   guint i;
473   guint const expected_comp_psize = TEST_WIDTH * TEST_HEIGHT;
474 
475   g_value_init (&plane_offsets_array, GST_TYPE_ARRAY);
476   g_value_init (&plane_strides_array, GST_TYPE_ARRAY);
477 
478   setup_rawvideoparse (FALSE, TRUE, NULL, GST_FORMAT_BYTES);
479 
480   /* The setup set a custom set of plane offsets and strides together with
481    * width=TEST_WIDTH and height=TEST_HEIGHT. Check that the offsets & strides
482    * are preserved even after setting new, different width & height values. */
483 
484   g_object_set (G_OBJECT (rawvideoparse), "width", TEST_WIDTH * 2,
485       "height", TEST_HEIGHT * 2, NULL);
486 
487   g_object_get_property (G_OBJECT (rawvideoparse), "plane-offsets",
488       &plane_offsets_array);
489   g_object_get_property (G_OBJECT (rawvideoparse), "plane-strides",
490       &plane_strides_array);
491 
492   for (i = 0; i < gst_value_array_get_size (&plane_offsets_array); ++i) {
493     const GValue *gvalue;
494 
495     /* See setup_rawvideoparse() for how the offsets & strides are defined
496      * there. Offsets are set to plane_size*plane_index, and strides are
497      * set to the properties_ctx.plane_stride value. */
498 
499     gvalue = gst_value_array_get_value (&plane_offsets_array, i);
500     fail_unless (gvalue != NULL);
501     fail_unless_equals_uint64 (properties_ctx.plane_size * i,
502         g_value_get_int (gvalue));
503 
504     gvalue = gst_value_array_get_value (&plane_strides_array, i);
505     fail_unless (gvalue != NULL);
506     fail_unless_equals_uint64 (properties_ctx.plane_stride,
507         g_value_get_int (gvalue));
508   }
509 
510   /* Discard the custom planes&offsets, re-enabling computed values. */
511   g_value_reset (&plane_offsets_array);
512   g_value_reset (&plane_strides_array);
513   g_object_set_property (G_OBJECT (rawvideoparse), "plane-offsets",
514       &plane_offsets_array);
515   g_object_set_property (G_OBJECT (rawvideoparse), "plane-strides",
516       &plane_strides_array);
517 
518 
519   /* The strides & offsets should have been recomputed by now. Since the Y444
520    * format is used, all strides are the same, and should equal the frame width
521    * (which was set to TEST_WIDTH*2 earlier). Plane offsets should be
522    * plane_size*plane_index, with plane_size set to (TEST_WIDTH*2 * TEST_HEIGHT*2),
523    * or TEST_WIDTH*TEST_HEIGHT*4 (-> expected_comp_psize*4). */
524 
525   g_object_get_property (G_OBJECT (rawvideoparse), "plane-offsets",
526       &plane_offsets_array);
527   g_object_get_property (G_OBJECT (rawvideoparse), "plane-strides",
528       &plane_strides_array);
529 
530   for (i = 0; i < gst_value_array_get_size (&plane_offsets_array); ++i) {
531     const GValue *gvalue;
532 
533     gvalue = gst_value_array_get_value (&plane_offsets_array, i);
534     fail_unless (gvalue != NULL);
535     fail_unless_equals_uint64 (expected_comp_psize * 4 * i,
536         g_value_get_int (gvalue));
537 
538     gvalue = gst_value_array_get_value (&plane_strides_array, i);
539     fail_unless (gvalue != NULL);
540     fail_unless_equals_uint64 (TEST_WIDTH * 2, g_value_get_int (gvalue));
541   }
542 
543   g_value_reset (&plane_offsets_array);
544   g_value_reset (&plane_strides_array);
545 
546 
547   /* Again change the width & height values. width=TEST_WIDTH, height=TEST_HEIGHT.
548    * However, this time, offsets&strides are computed; the current values should
549    * not be preserved. Expected plane stride and offset values are similar to
550    * above, expect that no multiplications by 2 are present (since the TEST_WIDTH
551    * and TEST_HEIGHT values were passed without multiplying them). */
552 
553   g_object_set (G_OBJECT (rawvideoparse), "width", TEST_WIDTH,
554       "height", TEST_HEIGHT, NULL);
555 
556 
557   g_object_get_property (G_OBJECT (rawvideoparse), "plane-offsets",
558       &plane_offsets_array);
559   g_object_get_property (G_OBJECT (rawvideoparse), "plane-strides",
560       &plane_strides_array);
561 
562   for (i = 0; i < gst_value_array_get_size (&plane_offsets_array); ++i) {
563     const GValue *gvalue;
564 
565     gvalue = gst_value_array_get_value (&plane_offsets_array, i);
566     fail_unless (gvalue != NULL);
567     fail_unless_equals_uint64 (expected_comp_psize * i,
568         g_value_get_int (gvalue));
569 
570     gvalue = gst_value_array_get_value (&plane_strides_array, i);
571     fail_unless (gvalue != NULL);
572     fail_unless_equals_uint64 (TEST_WIDTH, g_value_get_int (gvalue));
573   }
574 
575   g_value_unset (&plane_offsets_array);
576   g_value_unset (&plane_strides_array);
577 
578   cleanup_rawvideoparse ();
579 }
580 
581 GST_END_TEST;
582 
GST_START_TEST(test_change_caps)583 GST_START_TEST (test_change_caps)
584 {
585   GstVideoInfo vinfo;
586   GstCaps *caps;
587 
588   /* Start processing with the sink caps config active, using the
589    * default width/height/format and 25 Hz frame rate for the caps.
590    * Push some data, then change caps (25 Hz -> 50 Hz).
591    * Check that the changed caps are handled properly. */
592 
593   gst_video_info_set_format (&vinfo, TEST_FRAME_FORMAT, TEST_WIDTH,
594       TEST_HEIGHT);
595   GST_VIDEO_INFO_FPS_N (&vinfo) = 25;
596   GST_VIDEO_INFO_FPS_D (&vinfo) = 1;
597   caps = gst_video_info_to_caps (&vinfo);
598 
599   setup_rawvideoparse (TRUE, FALSE, caps, GST_FORMAT_BYTES);
600 
601   /* Push in data with sink config active, expecting duration calculations
602    * to be based on the 25 Hz frame rate */
603   push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 0,
604       GST_MSECOND * 40, 1, 0, 0, 0);
605   push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 40,
606       GST_MSECOND * 40, 2, 1, 1, 0);
607 
608   /* Change caps */
609   GST_VIDEO_INFO_FPS_N (&vinfo) = 50;
610   GST_VIDEO_INFO_FPS_D (&vinfo) = 1;
611   caps = gst_video_info_to_caps (&vinfo);
612   fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_caps (caps)));
613   gst_caps_unref (caps);
614 
615   /* Push in data with sink config active, expecting duration calculations
616    * to be based on the 50 Hz frame rate */
617   push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 80,
618       GST_MSECOND * 20, 3, 2, 2, 0);
619 
620   cleanup_rawvideoparse ();
621 }
622 
623 GST_END_TEST;
624 
GST_START_TEST(test_incomplete_last_buffer)625 GST_START_TEST (test_incomplete_last_buffer)
626 {
627   GstVideoInfo vinfo;
628   GstCaps *caps;
629 
630   /* Start processing with the sink caps config active, using the
631    * default width/height/format and 25 Hz frame rate for the caps.
632    * Push some data, then change caps (25 Hz -> 50 Hz).
633    * Check that the changed caps are handled properly. */
634 
635   gst_video_info_set_format (&vinfo, TEST_FRAME_FORMAT, TEST_WIDTH,
636       TEST_HEIGHT);
637   GST_VIDEO_INFO_FPS_N (&vinfo) = 25;
638   GST_VIDEO_INFO_FPS_D (&vinfo) = 1;
639   caps = gst_video_info_to_caps (&vinfo);
640 
641   setup_rawvideoparse (TRUE, FALSE, caps, GST_FORMAT_BYTES);
642 
643   push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 0,
644       GST_MSECOND * 40, 1, 0, 0, 0);
645   push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 40,
646       GST_MSECOND * 40, 2, 1, 1, 0);
647   push_data_and_check_output (&sinkcaps_ctx, 100, 192, GST_MSECOND * 40,
648       GST_MSECOND * 40, 2, 1, 1, 0);
649   gst_pad_push_event (mysrcpad, gst_event_new_eos ());
650   fail_unless_equals_int (g_list_length (buffers), 2);
651 
652   cleanup_rawvideoparse ();
653 }
654 
655 GST_END_TEST;
656 
657 static Suite *
rawvideoparse_suite(void)658 rawvideoparse_suite (void)
659 {
660   Suite *s = suite_create ("rawvideoparse");
661   TCase *tc_chain = tcase_create ("general");
662 
663   suite_add_tcase (s, tc_chain);
664   tcase_add_test (tc_chain, test_push_unaligned_data_properties_config);
665   tcase_add_test (tc_chain, test_push_unaligned_data_sink_caps_config);
666   tcase_add_test (tc_chain, test_config_switch);
667   tcase_add_test (tc_chain, test_push_with_no_framerate);
668   tcase_add_test (tc_chain, test_computed_plane_strides);
669   tcase_add_test (tc_chain, test_change_caps);
670   tcase_add_test (tc_chain, test_incomplete_last_buffer);
671 
672   return s;
673 }
674 
675 GST_CHECK_MAIN (rawvideoparse);
676