1 /* GStreamer ReplayGain limiter
2  *
3  * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
4  *
5  * rglimiter.c: Unit test for the rglimiter element
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  */
22 
23 #include <gst/check/gstcheck.h>
24 #include <gst/audio/audio.h>
25 
26 /* For ease of programming we use globals to keep refs for our floating
27  * src and sink pads we create; otherwise we always have to do get_pad,
28  * get_peer, and then remove references in every test function */
29 static GstPad *mysrcpad, *mysinkpad;
30 
31 #define RG_LIMITER_CAPS_TEMPLATE_STRING       \
32   "audio/x-raw, "                             \
33   "format = (string) "GST_AUDIO_NE (F32) ", " \
34   "layout = (string) interleaved, "           \
35   "channels = (int) [ 1, MAX ], "             \
36   "rate = (int) [ 1, MAX ]"
37 
38 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
39     GST_PAD_SINK,
40     GST_PAD_ALWAYS,
41     GST_STATIC_CAPS (RG_LIMITER_CAPS_TEMPLATE_STRING)
42     );
43 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
44     GST_PAD_SRC,
45     GST_PAD_ALWAYS,
46     GST_STATIC_CAPS (RG_LIMITER_CAPS_TEMPLATE_STRING)
47     );
48 
49 static GstElement *
setup_rglimiter(void)50 setup_rglimiter (void)
51 {
52   GstElement *element;
53 
54   GST_DEBUG ("setup_rglimiter");
55   element = gst_check_setup_element ("rglimiter");
56   mysrcpad = gst_check_setup_src_pad (element, &srctemplate);
57   mysinkpad = gst_check_setup_sink_pad (element, &sinktemplate);
58   gst_pad_set_active (mysrcpad, TRUE);
59   gst_pad_set_active (mysinkpad, TRUE);
60 
61   return element;
62 }
63 
64 static void
cleanup_rglimiter(GstElement * element)65 cleanup_rglimiter (GstElement * element)
66 {
67   GST_DEBUG ("cleanup_rglimiter");
68 
69   g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
70   g_list_free (buffers);
71   buffers = NULL;
72 
73   gst_check_teardown_src_pad (element);
74   gst_check_teardown_sink_pad (element);
75   gst_check_teardown_element (element);
76 }
77 
78 static void
set_playing_state(GstElement * element)79 set_playing_state (GstElement * element)
80 {
81   fail_unless (gst_element_set_state (element,
82           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
83       "Could not set state to PLAYING");
84 }
85 
86 static const gfloat test_input[] = {
87   -2.0, -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0, 2.0
88 };
89 
90 static const gfloat test_output[] = {
91   -0.99752737684336523,         /* -2.0  */
92   -0.88079707797788243,         /* -1.0  */
93   -0.7310585786300049,          /* -0.75 */
94   -0.5, -0.25, 0.0, 0.25, 0.5,
95   0.7310585786300049,           /* 0.75 */
96   0.88079707797788243,          /* 1.0  */
97   0.99752737684336523,          /* 2.0  */
98 };
99 
100 static void
setup_events(GstElement * element)101 setup_events (GstElement * element)
102 {
103   GstCaps *caps;
104 
105   caps = gst_caps_new_simple ("audio/x-raw",
106       "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1,
107       "format", G_TYPE_STRING, GST_AUDIO_NE (F32),
108       "layout", G_TYPE_STRING, "interleaved", NULL);
109 
110   gst_check_setup_events (mysrcpad, element, caps, GST_FORMAT_TIME);
111   gst_caps_unref (caps);
112 }
113 
114 static GstBuffer *
create_test_buffer(void)115 create_test_buffer (void)
116 {
117   GstBuffer *buf = gst_buffer_new_and_alloc (sizeof (test_input));
118 
119   gst_buffer_fill (buf, 0, test_input, sizeof (test_input));
120 
121   ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
122 
123   return buf;
124 }
125 
126 static void
verify_test_buffer(GstBuffer * buf)127 verify_test_buffer (GstBuffer * buf)
128 {
129   GstMapInfo map;
130   gfloat *output;
131   gint i;
132 
133   gst_buffer_map (buf, &map, GST_MAP_READ);
134   output = (gfloat *) map.data;
135   fail_unless (map.size == sizeof (test_output));
136 
137   for (i = 0; i < G_N_ELEMENTS (test_input); i++)
138     fail_unless (ABS (output[i] - test_output[i]) < 1.e-6,
139         "Incorrect output value %.6f for input %.2f, expected %.6f",
140         output[i], test_input[i], test_output[i]);
141 
142   gst_buffer_unmap (buf, &map);
143 }
144 
145 /* Start of tests. */
146 
GST_START_TEST(test_no_buffer)147 GST_START_TEST (test_no_buffer)
148 {
149   GstElement *element = setup_rglimiter ();
150 
151   set_playing_state (element);
152 
153   cleanup_rglimiter (element);
154 }
155 
156 GST_END_TEST;
157 
GST_START_TEST(test_disabled)158 GST_START_TEST (test_disabled)
159 {
160   GstElement *element = setup_rglimiter ();
161   GstBuffer *buf, *out_buf;
162 
163   g_object_set (element, "enabled", FALSE, NULL);
164   set_playing_state (element);
165   setup_events (element);
166 
167   buf = create_test_buffer ();
168   fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK);
169   fail_unless (g_list_length (buffers) == 1);
170   out_buf = buffers->data;
171   fail_if (out_buf == NULL);
172   buffers = g_list_remove (buffers, out_buf);
173   ASSERT_BUFFER_REFCOUNT (out_buf, "out_buf", 1);
174   fail_unless (buf == out_buf);
175   gst_buffer_unref (out_buf);
176 
177   cleanup_rglimiter (element);
178 }
179 
180 GST_END_TEST;
181 
GST_START_TEST(test_limiting)182 GST_START_TEST (test_limiting)
183 {
184   GstElement *element = setup_rglimiter ();
185   GstBuffer *buf, *out_buf;
186 
187   set_playing_state (element);
188   setup_events (element);
189 
190   /* Mutable variant. */
191   buf = create_test_buffer ();
192   GST_DEBUG ("push mutable buffer");
193   fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK);
194   fail_unless (g_list_length (buffers) == 1);
195   out_buf = buffers->data;
196   fail_if (out_buf == NULL);
197   ASSERT_BUFFER_REFCOUNT (out_buf, "out_buf", 1);
198   verify_test_buffer (out_buf);
199 
200   /* Immutable variant. */
201   buf = create_test_buffer ();
202   /* Extra ref: */
203   gst_buffer_ref (buf);
204   ASSERT_BUFFER_REFCOUNT (buf, "buf", 2);
205   GST_DEBUG ("push immutable buffer");
206   fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK);
207   ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
208   fail_unless (g_list_length (buffers) == 2);
209   out_buf = g_list_last (buffers)->data;
210   fail_if (out_buf == NULL);
211   ASSERT_BUFFER_REFCOUNT (out_buf, "out_buf", 1);
212   fail_unless (buf != out_buf);
213   /* Drop our extra ref: */
214   gst_buffer_unref (buf);
215   verify_test_buffer (out_buf);
216 
217   cleanup_rglimiter (element);
218 }
219 
220 GST_END_TEST;
221 
GST_START_TEST(test_gap)222 GST_START_TEST (test_gap)
223 {
224   GstElement *element = setup_rglimiter ();
225   GstBuffer *buf, *out_buf;
226   GstMapInfo m1, m2;
227 
228   set_playing_state (element);
229   setup_events (element);
230 
231   buf = create_test_buffer ();
232   GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_GAP);
233   fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK);
234   fail_unless (g_list_length (buffers) == 1);
235   out_buf = buffers->data;
236   fail_if (out_buf == NULL);
237   ASSERT_BUFFER_REFCOUNT (out_buf, "out_buf", 1);
238 
239   /* Verify that the baseclass does not lift the GAP flag: */
240   fail_unless (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_GAP));
241 
242   gst_buffer_map (out_buf, &m1, GST_MAP_READ);
243   gst_buffer_map (buf, &m2, GST_MAP_READ);
244 
245   g_assert (m1.size == m2.size);
246   /* We cheated by passing an input buffer with non-silence that has the GAP
247    * flag set.  The element cannot know that however and must have skipped
248    * adjusting the buffer because of the flag, which we can easily verify: */
249   fail_if (memcmp (m1.data, m2.data, m1.size) != 0);
250 
251   gst_buffer_unmap (out_buf, &m1);
252   gst_buffer_unmap (buf, &m2);
253 
254   cleanup_rglimiter (element);
255 }
256 
257 GST_END_TEST;
258 
259 static Suite *
rglimiter_suite(void)260 rglimiter_suite (void)
261 {
262   Suite *s = suite_create ("rglimiter");
263   TCase *tc_chain = tcase_create ("general");
264 
265   suite_add_tcase (s, tc_chain);
266 
267   tcase_add_test (tc_chain, test_no_buffer);
268   tcase_add_test (tc_chain, test_disabled);
269   tcase_add_test (tc_chain, test_limiting);
270   tcase_add_test (tc_chain, test_gap);
271 
272   return s;
273 }
274 
275 GST_CHECK_MAIN (rglimiter);
276