• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * GStreamer
3  *
4  * unit test for (audio) parser
5  *
6  * Copyright (C) 2008 Nokia Corporation. All rights reserved.
7  *
8  * Contact: Stefan Kost <stefan.kost@nokia.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25 
26 #include <gst/check/gstcheck.h>
27 #include "elements/parser.h"
28 
29 
30 /* context state variables */
31 const gchar *ctx_factory;
32 GstStaticPadTemplate *ctx_sink_template;
33 GstStaticPadTemplate *ctx_src_template;
34 GstCaps *ctx_input_caps;
35 GstCaps *ctx_output_caps;
36 guint ctx_discard = 0;
37 datablob ctx_headers[MAX_HEADERS] = { {NULL, 0}, };
38 
39 VerifyBuffer ctx_verify_buffer = NULL;
40 ElementSetup ctx_setup = NULL;
41 gboolean ctx_frame_generated = FALSE;
42 
43 gboolean ctx_no_metadata = FALSE;
44 
45 /* helper variables */
46 GList *current_buf = NULL;
47 
48 GstPad *srcpad, *sinkpad;
49 guint dataoffset = 0;
50 
51 /* takes a copy of the passed buffer data */
52 static GstBuffer *
buffer_new(const unsigned char * buffer_data,guint size)53 buffer_new (const unsigned char *buffer_data, guint size)
54 {
55   GstBuffer *buffer;
56 
57   buffer = gst_buffer_new_and_alloc (size);
58   if (buffer_data) {
59     gst_buffer_fill (buffer, 0, buffer_data, size);
60   } else {
61     guint i;
62     GstMapInfo map;
63 
64     /* Create a recognizable pattern (loop 0x00 -> 0xff) in the data block */
65     gst_buffer_map (buffer, &map, GST_MAP_WRITE);
66     for (i = 0; i < size; i++) {
67       map.data[i] = i % 0x100;
68     }
69     gst_buffer_unmap (buffer, &map);
70   }
71 
72   GST_BUFFER_OFFSET (buffer) = dataoffset;
73   dataoffset += size;
74   return buffer;
75 }
76 
77 /*
78  * Adds buffer sizes together.
79  */
80 static void
buffer_count_size(void * buffer,void * user_data)81 buffer_count_size (void *buffer, void *user_data)
82 {
83   guint *sum = (guint *) user_data;
84   *sum += gst_buffer_get_size (buffer);
85 }
86 
87 /*
88  * Verify that given buffer contains predefined ADTS frame.
89  */
90 static void
buffer_verify_data(void * buffer,void * user_data)91 buffer_verify_data (void *buffer, void *user_data)
92 {
93   buffer_verify_data_s *vdata;
94 
95   if (!user_data) {
96     return;
97   }
98 
99   vdata = (buffer_verify_data_s *) user_data;
100 
101   GST_DEBUG ("discard: %d", vdata->discard);
102   if (vdata->discard) {
103     if (ctx_verify_buffer)
104       ctx_verify_buffer (vdata, buffer);
105     vdata->buffer_counter++;
106     vdata->offset_counter += gst_buffer_get_size (buffer);
107     if (vdata->buffer_counter == vdata->discard) {
108       vdata->buffer_counter = 0;
109       vdata->discard = 0;
110     }
111     return;
112   }
113 
114   if (!ctx_verify_buffer || !ctx_verify_buffer (vdata, buffer)) {
115     fail_unless (gst_buffer_get_size (buffer) == vdata->data_to_verify_size);
116     fail_unless (gst_buffer_memcmp (buffer, 0, vdata->data_to_verify,
117             vdata->data_to_verify_size) == 0);
118   }
119 
120   if (vdata->buffers_before_offset_skip) {
121     /* This is for skipping the garbage in some test cases */
122     if (vdata->buffer_counter == vdata->buffers_before_offset_skip) {
123       vdata->offset_counter += vdata->offset_skip_amount;
124     }
125   }
126   if (!vdata->no_metadata) {
127     fail_unless (GST_BUFFER_DTS (buffer) == vdata->ts_counter);
128     fail_unless (GST_BUFFER_DURATION (buffer) != 0);
129     fail_unless (GST_BUFFER_OFFSET (buffer) == vdata->offset_counter);
130   }
131 
132   vdata->ts_counter += GST_BUFFER_DURATION (buffer);
133   vdata->offset_counter += gst_buffer_get_size (buffer);
134   vdata->buffer_counter++;
135 }
136 
137 static GstElement *
setup_element(const gchar * factory,ElementSetup setup,GstStaticPadTemplate * sink_template,GstCaps * sink_caps,GstStaticPadTemplate * src_template,GstCaps * src_caps)138 setup_element (const gchar * factory, ElementSetup setup,
139     GstStaticPadTemplate * sink_template,
140     GstCaps * sink_caps, GstStaticPadTemplate * src_template,
141     GstCaps * src_caps)
142 {
143   GstElement *element;
144   GstBus *bus;
145   gchar *caps_str = NULL;
146 
147   if (setup) {
148     element = setup (factory);
149   } else {
150     element = gst_check_setup_element (factory);
151   }
152   srcpad = gst_check_setup_src_pad (element, src_template);
153 
154   if (sink_caps) {
155     caps_str = gst_caps_to_string (sink_caps);
156     sink_template->static_caps.string = caps_str;
157   }
158 
159   sinkpad = gst_check_setup_sink_pad (element, sink_template);
160   gst_pad_set_active (srcpad, TRUE);
161   gst_check_setup_events (srcpad, element, src_caps, GST_FORMAT_BYTES);
162   gst_pad_set_active (sinkpad, TRUE);
163 
164   bus = gst_bus_new ();
165   gst_element_set_bus (element, bus);
166 
167   fail_unless (gst_element_set_state (element,
168           GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE,
169       "could not set to playing");
170 
171   buffers = NULL;
172   g_free (caps_str);
173   return element;
174 }
175 
176 static void
cleanup_element(GstElement * element)177 cleanup_element (GstElement * element)
178 {
179   GstBus *bus;
180 
181   /* Free parsed buffers */
182   gst_check_drop_buffers ();
183 
184   bus = GST_ELEMENT_BUS (element);
185   gst_bus_set_flushing (bus, TRUE);
186   gst_object_unref (bus);
187 
188   gst_pad_set_active (srcpad, FALSE);
189   gst_pad_set_active (sinkpad, FALSE);
190   gst_check_teardown_src_pad (element);
191   gst_check_teardown_sink_pad (element);
192   gst_check_teardown_element (element);
193 }
194 
195 /* inits a standard test */
196 void
gst_parser_test_init(GstParserTest * ptest,const guint8 * data,guint size,guint num)197 gst_parser_test_init (GstParserTest * ptest, const guint8 * data, guint size,
198     guint num)
199 {
200   /* need these */
201   fail_unless (ctx_factory != NULL);
202   fail_unless (ctx_src_template != NULL);
203   fail_unless (ctx_sink_template != NULL);
204 
205   /* basics */
206   memset (ptest, 0, sizeof (*ptest));
207   ptest->factory = ctx_factory;
208   ptest->factory_setup = ctx_setup;
209   ptest->sink_template = ctx_sink_template;
210   ptest->src_template = ctx_src_template;
211   ptest->framed = TRUE;
212   /* could be NULL if not relevant/needed */
213   ptest->src_caps = ctx_input_caps;
214   ptest->sink_caps = ctx_output_caps;
215   memcpy (ptest->headers, ctx_headers, sizeof (ptest->headers));
216   ptest->discard = ctx_discard;
217   /* some data that pleases caller */
218   ptest->series[0].data = data;
219   ptest->series[0].size = size;
220   ptest->series[0].num = num;
221   ptest->series[0].fpb = 1;
222   ptest->series[1].fpb = 1;
223   ptest->series[2].fpb = 1;
224   ptest->no_metadata = ctx_no_metadata;
225 }
226 
227 /*
228  * Test if the parser pushes clean data properly.
229  */
230 void
gst_parser_test_run(GstParserTest * test,GstCaps ** out_caps)231 gst_parser_test_run (GstParserTest * test, GstCaps ** out_caps)
232 {
233   buffer_verify_data_s vdata = { 0, 0, 0, NULL, 0, NULL, FALSE, 0, 0, 0 };
234   GstElement *element;
235   GstBuffer *buffer = NULL;
236   GstCaps *src_caps;
237   guint i, j, k;
238   guint frames = 0, size = 0;
239   guint added_frame = 0;
240 
241   element = setup_element (test->factory, test->factory_setup,
242       test->sink_template, NULL, test->src_template, test->src_caps);
243 
244   /* push some setup headers */
245   for (j = 0; j < G_N_ELEMENTS (test->headers) && test->headers[j].data; j++) {
246     buffer = buffer_new (test->headers[j].data, test->headers[j].size);
247     fail_unless_equals_int (gst_pad_push (srcpad, buffer), GST_FLOW_OK);
248   }
249 
250   if (ctx_frame_generated)
251     added_frame = test->series[0].fpb;
252 
253   for (j = 0; j < 3; j++) {
254     for (i = 0; i < test->series[j].num; i++) {
255       /* sanity enforcing */
256       for (k = 0; k < MAX (1, test->series[j].fpb); k++) {
257         if (!k)
258           buffer = buffer_new (test->series[j].data, test->series[j].size);
259         else {
260           buffer = gst_buffer_append (buffer,
261               buffer_new (test->series[j].data, test->series[j].size));
262         }
263       }
264       fail_unless_equals_int (gst_pad_push (srcpad, buffer), GST_FLOW_OK);
265       if (j == 0)
266         vdata.buffers_before_offset_skip++;
267       else if (j == 1)
268         vdata.offset_skip_amount += test->series[j].size * test->series[j].fpb;
269       if (j != 1) {
270         frames += test->series[j].fpb + added_frame;
271         size += test->series[j].size * test->series[j].fpb;
272       }
273     }
274   }
275   gst_pad_push_event (srcpad, gst_event_new_eos ());
276 
277   if (G_LIKELY (test->framed))
278     fail_unless_equals_int (g_list_length (buffers) - test->discard, frames);
279 
280   /* if all frames are identical, do extended test,
281    * otherwise only verify total data size */
282   if (test->series[0].data && (!test->series[2].size ||
283           (test->series[0].size == test->series[2].size && test->series[2].data
284               && !memcmp (test->series[0].data, test->series[2].data,
285                   test->series[0].size)))) {
286     vdata.data_to_verify = test->series[0].data;
287     vdata.data_to_verify_size = test->series[0].size;
288     vdata.caps = test->sink_caps;
289     vdata.discard = test->discard;
290     vdata.no_metadata = test->no_metadata;
291     g_list_foreach (buffers, buffer_verify_data, &vdata);
292   } else {
293     guint datasum = 0;
294 
295     g_list_foreach (buffers, buffer_count_size, &datasum);
296     size -= test->dropped;
297     fail_unless_equals_int (datasum, size);
298   }
299 
300   src_caps = gst_pad_get_current_caps (sinkpad);
301   GST_LOG ("output caps: %" GST_PTR_FORMAT, src_caps);
302 
303   if (test->sink_caps) {
304     GST_LOG ("%" GST_PTR_FORMAT " = %" GST_PTR_FORMAT " ?", src_caps,
305         test->sink_caps);
306     fail_unless (gst_caps_is_equal (src_caps, test->sink_caps));
307   }
308 
309   if (out_caps)
310     *out_caps = src_caps;
311   else
312     gst_caps_unref (src_caps);
313 
314   cleanup_element (element);
315 }
316 
317 /*
318  * Test if the parser pushes clean data properly.
319  */
320 void
gst_parser_test_normal(const guint8 * data,guint size)321 gst_parser_test_normal (const guint8 * data, guint size)
322 {
323   GstParserTest ptest;
324 
325   gst_parser_test_init (&ptest, data, size, 10);
326   gst_parser_test_run (&ptest, NULL);
327 }
328 
329 /*
330  * Test if parser drains its buffers properly. Even one single frame
331  * should be drained and pushed forward when EOS occurs. This single frame
332  * case is special, since normally the parser needs more data to be sure
333  * about stream format. But it should still push the frame forward in EOS.
334  */
335 void
gst_parser_test_drain_single(const guint8 * data,guint size)336 gst_parser_test_drain_single (const guint8 * data, guint size)
337 {
338   GstParserTest ptest;
339 
340   gst_parser_test_init (&ptest, data, size, 1);
341   gst_parser_test_run (&ptest, NULL);
342 }
343 
344 /*
345  * Make sure that parser does not drain garbage when EOS occurs.
346  */
347 void
gst_parser_test_drain_garbage(const guint8 * data,guint size,guint8 * garbage,guint gsize)348 gst_parser_test_drain_garbage (const guint8 * data, guint size,
349     guint8 * garbage, guint gsize)
350 {
351   GstParserTest ptest;
352 
353   gst_parser_test_init (&ptest, data, size, 1);
354   ptest.series[1].data = garbage;
355   ptest.series[1].size = gsize;
356   ptest.series[1].num = 1;
357   gst_parser_test_run (&ptest, NULL);
358 }
359 
360 /*
361  * Test if parser splits a buffer that contains two frames into two
362  * separate buffers properly.
363  */
364 void
gst_parser_test_split(const guint8 * data,guint size)365 gst_parser_test_split (const guint8 * data, guint size)
366 {
367   GstParserTest ptest;
368 
369   gst_parser_test_init (&ptest, data, size, 10);
370   ptest.series[0].fpb = 2;
371   gst_parser_test_run (&ptest, NULL);
372 }
373 
374 /*
375  * Test if the parser skips garbage between frames properly.
376  */
377 void
gst_parser_test_skip_garbage(const guint8 * data,guint size,guint8 * garbage,guint gsize)378 gst_parser_test_skip_garbage (const guint8 * data, guint size, guint8 * garbage,
379     guint gsize)
380 {
381   GstParserTest ptest;
382 
383   gst_parser_test_init (&ptest, data, size, 10);
384   ptest.series[1].data = garbage;
385   ptest.series[1].size = gsize;
386   ptest.series[1].num = 1;
387   ptest.series[2].data = data;
388   ptest.series[2].size = size;
389   ptest.series[2].num = 10;
390   gst_parser_test_run (&ptest, NULL);
391 }
392 
393 /*
394  * Test if the src caps are set according to stream format.
395  */
396 void
gst_parser_test_output_caps(const guint8 * data,guint size,const gchar * input_caps,const gchar * output_caps)397 gst_parser_test_output_caps (const guint8 * data, guint size,
398     const gchar * input_caps, const gchar * output_caps)
399 {
400   GstParserTest ptest;
401 
402   gst_parser_test_init (&ptest, data, size, 10);
403   if (input_caps) {
404     ptest.src_caps = gst_caps_from_string (input_caps);
405     fail_unless (ptest.src_caps != NULL);
406   }
407   if (output_caps) {
408     ptest.sink_caps = gst_caps_from_string (output_caps);
409     fail_unless (ptest.sink_caps != NULL);
410   }
411   gst_parser_test_run (&ptest, NULL);
412   if (ptest.sink_caps)
413     gst_caps_unref (ptest.sink_caps);
414   if (ptest.src_caps)
415     gst_caps_unref (ptest.src_caps);
416 }
417 
418 /*
419  * Test if the src caps are set according to stream format.
420  */
421 GstCaps *
gst_parser_test_get_output_caps(const guint8 * data,guint size,const gchar * input_caps)422 gst_parser_test_get_output_caps (const guint8 * data, guint size,
423     const gchar * input_caps)
424 {
425   GstParserTest ptest;
426   GstCaps *out_caps;
427 
428   gst_parser_test_init (&ptest, data, size, 10);
429   if (input_caps) {
430     ptest.src_caps = gst_caps_from_string (input_caps);
431     fail_unless (ptest.src_caps != NULL);
432   }
433   gst_parser_test_run (&ptest, &out_caps);
434   if (ptest.src_caps)
435     gst_caps_unref (ptest.src_caps);
436 
437   return out_caps;
438 }
439