From c9b637db9121fa20b9a6d23c1e10a7c72eee6d1c Mon Sep 17 00:00:00 2001 From: James Livingston Date: Mon, 4 Sep 2006 18:19:06 +0000 Subject: [PATCH] tests/check/: Add simple unit test for oggmux from #337026 with checking for the Original commit message from CVS: Patch by: James Livingston * tests/check/Makefile.am: * tests/check/pipelines/.cvsignore: * tests/check/pipelines/oggmux.c: (get_page_codec), (check_chain_final_state), (fail_if_audio), (validate_ogg_page), (eos_buffer_probe), (start_pipeline), (stop_pipeline), (eos_watch), (test_pipeline), (test_vorbis), (test_theora), (test_vorbis_theora), (test_theora_vorbis), (oggmux_suite): Add simple unit test for oggmux from #337026 with checking for the EOS flags disabled for the time being. --- ChangeLog | 14 ++ tests/check/Makefile.am | 14 +- tests/check/pipelines/.gitignore | 1 + tests/check/pipelines/oggmux.c | 327 +++++++++++++++++++++++++++++++ 4 files changed, 354 insertions(+), 2 deletions(-) create mode 100644 tests/check/pipelines/oggmux.c diff --git a/ChangeLog b/ChangeLog index d9aaa14017..2763ddce4b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2006-09-04 Tim-Philipp Müller + + Patch by: James Livingston + + * tests/check/Makefile.am: + * tests/check/pipelines/.cvsignore: + * tests/check/pipelines/oggmux.c: (get_page_codec), + (check_chain_final_state), (fail_if_audio), (validate_ogg_page), + (eos_buffer_probe), (start_pipeline), (stop_pipeline), (eos_watch), + (test_pipeline), (test_vorbis), (test_theora), (test_vorbis_theora), + (test_theora_vorbis), (oggmux_suite): + Add simple unit test for oggmux from #337026 with checking for the + EOS flags disabled for the time being. + 2006-09-04 Wim Taymans patch by: Alessandro Dessina diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index c388a6c6c4..f03497b30f 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -27,6 +27,12 @@ else check_alsa = endif +if USE_OGG +check_ogg = pipelines/oggmux +else +check_ogg = +endif + if USE_VORBIS check_vorbis = elements/vorbisdec pipelines/vorbisenc else @@ -41,9 +47,10 @@ endif check_PROGRAMS = \ $(check_alsa) \ + $(check_ogg) \ $(check_vorbis) \ $(check_theora) \ - elements/adder \ + elements/adder \ elements/audioconvert \ elements/audioresample \ elements/audiotestsrc \ @@ -59,7 +66,7 @@ check_PROGRAMS = \ libs/cddabasesrc \ libs/tag \ libs/video \ - pipelines/simple-launch-lines + pipelines/simple-launch-lines # TORTURE_TO_FIX = \ # elements/adder @@ -130,4 +137,7 @@ libs_video_LDADD = \ $(LDADD) libs_video_CFLAGS = -I$(top_srcdir)/gst-libs $(CFLAGS) $(AM_CFLAGS) +pipelines_oggmux_LDADD = $(OGG_LIBS) $(LDADD) +pipelines_oggmux_CFLAGS = $(OGG_CFLAGS) $(CFLAGS) $(AM_CFLAGS) + EXTRA_DIST = gst-plugins-base.supp diff --git a/tests/check/pipelines/.gitignore b/tests/check/pipelines/.gitignore index 70a2c5be79..516896e2d3 100644 --- a/tests/check/pipelines/.gitignore +++ b/tests/check/pipelines/.gitignore @@ -2,3 +2,4 @@ simple-launch-lines theoraenc vorbisenc +oggmux diff --git a/tests/check/pipelines/oggmux.c b/tests/check/pipelines/oggmux.c new file mode 100644 index 0000000000..ab945b091e --- /dev/null +++ b/tests/check/pipelines/oggmux.c @@ -0,0 +1,327 @@ +/* GStreamer + * + * unit tests for oggmux + * + * Copyright (C) 2006 James Livingston + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + + +typedef enum +{ + CODEC_UNKNOWN, + CODEC_VORBIS, + CODEC_THEORA, + CODEC_SPEEX, +} ChainCodec; + +typedef struct +{ + gboolean eos; + gulong serialno; + gint64 last_granule; + ChainCodec codec; +} ChainState; + +ogg_sync_state oggsync; +GHashTable *eos_chain_states; +static gulong probe_id; + + +static ChainCodec +get_page_codec (ogg_page * page) +{ + ChainCodec codec = CODEC_UNKNOWN; + ogg_stream_state state; + ogg_packet packet; + + ogg_stream_init (&state, ogg_page_serialno (page)); + if (ogg_stream_pagein (&state, page) == 0) { + if (ogg_stream_packetpeek (&state, &packet) > 0) { + if (strncmp ((char *) &packet.packet[1], "vorbis", + strlen ("vorbis")) == 0) + codec = CODEC_VORBIS; + else if (strncmp ((char *) &packet.packet[1], "theora", + strlen ("theora")) == 0) + codec = CODEC_THEORA; + else if (strncmp ((char *) &packet.packet[0], "Speex ", + strlen ("Speex ")) == 0) + codec = CODEC_SPEEX; + } + } + ogg_stream_clear (&state); + + return codec; +} + +static gboolean +check_chain_final_state (gpointer key, ChainState * state, gpointer data) +{ + /* FIXME: check disabled until oggmux is fixed (#337026) */ + /* fail_unless (state->eos, "missing EOS flag on chain %u", state->serialno); */ + + /* return TRUE to empty the chain table */ + return TRUE; +} + +static void +fail_if_audio (gpointer key, ChainState * state, gpointer data) +{ + fail_if (state->codec == CODEC_VORBIS, + "vorbis BOS occurred before theora BOS"); + fail_if (state->codec == CODEC_SPEEX, "speex BOS occurred before theora BOS"); +} + +static void +validate_ogg_page (ogg_page * page) +{ + gulong serialno; + gint64 granule; + ChainState *state; + + serialno = ogg_page_serialno (page); + granule = ogg_page_granulepos (page); + state = g_hash_table_lookup (eos_chain_states, GINT_TO_POINTER (serialno)); + + fail_if (ogg_page_packets (page) == 0 && granule != -1, + "Must have granulepos -1 when page has no packets, has %" G_GINT64_FORMAT, + granule); + + if (ogg_page_bos (page)) { + fail_unless (state == NULL, "Extraneous BOS flag on chain %u", serialno); + + state = g_new0 (ChainState, 1); + g_hash_table_insert (eos_chain_states, GINT_TO_POINTER (serialno), state); + state->serialno = serialno; + state->last_granule = granule; + state->codec = get_page_codec (page); + + /* check for things like BOS ordering, etc */ + switch (state->codec) { + case CODEC_THEORA: + /* check we have no vorbis/speex chains yet */ + g_hash_table_foreach (eos_chain_states, (GHFunc) fail_if_audio, NULL); + break; + case CODEC_VORBIS: + /* no checks (yet) */ + break; + case CODEC_UNKNOWN: + break; + } + } else if (ogg_page_eos (page)) { + fail_unless (state != NULL, "Missing BOS flag on chain %u", serialno); + state->eos = TRUE; + } else { + fail_unless (state != NULL, "Missing BOS flag on chain %u", serialno); + fail_unless (!state->eos, "Data after EOS flag on chain %u", serialno); + } + + if (granule != -1) { + fail_unless (granule >= state->last_granule, + "Granulepos out-of-order for chain %u: old=%" G_GINT64_FORMAT ", new=" + G_GINT64_FORMAT, serialno, state->last_granule, granule); + state->last_granule = granule; + } +} + +static gboolean +eos_buffer_probe (GstPad * pad, GstBuffer * buffer, gpointer unused) +{ + gint ret; + gint size; + guint8 *data; + gchar *oggbuffer; + + size = GST_BUFFER_SIZE (buffer); + data = GST_BUFFER_DATA (buffer); + + oggbuffer = ogg_sync_buffer (&oggsync, size); + memcpy (oggbuffer, data, size); + ogg_sync_wrote (&oggsync, size); + + while (ret != 0) { + ogg_page page; + + ret = ogg_sync_pageout (&oggsync, &page); + if (ret > 0) + validate_ogg_page (&page); + } + + return TRUE; +} + +static void +start_pipeline (GstElement * bin, GstPad * pad) +{ + GstStateChangeReturn ret; + + ogg_sync_init (&oggsync); + + eos_chain_states = + g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); + probe_id = + gst_pad_add_buffer_probe (pad, G_CALLBACK (eos_buffer_probe), NULL); + + ret = gst_element_set_state (bin, GST_STATE_PLAYING); + fail_if (ret == GST_STATE_CHANGE_FAILURE, "Could not start test pipeline"); + if (ret == GST_STATE_CHANGE_ASYNC) { + ret = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE); + fail_if (ret != GST_STATE_CHANGE_SUCCESS, "Could not start test pipeline"); + } +} + +static void +stop_pipeline (GstElement * bin, GstPad * pad) +{ + GstStateChangeReturn ret; + + ret = gst_element_set_state (bin, GST_STATE_NULL); + fail_if (ret == GST_STATE_CHANGE_FAILURE, "Could not stop test pipeline"); + if (ret == GST_STATE_CHANGE_ASYNC) { + ret = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE); + fail_if (ret != GST_STATE_CHANGE_SUCCESS, "Could not stop test pipeline"); + } + + gst_pad_remove_buffer_probe (pad, (guint) probe_id); + ogg_sync_clear (&oggsync); + + /* check end conditions, such as EOS flags */ + g_hash_table_foreach_remove (eos_chain_states, + (GHRFunc) check_chain_final_state, NULL); +} + +static gboolean +eos_watch (GstBus * bus, GstMessage * message, GMainLoop * loop) +{ + if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_EOS) { + g_main_loop_quit (loop); + } +} + +static void +test_pipeline (const char *pipeline) +{ + GstElement *bin, *sink; + GstPad *pad, *sinkpad; + GstBus *bus; + GError *error = NULL; + GMainLoop *loop; + char *pipe_str; + GstPadLinkReturn linkret; + + bin = gst_parse_launch (pipeline, &error); + fail_unless (bin != NULL, "Error parsing pipeline: %s", + error ? error->message : "(invalid error)"); + pad = gst_bin_find_unconnected_pad (GST_BIN (bin), GST_PAD_SRC); + fail_unless (pad != NULL, "Could not locate free src pad"); + + /* connect the fake sink */ + sink = gst_element_factory_make ("fakesink", "fake_sink"); + fail_unless (sink != NULL, "Could create fakesink"); + fail_unless (gst_bin_add (GST_BIN (bin), sink), "Could not insert fakesink"); + sinkpad = gst_element_get_pad (sink, "sink"); + fail_unless (sinkpad != NULL, "Could not get fakesink src pad"); + + linkret = gst_pad_link (pad, sinkpad); + fail_unless (GST_PAD_LINK_SUCCESSFUL (linkret), + "Could not link to fake sink"); + gst_object_unref (sinkpad); + + /* run until we receive EOS */ + loop = g_main_loop_new (NULL, FALSE); + bus = gst_element_get_bus (bin); + gst_bus_add_watch (bus, (GstBusFunc) eos_watch, loop); + gst_object_unref (bus); + + start_pipeline (bin, pad); + g_main_loop_run (loop); + stop_pipeline (bin, pad); + + /* clean up */ + g_main_loop_unref (loop); + gst_object_unref (pad); + gst_object_unref (bin); +} + +GST_START_TEST (test_vorbis) +{ + test_pipeline + ("audiotestsrc num-buffers=5 ! audioconvert ! vorbisenc ! oggmux"); +} + +GST_END_TEST; + +GST_START_TEST (test_theora) +{ + test_pipeline + ("videotestsrc num-buffers=5 ! ffmpegcolorspace ! theoraenc ! oggmux"); +} + +GST_END_TEST; + +GST_START_TEST (test_theora_vorbis) +{ + test_pipeline + ("videotestsrc num-buffers=10 ! ffmpegcolorspace ! theoraenc ! queue ! oggmux name=mux " + "audiotestsrc num-buffers=2 ! audioconvert ! vorbisenc ! queue ! mux."); +} + +GST_END_TEST; + +GST_START_TEST (test_vorbis_theora) +{ + test_pipeline + ("videotestsrc num-buffers=2 ! ffmpegcolorspace ! theoraenc ! queue ! oggmux name=mux " + "audiotestsrc num-buffers=10 ! audioconvert ! vorbisenc ! queue ! mux."); +} + +GST_END_TEST; + +static Suite * +oggmux_suite (void) +{ + Suite *s = suite_create ("oggmux"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + +#ifdef HAVE_VORBIS + tcase_add_test (tc_chain, test_vorbis); +#endif + +#ifdef HAVE_THEORA + tcase_add_test (tc_chain, test_theora); +#endif + +#if (defined (HAVE_THEORA) && defined (HAVE_VORBIS)) + tcase_add_test (tc_chain, test_vorbis_theora); + tcase_add_test (tc_chain, test_theora_vorbis); +#endif + + return s; +} + +GST_CHECK_MAIN (oggmux);