static GstFlowReturn gst_vorbisenc_output_buffers (GstVorbisEnc * vorbisenc);
-/* FIXME:
- * vorbis_granule_time was added between 1.0 and 1.0.1; it's too silly
- * to require a new version for such a simple function, but once we move
- * beyond 1.0 for other reasons we can remove this copy */
-
-static double
-vorbis_granule_time_copy (vorbis_dsp_state * v, ogg_int64_t granulepos)
+static GstClockTime
+granulepos_to_clocktime (GstVorbisEnc * vorbisenc, ogg_int64_t granulepos)
{
if (granulepos >= 0)
- return ((double) granulepos / v->vi->rate);
- return (-1);
+ return gst_util_uint64_scale ((guint64) granulepos
+ + vorbisenc->granulepos_offset, GST_SECOND, vorbisenc->frequency)
+ + vorbisenc->subgranule_offset;
+ return GST_CLOCK_TIME_NONE;
}
#if 0
vorbisenc->quality = QUALITY_DEFAULT;
vorbisenc->quality_set = FALSE;
vorbisenc->last_message = NULL;
-
}
vorbis_analysis_init (&vorbisenc->vd, &vorbisenc->vi);
vorbis_block_init (&vorbisenc->vd, &vorbisenc->vb);
- vorbisenc->prev_ts = 0;
+ vorbisenc->next_ts = 0;
vorbisenc->setup = TRUE;
outbuf = gst_buffer_new_and_alloc (packet->bytes);
memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes);
GST_BUFFER_OFFSET (outbuf) = vorbisenc->bytes_out;
- GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos;
- GST_BUFFER_TIMESTAMP (outbuf) =
- vorbis_granule_time_copy (&vorbisenc->vd,
- packet->granulepos) * GST_SECOND;
+ GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos +
+ vorbisenc->granulepos_offset;
+ GST_BUFFER_TIMESTAMP (outbuf) = vorbisenc->next_ts;
+
+ /* need to pass in an unadjusted granulepos here */
+ vorbisenc->next_ts = granulepos_to_clocktime (vorbisenc, packet->granulepos);
+
GST_BUFFER_DURATION (outbuf) =
- GST_BUFFER_TIMESTAMP (outbuf) - vorbisenc->prev_ts;
- vorbisenc->prev_ts = GST_BUFFER_TIMESTAMP (outbuf);
+ vorbisenc->next_ts - GST_BUFFER_TIMESTAMP (outbuf);
GST_DEBUG ("encoded buffer of %d bytes", GST_BUFFER_SIZE (outbuf));
return outbuf;
}
+/* the same as above, but different logic for setting timestamp and granulepos
+ * */
+static GstBuffer *
+gst_vorbisenc_buffer_from_header_packet (GstVorbisEnc * vorbisenc,
+ ogg_packet * packet)
+{
+ GstBuffer *outbuf;
+
+ outbuf = gst_buffer_new_and_alloc (packet->bytes);
+ memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes);
+ GST_BUFFER_OFFSET (outbuf) = vorbisenc->bytes_out;
+ GST_BUFFER_OFFSET_END (outbuf) = 0;
+ GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
+
+ GST_DEBUG ("created header packet buffer, %d bytes",
+ GST_BUFFER_SIZE (outbuf));
+ return outbuf;
+}
+
/* push out the buffer and do internal bookkeeping */
static GstFlowReturn
gst_vorbisenc_push_buffer (GstVorbisEnc * vorbisenc, GstBuffer * buffer)
static GstFlowReturn
gst_vorbisenc_chain (GstPad * pad, GstBuffer * buffer)
{
- GstBuffer *buf = GST_BUFFER (buffer);
GstVorbisEnc *vorbisenc;
GstFlowReturn ret = GST_FLOW_OK;
+ gfloat *data;
+ gulong size;
+ gulong i, j;
+ float **vorbis_buffer;
vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad));
- {
- gfloat *data;
- gulong size;
- gulong i, j;
- float **buffer;
-
- if (!vorbisenc->setup) {
- gst_buffer_unref (buf);
- GST_ELEMENT_ERROR (vorbisenc, CORE, NEGOTIATION, (NULL),
- ("encoder not initialized (input is not audio?)"));
- return GST_FLOW_UNEXPECTED;
- }
-
- if (!vorbisenc->header_sent) {
- /* Vorbis streams begin with three headers; the initial header (with
- most of the codec setup parameters) which is mandated by the Ogg
- bitstream spec. The second header holds any comment fields. The
- third header holds the bitstream codebook. We merely need to
- make the headers, then pass them to libvorbis one at a time;
- libvorbis handles the additional Ogg bitstream constraints */
- ogg_packet header;
- ogg_packet header_comm;
- ogg_packet header_code;
- GstBuffer *buf1, *buf2, *buf3;
- GstCaps *caps;
-
- GST_DEBUG_OBJECT (vorbisenc, "creating and sending header packets");
- gst_vorbisenc_set_metadata (vorbisenc);
- vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header,
- &header_comm, &header_code);
-
- /* create header buffers */
- buf1 = gst_vorbisenc_buffer_from_packet (vorbisenc, &header);
- buf2 = gst_vorbisenc_buffer_from_packet (vorbisenc, &header_comm);
- buf3 = gst_vorbisenc_buffer_from_packet (vorbisenc, &header_code);
-
- /* mark and put on caps */
- caps = gst_pad_get_caps (vorbisenc->srcpad);
- caps = gst_vorbisenc_set_header_on_caps (caps, buf1, buf2, buf3);
-
- /* negotiate with these caps */
- GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
- gst_pad_set_caps (vorbisenc->srcpad, caps);
-
- gst_buffer_set_caps (buf1, caps);
- gst_buffer_set_caps (buf2, caps);
- gst_buffer_set_caps (buf3, caps);
-
- /* push out buffers */
- if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf1)) != GST_FLOW_OK)
- goto done;
- if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf2)) != GST_FLOW_OK)
- goto done;
- if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf3)) != GST_FLOW_OK)
- goto done;
-
- vorbisenc->header_sent = TRUE;
- }
+ if (!vorbisenc->setup)
+ goto not_setup;
+
+ if (!vorbisenc->header_sent) {
+ /* Vorbis streams begin with three headers; the initial header (with
+ most of the codec setup parameters) which is mandated by the Ogg
+ bitstream spec. The second header holds any comment fields. The
+ third header holds the bitstream codebook. We merely need to
+ make the headers, then pass them to libvorbis one at a time;
+ libvorbis handles the additional Ogg bitstream constraints */
+ ogg_packet header;
+ ogg_packet header_comm;
+ ogg_packet header_code;
+ GstBuffer *buf1, *buf2, *buf3;
+ GstCaps *caps;
+
+ /* first, make sure header buffers get timestamp == 0 */
+ vorbisenc->next_ts = 0;
+ vorbisenc->granulepos_offset = 0;
+ vorbisenc->subgranule_offset = 0;
+
+ GST_DEBUG_OBJECT (vorbisenc, "creating and sending header packets");
+ gst_vorbisenc_set_metadata (vorbisenc);
+ vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header,
+ &header_comm, &header_code);
+
+ /* create header buffers */
+ buf1 = gst_vorbisenc_buffer_from_header_packet (vorbisenc, &header);
+ buf2 = gst_vorbisenc_buffer_from_header_packet (vorbisenc, &header_comm);
+ buf3 = gst_vorbisenc_buffer_from_header_packet (vorbisenc, &header_code);
+
+ /* mark and put on caps */
+ caps = gst_pad_get_caps (vorbisenc->srcpad);
+ caps = gst_vorbisenc_set_header_on_caps (caps, buf1, buf2, buf3);
+
+ /* negotiate with these caps */
+ GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
+ gst_pad_set_caps (vorbisenc->srcpad, caps);
+
+ gst_buffer_set_caps (buf1, caps);
+ gst_buffer_set_caps (buf2, caps);
+ gst_buffer_set_caps (buf3, caps);
+
+ /* push out buffers */
+ if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf1)) != GST_FLOW_OK)
+ goto failed_header_push;
+ if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf2)) != GST_FLOW_OK)
+ goto failed_header_push;
+ if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf3)) != GST_FLOW_OK)
+ goto failed_header_push;
+
+
+ /* now adjust starting granulepos accordingly if the buffer's timestamp is
+ nonzero */
+ vorbisenc->next_ts = GST_BUFFER_TIMESTAMP (buffer);
+ vorbisenc->granulepos_offset = gst_util_uint64_scale
+ (GST_BUFFER_TIMESTAMP (buffer), vorbisenc->frequency, GST_SECOND);
+ vorbisenc->subgranule_offset = 0;
+ vorbisenc->subgranule_offset =
+ vorbisenc->next_ts - granulepos_to_clocktime (vorbisenc, 0);
+
+ vorbisenc->header_sent = TRUE;
+ }
- /* data to encode */
- data = (gfloat *) GST_BUFFER_DATA (buf);
- size = GST_BUFFER_SIZE (buf) / (vorbisenc->channels * sizeof (float));
+ /* data to encode */
+ data = (gfloat *) GST_BUFFER_DATA (buffer);
+ size = GST_BUFFER_SIZE (buffer) / (vorbisenc->channels * sizeof (float));
- /* expose the buffer to submit data */
- buffer = vorbis_analysis_buffer (&vorbisenc->vd, size);
+ /* expose the buffer to submit data */
+ vorbis_buffer = vorbis_analysis_buffer (&vorbisenc->vd, size);
- /* uninterleave samples */
- for (i = 0; i < size; i++) {
- for (j = 0; j < vorbisenc->channels; j++) {
- buffer[j][i] = *data++;
- }
+ /* deinterleave samples, write the buffer data */
+ for (i = 0; i < size; i++) {
+ for (j = 0; j < vorbisenc->channels; j++) {
+ vorbis_buffer[j][i] = *data++;
}
+ }
- /* tell the library how much we actually submitted */
- vorbis_analysis_wrote (&vorbisenc->vd, size);
+ /* tell the library how much we actually submitted */
+ vorbis_analysis_wrote (&vorbisenc->vd, size);
- vorbisenc->samples_in += size;
+ vorbisenc->samples_in += size;
- gst_buffer_unref (buf);
- }
+ gst_buffer_unref (buffer);
ret = gst_vorbisenc_output_buffers (vorbisenc);
-done:
return ret;
+
+ /* error cases */
+not_setup:
+ {
+ gst_buffer_unref (buffer);
+ GST_ELEMENT_ERROR (vorbisenc, CORE, NEGOTIATION, (NULL),
+ ("encoder not initialized (input is not audio?)"));
+ return GST_FLOW_UNEXPECTED;
+ }
+failed_header_push:
+ {
+ gst_buffer_unref (buffer);
+ return ret;
+ }
}
static GstFlowReturn
--- /dev/null
+/* GStreamer
+ *
+ * unit test for vorbisenc
+ *
+ * Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart dot org>
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gst/check/gstcheck.h>
+
+#define TIMESTAMP_OFFSET G_GUINT64_CONSTANT(3249870963)
+
+static GCond *cond = NULL;
+static GMutex *lock = NULL;
+static GstBuffer *buf = NULL;
+static gulong id;
+
+static gboolean
+buffer_probe (GstPad * pad, GstBuffer * buffer, gpointer unused)
+{
+ g_mutex_lock (lock);
+
+ while (buf != NULL)
+ g_cond_wait (cond, lock);
+
+ buf = gst_buffer_ref (buffer);
+
+ g_cond_signal (cond);
+
+ g_mutex_unlock (lock);
+
+ return TRUE;
+}
+
+static void
+start_pipeline (GstElement * bin, GstPad * pad)
+{
+ id = gst_pad_add_buffer_probe (pad, G_CALLBACK (buffer_probe), NULL);
+
+ cond = g_cond_new ();
+ lock = g_mutex_new ();
+
+ gst_element_set_state (bin, GST_STATE_PLAYING);
+
+}
+
+static GstBuffer *
+get_buffer (GstElement * bin, GstPad * pad)
+{
+ GstBuffer *ret;
+
+ g_mutex_lock (lock);
+
+ while (buf == NULL)
+ g_cond_wait (cond, lock);
+
+ ret = buf;
+ buf = NULL;
+
+ g_cond_signal (cond);
+
+ g_mutex_unlock (lock);
+
+ return ret;
+}
+
+static void
+stop_pipeline (GstElement * bin, GstPad * pad)
+{
+ g_mutex_lock (lock);
+ if (buf)
+ gst_buffer_unref (buf);
+ buf = NULL;
+ gst_pad_remove_buffer_probe (pad, (guint) id);
+ id = 0;
+ g_cond_signal (cond);
+ g_mutex_unlock (lock);
+
+ gst_element_set_state (bin, GST_STATE_NULL);
+
+ g_mutex_lock (lock);
+ if (buf)
+ gst_buffer_unref (buf);
+ buf = NULL;
+ g_mutex_unlock (lock);
+
+ g_mutex_free (lock);
+ g_cond_free (cond);
+
+ lock = NULL;
+ cond = NULL;
+}
+
+static void
+check_buffer_timestamp (GstBuffer * buffer, GstClockTime timestamp)
+{
+ fail_unless (GST_BUFFER_TIMESTAMP (buffer) == timestamp,
+ "expected timestamp %" GST_TIME_FORMAT
+ ", but got timestamp %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (timestamp), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
+}
+
+static void
+check_buffer_duration (GstBuffer * buffer, GstClockTime duration)
+{
+ fail_unless (GST_BUFFER_DURATION (buffer) == duration,
+ "expected duration %" GST_TIME_FORMAT
+ ", but got duration %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (duration), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
+}
+
+static void
+check_buffer_granulepos (GstBuffer * buffer, GstClockTime granulepos)
+{
+ fail_unless (GST_BUFFER_OFFSET_END (buffer) == granulepos,
+ "expected granulepos %" G_GUINT64_FORMAT
+ ", but got granulepos %" G_GUINT64_FORMAT,
+ granulepos, GST_BUFFER_OFFSET_END (buffer));
+}
+
+/* this check is here to check that the granulepos we derive from the timestamp
+ is about correct. This is "about correct" because you can't precisely go from
+ timestamp to granulepos due to the downward-rounding characteristics of
+ gst_util_uint64_scale, so you check if granulepos is equal to the number, or
+ the number plus one. */
+static void
+check_buffer_granulepos_from_endtime (GstBuffer * buffer, GstClockTime endtime)
+{
+ GstClockTime granulepos, expected;
+
+ granulepos = GST_BUFFER_OFFSET_END (buffer);
+ expected = gst_util_uint64_scale (endtime, 44100, GST_SECOND);
+
+ fail_unless (granulepos == expected || granulepos == expected + 1,
+ "expected granulepos %" G_GUINT64_FORMAT
+ " or %" G_GUINT64_FORMAT
+ ", but got granulepos %" G_GUINT64_FORMAT,
+ expected, expected + 1, granulepos);
+}
+
+GST_START_TEST (test_granulepos_offset)
+{
+ GstElement *bin;
+ GstPad *pad;
+ gchar *pipe_str;
+ GstBuffer *buffer;
+ GError *error = NULL;
+ GstClockTime timestamp;
+
+ pipe_str = g_strdup_printf ("audiotestsrc timestamp-offset=%" G_GUINT64_FORMAT
+ " ! audio/x-raw-int,rate=44100"
+ " ! audioconvert ! vorbisenc ! fakesink", TIMESTAMP_OFFSET);
+
+ bin = gst_parse_launch (pipe_str, &error);
+ fail_unless (bin != NULL, "Error parsing pipeline: %s",
+ error ? error->message : "(invalid error)");
+ g_free (pipe_str);
+
+ /* get the pad */
+ {
+ GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "fakesink0");
+
+ fail_unless (sink != NULL, "Could not get fakesink out of bin");
+ pad = gst_element_get_pad (sink, "sink");
+ fail_unless (pad != NULL, "Could not get pad out of fakesink");
+ gst_object_unref (sink);
+ }
+
+ start_pipeline (bin, pad);
+
+ /* header packets should have timestamp == NONE, granulepos 0 */
+ buffer = get_buffer (bin, pad);
+ check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
+ check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
+ check_buffer_granulepos (buffer, 0);
+ gst_buffer_unref (buffer);
+
+ buffer = get_buffer (bin, pad);
+ check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
+ check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
+ check_buffer_granulepos (buffer, 0);
+ gst_buffer_unref (buffer);
+
+ buffer = get_buffer (bin, pad);
+ check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
+ check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
+ check_buffer_granulepos (buffer, 0);
+ gst_buffer_unref (buffer);
+
+ {
+ GstClockTime next_timestamp, last_granulepos;
+
+ /* first buffer should have timestamp of TIMESTAMP_OFFSET, granulepos to
+ * match the timestamp of the end of the last sample in the output buffer.
+ * Note that one cannot go timestamp->granulepos->timestamp and get the same
+ * value due to loss of precision with granulepos. vorbisenc does take care
+ * to timestamp correctly based on the offset of the input data however, so
+ * it does do sub-granulepos timestamping. */
+ buffer = get_buffer (bin, pad);
+ last_granulepos = GST_BUFFER_OFFSET_END (buffer);
+ check_buffer_timestamp (buffer, TIMESTAMP_OFFSET);
+ /* don't really have a good way of checking duration... */
+ check_buffer_granulepos_from_endtime (buffer,
+ TIMESTAMP_OFFSET + GST_BUFFER_DURATION (buffer));
+
+ next_timestamp = TIMESTAMP_OFFSET + GST_BUFFER_DURATION (buffer);
+
+ gst_buffer_unref (buffer);
+
+ /* check continuity with the next buffer */
+ buffer = get_buffer (bin, pad);
+ check_buffer_timestamp (buffer, next_timestamp);
+ check_buffer_duration (buffer,
+ gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), GST_SECOND,
+ 44100)
+ - gst_util_uint64_scale (last_granulepos, GST_SECOND, 44100));
+ check_buffer_granulepos_from_endtime (buffer,
+ next_timestamp + GST_BUFFER_DURATION (buffer));
+
+ gst_buffer_unref (buffer);
+ }
+
+ stop_pipeline (bin, pad);
+
+ gst_object_unref (pad);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_timestamps)
+{
+ GstElement *bin;
+ GstPad *pad;
+ gchar *pipe_str;
+ GstBuffer *buffer;
+ GError *error = NULL;
+ GstClockTime timestamp;
+
+ pipe_str = g_strdup_printf ("audiotestsrc"
+ " ! audio/x-raw-int,rate=44100" " ! audioconvert ! vorbisenc ! fakesink");
+
+ bin = gst_parse_launch (pipe_str, &error);
+ fail_unless (bin != NULL, "Error parsing pipeline: %s",
+ error ? error->message : "(invalid error)");
+ g_free (pipe_str);
+
+ /* get the pad */
+ {
+ GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "fakesink0");
+
+ fail_unless (sink != NULL, "Could not get fakesink out of bin");
+ pad = gst_element_get_pad (sink, "sink");
+ fail_unless (pad != NULL, "Could not get pad out of fakesink");
+ gst_object_unref (sink);
+ }
+
+ start_pipeline (bin, pad);
+
+ /* check header packets */
+ buffer = get_buffer (bin, pad);
+ check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
+ check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
+ check_buffer_granulepos (buffer, 0);
+ gst_buffer_unref (buffer);
+
+ buffer = get_buffer (bin, pad);
+ check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
+ check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
+ check_buffer_granulepos (buffer, 0);
+ gst_buffer_unref (buffer);
+
+ buffer = get_buffer (bin, pad);
+ check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
+ check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
+ check_buffer_granulepos (buffer, 0);
+ gst_buffer_unref (buffer);
+
+ {
+ GstClockTime next_timestamp, last_granulepos;
+
+ /* first buffer has timestamp 0 */
+ buffer = get_buffer (bin, pad);
+ last_granulepos = GST_BUFFER_OFFSET_END (buffer);
+ check_buffer_timestamp (buffer, 0);
+ /* don't really have a good way of checking duration... */
+ check_buffer_granulepos_from_endtime (buffer, GST_BUFFER_DURATION (buffer));
+
+ next_timestamp = GST_BUFFER_DURATION (buffer);
+
+ gst_buffer_unref (buffer);
+
+ /* check continuity with the next buffer */
+ buffer = get_buffer (bin, pad);
+ check_buffer_timestamp (buffer, next_timestamp);
+ check_buffer_duration (buffer,
+ gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), GST_SECOND,
+ 44100)
+ - gst_util_uint64_scale (last_granulepos, GST_SECOND, 44100));
+ check_buffer_granulepos_from_endtime (buffer,
+ next_timestamp + GST_BUFFER_DURATION (buffer));
+
+ gst_buffer_unref (buffer);
+ }
+
+ stop_pipeline (bin, pad);
+
+ gst_object_unref (pad);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+Suite *
+vorbisenc_suite (void)
+{
+ Suite *s = suite_create ("vorbisenc");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_granulepos_offset);
+ tcase_add_test (tc_chain, test_timestamps);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = vorbisenc_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;
+}