smart-adder: Implement a GESSmartAdder bin element to be used as mixing element
authorThibault Saunier <thibault.saunier@collabora.com>
Sat, 30 Mar 2013 23:08:15 +0000 (00:08 +0100)
committerThibault Saunier <thibault.saunier@collabora.com>
Sun, 23 Jun 2013 22:37:40 +0000 (18:37 -0400)
..in audio tracks

ges/Makefile.am
ges/ges-audio-track.c
ges/ges-audio-track.h
ges/ges-smart-adder.c [new file with mode: 0644]
ges/ges-smart-adder.h [new file with mode: 0644]
ges/ges-track.c
tests/check/Makefile.am
tests/check/ges/backgroundsource.c
tests/check/ges/effects.c
tests/check/ges/layer.c
tests/check/ges/mixers.c [new file with mode: 0644]

index ceb5171..66f8d6f 100644 (file)
@@ -61,6 +61,7 @@ libges_@GST_API_VERSION@_la_SOURCES =                 \
        ges-timeline-element.c \
        ges-container.c \
        ges-effect-asset.c \
+       ges-smart-adder.c \
        ges-utils.c
 
 libges_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/ges/
@@ -119,6 +120,7 @@ libges_@GST_API_VERSION@include_HEADERS =   \
        ges-timeline-element.h \
        ges-container.h \
        ges-effect-asset.h \
+       ges-smart-adder.h \
        ges-utils.h
 
 noinst_HEADERS = \
index cf21709..c63d6df 100644 (file)
  * @short_description: A standard GESTrack for raw audio
  */
 
-#include "ges-audio-track.h"
-
 #define DEFAULT_CAPS "audio/x-raw"
 
+#include "ges-smart-adder.h"
+#include "ges-audio-track.h"
+
 struct _GESAudioTrackPrivate
 {
   gpointer nothing;
@@ -77,6 +78,8 @@ ges_audio_track_class_init (GESAudioTrackClass * klass)
   g_type_class_add_private (klass, sizeof (GESAudioTrackPrivate));
 
   object_class->finalize = ges_audio_track_finalize;
+
+  GES_TRACK_CLASS (klass)->get_mixing_element = ges_smart_adder_new;
 }
 
 /****************************************************
index 85a37b1..3b920de 100644 (file)
@@ -39,13 +39,19 @@ typedef struct _GESAudioTrackPrivate GESAudioTrackPrivate;
 struct _GESAudioTrackClass
 {
   GESTrackClass parent_class;
+
+  /* Padding for API extension */
+  gpointer    _ges_reserved[GES_PADDING];
 };
 
 struct _GESAudioTrack
 {
   GESTrack parent_instance;
 
+  /*< private >*/
   GESAudioTrackPrivate *priv;
+  /* Padding for API extension */
+  gpointer         _ges_reserved[GES_PADDING];
 };
 
 GType          ges_audio_track_get_type (void) G_GNUC_CONST;
diff --git a/ges/ges-smart-adder.c b/ges/ges-smart-adder.c
new file mode 100644 (file)
index 0000000..8969fa7
--- /dev/null
@@ -0,0 +1,290 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-  */
+/*
+ * gst-editing-services
+ *
+ * Copyright (C) 2013 Thibault Saunier <tsaunier@gnome.org>
+
+ * gst-editing-services is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
+ */
+#include <gst/audio/audio.h>
+
+#include "ges-types.h"
+#include "ges-internal.h"
+#include "ges-smart-adder.h"
+
+G_DEFINE_TYPE (GESSmartAdder, ges_smart_adder, GST_TYPE_BIN);
+
+#define GET_LOCK(obj) (&((GESSmartAdder*)(obj))->lock)
+#define LOCK(obj) (g_mutex_lock (GET_LOCK(obj)))
+#define UNLOCK(obj) (g_mutex_unlock (GET_LOCK(obj)))
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("audio/x-raw")
+    );
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink_%u",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("audio/x-raw")
+    );
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define DEFAULT_CAPS "audio/x-raw,format=(string)S32LE;"
+#else
+#define DEFAULT_CAPS "audio/x-raw,format=(string)S32BE;";
+#endif
+
+typedef struct _PadInfos
+{
+  GESSmartAdder *self;
+  GstPad *ghost;
+  GstPad *adder_pad;
+  GstPad *parent_sinkpad;
+
+  GstElement *volume;
+  GstElement *audioconvert;
+  GstElement *audioresample;
+  GstElement *bin;
+
+} PadInfos;
+
+static void
+destroy_pad_info (PadInfos * infos)
+{
+  GST_DEBUG_OBJECT (infos->self, "Destroying pad %" GST_PTR_FORMAT,
+      infos->ghost);
+
+  if (G_LIKELY (infos->bin)) {
+    gst_element_set_state (infos->bin, GST_STATE_NULL);
+    gst_element_unlink (infos->bin, infos->self->adder);
+    gst_bin_remove (GST_BIN (infos->self), infos->bin);
+  }
+
+  if (infos->adder_pad)
+    gst_element_release_request_pad (infos->self->adder, infos->adder_pad);
+
+  g_slice_free (PadInfos, infos);
+}
+
+/****************************************************
+ *              Callbacks                           *
+ ****************************************************/
+static void
+_connected_to_gnlobject_cb (GstPad * pad, GstPad * peer, PadInfos * infos)
+{
+  GESTrack *track;
+  GESLayer *layer;
+
+  gfloat volume, track_volume, layer_volume;
+  GstElement *gnlobject = gst_pad_get_parent_element (peer);
+  GESTrackElement *track_element = g_object_get_qdata (G_OBJECT (gnlobject),
+      GNL_OBJECT_TRACK_ELEMENT_QUARK);
+
+  g_assert (track_element);
+  g_signal_handlers_disconnect_by_func (pad, _connected_to_gnlobject_cb, infos);
+
+  volume = track_volume = layer_volume = GES_META_VOLUME_DEFAULT;
+  track = ges_track_element_get_track (track_element);
+  layer = ges_clip_get_layer (GES_CLIP (GES_TIMELINE_ELEMENT_PARENT
+          (track_element)));
+
+  if (layer == NULL) {
+    GST_WARNING ("TrackElement is in no layer");
+    goto no_layer;
+  }
+
+  ges_meta_container_get_float (GES_META_CONTAINER (layer),
+      GES_META_VOLUME, &layer_volume);
+  gst_object_unref (layer);
+  ges_meta_container_get_float (GES_META_CONTAINER (track),
+      GES_META_VOLUME, &track_volume);
+
+  volume = track_volume * layer_volume;
+  g_object_set (infos->volume, "volume", volume, NULL);
+
+no_layer:
+  gst_object_unref (gnlobject);
+}
+
+/* Here we get the information that the pad is linked to a ghostpad GNL created,
+ * what we want is to get notify when the gnloperation (ghost)sinkpad gets linked
+ * to the pad of another gnlobject in the pipeline, so we can get the
+ * GESTrackElement that wraps the gnlobject that is linked */
+static void
+_sink_pad_linked_cb (GstPad * adder_pad, GstProxyPad * peer, PadInfos * infos)
+{
+  GESSmartAdder *self = infos->self;
+  /* The peer is a ProxyPad (inside ourself) that is linked to the gnloperation
+   * ProxyPad, we want to get notify about the gnloperation ProxyPad connection
+   */
+  GstProxyPad *parent_sinkpad =
+      gst_proxy_pad_get_internal (GST_PROXY_PAD (peer));
+
+  LOCK (self);
+  infos->parent_sinkpad = GST_PAD (parent_sinkpad);
+  UNLOCK (self);
+
+  g_signal_handlers_disconnect_by_func (adder_pad, _sink_pad_linked_cb, infos);
+  g_signal_connect (parent_sinkpad, "linked",
+      G_CALLBACK (_connected_to_gnlobject_cb), infos);
+}
+
+
+/****************************************************
+ *              GstElement vmetods                  *
+ ****************************************************/
+static GstPad *
+_request_new_pad (GstElement * element, GstPadTemplate * templ,
+    const gchar * name, const GstCaps * caps)
+{
+  GstPad *volume_srcpad, *audioconvert_sinkpad, *tmpghost;
+
+  PadInfos *infos = g_slice_new0 (PadInfos);
+  GESSmartAdder *self = GES_SMART_ADDER (element);
+
+  infos->adder_pad = gst_element_request_pad (self->adder, templ, NULL, caps);
+  if (infos->adder_pad == NULL) {
+    GST_WARNING_OBJECT (element, "Could not get any pad from GstAdder");
+
+    return NULL;
+  }
+  infos->self = gst_object_ref (self);
+
+  infos->bin = gst_bin_new (NULL);
+  infos->audioconvert = gst_element_factory_make ("audioconvert", NULL);
+  infos->audioresample = gst_element_factory_make ("audioresample", NULL);
+  infos->volume = gst_element_factory_make ("volume", NULL);
+  gst_bin_add_many (GST_BIN (infos->bin), infos->audioconvert,
+      infos->audioresample, infos->volume, NULL);
+  gst_element_link_many (infos->audioconvert, infos->audioresample,
+      infos->volume, NULL);
+
+  audioconvert_sinkpad = gst_element_get_static_pad (infos->audioconvert,
+      "sink");
+  tmpghost = GST_PAD (gst_ghost_pad_new (NULL, audioconvert_sinkpad));
+  gst_object_unref (audioconvert_sinkpad);
+  gst_pad_set_active (tmpghost, TRUE);
+  gst_element_add_pad (GST_ELEMENT (infos->bin), tmpghost);
+
+  gst_bin_add (GST_BIN (self), infos->bin);
+  infos->ghost = gst_ghost_pad_new (NULL, tmpghost);
+  gst_pad_set_active (infos->ghost, TRUE);
+  if (!gst_element_add_pad (GST_ELEMENT (self), infos->ghost))
+    goto could_not_add;
+
+
+  volume_srcpad = gst_element_get_static_pad (infos->volume, "src");
+  tmpghost = GST_PAD (gst_ghost_pad_new (NULL, volume_srcpad));
+  gst_object_unref (volume_srcpad);
+  gst_pad_set_active (tmpghost, TRUE);
+  gst_element_add_pad (GST_ELEMENT (infos->bin), tmpghost);
+  gst_pad_link (tmpghost, infos->adder_pad);
+
+  LOCK (self);
+  g_hash_table_insert (self->pads_infos, infos->ghost, infos);
+  UNLOCK (self);
+
+  g_signal_connect (infos->ghost, "linked",
+      G_CALLBACK (_sink_pad_linked_cb), infos);
+
+  GST_DEBUG_OBJECT (self, "Returning new pad %" GST_PTR_FORMAT, infos->ghost);
+  return infos->ghost;
+
+could_not_add:
+  {
+    GST_DEBUG_OBJECT (self, "could not add pad");
+    destroy_pad_info (infos);
+    return NULL;
+  }
+}
+
+static void
+_release_pad (GstElement * element, GstPad * pad)
+{
+  GST_DEBUG_OBJECT (element, "Releasing pad %" GST_PTR_FORMAT, pad);
+
+  LOCK (element);
+  g_hash_table_remove (GES_SMART_ADDER (element)->pads_infos, pad);
+  UNLOCK (element);
+}
+
+/****************************************************
+ *              GObject vmethods                    *
+ ****************************************************/
+static void
+ges_smart_adder_finalize (GObject * object)
+{
+  GESSmartAdder *self = GES_SMART_ADDER (object);
+
+  g_mutex_clear (&self->lock);
+
+  G_OBJECT_CLASS (ges_smart_adder_parent_class)->finalize (object);
+}
+
+static void
+ges_smart_adder_class_init (GESSmartAdderClass * klass)
+{
+/*   GstBinClass *parent_class = GST_BIN_CLASS (klass);
+ */
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  /* FIXME Make sure the AdderClass doesn get destroy before ourself */
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sink_template));
+  gst_element_class_set_static_metadata (element_class, "GES Smart adder",
+      "Generic/Audio",
+      "Use adder making use of GES informations",
+      "Thibault Saunier <thibault.saunier@collabora.com>");
+
+  element_class->request_new_pad = GST_DEBUG_FUNCPTR (_request_new_pad);
+  element_class->release_pad = GST_DEBUG_FUNCPTR (_release_pad);
+
+  object_class->finalize = ges_smart_adder_finalize;
+}
+
+static void
+ges_smart_adder_init (GESSmartAdder * self)
+{
+  GstPad *pad;
+
+  g_mutex_init (&self->lock);
+
+  self->adder = gst_element_factory_make ("adder", "smart-adder-adder");
+  gst_bin_add (GST_BIN (self), self->adder);
+
+  pad = gst_element_get_static_pad (self->adder, "src");
+  self->srcpad = gst_ghost_pad_new ("src", pad);
+  gst_pad_set_active (self->srcpad, TRUE);
+
+  gst_element_add_pad (GST_ELEMENT (self), self->srcpad);
+
+  self->pads_infos = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+      NULL, (GDestroyNotify) destroy_pad_info);
+}
+
+GstElement *
+ges_smart_adder_new (GESTrack * track)
+{
+  GESSmartAdder *self = g_object_new (GES_TYPE_SMART_ADDER, NULL);
+  self->track = track;
+
+  /* FIXME Make adder smart and let it properly negotiate caps! */
+  g_object_set (self->adder, "caps", gst_caps_from_string (DEFAULT_CAPS), NULL);
+  return GST_ELEMENT (self);
+}
diff --git a/ges/ges-smart-adder.h b/ges/ges-smart-adder.h
new file mode 100644 (file)
index 0000000..10cd3aa
--- /dev/null
@@ -0,0 +1,63 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-  */
+/*
+ * gst-editing-services
+ * Copyright (C) 2013 Thibault Saunier <tsaunier@gnome.org>
+ *
+ * gst-editing-services is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
+ */
+
+#ifndef _GES_SMART_ADDER_H_
+#define _GES_SMART_ADDER_H_
+
+#include <glib-object.h>
+#include <gst/gst.h>
+
+#include "ges-track.h"
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_SMART_ADDER             (ges_smart_adder_get_type ())
+#define GES_SMART_ADDER(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_SMART_ADDER, GESSmartAdder))
+#define GES_SMART_ADDER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_SMART_ADDER, GESSmartAdderClass))
+#define GES_IS_SMART_ADDER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_SMART_ADDER))
+#define GES_IS_SMART_ADDER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_SMART_ADDER))
+#define GES_SMART_ADDER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_SMART_ADDER, GESSmartAdderClass))
+
+typedef struct _GESSmartAdderClass GESSmartAdderClass;
+typedef struct _GESSmartAdder GESSmartAdder;
+
+struct _GESSmartAdderClass
+{
+  GstBinClass parent_class;
+};
+
+struct _GESSmartAdder
+{
+  GstBin parent_instance;
+
+  GHashTable *pads_infos;
+  GstPad *srcpad;
+  GstElement *adder;
+  GMutex lock;
+
+  GstCaps *caps;
+
+  GESTrack *track;
+};
+
+GType         ges_smart_adder_get_type (void) G_GNUC_CONST;
+GstElement*   ges_smart_adder_new      (GESTrack *track);
+
+G_END_DECLS
+#endif /* _GES_SMART_ADDER_H_ */
index 0c5168b..e51a086 100644 (file)
@@ -435,6 +435,42 @@ ges_track_finalize (GObject * object)
 }
 
 static void
+ges_track_constructed (GObject * object)
+{
+  GESTrack *self = GES_TRACK (object);
+
+  if (!gst_bin_add (GST_BIN (self), self->priv->composition))
+    GST_ERROR ("Couldn't add composition to bin !");
+
+  if (GES_TRACK_GET_CLASS (self)->get_mixing_element) {
+    GstElement *gnlobject;
+    GstElement *mixer = GES_TRACK_GET_CLASS (self)->get_mixing_element (self);
+
+    if (mixer == NULL) {
+      GST_WARNING_OBJECT (self, "Got no element fron get_mixing_element");
+
+      return;
+    }
+
+    gnlobject = gst_element_factory_make ("gnloperation", "mixing-operation");
+    if (!gst_bin_add (GST_BIN (gnlobject), mixer)) {
+      GST_WARNING_OBJECT (self, "Could not add the mixer to our composition");
+
+      return;
+    }
+    g_object_set (gnlobject, "expandable", TRUE, NULL);
+
+    if (!gst_bin_add (GST_BIN (self->priv->composition), gnlobject)) {
+      GST_WARNING_OBJECT (self, "Could not add the mixer to our composition");
+
+      return;
+    }
+  } else {
+    GST_INFO_OBJECT (self, "No way to create a main mixer");
+  }
+}
+
+static void
 ges_track_class_init (GESTrackClass * klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -445,6 +481,7 @@ ges_track_class_init (GESTrackClass * klass)
   object_class->set_property = ges_track_set_property;
   object_class->dispose = ges_track_dispose;
   object_class->finalize = ges_track_finalize;
+  object_class->constructed = ges_track_constructed;
 
   /**
    * GESTrack:caps:
@@ -541,35 +578,6 @@ ges_track_init (GESTrack * self)
       (GCallback) pad_added_cb, self);
   g_signal_connect (self->priv->composition, "pad-removed",
       (GCallback) pad_removed_cb, self);
-
-  if (!gst_bin_add (GST_BIN (self), self->priv->composition))
-    GST_ERROR ("Couldn't add composition to bin !");
-
-  if (GES_TRACK_GET_CLASS (self)->get_mixing_element) {
-    GstElement *gnlobject;
-    GstElement *mixer = GES_TRACK_GET_CLASS (self)->get_mixing_element (self);
-
-    if (mixer == NULL) {
-      GST_WARNING_OBJECT (self, "Got no element fron get_mixing_element");
-
-      return;
-    }
-
-    gnlobject = gst_element_factory_make ("gnloperation", "mixing-operation");
-    if (!gst_bin_add (GST_BIN (gnlobject), mixer)) {
-      GST_WARNING_OBJECT (self, "Could not add the mixer to our composition");
-
-      return;
-    }
-    g_object_set (gnlobject, "start", GST_CLOCK_TIME_NONE, "duration",
-        GST_CLOCK_TIME_NONE, "prioirity", 0, NULL);
-
-    if (!gst_bin_add (GST_BIN (self->priv->composition), gnlobject)) {
-      GST_WARNING_OBJECT (self, "Could not add the mixer to our composition");
-
-      return;
-    }
-  }
 }
 
 /**
index eb3663c..fab4ae5 100644 (file)
@@ -40,6 +40,7 @@ check_PROGRAMS = \
        ges/transition  \
        ges/overlays\
        ges/text_properties\
+       ges/mixers\
        ges/project
 
 noinst_LTLIBRARIES=$(testutils_noisnt_libraries)
index 2045b15..daf8b90 100644 (file)
@@ -293,8 +293,9 @@ GST_START_TEST (test_gap_filling_basic)
   assert_equals_uint64 (_START (trackelement), 0);
   assert_equals_uint64 (_DURATION (trackelement), 5);
 
-  /* Check no gap were wrongly added */
-  assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 1);
+  /* Check no gap were wrongly added
+   * 2: 1 for the trackelement and 1 for the mixer */
+  assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 2);
 
   clip1 = GES_CLIP (ges_test_clip_new ());
   fail_unless (clip1 != NULL);
@@ -316,18 +317,20 @@ GST_START_TEST (test_gap_filling_basic)
   assert_equals_uint64 (_DURATION (trackelement1), 5);
 
   /* Check the gap as properly been added */
-  assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 3);
+  assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 4);
 
   for (tmp = GST_BIN_CHILDREN (composition); tmp; tmp = tmp->next) {
+    guint prio;
     GstElement *tmp_gnlobj = GST_ELEMENT (tmp->data);
 
-    if (tmp_gnlobj != gnlsrc && tmp_gnlobj != gnlsrc1) {
+    g_object_get (tmp_gnlobj, "priority", &prio, NULL);
+    if (tmp_gnlobj != gnlsrc && tmp_gnlobj != gnlsrc1 && prio == 1) {
       gap = tmp_gnlobj;
     }
   }
   fail_unless (gap != NULL);
   fail_unless (ges_timeline_commit (timeline));
-  gap_object_check (gap, 5, 10, 0);
+  gap_object_check (gap, 5, 10, 1);
 
   clip2 = GES_CLIP (ges_test_clip_new ());
   fail_unless (clip2 != NULL);
@@ -339,7 +342,7 @@ GST_START_TEST (test_gap_filling_basic)
   fail_unless (ges_track_element_get_track (trackelement2) == track);
   assert_equals_uint64 (_START (trackelement2), 35);
   assert_equals_uint64 (_DURATION (trackelement2), 5);
-  assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 5);
+  assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 6);
 
   gst_object_unref (timeline);
 }
@@ -358,14 +361,15 @@ GST_START_TEST (test_gap_filling_empty_track)
 
   ges_init ();
 
-  track = GES_TRACK(ges_audio_track_new ());
+  track = GES_TRACK (ges_audio_track_new ());
 
   layer = ges_layer_new ();
   timeline = ges_timeline_new ();
   fail_unless (timeline != NULL);
   fail_unless (ges_timeline_add_layer (timeline, layer));
   fail_unless (ges_timeline_add_track (timeline, track));
-  fail_unless (ges_timeline_add_track (timeline, GES_TRACK(ges_video_track_new ())));
+  fail_unless (ges_timeline_add_track (timeline,
+          GES_TRACK (ges_video_track_new ())));
 
   /* Set some properties */
   asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL);
@@ -377,12 +381,13 @@ GST_START_TEST (test_gap_filling_empty_track)
 
   /* Check that a gap was properly added */
   composition = find_composition (track);
-  assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 1);
+  /* We also have an adder in that composition */
+  assert_equals_int (g_list_length (GST_BIN_CHILDREN (composition)), 2);
 
   gap = GST_BIN_CHILDREN (composition)->data;
   fail_unless (gap != NULL);
   fail_unless (ges_timeline_commit (timeline));
-  gap_object_check (gap, 0, 10, 0);
+  gap_object_check (gap, 0, 10, 1);
 
   gst_object_unref (timeline);
 }
index f030930..f35aa3e 100644 (file)
@@ -97,7 +97,7 @@ GST_START_TEST (test_get_effects_from_tl)
   ges_init ();
 
   timeline = ges_timeline_new ();
-  layer = (GESLayer *) ges_simple_layer_new ();
+  layer = (GESLayer *) ges_layer_new ();
   track_video = GES_TRACK (ges_video_track_new ());
 
   ges_timeline_add_track (timeline, track_video);
index f2c9e46..8e8b2d0 100644 (file)
@@ -265,13 +265,15 @@ GST_START_TEST (test_layer_priorities)
   assert_equals_int (prio3, LAYER_HEIGHT * 3 - 1 + MIN_GNL_PRIO);
 
   /* And change TrackElement-s priorities and check that changes are not
-   * refected on it containing Clip */
-  ges_timeline_element_set_priority (GES_TIMELINE_ELEMENT (trackelement3),
-      LAYER_HEIGHT * 2);
-  ges_timeline_commit (timeline);
-  g_object_get (gnlobj3, "priority", &prio3, NULL);
-  assert_equals_int (prio3, 2 * LAYER_HEIGHT);
-  assert_equals_int (_PRIORITY (clip3), LAYER_HEIGHT - 1);
+   * refected on it containing Clip
+   * FIXME : We should rework the way we handle the case were a trackobject
+   * prio is set outside the layer it is in.
+   * ges_timeline_element_set_priority (GES_TIMELINE_ELEMENT (trackelement3),
+   * ges_timeline_commit (timeline);
+   *     LAYER_HEIGHT * 2);
+   * g_object_get (gnlobj3, "priority", &prio3, NULL);
+   * assert_equals_int (prio3, 2 * LAYER_HEIGHT);
+   * assert_equals_int (_PRIORITY (clip3), LAYER_HEIGHT - 1); */
 
   gst_object_unref (trackelement1);
   gst_object_unref (trackelement2);
diff --git a/tests/check/ges/mixers.c b/tests/check/ges/mixers.c
new file mode 100644 (file)
index 0000000..6809997
--- /dev/null
@@ -0,0 +1,171 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-  */
+/*
+ * gst-editing-services
+ *
+ * Copyright (C) 2013 Thibault Saunier <tsaunier@gnome.org>
+ *
+ * gst-editing-services is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
+ *
+ */
+#include "test-utils.h"
+#include <ges/ges.h>
+#include <gst/check/gstcheck.h>
+
+#include <ges/ges-smart-adder.h>
+
+static GMainLoop *main_loop;
+
+GST_START_TEST (simple_smart_adder_test)
+{
+  GstPad *requested_pad;
+  GstPadTemplate *template = NULL;
+  GESTrack *track = GES_TRACK (ges_audio_track_new ());
+  GstElement *smart_adder = ges_smart_adder_new (track);
+
+  fail_unless (GES_IS_SMART_ADDER (smart_adder));
+  fail_unless (GST_IS_ELEMENT (smart_adder));
+  fail_unless (GST_IS_ELEMENT (GES_SMART_ADDER (smart_adder)->adder));
+  fail_unless (GST_IS_PAD (GES_SMART_ADDER (smart_adder)->srcpad));
+
+  template =
+      gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (smart_adder),
+      "sink_%u");
+  fail_unless (template != NULL);
+  requested_pad = gst_element_request_pad (GST_ELEMENT (smart_adder),
+      template, NULL, NULL);
+  fail_unless (GST_IS_PAD (requested_pad));
+
+  gst_object_unref (smart_adder);
+  gst_object_unref (track);
+}
+
+GST_END_TEST;
+
+static void
+message_received_cb (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
+{
+  GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT,
+      GST_MESSAGE_SRC (message), message);
+  switch (GST_MESSAGE_TYPE (message)) {
+    case GST_MESSAGE_EOS:
+      /* we should check if we really finished here */
+      GST_WARNING ("Got an EOS");
+      g_main_loop_quit (main_loop);
+      break;
+    case GST_MESSAGE_SEGMENT_START:
+    case GST_MESSAGE_SEGMENT_DONE:
+      /* We shouldn't see any segement messages, since we didn't do a segment seek */
+      GST_WARNING ("Saw a Segment start/stop");
+      fail_if (TRUE);
+      g_main_loop_quit (main_loop);
+      break;
+    case GST_MESSAGE_ERROR:
+      fail_error_message (message);
+      g_main_loop_quit (main_loop);
+    default:
+      break;
+  }
+}
+
+GST_START_TEST (simple_audio_mixed_with_pipeline)
+{
+  GstBus *bus;
+  GESAsset *asset;
+  GESClip *tmpclip;
+  GstMessage *message;
+  GESLayer *layer, *layer1;
+  GESTrack *track = GES_TRACK (ges_audio_track_new ());
+  GESTimeline *timeline = ges_timeline_new ();
+  GESTimelinePipeline *pipeline = ges_test_create_pipeline (timeline);
+
+  ges_timeline_add_track (timeline, track);
+  layer = ges_timeline_append_layer (timeline);
+  layer1 = ges_timeline_append_layer (timeline);
+
+  asset = GES_ASSET (ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL));
+
+  GST_DEBUG ("Setting volume on the layer");
+  ges_meta_container_set_float (GES_META_CONTAINER (layer), GES_META_VOLUME,
+      1.5);
+
+  tmpclip = ges_layer_add_asset (layer, asset, 0, 0, 1 * GST_SECOND,
+      GES_TRACK_TYPE_AUDIO);
+  ges_audio_test_source_set_volume (GES_CONTAINER_CHILDREN (tmpclip)->data,
+      1.0);
+  ges_audio_test_source_set_freq (GES_CONTAINER_CHILDREN (tmpclip)->data, 550);
+
+  tmpclip = ges_layer_add_asset (layer1, asset, 0, 0, 2 * GST_SECOND,
+      GES_TRACK_TYPE_AUDIO);
+
+  ges_audio_test_source_set_volume (GES_CONTAINER_CHILDREN (tmpclip)->data, 1);
+
+  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+  main_loop = g_main_loop_new (NULL, FALSE);
+
+  gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+  g_signal_connect (bus, "message", (GCallback) message_received_cb, pipeline);
+  fail_if (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING)
+      == GST_STATE_CHANGE_FAILURE);
+  message = gst_bus_timed_pop_filtered (bus, 5 * GST_SECOND,
+      GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
+
+  if (message == NULL) {
+    fail_unless ("No message after 5 seconds" == NULL);
+    goto done;
+  } else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR)
+    fail_error_message (message);
+
+  GST_INFO ("running main loop");
+  g_main_loop_run (main_loop);
+  g_main_loop_unref (main_loop);
+
+done:
+  gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+  gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+ges_suite (void)
+{
+  Suite *s = suite_create ("Smart mixers");
+  TCase *tc_chain = tcase_create ("smart-mixers");
+
+  suite_add_tcase (s, tc_chain);
+
+  tcase_add_test (tc_chain, simple_smart_adder_test);
+  tcase_add_test (tc_chain, simple_audio_mixed_with_pipeline);
+
+  return s;
+}
+
+int
+main (int argc, char **argv)
+{
+  int nf;
+
+  Suite *s = ges_suite ();
+  SRunner *sr = srunner_create (s);
+
+  gst_check_init (&argc, &argv);
+  ges_init ();
+
+  srunner_run_all (sr, CK_NORMAL);
+  nf = srunner_ntests_failed (sr);
+  srunner_free (sr);
+
+  return nf;
+}