splitmux: Add unit test for file splitting
authorJan Schmidt <jan@centricular.com>
Fri, 6 Feb 2015 14:41:49 +0000 (01:41 +1100)
committerJan Schmidt <jan@centricular.com>
Fri, 6 Feb 2015 16:58:30 +0000 (03:58 +1100)
Add a unit test for file splitting, and fix the leaks in the
splitmuxsink it found

gst/multifile/gstsplitmuxpartreader.c
gst/multifile/gstsplitmuxsink.c
tests/check/elements/splitmux.c

index 97e0b75..fc87f81 100644 (file)
@@ -894,7 +894,7 @@ type_found (GstElement * typefind, guint probability,
 {
   GstElement *demux;
 
-  GST_WARNING ("Got type %" GST_PTR_FORMAT, caps);
+  GST_INFO_OBJECT (reader, "Got type %" GST_PTR_FORMAT, caps);
 
   /* typefind found a type. Look for the demuxer to handle it */
   demux = reader->demux = find_demuxer (caps);
index c6b4e4a..9034a19 100644 (file)
@@ -130,6 +130,7 @@ static void bus_handler (GstBin * bin, GstMessage * msg);
 static void set_next_filename (GstSplitMuxSink * splitmux);
 static void start_next_fragment (GstSplitMuxSink * splitmux);
 static void check_queue_length (GstSplitMuxSink * splitmux, MqStreamCtx * ctx);
+static void mq_stream_ctx_unref (MqStreamCtx * ctx);
 
 static MqStreamBuf *
 mq_stream_buf_new (void)
@@ -249,9 +250,15 @@ gst_splitmux_sink_finalize (GObject * object)
   g_cond_clear (&splitmux->data_cond);
   if (splitmux->provided_sink)
     gst_object_unref (splitmux->provided_sink);
+  if (splitmux->provided_muxer)
+    gst_object_unref (splitmux->provided_muxer);
 
   g_free (splitmux->location);
 
+  /* Make sure to free any un-released contexts */
+  g_list_foreach (splitmux->contexts, (GFunc) mq_stream_ctx_unref, NULL);
+  g_list_free (splitmux->contexts);
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -648,6 +655,8 @@ restart_context (MqStreamCtx * ctx, GstSplitMuxSink * splitmux)
 
   /* Clear EOS flag */
   ctx->out_eos = FALSE;
+
+  gst_object_unref (peer);
 }
 
 /* Called with lock held when a fragment
@@ -1133,15 +1142,20 @@ gst_splitmux_sink_request_new_pad (GstElement * element,
 
   if (!get_pads_from_mq (splitmux, &mq_sink, &mq_src)) {
     gst_element_release_request_pad (splitmux->muxer, res);
+    gst_object_unref (GST_OBJECT (res));
     goto fail;
   }
 
   if (gst_pad_link (mq_src, res) != GST_PAD_LINK_OK) {
     gst_element_release_request_pad (splitmux->muxer, res);
+    gst_object_unref (GST_OBJECT (res));
     gst_element_release_request_pad (splitmux->mq, mq_sink);
+    gst_object_unref (GST_OBJECT (mq_sink));
     goto fail;
   }
 
+  gst_object_unref (GST_OBJECT (res));
+
   ctx = mq_stream_ctx_new (splitmux);
   ctx->is_video = is_video;
   ctx->srcpad = mq_src;
index acdf970..16e2b43 100644 (file)
@@ -52,16 +52,37 @@ tempdir_cleanup (void)
   d = g_dir_open (tmpdir, 0, NULL);
   fail_if (d == NULL);
 
-  while ((f = g_dir_read_name (d)) != NULL)
-    fail_if (g_remove (f) != 0);
+  while ((f = g_dir_read_name (d)) != NULL) {
+    gchar *fname = g_build_filename (tmpdir, f, NULL);
+    fail_if (g_remove (fname) != 0, "Failed to remove tmp file %s", fname);
+    g_free (fname);
+  }
   g_dir_close (d);
 
-  fail_if (g_remove (tmpdir) != 0);
+  fail_if (g_remove (tmpdir) != 0, "Failed to delete tmpdir %s", tmpdir);
 
   g_free (tmpdir);
   tmpdir = NULL;
 }
 
+static guint
+count_files (const gchar * target)
+{
+  GDir *d;
+  const gchar *f;
+  guint ret = 0;
+
+  d = g_dir_open (target, 0, NULL);
+  fail_if (d == NULL);
+
+  while ((f = g_dir_read_name (d)) != NULL)
+    ret++;
+  g_dir_close (d);
+
+  return ret;
+}
+
+
 static GstMessage *
 run_pipeline (GstElement * pipeline)
 {
@@ -77,12 +98,27 @@ run_pipeline (GstElement * pipeline)
   return msg;
 }
 
-GST_START_TEST (test_splitmuxsrc)
+static void
+dump_error (GstMessage * msg)
+{
+  GError *err = NULL;
+  gchar *dbg_info;
+
+  fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR);
+
+  g_printerr ("ERROR from element %s: %s\n",
+      GST_OBJECT_NAME (msg->src), err->message);
+  g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
+  g_error_free (err);
+  g_free (dbg_info);
+}
+
+static void
+test_playback (const gchar * in_pattern)
 {
   GstMessage *msg;
   GstElement *pipeline;
   GstElement *fakesink;
-  gchar *in_pattern;
   gchar *uri;
 
   pipeline = gst_element_factory_make ("playbin", NULL);
@@ -92,20 +128,70 @@ GST_START_TEST (test_splitmuxsrc)
   fail_if (fakesink == NULL);
   g_object_set (G_OBJECT (pipeline), "video-sink", fakesink, NULL);
 
-  in_pattern = g_build_filename (GST_TEST_FILES_PATH, "splitvideo*.ogg", NULL);
   uri = g_strdup_printf ("splitmux://%s", in_pattern);
-  g_free (in_pattern);
 
   g_object_set (G_OBJECT (pipeline), "uri", uri, NULL);
   g_free (uri);
 
   msg = run_pipeline (pipeline);
 
-  fail_if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR);
+  if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
+    dump_error (msg);
+  fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
   gst_message_unref (msg);
   gst_object_unref (pipeline);
 }
 
+GST_START_TEST (test_splitmuxsrc)
+{
+  gchar *in_pattern =
+      g_build_filename (GST_TEST_FILES_PATH, "splitvideo*.ogg", NULL);
+  test_playback (in_pattern);
+  g_free (in_pattern);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_splitmuxsink)
+{
+  GstMessage *msg;
+  GstElement *pipeline;
+  GstElement *sink;
+  gchar *dest_pattern;
+  guint count;
+  gchar *in_pattern;
+
+  /* This pipeline has a small time cutoff - it should start a new file
+   * every GOP, ie 1 second */
+  pipeline =
+      gst_parse_launch
+      ("videotestsrc num-buffers=15 ! video/x-raw,width=80,height=64,framerate=5/1 ! videoconvert !"
+      " queue ! theoraenc keyframe-force=5 ! splitmuxsink name=splitsink "
+      " max-size-time=1000000 max-size-bytes=1000000 muxer=oggmux", NULL);
+  fail_if (pipeline == NULL);
+  sink = gst_bin_get_by_name (GST_BIN (pipeline), "splitsink");
+  fail_if (sink == NULL);
+  dest_pattern = g_build_filename (tmpdir, "out%05d.ogg", NULL);
+  g_object_set (G_OBJECT (sink), "location", dest_pattern, NULL);
+  g_free (dest_pattern);
+  g_object_unref (sink);
+
+  msg = run_pipeline (pipeline);
+
+  if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
+    dump_error (msg);
+  fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
+  gst_message_unref (msg);
+  gst_object_unref (pipeline);
+
+  count = count_files (tmpdir);
+  fail_unless (count == 3, "Expected 3 output files, got %d", count);
+
+  in_pattern = g_build_filename (tmpdir, "out*.ogg", NULL);
+  test_playback (in_pattern);
+  g_free (in_pattern);
+}
+
 GST_END_TEST;
 
 static Suite *
@@ -119,6 +205,7 @@ splitmux_suite (void)
   tcase_add_checked_fixture (tc_chain, tempdir_setup, tempdir_cleanup);
 
   tcase_add_test (tc_chain, test_splitmuxsrc);
+  tcase_add_test (tc_chain, test_splitmuxsink);
 
   return s;
 }