mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-24 18:51:11 +00:00
672ab8fb5b
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
496 lines
14 KiB
C
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;
|
|
}
|