/* GStreamer unit tests for the interleave element * Copyright (C) 2008 Sebastian Dröge * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include GST_START_TEST (test_create_and_unref) { GstElement *deinterleave; deinterleave = gst_element_factory_make ("deinterleave", NULL); fail_unless (deinterleave != NULL); gst_element_set_state (deinterleave, GST_STATE_NULL); gst_object_unref (deinterleave); } GST_END_TEST; static GstPad *mysrcpad, **mysinkpads; static gint nsinkpads; static GstBus *bus; static GstElement *deinterleave; static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw, " "format = (string) " GST_AUDIO_NE (F32) ", " "channels = (int) 1, layout = (string) {interleaved, non-interleaved}, rate = (int) {32000, 48000}")); static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("audio/x-raw, " "format = (string) " GST_AUDIO_NE (F32) ", " "channels = (int) { 2, 3 }, layout = (string) interleaved, rate = (int) {32000, 48000}")); #define CAPS_32khz \ "audio/x-raw, " \ "format = (string) "GST_AUDIO_NE (F32) ", " \ "channels = (int) 2, layout = (string) interleaved, " \ "rate = (int) 32000" #define CAPS_48khz \ "audio/x-raw, " \ "format = (string) "GST_AUDIO_NE (F32) ", " \ "channels = (int) 2, layout = (string) interleaved, " \ "rate = (int) 48000" #define CAPS_48khz_3CH \ "audio/x-raw, " \ "format = (string) "GST_AUDIO_NE (F32) ", " \ "channels = (int) 3, layout = (string) interleaved, " \ "rate = (int) 48000" static GstFlowReturn deinterleave_chain_func (GstPad * pad, GstObject * parent, GstBuffer * buffer) { gint i; GstMapInfo map; gfloat *indata; fail_unless (GST_IS_BUFFER (buffer)); gst_buffer_map (buffer, &map, GST_MAP_READ); indata = (gfloat *) map.data; fail_unless_equals_int (map.size, 48000 * sizeof (gfloat)); fail_unless (indata != NULL); if (strcmp (GST_PAD_NAME (pad), "sink0") == 0) { for (i = 0; i < 48000; i++) fail_unless_equals_float (indata[i], -1.0); } else if (strcmp (GST_PAD_NAME (pad), "sink1") == 0) { for (i = 0; i < 48000; i++) fail_unless_equals_float (indata[i], 1.0); } else { g_assert_not_reached (); } gst_buffer_unmap (buffer, &map); gst_buffer_unref (buffer); return GST_FLOW_OK; } static void deinterleave_pad_added (GstElement * src, GstPad * pad, gpointer data) { gchar *name; gint link = GPOINTER_TO_INT (data); if (nsinkpads >= link) return; name = g_strdup_printf ("sink%d", nsinkpads); mysinkpads[nsinkpads] = gst_pad_new_from_static_template (&sinktemplate, name); g_free (name); fail_if (mysinkpads[nsinkpads] == NULL); gst_pad_set_chain_function (mysinkpads[nsinkpads], deinterleave_chain_func); fail_unless (gst_pad_link (pad, mysinkpads[nsinkpads]) == GST_PAD_LINK_OK); gst_pad_set_active (mysinkpads[nsinkpads], TRUE); nsinkpads++; } GST_START_TEST (test_2_channels) { GstPad *sinkpad; gint i; GstBuffer *inbuf; GstCaps *caps; gfloat *indata; GstMapInfo map; guint64 channel_mask = 0; mysinkpads = g_new0 (GstPad *, 2); nsinkpads = 0; deinterleave = gst_element_factory_make ("deinterleave", NULL); fail_unless (deinterleave != NULL); mysrcpad = gst_pad_new_from_static_template (&srctemplate, "src"); fail_unless (mysrcpad != NULL); gst_pad_set_active (mysrcpad, TRUE); caps = gst_caps_from_string (CAPS_48khz); channel_mask |= G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; channel_mask |= G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; gst_caps_set_simple (caps, "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL); gst_check_setup_events (mysrcpad, deinterleave, caps, GST_FORMAT_TIME); sinkpad = gst_element_get_static_pad (deinterleave, "sink"); fail_unless (sinkpad != NULL); fail_unless (gst_pad_link (mysrcpad, sinkpad) == GST_PAD_LINK_OK); g_object_unref (sinkpad); g_signal_connect (deinterleave, "pad-added", G_CALLBACK (deinterleave_pad_added), GINT_TO_POINTER (2)); bus = gst_bus_new (); gst_element_set_bus (deinterleave, bus); fail_unless (gst_element_set_state (deinterleave, GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS); inbuf = gst_buffer_new_and_alloc (2 * 48000 * sizeof (gfloat)); inbuf = gst_buffer_make_writable (inbuf); gst_buffer_map (inbuf, &map, GST_MAP_WRITE); indata = (gfloat *) map.data; for (i = 0; i < 2 * 48000; i += 2) { indata[i] = -1.0; indata[i + 1] = 1.0; } gst_buffer_unmap (inbuf, &map); fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK); fail_unless (gst_element_set_state (deinterleave, GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS); for (i = 0; i < nsinkpads; i++) g_object_unref (mysinkpads[i]); g_free (mysinkpads); mysinkpads = NULL; g_object_unref (deinterleave); g_object_unref (bus); gst_caps_unref (caps); } GST_END_TEST; GST_START_TEST (test_2_channels_1_linked) { GstPad *sinkpad; gint i; GstBuffer *inbuf; GstCaps *caps; gfloat *indata; GstMapInfo map; guint64 channel_mask = 0; nsinkpads = 0; mysinkpads = g_new0 (GstPad *, 2); deinterleave = gst_element_factory_make ("deinterleave", NULL); fail_unless (deinterleave != NULL); mysrcpad = gst_pad_new_from_static_template (&srctemplate, "src"); fail_unless (mysrcpad != NULL); gst_pad_set_active (mysrcpad, TRUE); caps = gst_caps_from_string (CAPS_48khz); channel_mask |= G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; channel_mask |= G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; gst_caps_set_simple (caps, "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL); gst_check_setup_events (mysrcpad, deinterleave, caps, GST_FORMAT_TIME); sinkpad = gst_element_get_static_pad (deinterleave, "sink"); fail_unless (sinkpad != NULL); fail_unless (gst_pad_link (mysrcpad, sinkpad) == GST_PAD_LINK_OK); g_object_unref (sinkpad); g_signal_connect (deinterleave, "pad-added", G_CALLBACK (deinterleave_pad_added), GINT_TO_POINTER (1)); bus = gst_bus_new (); gst_element_set_bus (deinterleave, bus); fail_unless (gst_element_set_state (deinterleave, GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS); inbuf = gst_buffer_new_and_alloc (2 * 48000 * sizeof (gfloat)); inbuf = gst_buffer_make_writable (inbuf); gst_buffer_map (inbuf, &map, GST_MAP_WRITE); indata = (gfloat *) map.data; for (i = 0; i < 2 * 48000; i += 2) { indata[i] = -1.0; indata[i + 1] = 1.0; } gst_buffer_unmap (inbuf, &map); fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK); fail_unless (gst_element_set_state (deinterleave, GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS); for (i = 0; i < nsinkpads; i++) g_object_unref (mysinkpads[i]); g_free (mysinkpads); mysinkpads = NULL; g_object_unref (deinterleave); g_object_unref (bus); gst_caps_unref (caps); } GST_END_TEST; GST_START_TEST (test_2_channels_caps_change) { GstPad *sinkpad; GstCaps *caps, *caps2; GstCaps *ret_caps; gint i; GstBuffer *inbuf; gfloat *indata; GstMapInfo map; guint64 channel_mask; nsinkpads = 0; mysinkpads = g_new0 (GstPad *, 2); deinterleave = gst_element_factory_make ("deinterleave", NULL); fail_unless (deinterleave != NULL); mysrcpad = gst_pad_new_from_static_template (&srctemplate, "src"); fail_unless (mysrcpad != NULL); gst_pad_set_active (mysrcpad, TRUE); caps = gst_caps_from_string (CAPS_48khz); channel_mask = 0; channel_mask |= G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; channel_mask |= G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; gst_caps_set_simple (caps, "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL); sinkpad = gst_element_get_static_pad (deinterleave, "sink"); fail_unless (sinkpad != NULL); fail_unless (gst_pad_link (mysrcpad, sinkpad) == GST_PAD_LINK_OK); g_object_unref (sinkpad); ret_caps = gst_pad_peer_query_caps (mysrcpad, caps); fail_if (gst_caps_is_empty (ret_caps)); gst_caps_unref (ret_caps); gst_check_setup_events (mysrcpad, deinterleave, caps, GST_FORMAT_TIME); g_signal_connect (deinterleave, "pad-added", G_CALLBACK (deinterleave_pad_added), GINT_TO_POINTER (2)); bus = gst_bus_new (); gst_element_set_bus (deinterleave, bus); fail_unless (gst_element_set_state (deinterleave, GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS); inbuf = gst_buffer_new_and_alloc (2 * 48000 * sizeof (gfloat)); inbuf = gst_buffer_make_writable (inbuf); gst_buffer_map (inbuf, &map, GST_MAP_WRITE); indata = (gfloat *) map.data; for (i = 0; i < 2 * 48000; i += 2) { indata[i] = -1.0; indata[i + 1] = 1.0; } gst_buffer_unmap (inbuf, &map); fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK); caps2 = gst_caps_from_string (CAPS_32khz); channel_mask = 0; channel_mask |= G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; channel_mask |= G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; gst_caps_set_simple (caps2, "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL); ret_caps = gst_pad_peer_query_caps (mysrcpad, caps2); fail_if (gst_caps_is_empty (ret_caps)); gst_caps_unref (ret_caps); gst_pad_set_caps (mysrcpad, caps2); inbuf = gst_buffer_new_and_alloc (2 * 48000 * sizeof (gfloat)); inbuf = gst_buffer_make_writable (inbuf); gst_buffer_map (inbuf, &map, GST_MAP_WRITE); indata = (gfloat *) map.data; for (i = 0; i < 2 * 48000; i += 2) { indata[i] = -1.0; indata[i + 1] = 1.0; } gst_buffer_unmap (inbuf, &map); /* Should work fine because the caps changed in a compatible way */ fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK); gst_caps_unref (caps2); caps2 = gst_caps_from_string (CAPS_48khz_3CH); channel_mask = 0; channel_mask |= G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; channel_mask |= G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; channel_mask |= G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER; gst_caps_set_simple (caps2, "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL); ret_caps = gst_pad_peer_query_caps (mysrcpad, caps2); fail_unless (gst_caps_is_empty (ret_caps)); gst_caps_unref (ret_caps); gst_pad_set_caps (mysrcpad, caps2); inbuf = gst_buffer_new_and_alloc (3 * 48000 * sizeof (gfloat)); inbuf = gst_buffer_make_writable (inbuf); gst_buffer_map (inbuf, &map, GST_MAP_WRITE); indata = (gfloat *) map.data; for (i = 0; i < 3 * 48000; i += 3) { indata[i] = -1.0; indata[i + 1] = 1.0; indata[i + 2] = 0.0; } gst_buffer_unmap (inbuf, &map); /* Should break because the caps changed in an incompatible way */ fail_if (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK); fail_unless (gst_element_set_state (deinterleave, GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS); for (i = 0; i < nsinkpads; i++) g_object_unref (mysinkpads[i]); g_free (mysinkpads); mysinkpads = NULL; g_object_unref (deinterleave); g_object_unref (bus); gst_caps_unref (caps); gst_caps_unref (caps2); } GST_END_TEST; #define SAMPLES_PER_BUFFER 10 #define NUM_CHANNELS 8 #define SAMPLE_RATE 44100 static guint pads_created; static void set_channel_positions (GstCaps * caps, int channels, GstAudioChannelPosition * channelpositions) { int c; guint64 channel_mask = 0; for (c = 0; c < channels; c++) channel_mask |= G_GUINT64_CONSTANT (1) << channelpositions[c]; gst_caps_set_simple (caps, "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL); } static void src_handoff_float32_8ch (GstElement * src, GstBuffer * buf, GstPad * pad, gpointer user_data) { gfloat *data, *p; guint size, i, c; size = sizeof (gfloat) * SAMPLES_PER_BUFFER * NUM_CHANNELS; data = p = (gfloat *) g_malloc (size); for (i = 0; i < SAMPLES_PER_BUFFER; ++i) { for (c = 0; c < NUM_CHANNELS; ++c) { *p = (gfloat) ((i * NUM_CHANNELS) + c); ++p; } } if (gst_buffer_n_memory (buf)) { gst_buffer_replace_memory_range (buf, 0, -1, gst_memory_new_wrapped (0, data, size, 0, size, data, g_free)); } else { gst_buffer_insert_memory (buf, 0, gst_memory_new_wrapped (0, data, size, 0, size, data, g_free)); } GST_BUFFER_OFFSET (buf) = 0; GST_BUFFER_TIMESTAMP (buf) = 0; } static GstPadProbeReturn src_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer userdata) { GstAudioChannelPosition layout[NUM_CHANNELS]; GstCaps *caps; guint i; if ((info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) && GST_EVENT_TYPE (info->data) == GST_EVENT_STREAM_START) { gst_pad_remove_probe (pad, info->id); caps = gst_caps_new_simple ("audio/x-raw", "format", G_TYPE_STRING, GST_AUDIO_NE (F32), "channels", G_TYPE_INT, NUM_CHANNELS, "layout", G_TYPE_STRING, "interleaved", "rate", G_TYPE_INT, SAMPLE_RATE, NULL); for (i = 0; i < NUM_CHANNELS; ++i) layout[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT + i; set_channel_positions (caps, NUM_CHANNELS, layout); gst_pad_set_caps (pad, caps); gst_caps_unref (caps); } return GST_PAD_PROBE_OK; } static GstPadProbeReturn float_buffer_check_probe (GstPad * pad, GstPadProbeInfo * info, gpointer userdata) { GstMapInfo map; gfloat *data; guint padnum, numpads; guint num, i; GstCaps *caps; GstStructure *s; GstAudioChannelPosition *pos; gint channels; GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info); GstAudioInfo audio_info; guint pad_id = GPOINTER_TO_UINT (userdata); fail_unless_equals_int (sscanf (GST_PAD_NAME (pad), "src_%u", &padnum), 1); numpads = pads_created; /* Check caps */ caps = gst_pad_get_current_caps (pad); fail_unless (caps != NULL); s = gst_caps_get_structure (caps, 0); fail_unless (gst_structure_get_int (s, "channels", &channels)); fail_unless_equals_int (channels, 1); gst_audio_info_init (&audio_info); fail_unless (gst_audio_info_from_caps (&audio_info, caps)); pos = audio_info.position; fail_unless (pos != NULL && pos[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT + pad_id); gst_caps_unref (caps); gst_buffer_map (buffer, &map, GST_MAP_READ); data = (gfloat *) map.data; num = map.size / sizeof (gfloat); /* Check buffer content */ for (i = 0; i < num; ++i) { guint val, rest; val = (guint) data[i]; GST_LOG ("%s[%u]: %8f", GST_PAD_NAME (pad), i, data[i]); /* can't use the modulo operator in the assertion statement, since due to * the way it gets expanded it would be interpreted as a printf operator * in the failure case, which will result in segfaults */ rest = val % numpads; /* check that the first channel is on pad src0, the second on src1 etc. */ fail_unless_equals_int (rest, padnum); } gst_buffer_unmap (buffer, &map); return GST_PAD_PROBE_OK; /* don't drop data */ } static void pad_added_setup_data_check_float32_8ch_cb (GstElement * deinterleave, GstPad * pad, GstElement * pipeline) { GstElement *queue, *sink; GstPad *sinkpad; queue = gst_element_factory_make ("queue", NULL); fail_unless (queue != NULL); sink = gst_element_factory_make ("fakesink", NULL); fail_unless (sink != NULL); gst_bin_add_many (GST_BIN (pipeline), queue, sink, NULL); gst_element_link_pads_full (queue, "src", sink, "sink", GST_PAD_LINK_CHECK_NOTHING); sinkpad = gst_element_get_static_pad (queue, "sink"); fail_unless_equals_int (gst_pad_link (pad, sinkpad), GST_PAD_LINK_OK); gst_object_unref (sinkpad); gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, float_buffer_check_probe, GUINT_TO_POINTER (pads_created), NULL); gst_element_set_state (sink, GST_STATE_PLAYING); gst_element_set_state (queue, GST_STATE_PLAYING); GST_LOG ("new pad: %s", GST_PAD_NAME (pad)); ++pads_created; } static GstElement * make_fake_src_8chans_float32 (void) { GstElement *src; GstPad *pad; src = gst_element_factory_make ("fakesrc", "src"); fail_unless (src != NULL, "failed to create fakesrc element"); g_object_set (src, "num-buffers", 1, NULL); g_object_set (src, "signal-handoffs", TRUE, NULL); g_signal_connect (src, "handoff", G_CALLBACK (src_handoff_float32_8ch), NULL); pad = gst_element_get_static_pad (src, "src"); gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, src_event_probe, NULL, NULL); gst_object_unref (pad); return src; } GST_START_TEST (test_8_channels_float32) { GstElement *pipeline, *src, *deinterleave; GstMessage *msg; pipeline = (GstElement *) gst_pipeline_new ("pipeline"); fail_unless (pipeline != NULL, "failed to create pipeline"); src = make_fake_src_8chans_float32 (); deinterleave = gst_element_factory_make ("deinterleave", "deinterleave"); fail_unless (deinterleave != NULL, "failed to create deinterleave element"); g_object_set (deinterleave, "keep-positions", TRUE, NULL); gst_bin_add_many (GST_BIN (pipeline), src, deinterleave, NULL); fail_unless (gst_element_link (src, deinterleave), "failed to link src <=> deinterleave"); g_signal_connect (deinterleave, "pad-added", G_CALLBACK (pad_added_setup_data_check_float32_8ch_cb), pipeline); pads_created = 0; gst_element_set_state (pipeline, GST_STATE_PLAYING); msg = gst_bus_poll (GST_ELEMENT_BUS (pipeline), GST_MESSAGE_EOS, -1); gst_message_unref (msg); fail_unless_equals_int (pads_created, NUM_CHANNELS); gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (pipeline); } GST_END_TEST; static Suite * deinterleave_suite (void) { Suite *s = suite_create ("deinterleave"); TCase *tc_chain = tcase_create ("general"); suite_add_tcase (s, tc_chain); tcase_set_timeout (tc_chain, 180); tcase_add_test (tc_chain, test_create_and_unref); tcase_add_test (tc_chain, test_2_channels); tcase_add_test (tc_chain, test_2_channels_1_linked); tcase_add_test (tc_chain, test_2_channels_caps_change); tcase_add_test (tc_chain, test_8_channels_float32); return s; } GST_CHECK_MAIN (deinterleave);