gstreamer/tests/check/elements/matroskamux.c
Tim-Philipp Müller 672ab8fb5b webmux: fix linking with shout2send element
Shout2send only accepts webm format, not matroska, but due
to a bug in matroskamux, webmmux's source pad is also created
with the matroska source pad template as pad template, which
makes the link function think it can't link webmmux to shout2send.

Also add unit test.

https://bugzilla.gnome.org/show_bug.cgi?id=689336
2012-11-30 17:22:34 +00:00

496 lines
14 KiB
C

/* GStreamer
*
* unit test for matroskamux
*
* Copyright (C) <2005> Michal Benes <michal.benes@xeris.cz>
*
* 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.
*/
#include <unistd.h>
#include <gst/check/gstcheck.h>
#include <gst/base/gstadapter.h>
/* For ease of programming we use globals to keep refs for our floating
* src and sink pads we create; otherwise we always have to do get_pad,
* get_peer, and then remove references in every test function */
GstPad *mysrcpad, *mysinkpad;
#define AC3_CAPS_STRING "audio/x-ac3, " \
"channels = (int) 1, " \
"rate = (int) 8000"
#define VORBIS_CAPS_STRING "audio/x-vorbis, " \
"channels = (int) 1, " \
"rate = (int) 8000, " \
"streamheader=(buffer)<10, 2020, 303030>"
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-matroska; audio/x-matroska"));
static GstStaticPadTemplate srcvorbistemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (VORBIS_CAPS_STRING));
static GstStaticPadTemplate srcac3template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (AC3_CAPS_STRING));
static GstPad *
setup_src_pad (GstElement * element,
GstStaticPadTemplate * template, GstCaps * caps)
{
GstPad *srcpad, *sinkpad;
GST_DEBUG_OBJECT (element, "setting up sending pad");
/* sending pad */
srcpad = gst_pad_new_from_static_template (template, "src");
fail_if (srcpad == NULL, "Could not create a srcpad");
ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1);
gst_pad_set_active (srcpad, TRUE);
if (!(sinkpad = gst_element_get_static_pad (element, "audio_%u")))
sinkpad = gst_element_get_request_pad (element, "audio_%u");
fail_if (sinkpad == NULL, "Could not get sink pad from %s",
GST_ELEMENT_NAME (element));
/* references are owned by: 1) us, 2) matroskamux, 3) collect pads */
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
if (caps)
fail_unless (gst_pad_set_caps (srcpad, caps));
fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
"Could not link source and %s sink pads", GST_ELEMENT_NAME (element));
gst_object_unref (sinkpad); /* because we got it higher up */
/* references are owned by: 1) matroskamux, 2) collect pads */
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
return srcpad;
}
static void
teardown_src_pad (GstElement * element)
{
GstPad *srcpad, *sinkpad;
/* clean up floating src pad */
if (!(sinkpad = gst_element_get_static_pad (element, "audio_0")))
sinkpad = gst_element_get_request_pad (element, "audio_0");
/* references are owned by: 1) us, 2) matroskamux, 3) collect pads */
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
srcpad = gst_pad_get_peer (sinkpad);
gst_pad_unlink (srcpad, sinkpad);
/* references are owned by: 1) us, 2) matroskamux, 3) collect pads */
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
gst_object_unref (sinkpad);
/* one more ref is held by element itself */
/* pad refs held by both creator and this function (through _get_peer) */
ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 2);
gst_object_unref (srcpad);
gst_object_unref (srcpad);
}
static GstPad *
setup_sink_pad (GstElement * element, GstStaticPadTemplate * template,
GstCaps * caps)
{
GstPad *srcpad, *sinkpad;
GST_DEBUG_OBJECT (element, "setting up receiving pad");
/* receiving pad */
sinkpad = gst_pad_new_from_static_template (template, "sink");
fail_if (sinkpad == NULL, "Could not create a sinkpad");
gst_pad_set_active (sinkpad, TRUE);
srcpad = gst_element_get_static_pad (element, "src");
fail_if (srcpad == NULL, "Could not get source pad from %s",
GST_ELEMENT_NAME (element));
if (caps)
fail_unless (gst_pad_set_caps (sinkpad, caps));
gst_pad_set_chain_function (sinkpad, gst_check_chain_func);
fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
"Could not link %s source and sink pads", GST_ELEMENT_NAME (element));
gst_object_unref (srcpad); /* because we got it higher up */
ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 2);
return sinkpad;
}
static void
teardown_sink_pad (GstElement * element)
{
GstPad *srcpad, *sinkpad;
/* clean up floating sink pad */
srcpad = gst_element_get_static_pad (element, "src");
sinkpad = gst_pad_get_peer (srcpad);
gst_pad_unlink (srcpad, sinkpad);
/* pad refs held by both creator and this function (through _get_pad) */
ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 3);
gst_object_unref (srcpad);
/* one more ref is held by element itself */
/* pad refs held by both creator and this function (through _get_peer) */
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
gst_object_unref (sinkpad);
gst_object_unref (sinkpad);
}
static GstElement *
setup_matroskamux (GstStaticPadTemplate * srctemplate)
{
GstElement *matroskamux;
GstSegment segment;
GST_DEBUG ("setup_matroskamux");
matroskamux = gst_check_setup_element ("matroskamux");
g_object_set (matroskamux, "version", 1, NULL);
mysrcpad = setup_src_pad (matroskamux, srctemplate, NULL);
mysinkpad = setup_sink_pad (matroskamux, &sinktemplate, NULL);
fail_unless (gst_element_set_state (matroskamux,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
gst_segment_init (&segment, GST_FORMAT_TIME);
fail_unless (gst_pad_push_event (mysrcpad,
gst_event_new_segment (&segment)), "Segment event rejected");
return matroskamux;
}
static void
cleanup_matroskamux (GstElement * matroskamux)
{
GST_DEBUG ("cleanup_matroskamux");
gst_element_set_state (matroskamux, GST_STATE_NULL);
teardown_src_pad (matroskamux);
teardown_sink_pad (matroskamux);
gst_check_teardown_element (matroskamux);
}
static void
check_buffer_data (GstBuffer * buffer, void *data, size_t data_size)
{
fail_unless (gst_buffer_get_size (buffer) == data_size);
fail_unless (gst_buffer_memcmp (buffer, 0, data, data_size) == 0);
}
GST_START_TEST (test_ebml_header)
{
GstElement *matroskamux;
GstBuffer *inbuffer, *outbuffer;
GstAdapter *adapter;
int num_buffers;
int i;
gint available;
guint8 data[] =
{ 0x1a, 0x45, 0xdf, 0xa3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
0x42, 0x82, 0x89, 0x6d, 0x61, 0x74, 0x72, 0x6f, 0x73, 0x6b, 0x61, 0x00,
0x42, 0x87, 0x81, 0x01,
0x42, 0x85, 0x81, 0x01
};
matroskamux = setup_matroskamux (&srcac3template);
inbuffer = gst_buffer_new_allocate (NULL, 1, 0);
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
num_buffers = g_list_length (buffers);
fail_unless (num_buffers >= 1,
"expected at least 5 buffers, but got only %d", num_buffers);
adapter = gst_adapter_new ();
for (i = 0; i < num_buffers; ++i) {
outbuffer = GST_BUFFER (buffers->data);
fail_if (outbuffer == NULL);
buffers = g_list_remove (buffers, outbuffer);
ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
gst_adapter_push (adapter, outbuffer);
}
available = gst_adapter_available (adapter);
fail_unless (available >= sizeof (data));
outbuffer = gst_adapter_take_buffer (adapter, sizeof (data));
g_object_unref (adapter);
check_buffer_data (outbuffer, data, sizeof (data));
gst_buffer_unref (outbuffer);
cleanup_matroskamux (matroskamux);
g_list_free (buffers);
buffers = NULL;
}
GST_END_TEST;
GST_START_TEST (test_vorbis_header)
{
GstElement *matroskamux;
GstBuffer *inbuffer, *outbuffer;
GstCaps *caps;
int num_buffers;
int i;
gboolean vorbis_header_found = FALSE;
guint8 data[12] =
{ 0x63, 0xa2, 0x89, 0x02, 0x01, 0x02, 0x10, 0x20, 0x20, 0x30, 0x30,
0x30
};
matroskamux = setup_matroskamux (&srcvorbistemplate);
caps = gst_caps_from_string (VORBIS_CAPS_STRING);
fail_unless (gst_pad_set_caps (mysrcpad, caps));
gst_caps_unref (caps);
inbuffer = gst_buffer_new_allocate (NULL, 1, 0);
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
num_buffers = g_list_length (buffers);
for (i = 0; i < num_buffers; ++i) {
gint j;
gsize buffer_size;
outbuffer = GST_BUFFER (buffers->data);
fail_if (outbuffer == NULL);
buffer_size = gst_buffer_get_size (outbuffer);
buffers = g_list_remove (buffers, outbuffer);
if (!vorbis_header_found && buffer_size >= sizeof (data)) {
for (j = 0; j <= buffer_size - sizeof (data); j++) {
if (gst_buffer_memcmp (outbuffer, j, data, sizeof (data)) == 0) {
vorbis_header_found = TRUE;
break;
}
}
}
ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
gst_buffer_unref (outbuffer);
outbuffer = NULL;
}
fail_unless (vorbis_header_found);
cleanup_matroskamux (matroskamux);
g_list_free (buffers);
buffers = NULL;
}
GST_END_TEST;
GST_START_TEST (test_block_group)
{
GstElement *matroskamux;
GstBuffer *inbuffer, *outbuffer;
guint8 *indata;
GstCaps *caps;
int num_buffers;
int i;
guint8 data0[] = { 0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0xa1, 0x85,
0x81, 0x00, 0x01, 0x00
};
guint8 data1[] = { 0x42 };
matroskamux = setup_matroskamux (&srcac3template);
caps = gst_caps_from_string (AC3_CAPS_STRING);
fail_unless (gst_pad_set_caps (mysrcpad, caps));
gst_caps_unref (caps);
/* Generate the header */
inbuffer = gst_buffer_new_allocate (NULL, 1, 0);
GST_BUFFER_TIMESTAMP (inbuffer) = 0;
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer), GST_FLOW_OK);
num_buffers = g_list_length (buffers);
for (i = 0; i < num_buffers; ++i) {
outbuffer = GST_BUFFER (buffers->data);
fail_if (outbuffer == NULL);
buffers = g_list_remove (buffers, outbuffer);
ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
gst_buffer_unref (outbuffer);
outbuffer = NULL;
}
g_list_free (buffers);
buffers = NULL;
/* Now push a buffer */
indata = g_malloc (1);
inbuffer = gst_buffer_new_wrapped (indata, 1);
indata[0] = 0x42;
GST_BUFFER_TIMESTAMP (inbuffer) = 1000000;
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
num_buffers = g_list_length (buffers);
fail_unless (num_buffers >= 2);
for (i = 0; i < num_buffers; ++i) {
outbuffer = GST_BUFFER (buffers->data);
fail_if (outbuffer == NULL);
buffers = g_list_remove (buffers, outbuffer);
switch (i) {
case 0:
check_buffer_data (outbuffer, data0, sizeof (data0));
break;
case 1:
check_buffer_data (outbuffer, data1, sizeof (data1));
break;
default:
break;
}
ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
gst_buffer_unref (outbuffer);
outbuffer = NULL;
}
g_list_free (buffers);
buffers = NULL;
cleanup_matroskamux (matroskamux);
}
GST_END_TEST;
GST_START_TEST (test_reset)
{
GstElement *matroskamux;
GstBuffer *inbuffer;
GstBuffer *outbuffer;
int num_buffers;
int i;
matroskamux = setup_matroskamux (&srcac3template);
inbuffer = gst_buffer_new_allocate (NULL, 1, 0);
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
num_buffers = g_list_length (buffers);
fail_unless (num_buffers >= 1,
"expected at least 1 buffer, but got only %d", num_buffers);
fail_unless (gst_element_set_state (matroskamux,
GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
fail_unless (gst_element_set_state (matroskamux,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
inbuffer = gst_buffer_new_allocate (NULL, 1, 0);
ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
num_buffers = g_list_length (buffers);
fail_unless (num_buffers >= 2,
"expected at least 2 buffers, but got only %d", num_buffers);
for (i = 0; i < num_buffers; ++i) {
outbuffer = GST_BUFFER (buffers->data);
fail_if (outbuffer == NULL);
buffers = g_list_remove (buffers, outbuffer);
ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
gst_buffer_unref (outbuffer);
}
cleanup_matroskamux (matroskamux);
g_list_free (buffers);
buffers = NULL;
}
GST_END_TEST;
GST_START_TEST (test_link_webmmux_webm_sink)
{
static GstStaticPadTemplate webm_sinktemplate =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/webm; audio/webm"));
GstElement *mux;
mux = gst_check_setup_element ("webmmux");
mysinkpad = setup_sink_pad (mux, &webm_sinktemplate, NULL);
fail_unless (mysinkpad != NULL);
fail_unless (gst_element_set_state (mux,
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
"could not set to playing");
gst_element_set_state (mux, GST_STATE_NULL);
teardown_sink_pad (mux);
gst_check_teardown_element (mux);
}
GST_END_TEST;
static Suite *
matroskamux_suite (void)
{
Suite *s = suite_create ("matroskamux");
TCase *tc_chain = tcase_create ("general");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_ebml_header);
tcase_add_test (tc_chain, test_vorbis_header);
tcase_add_test (tc_chain, test_block_group);
tcase_add_test (tc_chain, test_reset);
tcase_add_test (tc_chain, test_link_webmmux_webm_sink);
return s;
}
int
main (int argc, char **argv)
{
int nf;
Suite *s = matroskamux_suite ();
SRunner *sr = srunner_create (s);
gst_check_init (&argc, &argv);
srunner_run_all (sr, CK_NORMAL);
nf = srunner_ntests_failed (sr);
srunner_free (sr);
return nf;
}