• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  *
3  * unit test for opus
4  *
5  * Copyright (C) <2011> Vincent Penquerc'h <vincent.penquerch@collbaora.co.uk>
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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <gst/check/gstcheck.h>
28 #include <gst/check/gstharness.h>
29 
30 #if G_BYTE_ORDER == G_BIG_ENDIAN
31 #define AFORMAT "S16BE"
32 #else
33 #define AFORMAT "S16LE"
34 #endif
35 
36 #define AUDIO_CAPS_STRING "audio/x-raw, " \
37                            "format = (string) " AFORMAT ", "\
38                            "layout = (string) interleaved, " \
39                            "rate = (int) 48000, " \
40                            "channels = (int) 1 "
41 
42 /* A lot of these taken from the vorbisdec test */
43 
44 /* For ease of programming we use globals to keep refs for our floating
45  * src and sink pads we create; otherwise we always have to do get_pad,
46  * get_peer, and then remove references in every test function */
47 static GstPad *mydecsrcpad, *mydecsinkpad;
48 static GstPad *myencsrcpad, *myencsinkpad;
49 
50 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
51     GST_PAD_SINK,
52     GST_PAD_ALWAYS,
53     GST_STATIC_CAPS_ANY);
54 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
55     GST_PAD_SRC,
56     GST_PAD_ALWAYS,
57     GST_STATIC_CAPS_ANY);
58 
59 static GstElement *
setup_opusdec(void)60 setup_opusdec (void)
61 {
62   GstElement *opusdec;
63 
64   GST_DEBUG ("setup_opusdec");
65   opusdec = gst_check_setup_element ("opusdec");
66   mydecsrcpad = gst_check_setup_src_pad (opusdec, &srctemplate);
67   mydecsinkpad = gst_check_setup_sink_pad (opusdec, &sinktemplate);
68   gst_pad_set_active (mydecsrcpad, TRUE);
69   gst_pad_set_active (mydecsinkpad, TRUE);
70 
71   return opusdec;
72 }
73 
74 static void
cleanup_opusdec(GstElement * opusdec)75 cleanup_opusdec (GstElement * opusdec)
76 {
77   GST_DEBUG ("cleanup_opusdec");
78   gst_element_set_state (opusdec, GST_STATE_NULL);
79 
80   gst_pad_set_active (mydecsrcpad, FALSE);
81   gst_pad_set_active (mydecsinkpad, FALSE);
82   gst_check_teardown_src_pad (opusdec);
83   gst_check_teardown_sink_pad (opusdec);
84   gst_check_teardown_element (opusdec);
85 }
86 
87 static GstElement *
setup_opusenc(void)88 setup_opusenc (void)
89 {
90   GstElement *opusenc;
91 
92   GST_DEBUG ("setup_opusenc");
93   opusenc = gst_check_setup_element ("opusenc");
94   myencsrcpad = gst_check_setup_src_pad (opusenc, &srctemplate);
95   myencsinkpad = gst_check_setup_sink_pad (opusenc, &sinktemplate);
96   gst_pad_set_active (myencsrcpad, TRUE);
97   gst_pad_set_active (myencsinkpad, TRUE);
98 
99   return opusenc;
100 }
101 
102 static void
cleanup_opusenc(GstElement * opusenc)103 cleanup_opusenc (GstElement * opusenc)
104 {
105   GST_DEBUG ("cleanup_opusenc");
106   gst_element_set_state (opusenc, GST_STATE_NULL);
107 
108   gst_pad_set_active (myencsrcpad, FALSE);
109   gst_pad_set_active (myencsinkpad, FALSE);
110   gst_check_teardown_src_pad (opusenc);
111   gst_check_teardown_sink_pad (opusenc);
112   gst_check_teardown_element (opusenc);
113 }
114 
115 static void
check_buffers(guint expected)116 check_buffers (guint expected)
117 {
118   GstBuffer *outbuffer;
119   guint i, num_buffers;
120 
121   /* check buffers are the type we expect */
122   num_buffers = g_list_length (buffers);
123   fail_unless (num_buffers >= expected);
124   for (i = 0; i < num_buffers; ++i) {
125     outbuffer = GST_BUFFER (buffers->data);
126     fail_if (outbuffer == NULL);
127     fail_if (gst_buffer_get_size (outbuffer) == 0);
128 
129     buffers = g_list_remove (buffers, outbuffer);
130 
131     ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
132     gst_buffer_unref (outbuffer);
133     outbuffer = NULL;
134   }
135 }
136 
GST_START_TEST(test_opus_encode_nothing)137 GST_START_TEST (test_opus_encode_nothing)
138 {
139   GstElement *opusenc;
140 
141   opusenc = setup_opusenc ();
142   fail_unless (gst_element_set_state (opusenc,
143           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
144       "could not set to playing");
145 
146   fail_unless (gst_pad_push_event (myencsrcpad, gst_event_new_eos ()) == TRUE);
147 
148   fail_unless (gst_element_set_state (opusenc,
149           GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
150       "could not set to ready");
151 
152   /* cleanup */
153   cleanup_opusenc (opusenc);
154 }
155 
156 GST_END_TEST;
157 
GST_START_TEST(test_opus_decode_nothing)158 GST_START_TEST (test_opus_decode_nothing)
159 {
160   GstElement *opusdec;
161 
162   opusdec = setup_opusdec ();
163   fail_unless (gst_element_set_state (opusdec,
164           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
165       "could not set to playing");
166 
167   fail_unless (gst_pad_push_event (mydecsrcpad, gst_event_new_eos ()) == TRUE);
168 
169   fail_unless (gst_element_set_state (opusdec,
170           GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
171       "could not set to ready");
172 
173   /* cleanup */
174   cleanup_opusdec (opusdec);
175 }
176 
177 GST_END_TEST;
178 
GST_START_TEST(test_opus_encode_samples)179 GST_START_TEST (test_opus_encode_samples)
180 {
181   const unsigned int nsamples = 4096;
182   GstElement *opusenc;
183   GstBuffer *inbuffer;
184   GstCaps *caps;
185 
186   opusenc = setup_opusenc ();
187 
188   fail_unless (gst_element_set_state (opusenc,
189           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
190       "could not set to playing");
191 
192   inbuffer = gst_buffer_new_and_alloc (nsamples * 2);
193   gst_buffer_memset (inbuffer, 0, 0, nsamples * 2);
194 
195   GST_BUFFER_TIMESTAMP (inbuffer) = GST_BUFFER_OFFSET (inbuffer) = 0;
196   GST_BUFFER_DURATION (inbuffer) = GST_CLOCK_TIME_NONE;
197   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
198 
199   caps = gst_caps_from_string (AUDIO_CAPS_STRING);
200   fail_unless (caps != NULL);
201   gst_check_setup_events (myencsrcpad, opusenc, caps, GST_FORMAT_TIME);
202   gst_caps_unref (caps);
203   gst_buffer_ref (inbuffer);
204 
205   /* pushing gives away my reference ... */
206   fail_unless (gst_pad_push (myencsrcpad, inbuffer) == GST_FLOW_OK);
207   /* ... and nothing ends up on the global buffer list */
208   fail_unless (gst_pad_push_event (myencsrcpad, gst_event_new_eos ()) == TRUE);
209 
210   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
211   gst_buffer_unref (inbuffer);
212 
213   fail_unless (gst_element_set_state (opusenc,
214           GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
215       "could not set to ready");
216 
217   /* default frame size is 20 ms, at 48000 Hz that's 960 samples */
218   check_buffers ((nsamples + 959) / 960);
219 
220   /* cleanup */
221   cleanup_opusenc (opusenc);
222   g_list_free (buffers);
223 }
224 
225 GST_END_TEST;
226 
GST_START_TEST(test_opus_encode_properties)227 GST_START_TEST (test_opus_encode_properties)
228 {
229   const unsigned int nsamples = 4096;
230   enum
231   { steps = 20 };
232   GstElement *opusenc;
233   GstBuffer *inbuffer;
234   GstCaps *caps;
235   unsigned int step;
236   static const struct
237   {
238     const char *param;
239     int value;
240   } param_changes[steps] = {
241     {
242     "frame-size", 40}, {
243     "inband-fec", 1}, {
244     "complexity", 5}, {
245     "bandwidth", 1104}, {
246     "frame-size", 2}, {
247     "max-payload-size", 80}, {
248     "frame-size", 60}, {
249     "max-payload-size", 900}, {
250     "complexity", 1}, {
251     "bitrate", 30000}, {
252     "frame-size", 10}, {
253     "bitrate", 300000}, {
254     "inband-fec", 0}, {
255     "frame-size", 5}, {
256     "bandwidth", 1101}, {
257     "frame-size", 10}, {
258     "bitrate", 500000}, {
259     "frame-size", 5}, {
260     "bitrate", 80000}, {
261   "complexity", 8},};
262 
263   opusenc = setup_opusenc ();
264 
265   fail_unless (gst_element_set_state (opusenc,
266           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
267       "could not set to playing");
268 
269   caps = gst_caps_from_string (AUDIO_CAPS_STRING);
270   fail_unless (caps != NULL);
271 
272   gst_check_setup_events (myencsrcpad, opusenc, caps, GST_FORMAT_TIME);
273 
274   for (step = 0; step < steps; ++step) {
275     GstSegment segment;
276 
277     gst_segment_init (&segment, GST_FORMAT_TIME);
278     gst_pad_push_event (myencsrcpad, gst_event_new_segment (&segment));
279 
280     inbuffer = gst_buffer_new_and_alloc (nsamples * 2);
281     gst_buffer_memset (inbuffer, 0, 0, nsamples * 2);
282 
283     GST_BUFFER_TIMESTAMP (inbuffer) = GST_BUFFER_OFFSET (inbuffer) = 0;
284     GST_BUFFER_DURATION (inbuffer) = GST_CLOCK_TIME_NONE;
285     ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
286 
287     gst_buffer_ref (inbuffer);
288 
289     /* pushing gives away my reference ... */
290     fail_unless (gst_pad_push (myencsrcpad, inbuffer) == GST_FLOW_OK);
291     /* ... and nothing ends up on the global buffer list */
292     fail_unless (gst_pad_push_event (myencsrcpad,
293             gst_event_new_eos ()) == TRUE);
294 
295     ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
296     gst_buffer_unref (inbuffer);
297 
298     /* change random parameters */
299     g_object_set (opusenc, param_changes[step].param, param_changes[step].value,
300         NULL);
301 
302     check_buffers (1);
303 
304     fail_unless (gst_pad_push_event (myencsrcpad,
305             gst_event_new_flush_start ()) == TRUE);
306     fail_unless (gst_pad_push_event (myencsrcpad,
307             gst_event_new_flush_stop (TRUE)) == TRUE);
308   }
309 
310   gst_caps_unref (caps);
311 
312   fail_unless (gst_element_set_state (opusenc,
313           GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS,
314       "could not set to ready");
315 
316   /* cleanup */
317   cleanup_opusenc (opusenc);
318   g_list_free (buffers);
319 }
320 
321 GST_END_TEST;
322 
323 /* removes fields that do not interest our tests to
324  * allow using gst_caps_is_equal for comparison */
325 static GstCaps *
remove_extra_caps_fields(GstCaps * caps)326 remove_extra_caps_fields (GstCaps * caps)
327 {
328   gint i;
329   for (i = 0; i < gst_caps_get_size (caps); i++) {
330     GstStructure *s = gst_caps_get_structure (caps, i);
331 
332     gst_structure_remove_field (s, "channel-mapping-family");
333     gst_structure_remove_field (s, "coupled-count");
334     gst_structure_remove_field (s, "stream-count");
335   }
336   return gst_caps_simplify (caps);
337 }
338 
339 static void
run_getcaps_check(GstCaps * filter,GstCaps * downstream_caps,GstCaps * expected_result)340 run_getcaps_check (GstCaps * filter, GstCaps * downstream_caps,
341     GstCaps * expected_result)
342 {
343   GstElement *opusdec;
344   GstElement *capsfilter;
345   GstPad *sinkpad;
346   GstCaps *result;
347   gchar *caps_str;
348 
349   opusdec = gst_element_factory_make ("opusdec", NULL);
350   capsfilter = gst_element_factory_make ("capsfilter", NULL);
351   sinkpad = gst_element_get_static_pad (opusdec, "sink");
352   fail_unless (gst_element_link (opusdec, capsfilter));
353 
354   if (downstream_caps)
355     g_object_set (capsfilter, "caps", downstream_caps, NULL);
356   result = gst_pad_query_caps (sinkpad, filter);
357   result = remove_extra_caps_fields (result);
358   caps_str = gst_caps_to_string (result);
359   fail_unless (gst_caps_is_equal (expected_result, result),
360       "Unexpected output caps: %s", caps_str);
361 
362   if (filter)
363     gst_caps_unref (filter);
364   gst_caps_unref (result);
365   gst_caps_unref (expected_result);
366   if (downstream_caps)
367     gst_caps_unref (downstream_caps);
368   gst_object_unref (sinkpad);
369   gst_object_unref (opusdec);
370   gst_object_unref (capsfilter);
371   g_free (caps_str);
372 }
373 
374 static void
run_getcaps_check_from_strings(const gchar * filter,const gchar * downstream_caps,const gchar * expected_result)375 run_getcaps_check_from_strings (const gchar * filter,
376     const gchar * downstream_caps, const gchar * expected_result)
377 {
378   run_getcaps_check (filter ? gst_caps_from_string (filter) : NULL,
379       downstream_caps ? gst_caps_from_string (downstream_caps) : NULL,
380       gst_caps_from_string (expected_result));
381 }
382 
GST_START_TEST(test_opusdec_getcaps)383 GST_START_TEST (test_opusdec_getcaps)
384 {
385   /* default result */
386   run_getcaps_check_from_strings (NULL, NULL,
387       "audio/x-opus, rate=(int){48000, 24000, 16000, 12000, 8000}, channels=(int)[1,8]");
388 
389   /* A single supported rate downstream - should accept any upstream anyway */
390   run_getcaps_check_from_strings (NULL, "audio/x-raw, rate=(int)8000",
391       "audio/x-opus, rate=(int){48000, 24000, 16000, 12000, 8000}, channels=(int)[1,8]");
392 
393   /* Two supported rates (fields as a array, not as a single int) */
394   run_getcaps_check_from_strings (NULL, "audio/x-raw, rate=(int){24000, 8000}",
395       "audio/x-opus, rate=(int){48000, 24000, 16000, 12000, 8000}, channels=(int)[1,8]");
396 
397   /* One supported and one unsupported rate */
398   run_getcaps_check_from_strings (NULL, "audio/x-raw, rate=(int){24000, 1000}",
399       "audio/x-opus, rate=(int){48000, 24000, 16000, 12000, 8000}, channels=(int)[1,8]");
400 
401   /* Unsupported rate */
402   run_getcaps_check_from_strings (NULL, "audio/x-raw, rate=(int)1000", "EMPTY");
403 
404   /* same tests for channels */
405   run_getcaps_check_from_strings (NULL, "audio/x-raw, channels=(int)2",
406       "audio/x-opus, rate=(int){48000, 24000, 16000, 12000, 8000}, channels=(int)[1,2]");
407   run_getcaps_check_from_strings (NULL, "audio/x-raw, channels=(int)[1, 2]",
408       "audio/x-opus, rate=(int){48000, 24000, 16000, 12000, 8000}, channels=(int)[1,2]");
409   run_getcaps_check_from_strings (NULL, "audio/x-raw, channels=(int)5000",
410       "EMPTY");
411 
412   /* Now add filters */
413 
414   /* Formats not acceptable */
415   run_getcaps_check_from_strings ("audio/x-opus, rate=(int)1000", NULL,
416       "EMPTY");
417   run_getcaps_check_from_strings ("audio/x-opus, channels=(int)200", NULL,
418       "EMPTY");
419 
420   /* Should restrict the result of the caps query to the selected rate/channels */
421   run_getcaps_check_from_strings ("audio/x-opus, rate=(int)8000", NULL,
422       "audio/x-opus, rate=(int)8000, channels=(int)[1,8]");
423   run_getcaps_check_from_strings ("audio/x-opus, channels=(int)2", NULL,
424       "audio/x-opus, rate=(int){48000, 24000, 16000, 12000, 8000}, channels=(int)2");
425 }
426 
427 GST_END_TEST;
428 
GST_START_TEST(test_opus_decode_plc_timestamps_with_fec)429 GST_START_TEST (test_opus_decode_plc_timestamps_with_fec)
430 {
431   GstBuffer *buf;
432   GstHarness *h =
433       gst_harness_new_parse ("opusdec use-inband-fec=TRUE plc=TRUE");
434   GstClockTime dur0 = GST_MSECOND * 35 / 10;    /* because of lookahead */
435   GstClockTime dur = GST_MSECOND * 10;
436 
437   gst_harness_add_src_parse (h,
438       "audiotestsrc samplesperbuffer=480 is-live=TRUE ! "
439       "opusenc frame-size=10 inband-fec=TRUE", TRUE);
440 
441   /* Push first buffer from encoder to decoder. It will not be decoded yet
442    * because of the delay introduced by FEC */
443   gst_harness_src_crank_and_push_many (h, 1, 1);
444   fail_unless_equals_int (0, gst_harness_buffers_received (h));
445 
446   /* Drop second buffer from encoder and send a GAP event to decoder
447    * instead with 2x duration */
448   gst_harness_src_crank_and_push_many (h, 1, 0);
449   fail_unless (buf = gst_harness_pull (h->src_harness));
450   fail_unless (gst_harness_push_event (h,
451           gst_event_new_gap (GST_BUFFER_PTS (buf),
452               GST_BUFFER_DURATION (buf) * 2)));
453   gst_buffer_unref (buf);
454 
455   /* Extract first buffer from decoder and verify timstamps */
456   fail_unless (buf = gst_harness_pull (h));
457   fail_unless_equals_int64 (0, GST_BUFFER_PTS (buf));
458   fail_unless_equals_int64 (dur0, GST_BUFFER_DURATION (buf));
459   fail_unless (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT));
460   gst_buffer_unref (buf);
461 
462   /* Third buffer is pushed from encoder to decoder with DISCONT set */
463   gst_harness_src_crank_and_push_many (h, 1, 0);
464   fail_unless (buf = gst_harness_pull (h->src_harness));
465   GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
466   gst_harness_push (h, buf);
467 
468   /* Extract second (concealed) buffer from decoder and verify timestamp
469      and the 2x duration */
470   fail_unless (buf = gst_harness_pull (h));
471   fail_unless_equals_int64 (dur0, GST_BUFFER_PTS (buf));
472   fail_unless_equals_int64 (dur * 2, GST_BUFFER_DURATION (buf));
473   fail_if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT));
474   gst_buffer_unref (buf);
475 
476   /* Push fourth buffer from encoder to decoder as normal */
477   gst_harness_src_crank_and_push_many (h, 1, 1);
478 
479   /* Extract third buffer from decoder and verify timestamps */
480   fail_unless (buf = gst_harness_pull (h));
481   fail_unless_equals_int64 (dur0 + 1 * dur, GST_BUFFER_PTS (buf));
482   fail_unless_equals_int64 (dur, GST_BUFFER_DURATION (buf));
483   fail_if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT));
484   gst_buffer_unref (buf);
485 
486   gst_harness_teardown (h);
487 }
488 
489 GST_END_TEST;
490 
491 static Suite *
opus_suite(void)492 opus_suite (void)
493 {
494   Suite *s = suite_create ("opus");
495   TCase *tc_chain = tcase_create ("general");
496 
497   suite_add_tcase (s, tc_chain);
498 
499   tcase_add_test (tc_chain, test_opus_encode_nothing);
500   tcase_add_test (tc_chain, test_opus_decode_nothing);
501   tcase_add_test (tc_chain, test_opus_encode_samples);
502   tcase_add_test (tc_chain, test_opus_encode_properties);
503   tcase_add_test (tc_chain, test_opusdec_getcaps);
504   tcase_add_test (tc_chain, test_opus_decode_plc_timestamps_with_fec);
505 
506   return s;
507 }
508 
509 GST_CHECK_MAIN (opus);
510