gst_plugins_base/tests/check/pipelines/oggmux.c
changeset 0 0e761a78d257
equal deleted inserted replaced
-1:000000000000 0:0e761a78d257
       
     1 /* GStreamer
       
     2  *
       
     3  * unit tests for oggmux
       
     4  *
       
     5  * Copyright (C) 2006 James Livingston <doclivingston at gmail.com>
       
     6  *
       
     7  * This library is free software; you can redistribute it and/or
       
     8  * modify it under the terms of the GNU Library General Public
       
     9  * License as published by the Free Software Foundation; either
       
    10  * version 2 of the License, or (at your option) any later version.
       
    11  *
       
    12  * This library is distributed in the hope that it will be useful,
       
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    15  * Library General Public License for more details.
       
    16  *
       
    17  * You should have received a copy of the GNU Library General Public
       
    18  * License along with this library; if not, write to the
       
    19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    20  * Boston, MA 02111-1307, USA.
       
    21  */
       
    22 
       
    23 #ifdef HAVE_CONFIG_H
       
    24 #include "config.h"
       
    25 #endif
       
    26 
       
    27 #include <string.h>
       
    28 
       
    29 #include <gst/check/gstcheck.h>
       
    30 #include <ogg/ogg.h>
       
    31 
       
    32 
       
    33 typedef enum
       
    34 {
       
    35   CODEC_UNKNOWN,
       
    36   CODEC_VORBIS,
       
    37   CODEC_THEORA,
       
    38   CODEC_SPEEX,
       
    39 } ChainCodec;
       
    40 
       
    41 typedef struct
       
    42 {
       
    43   gboolean eos;
       
    44   gulong serialno;
       
    45   gint64 last_granule;
       
    46   ChainCodec codec;
       
    47 } ChainState;
       
    48 
       
    49 static ogg_sync_state oggsync;
       
    50 static GHashTable *eos_chain_states;
       
    51 static gulong probe_id;
       
    52 
       
    53 
       
    54 static ChainCodec
       
    55 get_page_codec (ogg_page * page)
       
    56 {
       
    57   ChainCodec codec = CODEC_UNKNOWN;
       
    58   ogg_stream_state state;
       
    59   ogg_packet packet;
       
    60 
       
    61   ogg_stream_init (&state, ogg_page_serialno (page));
       
    62   if (ogg_stream_pagein (&state, page) == 0) {
       
    63     if (ogg_stream_packetpeek (&state, &packet) > 0) {
       
    64       if (strncmp ((char *) &packet.packet[1], "vorbis",
       
    65               strlen ("vorbis")) == 0)
       
    66         codec = CODEC_VORBIS;
       
    67       else if (strncmp ((char *) &packet.packet[1], "theora",
       
    68               strlen ("theora")) == 0)
       
    69         codec = CODEC_THEORA;
       
    70       else if (strncmp ((char *) &packet.packet[0], "Speex   ",
       
    71               strlen ("Speex   ")) == 0)
       
    72         codec = CODEC_SPEEX;
       
    73     }
       
    74   }
       
    75   ogg_stream_clear (&state);
       
    76 
       
    77   return codec;
       
    78 }
       
    79 
       
    80 static gboolean
       
    81 check_chain_final_state (gpointer key, ChainState * state, gpointer data)
       
    82 {
       
    83   fail_unless (state->eos, "missing EOS flag on chain %u", state->serialno);
       
    84 
       
    85   /* return TRUE to empty the chain table */
       
    86   return TRUE;
       
    87 }
       
    88 
       
    89 static void
       
    90 fail_if_audio (gpointer key, ChainState * state, gpointer data)
       
    91 {
       
    92   fail_if (state->codec == CODEC_VORBIS,
       
    93       "vorbis BOS occurred before theora BOS");
       
    94   fail_if (state->codec == CODEC_SPEEX, "speex BOS occurred before theora BOS");
       
    95 }
       
    96 
       
    97 static ChainState *
       
    98 validate_ogg_page (ogg_page * page)
       
    99 {
       
   100   gulong serialno;
       
   101   gint64 granule;
       
   102   ChainState *state;
       
   103 
       
   104   serialno = ogg_page_serialno (page);
       
   105   granule = ogg_page_granulepos (page);
       
   106   state = g_hash_table_lookup (eos_chain_states, GINT_TO_POINTER (serialno));
       
   107 
       
   108   fail_if (ogg_page_packets (page) == 0 && granule != -1,
       
   109       "Must have granulepos -1 when page has no packets, has %" G_GINT64_FORMAT,
       
   110       granule);
       
   111 
       
   112   if (ogg_page_bos (page)) {
       
   113     fail_unless (state == NULL, "Extraneous BOS flag on chain %u", serialno);
       
   114 
       
   115     state = g_new0 (ChainState, 1);
       
   116     g_hash_table_insert (eos_chain_states, GINT_TO_POINTER (serialno), state);
       
   117     state->serialno = serialno;
       
   118     state->last_granule = granule;
       
   119     state->codec = get_page_codec (page);
       
   120 
       
   121     /* check for things like BOS ordering, etc */
       
   122     switch (state->codec) {
       
   123       case CODEC_THEORA:
       
   124         /* check we have no vorbis/speex chains yet */
       
   125         g_hash_table_foreach (eos_chain_states, (GHFunc) fail_if_audio, NULL);
       
   126         break;
       
   127       case CODEC_VORBIS:
       
   128       case CODEC_SPEEX:
       
   129         /* no checks (yet) */
       
   130         break;
       
   131       case CODEC_UNKNOWN:
       
   132       default:
       
   133         break;
       
   134     }
       
   135   } else if (ogg_page_eos (page)) {
       
   136     fail_unless (state != NULL, "Missing BOS flag on chain %u", serialno);
       
   137     state->eos = TRUE;
       
   138   } else {
       
   139     fail_unless (state != NULL, "Missing BOS flag on chain %u", serialno);
       
   140     fail_unless (!state->eos, "Data after EOS flag on chain %u", serialno);
       
   141   }
       
   142 
       
   143   if (granule != -1) {
       
   144     fail_unless (granule >= state->last_granule,
       
   145         "Granulepos out-of-order for chain %u: old=%" G_GINT64_FORMAT ", new="
       
   146         G_GINT64_FORMAT, serialno, state->last_granule, granule);
       
   147     state->last_granule = granule;
       
   148   }
       
   149 
       
   150   return state;
       
   151 }
       
   152 
       
   153 static void
       
   154 is_video (gpointer key, ChainState * state, gpointer data)
       
   155 {
       
   156   if (state->codec == CODEC_THEORA)
       
   157     *((gboolean *) data) = TRUE;
       
   158 }
       
   159 
       
   160 
       
   161 static gboolean
       
   162 eos_buffer_probe (GstPad * pad, GstBuffer * buffer, gpointer unused)
       
   163 {
       
   164   gint ret;
       
   165   gint size;
       
   166   guint8 *data;
       
   167   gchar *oggbuffer;
       
   168   ChainState *state = NULL;
       
   169   gboolean has_video = FALSE;
       
   170 
       
   171   size = GST_BUFFER_SIZE (buffer);
       
   172   data = GST_BUFFER_DATA (buffer);
       
   173 
       
   174   oggbuffer = ogg_sync_buffer (&oggsync, size);
       
   175   memcpy (oggbuffer, data, size);
       
   176   ogg_sync_wrote (&oggsync, size);
       
   177 
       
   178   do {
       
   179     ogg_page page;
       
   180 
       
   181     ret = ogg_sync_pageout (&oggsync, &page);
       
   182     if (ret > 0)
       
   183       state = validate_ogg_page (&page);
       
   184   }
       
   185   while (ret != 0);
       
   186 
       
   187   if (state) {
       
   188     /* Now, we can do buffer-level checks...
       
   189      * If we have video somewhere, then we should have DELTA_UNIT set on all
       
   190      * non-header (not IN_CAPS), non-video buffers
       
   191      */
       
   192     g_hash_table_foreach (eos_chain_states, (GHFunc) is_video, &has_video);
       
   193     if (has_video && state->codec != CODEC_THEORA) {
       
   194       if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_IN_CAPS))
       
   195         fail_unless (GST_BUFFER_FLAG_IS_SET (buffer,
       
   196                 GST_BUFFER_FLAG_DELTA_UNIT),
       
   197             "Non-video buffer doesn't have DELTA_UNIT in stream with video");
       
   198     }
       
   199   }
       
   200 
       
   201   return TRUE;
       
   202 }
       
   203 
       
   204 static void
       
   205 start_pipeline (GstElement * bin, GstPad * pad)
       
   206 {
       
   207   GstStateChangeReturn ret;
       
   208 
       
   209   ogg_sync_init (&oggsync);
       
   210 
       
   211   eos_chain_states =
       
   212       g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
       
   213   probe_id =
       
   214       gst_pad_add_buffer_probe (pad, G_CALLBACK (eos_buffer_probe), NULL);
       
   215 
       
   216   ret = gst_element_set_state (bin, GST_STATE_PLAYING);
       
   217   fail_if (ret == GST_STATE_CHANGE_FAILURE, "Could not start test pipeline");
       
   218   if (ret == GST_STATE_CHANGE_ASYNC) {
       
   219     ret = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE);
       
   220     fail_if (ret != GST_STATE_CHANGE_SUCCESS, "Could not start test pipeline");
       
   221   }
       
   222 }
       
   223 
       
   224 static void
       
   225 stop_pipeline (GstElement * bin, GstPad * pad)
       
   226 {
       
   227   GstStateChangeReturn ret;
       
   228 
       
   229   ret = gst_element_set_state (bin, GST_STATE_NULL);
       
   230   fail_if (ret == GST_STATE_CHANGE_FAILURE, "Could not stop test pipeline");
       
   231   if (ret == GST_STATE_CHANGE_ASYNC) {
       
   232     ret = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE);
       
   233     fail_if (ret != GST_STATE_CHANGE_SUCCESS, "Could not stop test pipeline");
       
   234   }
       
   235 
       
   236   gst_pad_remove_buffer_probe (pad, (guint) probe_id);
       
   237   ogg_sync_clear (&oggsync);
       
   238 
       
   239   /* check end conditions, such as EOS flags */
       
   240   g_hash_table_foreach_remove (eos_chain_states,
       
   241       (GHRFunc) check_chain_final_state, NULL);
       
   242 }
       
   243 
       
   244 static gboolean
       
   245 eos_watch (GstBus * bus, GstMessage * message, GMainLoop * loop)
       
   246 {
       
   247   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_EOS) {
       
   248     g_main_loop_quit (loop);
       
   249   }
       
   250   return TRUE;
       
   251 }
       
   252 
       
   253 static void
       
   254 test_pipeline (const char *pipeline)
       
   255 {
       
   256   GstElement *bin, *sink;
       
   257   GstPad *pad, *sinkpad;
       
   258   GstBus *bus;
       
   259   GError *error = NULL;
       
   260   GMainLoop *loop;
       
   261   GstPadLinkReturn linkret;
       
   262 
       
   263   bin = gst_parse_launch (pipeline, &error);
       
   264   fail_unless (bin != NULL, "Error parsing pipeline: %s",
       
   265       error ? error->message : "(invalid error)");
       
   266   pad = gst_bin_find_unconnected_pad (GST_BIN (bin), GST_PAD_SRC);
       
   267   fail_unless (pad != NULL, "Could not locate free src pad");
       
   268 
       
   269   /* connect the fake sink */
       
   270   sink = gst_element_factory_make ("fakesink", "fake_sink");
       
   271   fail_unless (sink != NULL, "Could create fakesink");
       
   272   fail_unless (gst_bin_add (GST_BIN (bin), sink), "Could not insert fakesink");
       
   273   sinkpad = gst_element_get_pad (sink, "sink");
       
   274   fail_unless (sinkpad != NULL, "Could not get fakesink src pad");
       
   275 
       
   276   linkret = gst_pad_link (pad, sinkpad);
       
   277   fail_unless (GST_PAD_LINK_SUCCESSFUL (linkret),
       
   278       "Could not link to fake sink");
       
   279   gst_object_unref (sinkpad);
       
   280 
       
   281   /* run until we receive EOS */
       
   282   loop = g_main_loop_new (NULL, FALSE);
       
   283   bus = gst_element_get_bus (bin);
       
   284   gst_bus_add_watch (bus, (GstBusFunc) eos_watch, loop);
       
   285   gst_object_unref (bus);
       
   286 
       
   287   start_pipeline (bin, pad);
       
   288   g_main_loop_run (loop);
       
   289   stop_pipeline (bin, pad);
       
   290 
       
   291   /* clean up */
       
   292   g_main_loop_unref (loop);
       
   293   gst_object_unref (pad);
       
   294   gst_object_unref (bin);
       
   295 }
       
   296 
       
   297 GST_START_TEST (test_vorbis)
       
   298 {
       
   299   test_pipeline
       
   300       ("audiotestsrc num-buffers=5 ! audioconvert ! vorbisenc ! oggmux");
       
   301 }
       
   302 
       
   303 GST_END_TEST;
       
   304 
       
   305 GST_START_TEST (test_theora)
       
   306 {
       
   307   test_pipeline
       
   308       ("videotestsrc num-buffers=5 ! ffmpegcolorspace ! theoraenc ! oggmux");
       
   309 }
       
   310 
       
   311 GST_END_TEST;
       
   312 
       
   313 GST_START_TEST (test_theora_vorbis)
       
   314 {
       
   315   test_pipeline
       
   316       ("videotestsrc num-buffers=10 ! ffmpegcolorspace ! theoraenc ! queue ! oggmux name=mux "
       
   317       "audiotestsrc num-buffers=2 ! audioconvert ! vorbisenc ! queue ! mux.");
       
   318 }
       
   319 
       
   320 GST_END_TEST;
       
   321 
       
   322 GST_START_TEST (test_vorbis_theora)
       
   323 {
       
   324   test_pipeline
       
   325       ("videotestsrc num-buffers=2 ! ffmpegcolorspace ! theoraenc ! queue ! oggmux name=mux "
       
   326       "audiotestsrc num-buffers=10 ! audioconvert ! vorbisenc ! queue ! mux.");
       
   327 }
       
   328 
       
   329 GST_END_TEST;
       
   330 
       
   331 GST_START_TEST (test_simple_cleanup)
       
   332 {
       
   333   GstElement *oggmux;
       
   334 
       
   335   oggmux = gst_element_factory_make ("oggmux", NULL);
       
   336   gst_object_unref (oggmux);
       
   337 }
       
   338 
       
   339 GST_END_TEST;
       
   340 
       
   341 GST_START_TEST (test_request_pad_cleanup)
       
   342 {
       
   343   GstElement *oggmux;
       
   344   GstPad *pad;
       
   345 
       
   346   oggmux = gst_element_factory_make ("oggmux", NULL);
       
   347   pad = gst_element_get_request_pad (oggmux, "sink_%d");
       
   348   fail_unless (pad != NULL);
       
   349   gst_object_unref (pad);
       
   350   pad = gst_element_get_request_pad (oggmux, "sink_%d");
       
   351   fail_unless (pad != NULL);
       
   352   gst_object_unref (pad);
       
   353   gst_object_unref (oggmux);
       
   354 }
       
   355 
       
   356 GST_END_TEST;
       
   357 
       
   358 static Suite *
       
   359 oggmux_suite (void)
       
   360 {
       
   361   Suite *s = suite_create ("oggmux");
       
   362   TCase *tc_chain = tcase_create ("general");
       
   363 
       
   364   suite_add_tcase (s, tc_chain);
       
   365 #ifdef HAVE_VORBIS
       
   366   tcase_add_test (tc_chain, test_vorbis);
       
   367 #endif
       
   368 
       
   369 #ifdef HAVE_THEORA
       
   370   tcase_add_test (tc_chain, test_theora);
       
   371 #endif
       
   372 
       
   373 #if (defined (HAVE_THEORA) && defined (HAVE_VORBIS))
       
   374   tcase_add_test (tc_chain, test_vorbis_theora);
       
   375   tcase_add_test (tc_chain, test_theora_vorbis);
       
   376 #endif
       
   377 
       
   378   tcase_add_test (tc_chain, test_simple_cleanup);
       
   379   tcase_add_test (tc_chain, test_request_pad_cleanup);
       
   380   return s;
       
   381 }
       
   382 
       
   383 GST_CHECK_MAIN (oggmux);