• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  *
3  * unit test for mpg123audiodec
4  *
5  * Copyright (c) 2012 Carlos Rafael Giani <dv@pseudoterminal.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 
23 #include <unistd.h>
24 
25 #include <gst/check/gstcheck.h>
26 #include <gst/audio/audio.h>
27 
28 #include <gst/fft/gstfft.h>
29 #include <gst/fft/gstffts16.h>
30 #include <gst/fft/gstffts32.h>
31 #include <gst/fft/gstfftf32.h>
32 #include <gst/fft/gstfftf64.h>
33 
34 #include <gst/app/gstappsink.h>
35 
36 /* For ease of programming we use globals to keep refs for our floating
37  * src and sink pads we create; otherwise we always have to do get_pad,
38  * get_peer, and then remove references in every test function */
39 static GstPad *mysrcpad, *mysinkpad;
40 
41 
42 #define MP2_STREAM_FILENAME "stream.mp2"
43 #define MP3_CBR_STREAM_FILENAME "cbr_stream.mp3"
44 #define MP3_VBR_STREAM_FILENAME "vbr_stream.mp3"
45 
46 
47 /* mpeg 1 layer 2 stream created with:
48  * gst-launch-1.0 -v audiotestsrc wave=sine freq=440 volume=1 num-buffers=32 ! \
49  *   "audio/x-raw, format=(string)S16LE, layout=(string)interleaved, rate=(int)44100, channels=(int)1" ! \
50  *   avenc_mp2 bitrate=32000 ! tee name=t \
51  *   t. ! queue ! fakesink silent=false \
52  *   t. ! queue ! filesink location=test.mp2
53  *
54  * mpeg 1 layer 3 CBR stream created with:
55  * gst-launch-1.0 -v audiotestsrc wave=sine freq=440 volume=1 num-buffers=32 ! \
56  *   "audio/x-raw, format=(string)S16LE, layout=(string)interleaved, rate=(int)44100, channels=(int)1" ! \
57  *   lamemp3enc encoding-engine-quality=high cbr=true target=bitrate bitrate=32 ! \
58  *   "audio/mpeg, rate=(int)44100, channels=(int)1" ! tee name=t \
59  *   t. ! queue ! fakesink silent=false \
60  *   t. ! queue ! filesink location=test.mp3
61  *
62  * mpeg 1 layer 3 VBR stream created with:
63  * gst-launch-1.0 -v audiotestsrc wave=sine freq=440 volume=1 num-buffers=32 ! \
64  *   "audio/x-raw, format=(string)S16LE, layout=(string)interleaved, rate=(int)44100, channels=(int)1" ! \
65  *   lamemp3enc encoding-engine-quality=high cbr=false target=quality quality=7 ! \
66  *   "audio/mpeg, rate=(int)44100, channels=(int)1" ! tee name=t \
67  *   t. ! queue ! fakesink silent=false \
68  *   t. ! queue ! filesink location=test.mp3
69  */
70 
71 
72 /* FFT test helpers taken from gst-plugins-base tests/check/audioresample.c */
73 
74 #define FFT_HELPERS(type,ffttag,ffttag2,scale)                                \
75 static gdouble magnitude##ffttag (const GstFFT##ffttag##Complex *c)           \
76 {                                                                             \
77   gdouble mag = (gdouble) c->r * (gdouble) c->r;                              \
78   mag += (gdouble) c->i * (gdouble) c->i;                                     \
79   mag /= scale * scale;                                                       \
80   mag = 10.0 * log10 (mag);                                                   \
81   return mag;                                                                 \
82 }                                                                             \
83 static gdouble find_main_frequency_spot_##ffttag (                            \
84     const GstFFT##ffttag##Complex *v, int elements)                           \
85 {                                                                             \
86   int i;                                                                      \
87   gdouble maxmag = -9999;                                                     \
88   int maxidx = 0;                                                             \
89   for (i=0; i<elements; ++i) {                                                \
90     gdouble mag = magnitude##ffttag (v+i);                                    \
91     if (mag > maxmag) {                                                       \
92       maxmag = mag;                                                           \
93       maxidx = i;                                                             \
94     }                                                                         \
95   }                                                                           \
96   return maxidx / (gdouble) elements;                                         \
97 }                                                                             \
98 static gboolean is_zero_except_##ffttag (const GstFFT##ffttag##Complex *v,    \
99     int elements, gdouble spot)                                               \
100 {                                                                             \
101   int i;                                                                      \
102   for (i=0; i<elements; ++i) {                                                \
103     gdouble pos = i / (gdouble) elements;                                     \
104     gdouble mag = magnitude##ffttag (v+i);                                    \
105     if (fabs (pos - spot) > 0.01) {                                           \
106       if (mag > -35.0) {                                                      \
107         GST_LOG("Found magnitude at %f : %f (peak at %f)\n", pos, mag, spot); \
108         return FALSE;                                                         \
109       }                                                                       \
110     }                                                                         \
111   }                                                                           \
112   return TRUE;                                                                \
113 }                                                                             \
114 static void check_main_frequency_spot_##ffttag (GstBuffer *buffer, gdouble    \
115     expected_spot)                                                            \
116 {                                                                             \
117   GstMapInfo map;                                                             \
118   int num_samples;                                                            \
119   gdouble actual_spot;                                                        \
120   GstFFT##ffttag *ctx;                                                        \
121   GstFFT##ffttag##Complex *fftdata;                                           \
122                                                                               \
123   gst_buffer_map (buffer, &map, GST_MAP_READ);                                \
124                                                                               \
125   num_samples = map.size / sizeof(type) & ~1;                                 \
126   ctx = gst_fft_##ffttag2##_new (num_samples, FALSE);                         \
127   fftdata = g_new (GstFFT##ffttag##Complex, num_samples / 2 + 1);             \
128                                                                               \
129   gst_fft_##ffttag2##_window (ctx, (type*)map.data,                           \
130     GST_FFT_WINDOW_HAMMING);                                                  \
131   gst_fft_##ffttag2##_fft (ctx, (type*)map.data, fftdata);                    \
132                                                                               \
133   actual_spot = find_main_frequency_spot_##ffttag (fftdata,                   \
134     num_samples / 2 + 1);                                                     \
135   GST_LOG ("Expected spot: %.3f actual: %.3f %f", expected_spot, actual_spot, \
136     fabs (expected_spot - actual_spot));                                      \
137   fail_unless (fabs (expected_spot - actual_spot) < 0.05,                     \
138     "Actual main frequency spot is too far away from expected one");          \
139   fail_unless (is_zero_except_##ffttag (fftdata, num_samples / 2 + 1,         \
140     actual_spot), "One secondary peak in spectrum exceeds threshold");        \
141                                                                               \
142   gst_buffer_unmap (buffer, &map);                                            \
143                                                                               \
144   gst_fft_##ffttag2##_free (ctx);                                             \
145   g_free (fftdata);                                                           \
146 }
147 FFT_HELPERS (gint32, S32, s32, 2147483647.0);
148 
149 
150 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
151     GST_PAD_SINK,
152     GST_PAD_ALWAYS,
153     GST_STATIC_CAPS ("audio/x-raw, format = (string) " GST_AUDIO_NE (S32))
154     );
155 static GstStaticPadTemplate layer2_srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
156     GST_PAD_SRC,
157     GST_PAD_ALWAYS,
158     GST_STATIC_CAPS_ANY);
159 static GstStaticPadTemplate layer3_srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
160     GST_PAD_SRC,
161     GST_PAD_ALWAYS,
162     GST_STATIC_CAPS_ANY);
163 
164 
165 static void
setup_input_pipeline(gchar const * stream_filename,GstElement ** pipeline,GstElement ** appsink)166 setup_input_pipeline (gchar const *stream_filename, GstElement ** pipeline,
167     GstElement ** appsink)
168 {
169   GstElement *source, *parser;
170 
171   *pipeline = gst_pipeline_new (NULL);
172   source = gst_element_factory_make ("filesrc", NULL);
173   parser = gst_element_factory_make ("mpegaudioparse", NULL);
174   *appsink = gst_element_factory_make ("appsink", NULL);
175 
176   gst_bin_add_many (GST_BIN (*pipeline), source, parser, *appsink, NULL);
177   gst_element_link_many (source, parser, *appsink, NULL);
178 
179   {
180     char *full_filename =
181         g_build_filename (GST_TEST_FILES_PATH, stream_filename, NULL);
182     g_object_set (G_OBJECT (source), "location", full_filename, NULL);
183     g_free (full_filename);
184   }
185 
186   gst_element_set_state (*pipeline, GST_STATE_PLAYING);
187 }
188 
189 static void
cleanup_input_pipeline(GstElement * pipeline)190 cleanup_input_pipeline (GstElement * pipeline)
191 {
192   gst_element_set_state (pipeline, GST_STATE_NULL);
193   gst_object_unref (pipeline);
194 }
195 
196 static GstElement *
setup_mpeg1layer2dec(void)197 setup_mpeg1layer2dec (void)
198 {
199   GstElement *mpg123audiodec;
200   GstCaps *caps;
201 
202   GST_DEBUG ("setup_mpeg1layer2dec");
203   mpg123audiodec = gst_check_setup_element ("mpg123audiodec");
204   mysrcpad = gst_check_setup_src_pad (mpg123audiodec, &layer2_srctemplate);
205   mysinkpad = gst_check_setup_sink_pad (mpg123audiodec, &sinktemplate);
206   gst_pad_set_active (mysrcpad, TRUE);
207   gst_pad_set_active (mysinkpad, TRUE);
208 
209   /* This is necessary to trigger a set_format call in the decoder;
210    * fixed caps don't trigger it */
211   caps = gst_caps_new_simple ("audio/mpeg",
212       "mpegversion", G_TYPE_INT, 1,
213       "layer", G_TYPE_INT, 2,
214       "rate", G_TYPE_INT, 44100,
215       "channels", G_TYPE_INT, 1, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
216   gst_check_setup_events (mysrcpad, mpg123audiodec, caps, GST_FORMAT_TIME);
217   gst_caps_unref (caps);
218 
219   return mpg123audiodec;
220 }
221 
222 static GstElement *
setup_mpeg1layer3dec(void)223 setup_mpeg1layer3dec (void)
224 {
225   GstElement *mpg123audiodec;
226   GstCaps *caps;
227 
228   GST_DEBUG ("setup_mpeg1layer3dec");
229   mpg123audiodec = gst_check_setup_element ("mpg123audiodec");
230   mysrcpad = gst_check_setup_src_pad (mpg123audiodec, &layer3_srctemplate);
231   mysinkpad = gst_check_setup_sink_pad (mpg123audiodec, &sinktemplate);
232   gst_pad_set_active (mysrcpad, TRUE);
233   gst_pad_set_active (mysinkpad, TRUE);
234 
235   /* This is necessary to trigger a set_format call in the decoder;
236    * fixed caps don't trigger it */
237   caps = gst_caps_new_simple ("audio/mpeg",
238       "mpegversion", G_TYPE_INT, 1,
239       "layer", G_TYPE_INT, 3,
240       "rate", G_TYPE_INT, 44100,
241       "channels", G_TYPE_INT, 1, "parsed", G_TYPE_BOOLEAN, TRUE, NULL);
242   gst_check_setup_events (mysrcpad, mpg123audiodec, caps, GST_FORMAT_TIME);
243   gst_caps_unref (caps);
244 
245   return mpg123audiodec;
246 }
247 
248 static void
cleanup_mpg123audiodec(GstElement * mpg123audiodec)249 cleanup_mpg123audiodec (GstElement * mpg123audiodec)
250 {
251   GST_DEBUG ("cleanup_mpeg1layer2dec");
252   gst_element_set_state (mpg123audiodec, GST_STATE_NULL);
253 
254   gst_pad_set_active (mysrcpad, FALSE);
255   gst_pad_set_active (mysinkpad, FALSE);
256   gst_check_teardown_src_pad (mpg123audiodec);
257   gst_check_teardown_sink_pad (mpg123audiodec);
258   gst_check_teardown_element (mpg123audiodec);
259 }
260 
261 static void
run_decoding_test(GstElement * mpg123audiodec,gchar const * filename)262 run_decoding_test (GstElement * mpg123audiodec, gchar const *filename)
263 {
264   GstBus *bus;
265   unsigned int num_input_buffers, num_decoded_buffers;
266   gint expected_size;
267   GstCaps *out_caps, *caps;
268   GstAudioInfo audioinfo;
269   GstElement *input_pipeline, *input_appsink;
270   int i;
271   GstBuffer *outbuffer;
272 
273   /* 440 Hz = frequency of sine wave in audio data
274    * 44100 Hz = sample rate
275    * (44100 / 2) Hz = Nyquist frequency */
276   static double const expected_frequency_spot = 440.0 / (44100.0 / 2.0);
277 
278   fail_unless (gst_element_set_state (mpg123audiodec,
279           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
280       "could not set to playing");
281   bus = gst_bus_new ();
282 
283   gst_element_set_bus (mpg123audiodec, bus);
284 
285   setup_input_pipeline (filename, &input_pipeline, &input_appsink);
286 
287   num_input_buffers = 0;
288   while (TRUE) {
289     GstSample *sample;
290     GstBuffer *input_buffer;
291 
292     sample = gst_app_sink_pull_sample (GST_APP_SINK (input_appsink));
293     if (sample == NULL)
294       break;
295 
296     fail_unless (GST_IS_SAMPLE (sample));
297 
298     input_buffer = gst_sample_get_buffer (sample);
299     fail_if (input_buffer == NULL);
300 
301     /* This is done to be on the safe side - docs say lifetime of the input buffer
302      * depends *solely* on the sample */
303     input_buffer = gst_buffer_copy (input_buffer);
304 
305     fail_unless_equals_int (gst_pad_push (mysrcpad, input_buffer), GST_FLOW_OK);
306 
307     ++num_input_buffers;
308 
309     gst_sample_unref (sample);
310   }
311 
312   num_decoded_buffers = g_list_length (buffers);
313 
314   /* check number of decoded buffers */
315   fail_unless_equals_int (num_decoded_buffers, num_input_buffers - 2);
316 
317   caps = gst_pad_get_current_caps (mysinkpad);
318   GST_LOG ("output caps %" GST_PTR_FORMAT, caps);
319   fail_unless (gst_audio_info_from_caps (&audioinfo, caps),
320       "Getting audio info from caps failed");
321 
322   /* check caps */
323   out_caps = gst_caps_new_simple ("audio/x-raw",
324       "format", G_TYPE_STRING, GST_AUDIO_NE (S32),
325       "layout", G_TYPE_STRING, "interleaved",
326       "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1, NULL);
327 
328   fail_unless (gst_caps_is_equal_fixed (caps, out_caps), "Incorrect out caps");
329 
330   gst_caps_unref (out_caps);
331   gst_caps_unref (caps);
332 
333   /* here, test if decoded data is a sine tone, and if the sine frequency is at the
334    * right spot in the spectrum */
335   for (i = 0; i < num_decoded_buffers; ++i) {
336     outbuffer = GST_BUFFER (buffers->data);
337     fail_if (outbuffer == NULL, "Invalid buffer retrieved");
338 
339     /* MPEG 1 layer 2 uses 1152 samples per frame */
340     expected_size = 1152 * GST_AUDIO_INFO_BPF (&audioinfo);
341     fail_unless_equals_int (gst_buffer_get_size (outbuffer), expected_size);
342 
343     check_main_frequency_spot_S32 (outbuffer, expected_frequency_spot);
344 
345     buffers = g_list_remove (buffers, outbuffer);
346     gst_buffer_unref (outbuffer);
347     outbuffer = NULL;
348   }
349 
350   g_list_free (buffers);
351   buffers = NULL;
352 
353   cleanup_input_pipeline (input_pipeline);
354   gst_bus_set_flushing (bus, TRUE);
355   gst_element_set_bus (mpg123audiodec, NULL);
356   gst_object_unref (GST_OBJECT (bus));
357 }
358 
359 
GST_START_TEST(test_decode_mpeg1layer2)360 GST_START_TEST (test_decode_mpeg1layer2)
361 {
362   GstElement *mpg123audiodec;
363   mpg123audiodec = setup_mpeg1layer2dec ();
364   run_decoding_test (mpg123audiodec, MP2_STREAM_FILENAME);
365   cleanup_mpg123audiodec (mpg123audiodec);
366   mpg123audiodec = NULL;
367 }
368 
369 GST_END_TEST;
370 
371 
GST_START_TEST(test_decode_mpeg1layer3_cbr)372 GST_START_TEST (test_decode_mpeg1layer3_cbr)
373 {
374   GstElement *mpg123audiodec;
375   mpg123audiodec = setup_mpeg1layer3dec ();
376   run_decoding_test (mpg123audiodec, MP3_CBR_STREAM_FILENAME);
377   cleanup_mpg123audiodec (mpg123audiodec);
378 }
379 
380 GST_END_TEST;
381 
382 
GST_START_TEST(test_decode_mpeg1layer3_vbr)383 GST_START_TEST (test_decode_mpeg1layer3_vbr)
384 {
385   GstElement *mpg123audiodec;
386   mpg123audiodec = setup_mpeg1layer3dec ();
387   run_decoding_test (mpg123audiodec, MP3_VBR_STREAM_FILENAME);
388   cleanup_mpg123audiodec (mpg123audiodec);
389 }
390 
391 GST_END_TEST;
392 
393 
GST_START_TEST(test_decode_garbage_mpeg1layer2)394 GST_START_TEST (test_decode_garbage_mpeg1layer2)
395 {
396   GstElement *mpg123audiodec;
397   GstBuffer *inbuffer;
398   GstBus *bus;
399   int i, num_buffers;
400   guint32 *tmpbuf;
401 
402   mpg123audiodec = setup_mpeg1layer2dec ();
403 
404   fail_unless (gst_element_set_state (mpg123audiodec,
405           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
406       "could not set to playing");
407   bus = gst_bus_new ();
408 
409   /* initialize the buffer with something that is no mpeg2 */
410   tmpbuf = g_new (guint32, 4096);
411   for (i = 0; i < 4096; i++) {
412     tmpbuf[i] = i;
413   }
414   inbuffer = gst_buffer_new_wrapped (tmpbuf, 4096 * sizeof (guint32));
415 
416   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
417 
418   gst_element_set_bus (mpg123audiodec, bus);
419 
420   /* should be possible to push without problems but nothing gets decoded */
421   fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer), GST_FLOW_OK);
422 
423   num_buffers = g_list_length (buffers);
424 
425   /* should be 0 buffers as decoding should've been impossible */
426   fail_unless_equals_int (num_buffers, 0);
427 
428   g_list_free (buffers);
429   buffers = NULL;
430 
431   gst_bus_set_flushing (bus, TRUE);
432   gst_element_set_bus (mpg123audiodec, NULL);
433   gst_object_unref (GST_OBJECT (bus));
434   cleanup_mpg123audiodec (mpg123audiodec);
435   mpg123audiodec = NULL;
436 }
437 
438 GST_END_TEST;
439 
440 
GST_START_TEST(test_decode_garbage_mpeg1layer3)441 GST_START_TEST (test_decode_garbage_mpeg1layer3)
442 {
443   GstElement *mpg123audiodec;
444   GstBuffer *inbuffer;
445   GstBus *bus;
446   int i, num_buffers;
447   guint32 *tmpbuf;
448 
449   mpg123audiodec = setup_mpeg1layer3dec ();
450 
451   fail_unless (gst_element_set_state (mpg123audiodec,
452           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
453       "could not set to playing");
454   bus = gst_bus_new ();
455 
456   /* initialize the buffer with something that is no mpeg2 */
457   tmpbuf = g_new (guint32, 4096);
458   for (i = 0; i < 4096; i++) {
459     tmpbuf[i] = i;
460   }
461   inbuffer = gst_buffer_new_wrapped (tmpbuf, 4096 * sizeof (guint32));
462 
463   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
464 
465   gst_element_set_bus (mpg123audiodec, bus);
466 
467   /* should be possible to push without problems but nothing gets decoded */
468   fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer), GST_FLOW_OK);
469 
470   num_buffers = g_list_length (buffers);
471 
472   /* should be 0 buffers as decoding should've been impossible */
473   fail_unless_equals_int (num_buffers, 0);
474 
475   g_list_free (buffers);
476   buffers = NULL;
477 
478   gst_bus_set_flushing (bus, TRUE);
479   gst_element_set_bus (mpg123audiodec, NULL);
480   gst_object_unref (GST_OBJECT (bus));
481   cleanup_mpg123audiodec (mpg123audiodec);
482   mpg123audiodec = NULL;
483 }
484 
485 GST_END_TEST;
486 
487 
488 static gboolean
is_test_file_available(gchar const * filename)489 is_test_file_available (gchar const *filename)
490 {
491   gboolean ret;
492   gchar *full_filename;
493   gchar *cwd;
494 
495   cwd = g_get_current_dir ();
496   full_filename = g_build_filename (cwd, GST_TEST_FILES_PATH, filename, NULL);
497   ret =
498       g_file_test (full_filename, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS);
499   g_free (full_filename);
500   g_free (cwd);
501   return ret;
502 }
503 
504 static Suite *
mpg123audiodec_suite(void)505 mpg123audiodec_suite (void)
506 {
507   GstRegistry *registry;
508   Suite *s = suite_create ("mpg123audiodec");
509   TCase *tc_chain = tcase_create ("general");
510 
511   registry = gst_registry_get ();
512 
513   suite_add_tcase (s, tc_chain);
514   if (gst_registry_check_feature_version (registry, "filesrc",
515           GST_VERSION_MAJOR, GST_VERSION_MINOR, 0) &&
516       gst_registry_check_feature_version (registry, "mpegaudioparse",
517           GST_VERSION_MAJOR, GST_VERSION_MINOR, 0) &&
518       gst_registry_check_feature_version (registry, "appsrc",
519           GST_VERSION_MAJOR, GST_VERSION_MINOR, 0)) {
520     if (is_test_file_available (MP2_STREAM_FILENAME))
521       tcase_add_test (tc_chain, test_decode_mpeg1layer2);
522     if (is_test_file_available (MP3_CBR_STREAM_FILENAME))
523       tcase_add_test (tc_chain, test_decode_mpeg1layer3_cbr);
524     if (is_test_file_available (MP3_VBR_STREAM_FILENAME))
525       tcase_add_test (tc_chain, test_decode_mpeg1layer3_vbr);
526   }
527   tcase_add_test (tc_chain, test_decode_garbage_mpeg1layer2);
528   tcase_add_test (tc_chain, test_decode_garbage_mpeg1layer3);
529 
530   return s;
531 }
532 
533 
534 GST_CHECK_MAIN (mpg123audiodec)
535