[gst-devel] [PATCH 3/3]: gst-plugins-0.8.8-videoblender.patch

Pfiffer, AndrewX K andrewx.k.pfiffer at intel.com
Fri Jun 10 13:48:38 CEST 2005


[This patch is relative to gst-plugins-0.8.8]
--

This patch was developed as part of ongoing work in support of the
Consumer Electronics Linux Forum (CELF). [ http://www.celinuxforum.org/
]

Signed-off-by: Saikat Sanyal <saikat at alumnux.com>
Signed-off-by: Andrew Pfiffer <andrewx.k.pfiffer at intel.com>

This is a new plug-in - Video Blender.

 FEATURES
 --------
  1. Accepts I420 format media in its input (sink) pads.
  2. Blends I420 video streams to provide Picture-in-Picture.
  3. The element has three input pads with no relative ordering of
linking.
  4. At most 2 movies are blended - i.e. at most 2 sink pads can remain
     linked simultaneously.
  5. Support for dynamic linking and unlinking of inputs.
  6. Support for changing the z-order of the sink pads.
  7. Support for keeping on demand cached data.
  8. Support for directly render onto SDL.
  9. Support for applications to register for Key Frames in MPEG movies.

 LIMITATIONS
 -----------
  a. Movies should be of fixed (currently 640x480) resolution.
  b. Blended movies are rendered onto SDL surface unconditionally.
  c. The Scale-down factor is not configurable - value is fixed (4).


--
diff -Naur gst-plugins-0.8.8/configure.ac
gst-plugins-0.8.8-ipvr/configure.ac
--- gst-plugins-0.8.8/configure.ac	2005-03-07 22:10:00.000000000
+0530
+++ gst-plugins-0.8.8-ipvr/configure.ac	2005-04-19 18:00:17.927946256
+0530
@@ -426,6 +426,7 @@
 	typefind \
 	udp \
 	vbidec \
+	videoblender \
 	videobox \
 	videocrop \
 	videodrop \
@@ -2011,6 +2012,7 @@
 gst/typefind/Makefile
 gst/udp/Makefile
 gst/vbidec/Makefile
+gst/videoblender/Makefile
 gst/videobox/Makefile
 gst/videocrop/Makefile
 gst/videodrop/Makefile
diff -Naur gst-plugins-0.8.8/gst/videoblender/Makefile.am
gst-plugins-0.8.8-ipvr/gst/videoblender/Makefile.am
--- gst-plugins-0.8.8/gst/videoblender/Makefile.am	1970-01-01
05:30:00.000000000 +0530
+++ gst-plugins-0.8.8-ipvr/gst/videoblender/Makefile.am	2005-04-19
17:55:41.578957688 +0530
@@ -0,0 +1,6 @@
+plugin_LTLIBRARIES = libgstvideoblender.la
+
+libgstvideoblender_la_SOURCES = videoblender.c
+libgstvideoblender_la_CFLAGS = $(GST_CFLAGS) $(SDL_CFLAGS)
+libgstvideoblender_la_LIBADD = $(SDL_LIBS)
+libgstvideoblender_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
diff -Naur gst-plugins-0.8.8/gst/videoblender/README
gst-plugins-0.8.8-ipvr/gst/videoblender/README
--- gst-plugins-0.8.8/gst/videoblender/README	1970-01-01
05:30:00.000000000 +0530
+++ gst-plugins-0.8.8-ipvr/gst/videoblender/README	2005-04-19
17:55:41.577957840 +0530
@@ -0,0 +1,19 @@
+Video Blender
+-------------
+
+  1. Accepts I420 format media in its input (sink) pads.
+  2. Blends I420 video streams to provide Picture-in-Picture.
+  3. The element has three input pads with no relative ordering of
linking.
+  4. At most 2 movies are blended - i.e. at most 2 sink pads can remain
+     linked simultaneously.
+  5. Support for dynamic linking and unlinking of inputs.
+  6. Support for changing the z-order of the sink pads.
+  7. Support for keeping on demand cached data.
+  8. Support for directly render onto SDL.
+  9. Support for applications to register for Key Frames in MPEG
movies.
+
+LIMITATIONS
+-----------
+  a. Movies should be of fixed (640x480) resolution.
+  b. Blended movies are rendered onto SDL surface unconditionally.
+  c. The Scale-down factor is not configurable - value is fixed (4).
diff -Naur gst-plugins-0.8.8/gst/videoblender/videoblender.c
gst-plugins-0.8.8-ipvr/gst/videoblender/videoblender.c
--- gst-plugins-0.8.8/gst/videoblender/videoblender.c	1970-01-01
05:30:00.000000000 +0530
+++ gst-plugins-0.8.8-ipvr/gst/videoblender/videoblender.c
2005-04-19 17:55:41.577957840 +0530
@@ -0,0 +1,1195 @@
+/* Generic video blender plugin
+ * Copyright (C) 2004 Wim Taymans <wim at fluendo.com>
+ *
+ * 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 <gst/gst.h>
+#include <string.h>
+
+#include <SDL.h>
+#include <gst/video/videosink.h>
+#include <gst/xoverlay/xoverlay.h>
+
+/* For the time being, the Screen Height and Width are fixed */
+#define SCREEN_WIDTH      640
+#define SCREEN_HEIGHT     480
+#define SCALE_DOWN_FACTOR   4
+
+GST_DEBUG_CATEGORY_STATIC (gst_videoblender_debug);
+#define GST_CAT_DEFAULT gst_videoblender_debug
+
+#define GST_TYPE_VIDEO_BLENDER_PAD (gst_videoblender_pad_get_type())
+#define GST_VIDEO_BLENDER_PAD(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_BLENDER_PAD,
GstVideoBlenderPad))
+#define GST_VIDEO_BLENDER_PAD_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_BLENDER_PAD,
GstVideoBlenderPadiClass))
+#define GST_IS_VIDEO_BLENDER_PAD(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_BLENDER_PAD))
+#define GST_IS_VIDEO_BLENDER_PAD_CLASS(obj) \
+	(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_BLENDER_PAD))
+
+typedef struct _GstVideoBlenderPad GstVideoBlenderPad;
+typedef struct _GstVideoBlenderPadClass GstVideoBlenderPadClass;
+
+#define GST_TYPE_VIDEO_BLENDER (gst_videoblender_get_type())
+#define GST_VIDEO_BLENDER(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_BLENDER,
GstVideoBlender))
+#define GST_VIDEO_BLENDER_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_BLENDER,
GstVideoBlenderClass))
+#define GST_IS_VIDEO_BLENDER(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_BLENDER))
+#define GST_IS_VIDEO_BLENDER_CLASS(obj) \
+	(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_BLENDER))
+
+#ifdef GST_VIDEOSINK_HEIGHT
+#undef GST_VIDEOSINK_HEIGHT
+#define GST_VIDEOSINK_HEIGHT(obj) ((obj)->height)
+#endif
+#ifdef GST_VIDEOSINK_WIDTH
+#undef GST_VIDEOSINK_WIDTH
+#define GST_VIDEOSINK_WIDTH(obj) ((obj)->width)
+#endif
+#define GST_VIDEOBLENDER_CLOCK(obj) (GST_VIDEO_BLENDER (obj)->clock)
+
+static GType gst_videoblender_get_type (void);
+
+typedef struct _GstVideoBlender GstVideoBlender;
+typedef struct _GstVideoBlenderClass GstVideoBlenderClass;
+
+static gboolean gst_sdlvideosink_create(GstVideoBlender *);
+static guint32 gst_sdlvideosink_get_sdl_from_fourcc(GstVideoBlender *,
guint32);
+
+
+static void gst_videoblender_pad_base_init (gpointer g_class);
+static void gst_videoblender_pad_class_init (GstVideoBlenderPadClass *
klass);
+static void gst_videoblender_pad_init (GstVideoBlenderPad *
blenderpad);
+
+static void gst_videoblender_pad_get_property (GObject * object, guint
prop_id,
+    GValue * value, GParamSpec * pspec);
+static void gst_videoblender_pad_set_property (GObject * object, guint
prop_id,
+    const GValue * value, GParamSpec * pspec);
+enum
+{
+  SIGNAL_HANDLE_KEYFRAME,
+  LAST_SIGNAL
+};
+typedef struct _MPEG_KEYFRAME {
+  guint64 offset;
+  gint64  timestamp;
+} MPEG_KEYFRAME;
+static guint gst_videoblender_pad_signals[LAST_SIGNAL] = { 0 };
+
+#define DEFAULT_PAD_ZORDER 0
+#define DEFAULT_PAD_XPOS   0
+#define DEFAULT_PAD_YPOS   0
+#define DEFAULT_PAD_ALPHA  1.0
+enum
+{
+  ARG_PAD_0,
+  ARG_PAD_ZORDER,
+  ARG_PAD_CACHE_DATA,
+  ARG_PAD_XPOS,
+  ARG_PAD_YPOS,
+  ARG_PAD_ALPHA,
+};
+
+static int gZorder = 0;
+
+/* all information needed for one video stream */
+struct _GstVideoBlenderPad
+{
+  GstRealPad parent;            /* subclass the pad */
+
+  GstBuffer *buffer;            /* the queued buffer for this pad */
+  gboolean eos;
+
+  gint64 queued;
+
+  guint in_width, in_height;
+  gdouble in_framerate;
+
+  gint xpos, ypos;
+  guint zorder;
+  gint blend_mode;
+  gdouble alpha;
+
+  guint8 *oldBuffer;
+  uint toTakeOld;
+};
+
+struct _GstVideoBlenderPadClass
+{
+  GstRealPadClass parent_class;
+};
+
+static GType
+gst_videoblender_pad_get_type (void)
+{
+  static GType videoblender_pad_type = 0;
+
+  if (!videoblender_pad_type) {
+    static const GTypeInfo videoblender_pad_info = {
+      sizeof (GstVideoBlenderPadClass),
+      gst_videoblender_pad_base_init,
+      NULL,
+      (GClassInitFunc) gst_videoblender_pad_class_init,
+      NULL,
+      NULL,
+      sizeof (GstVideoBlenderPad),
+      0,
+      (GInstanceInitFunc) gst_videoblender_pad_init,
+    };
+
+    videoblender_pad_type =
+        g_type_register_static (GST_TYPE_REAL_PAD,
+        "GstVideoBlenderPad", &videoblender_pad_info, 0);
+  }
+  return videoblender_pad_type;
+}
+
+static void
+gst_videoblender_pad_base_init (gpointer g_class)
+{
+}
+
+static void
+gst_videoblender_pad_class_init (GstVideoBlenderPadClass * klass)
+{
+  GObjectClass *gobject_class;
+
+  gobject_class = (GObjectClass *) klass;
+
+  gobject_class->set_property =
+      GST_DEBUG_FUNCPTR (gst_videoblender_pad_set_property);
+  gobject_class->get_property =
+      GST_DEBUG_FUNCPTR (gst_videoblender_pad_get_property);
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
ARG_PAD_ZORDER,
+      g_param_spec_uint ("zorder", "Z-Order", "Z Order of the picture",
+          0, 10000, DEFAULT_PAD_ZORDER, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
ARG_PAD_CACHE_DATA,
+      g_param_spec_int ("cachedata", "Cache Old Data", "Cache Old
Data",
+          G_MININT, G_MAXINT, DEFAULT_PAD_XPOS, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
ARG_PAD_XPOS,
+      g_param_spec_int ("xpos", "X Position", "X Position of the
picture",
+          G_MININT, G_MAXINT, DEFAULT_PAD_XPOS, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
ARG_PAD_YPOS,
+      g_param_spec_int ("ypos", "Y Position", "Y Position of the
picture",
+          G_MININT, G_MAXINT, DEFAULT_PAD_YPOS, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
ARG_PAD_ALPHA,
+      g_param_spec_double ("alpha", "Alpha", "Alpha of the picture",
+          0.0, 1.0, DEFAULT_PAD_ALPHA, G_PARAM_READWRITE));
+}
+
+static const GstEventMask *
+gst_videoblender_pad_get_sink_event_masks (GstPad * pad)
+{
+  static const GstEventMask gst_videoblender_sink_event_masks[] = {
+    {GST_EVENT_EOS, 0},
+    {GST_EVENT_INTERRUPT, 0},
+    {0,}
+  };
+
+  return gst_videoblender_sink_event_masks;
+}
+
+static void
+gst_videoblender_pad_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstVideoBlenderPad *pad;
+
+  g_return_if_fail (GST_IS_VIDEO_BLENDER_PAD (object));
+
+  pad = GST_VIDEO_BLENDER_PAD (object);
+
+  switch (prop_id) {
+    case ARG_PAD_ZORDER:
+      g_value_set_uint (value, pad->zorder);
+      break;
+    case ARG_PAD_XPOS:
+      g_value_set_int (value, pad->xpos);
+      break;
+    case ARG_PAD_YPOS:
+      g_value_set_int (value, pad->ypos);
+      break;
+    case ARG_PAD_ALPHA:
+      g_value_set_double (value, pad->alpha);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+static void
+gst_videoblender_pad_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstVideoBlenderPad *pad;
+  GstVideoBlender *mix;
+
+  g_return_if_fail (GST_IS_PAD (object));
+
+  pad = GST_VIDEO_BLENDER_PAD (object);
+  mix = GST_VIDEO_BLENDER (gst_pad_get_parent (GST_PAD (pad)));
+
+  switch (prop_id) {
+    case ARG_PAD_ZORDER:
+      pad->zorder = g_value_get_uint (value);
+      break;
+    case ARG_PAD_CACHE_DATA:
+      pad->toTakeOld = g_value_get_int (value);
+      break;
+    case ARG_PAD_XPOS:
+      pad->xpos = g_value_get_int (value);
+      break;
+    case ARG_PAD_YPOS:
+      pad->ypos = g_value_get_int (value);
+      break;
+    case ARG_PAD_ALPHA:
+      pad->alpha = g_value_get_double (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+typedef enum
+{
+  VIDEO_BLENDER_BACKGROUND_CHECKER,
+  VIDEO_BLENDER_BACKGROUND_BLACK,
+  VIDEO_BLENDER_BACKGROUND_WHITE,
+}
+GstVideoBlenderBackground;
+
+struct _GstVideoBlender
+{
+  GstElement element;
+
+  /* pad */
+  GstPad *srcpad;
+
+  GstPad *sinkpads[3];
+  gint numpads;
+
+  /* the master pad */
+  GstVideoBlenderPad *master;
+
+  gint in_width, in_height;
+  gint out_width, out_height;
+
+  GstVideoBlenderBackground background;
+
+  gdouble in_framerate;
+
+  GstVideoSink videosink;
+
+  guint32 format;
+  gint width, height;    /* the size of the incoming YUV stream */
+  unsigned long xwindow_id;
+
+  gboolean init;
+
+  SDL_Surface *screen;
+  SDL_Overlay *overlay;
+  SDL_Rect rect;
+
+  GMutex *lock;
+  GstClock *clock;
+
+};
+
+struct _GstVideoBlenderClass
+{
+  GstElementClass parent_class;
+
+  void  (*getkeyframe) (GstElement *mix, const char *name, void
*mpeg_keyframe);
+};
+
+static GstPadLinkReturn
+gst_videoblender_pad_sinkconnect (GstPad * pad, const GstCaps * vscaps)
+{
+  GstVideoBlender *mix;
+  GstVideoBlenderPad *mixpad;
+  GstStructure *structure;
+  guint32 format;
+
+
+  mix = GST_VIDEO_BLENDER (gst_pad_get_parent (pad));
+  mixpad = GST_VIDEO_BLENDER_PAD (pad);
+
+  GST_DEBUG ("videoblender: sinkconnect triggered on %s",
gst_pad_get_name (pad));
+
+  structure = gst_caps_get_structure (vscaps, 0);
+
+
+  gst_structure_get_fourcc (structure, "format", &format);
+  mix->format = gst_sdlvideosink_get_sdl_from_fourcc (mix, format);
+  gst_structure_get_int (structure, "width", &mix->width);
+  gst_structure_get_int (structure, "height", &mix->height);
+
+  gst_structure_get_int (structure, "width", &mixpad->in_width);
+  gst_structure_get_int (structure, "height", &mixpad->in_height);
+  gst_structure_get_double (structure, "framerate",
&mixpad->in_framerate);
+
+  mixpad->xpos = 0;
+  mixpad->ypos = 0;
+
+  mix->in_width = MAX (mix->in_width, mixpad->in_width);
+  mix->in_height = MAX (mix->in_height, mixpad->in_height);
+  if (mix->in_framerate < mixpad->in_framerate) {
+    mix->in_framerate = mixpad->in_framerate;
+    mix->master = mixpad;
+  }
+
+  if (!gst_sdlvideosink_create (mix))
+    return GST_PAD_LINK_REFUSED;
+
+  return GST_PAD_LINK_OK;
+}
+
+static void
+gst_videoblender_pad_link (GstPad * pad, GstPad * peer, gpointer data)
+{
+  GstVideoBlender *mix = GST_VIDEO_BLENDER (gst_pad_get_parent (pad));
+  G_CONST_RETURN gchar *name = gst_pad_get_name(pad);
+  int padPos;
+  GstVideoBlenderPad *blenderpad = GST_VIDEO_BLENDER_PAD(pad);
+ 
+  GST_DEBUG ("pad '%s' connected", gst_pad_get_name(pad));
+  padPos = name[strlen(name) - 1] - '0';
+  mix->sinkpads[padPos] = pad;
+  blenderpad->zorder = gZorder++;
+  blenderpad->eos = FALSE;
+  mix->numpads++;
+  return;
+}
+
+static void
+gst_videoblender_pad_unlink (GstPad * pad, GstPad * peer, gpointer
data)
+{
+  GstVideoBlender *mix = GST_VIDEO_BLENDER (gst_pad_get_parent (pad));
+  GstVideoBlenderPad *blenderpad = GST_VIDEO_BLENDER_PAD (pad);
+  G_CONST_RETURN gchar *name = gst_pad_get_name(pad);
+  int padPos;
+
+  //GstVideoBlender *videoblender = GST_VIDEO_BLENDER (data);
+
+  GST_DEBUG ("pad '%s' unlinked", gst_pad_get_name (pad));
+  padPos = name[strlen(name) - 1] - '0';
+  mix->sinkpads[padPos] = NULL;
+  mix->numpads--;
+  blenderpad->eos = TRUE;
+}
+
+static void
+gst_videoblender_pad_init (GstVideoBlenderPad * blenderpad)
+{
+  g_signal_connect (blenderpad, "linked",
+      G_CALLBACK (gst_videoblender_pad_link), (gpointer) blenderpad);
+  g_signal_connect (blenderpad, "unlinked",
+      G_CALLBACK (gst_videoblender_pad_unlink), (gpointer) blenderpad);
+
+  /* setup some pad functions */
+  gst_pad_set_link_function (GST_PAD (blenderpad),
+      gst_videoblender_pad_sinkconnect);
+  gst_pad_set_event_mask_function (GST_PAD (blenderpad),
+      gst_videoblender_pad_get_sink_event_masks);
+
+  blenderpad->zorder = DEFAULT_PAD_ZORDER;
+  blenderpad->xpos = DEFAULT_PAD_XPOS;
+  blenderpad->ypos = DEFAULT_PAD_YPOS;
+  blenderpad->alpha = DEFAULT_PAD_ALPHA;
+
+  blenderpad->oldBuffer =
+                 (guint8 *) malloc(SCREEN_WIDTH * SCREEN_HEIGHT * 3 /
2);
+  memset(blenderpad->oldBuffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT * 3 /
2);
+  blenderpad->toTakeOld = 0;
+}
+
+
+
+/* elementfactory information */
+static GstElementDetails gst_videoblender_details =
+GST_ELEMENT_DETAILS ("video blender",
+    "Filter/Editor/Video",
+    "Mix multiple video streams",
+    "Wim Taymans <wim at fluendo.com>");
+
+#define DEFAULT_BACKGROUND VIDEO_BLENDER_BACKGROUND_CHECKER
+enum
+{
+  ARG_0,
+  ARG_BACKGROUND,
+};
+
+#define GST_TYPE_VIDEO_BLENDER_BACKGROUND
(gst_video_blender_background_get_type())
+static GType
+gst_video_blender_background_get_type (void)
+{
+  static GType video_blender_background_type = 0;
+  static GEnumValue video_blender_background[] = {
+    {VIDEO_BLENDER_BACKGROUND_CHECKER, "0", "Checker pattern"},
+    {VIDEO_BLENDER_BACKGROUND_BLACK, "1", "Black"},
+    {VIDEO_BLENDER_BACKGROUND_WHITE, "2", "White"},
+    {0, NULL, NULL},
+  };
+
+  if (!video_blender_background_type) {
+    video_blender_background_type =
+        g_enum_register_static ("GstVideoBlenderBackground",
+        video_blender_background);
+  }
+  return video_blender_background_type;
+}
+
+static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE
("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-raw-yuv,"
+        "format = (fourcc) I420,"
+        "width = (int) [ 16, 4096 ],"
+        "height = (int) [ 16, 4096 ]," "framerate = (double) [ 0, max
]")
+    );
+
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE
("sink_%d",
+    GST_PAD_SINK,
+    GST_PAD_REQUEST,
+    GST_STATIC_CAPS ("video/x-raw-yuv,"
+        "format = (fourcc) I420,"
+        "width = (int) [ 16, 4096 ],"
+        "height = (int) [ 16, 4096 ]," "framerate = (double) [ 0, max
]")
+    );
+
+static void gst_videoblender_base_init (gpointer g_class);
+static void gst_videoblender_class_init (GstVideoBlenderClass * klass);
+static void gst_videoblender_init (GstVideoBlender * videoblender);
+
+static GstCaps *gst_videoblender_getcaps (GstPad * pad);
+
+static void gst_videoblender_loop (GstElement * element);
+static gboolean gst_videoblender_handle_src_event (GstPad * pad,
+    GstEvent * event);
+static GstPad *gst_videoblender_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * name);
+static void gst_videoblender_set_property (GObject * object, guint
prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_videoblender_get_property (GObject * object, guint
prop_id,
+    GValue * value, GParamSpec * pspec);
+static GstElementStateReturn gst_videoblender_change_state (GstElement
* element);
+
+static GstElementClass *parent_class = NULL;
+
+/*static guint gst_videoblender_signals[LAST_SIGNAL] = { 0 }; */
+
+static GType
+gst_videoblender_get_type (void)
+{
+  static GType videoblender_type = 0;
+
+  if (!videoblender_type) {
+    static const GTypeInfo videoblender_info = {
+      sizeof (GstVideoBlenderClass),
+      gst_videoblender_base_init,
+      NULL,
+      (GClassInitFunc) gst_videoblender_class_init,
+      NULL,
+      NULL,
+      sizeof (GstVideoBlender),
+      0,
+      (GInstanceInitFunc) gst_videoblender_init,
+    };
+
+    videoblender_type =
+        g_type_register_static (GST_TYPE_ELEMENT, "GstVideoBlender",
+        &videoblender_info, 0);
+  }
+  return videoblender_type;
+}
+
+static void
+gst_videoblender_base_init (gpointer g_class)
+{
+
+  SDL_Quit ();
+
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_factory));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sink_factory));
+
+  gst_element_class_set_details (element_class,
&gst_videoblender_details);
+}
+
+static void
+gst_videoblender_set_clock (GstElement * element, GstClock * clock)
+{
+  GstVideoBlender *videoblender;
+  videoblender = GST_VIDEO_BLENDER (element);
+  videoblender->clock = clock;
+}
+
+static void
+gst_videoblender_class_init (GstVideoBlenderClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
ARG_BACKGROUND,
+      g_param_spec_enum ("background", "Background", "Background type",
+          GST_TYPE_VIDEO_BLENDER_BACKGROUND,
+          DEFAULT_BACKGROUND, G_PARAM_READWRITE));
+
+  gstelement_class->request_new_pad = gst_videoblender_request_new_pad;
+
+  gstelement_class->change_state = gst_videoblender_change_state;
+
+  gstelement_class->get_property = gst_videoblender_get_property;
+  gstelement_class->set_property = gst_videoblender_set_property;
+  gstelement_class->set_clock = gst_videoblender_set_clock;
+
+  gst_videoblender_pad_signals[SIGNAL_HANDLE_KEYFRAME] =
+      g_signal_new ("getkeyframe", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
+      G_STRUCT_OFFSET (GstVideoBlenderClass, getkeyframe), NULL, NULL,
+      gst_marshal_VOID__OBJECT_POINTER, G_TYPE_NONE,
+      2, G_TYPE_POINTER, G_TYPE_POINTER);
+//g_print("%s:%d\n", __FUNCTION__, __LINE__);
+}
+
+static void
+gst_videoblender_init (GstVideoBlender * mix)
+{
+  GstElementClass *klass = GST_ELEMENT_GET_CLASS (mix);
+
+  mix->srcpad =
+      gst_pad_new_from_template (gst_element_class_get_pad_template
(klass,
+          "src"), "src");
+  gst_pad_set_getcaps_function (GST_PAD (mix->srcpad),
gst_videoblender_getcaps);
+  gst_pad_set_event_function (mix->srcpad,
gst_videoblender_handle_src_event);
+  gst_element_add_pad (GST_ELEMENT (mix), mix->srcpad);
+
+  GST_FLAG_SET (GST_ELEMENT (mix), GST_ELEMENT_EVENT_AWARE);
+
+//  mix->sinkpads = NULL;
+  mix->sinkpads[0] = mix->sinkpads[1] = mix->sinkpads[2] = NULL;
+  mix->numpads = 0;
+  mix->background = DEFAULT_BACKGROUND;
+  mix->in_width = 0;
+  mix->in_height = 0;
+  mix->out_width = 0;
+  mix->out_height = 0;
+
+  gst_element_set_loop_function (GST_ELEMENT (mix),
gst_videoblender_loop);
+
+  mix->lock = g_mutex_new();
+  mix->screen = NULL;
+}
+
+static GstCaps *
+gst_videoblender_getcaps (GstPad * pad)
+{
+  GstVideoBlender *mix;
+  GstCaps *caps;
+  GstPadTemplate *src_pad_template;
+  GstStructure *structure;
+
+  mix = GST_VIDEO_BLENDER (gst_pad_get_parent (pad));
+  src_pad_template = gst_static_pad_template_get (&src_factory);
+  caps = gst_caps_copy (gst_pad_template_get_caps (src_pad_template));
+
+  structure = gst_caps_get_structure (caps, 0);
+
+  if (mix->out_width != 0) {
+    gst_structure_set (structure, "width", G_TYPE_INT, mix->out_width,
NULL);
+  }
+  if (mix->out_height != 0) {
+    gst_structure_set (structure, "height", G_TYPE_INT,
mix->out_height, NULL);
+  }
+  if (mix->in_framerate != 0) {
+    gst_structure_set (structure,
+        "framerate", G_TYPE_DOUBLE, mix->in_framerate, NULL);
+  }
+
+  return caps;
+}
+
+static GstPad *
+gst_videoblender_request_new_pad (GstElement * element,
+    GstPadTemplate * templ, const gchar * req_name)
+{
+  GstVideoBlender *mix;
+  GstPad *newpad;
+  GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
+
+  g_return_val_if_fail (templ != NULL, NULL);
+
+  if (templ->direction != GST_PAD_SINK) {
+    g_warning ("videoblender: request pad that is not a SINK pad\n");
+    return NULL;
+  }
+
+  g_return_val_if_fail (GST_IS_VIDEO_BLENDER (element), NULL);
+
+  mix = GST_VIDEO_BLENDER (element);
+
+  if (templ == gst_element_class_get_pad_template (klass, "sink_%d")) {
+    gchar *name;
+    GstVideoBlenderPad *mixpad;
+
+    /* create new pad with the name */
+    name = g_strdup_printf ("sink_%02d", mix->numpads);
+    newpad =
+        gst_pad_custom_new_from_template (GST_TYPE_VIDEO_BLENDER_PAD,
templ,
+        req_name /*name*/);
+    g_free (name);
+
+    mixpad = GST_VIDEO_BLENDER_PAD (newpad);
+
+  } else {
+    g_warning ("videoblender: this is not our template!\n");
+    return NULL;
+  }
+
+  /* dd the pad to the element */
+  gst_element_add_pad (element, newpad);
+
+  return newpad;
+}
+
+/* handle events */
+static gboolean
+gst_videoblender_handle_src_event (GstPad * pad, GstEvent * event)
+{
+  GstVideoBlender *mix;
+  GstEventType type;
+
+  mix = GST_VIDEO_BLENDER (gst_pad_get_parent (pad));
+
+  type = event ? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
+
+  switch (type) {
+    case GST_EVENT_SEEK:
+      break;
+    default:
+      break;
+  }
+
+  return gst_pad_event_default (pad, event);
+}
+
+
+static guint32
+gst_sdlvideosink_get_sdl_from_fourcc (GstVideoBlender * sdlvideosink,
+              guint32 code)
+{
+  switch (code)
+    {
+    case GST_MAKE_FOURCC ('I', '4', '2', '0'):
+      return SDL_IYUV_OVERLAY;
+    case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
+      return SDL_YV12_OVERLAY;
+    case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
+      return SDL_YUY2_OVERLAY;
+    case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
+      return SDL_UYVY_OVERLAY;
+    case GST_MAKE_FOURCC ('Y', 'V', 'Y', 'U'):
+      return SDL_YVYU_OVERLAY;
+    default:
+      return 0;
+    }
+}
+
+static void
+gst_sdlvideosink_destroy (GstVideoBlender * sdlvideosink)
+{
+  if (sdlvideosink->overlay)
+  {
+    SDL_FreeYUVOverlay (sdlvideosink->overlay);
+    sdlvideosink->overlay = NULL;
+  }
+
+  if (sdlvideosink->screen)
+  {
+    SDL_FreeSurface (sdlvideosink->screen);
+    sdlvideosink->screen = NULL;
+  }
+}
+
+static gboolean
+gst_sdlvideosink_create (GstVideoBlender * sdlvideosink)
+{
+  /* Return when one pad is already linked */
+  //if (sdlvideosink->numpads > 1) return TRUE;
+
+  //if (sdlvideosink->screen) return TRUE;
+  if (GST_VIDEOSINK_HEIGHT (sdlvideosink) <= 0)
+    GST_VIDEOSINK_HEIGHT (sdlvideosink) = sdlvideosink->height;
+  if (GST_VIDEOSINK_WIDTH (sdlvideosink) <= 0)
+    GST_VIDEOSINK_WIDTH (sdlvideosink) = sdlvideosink->width;
+
+  g_mutex_lock((sdlvideosink->lock));
+  gst_sdlvideosink_destroy (sdlvideosink);
+
+  /* create a SDL window of the size requested by the user */
+  sdlvideosink->screen = SDL_SetVideoMode (GST_VIDEOSINK_WIDTH
(sdlvideosink),
+             GST_VIDEOSINK_HEIGHT
+             (sdlvideosink), 0,
+             SDL_HWSURFACE | SDL_RESIZABLE | SDL_ANYFORMAT);
+  if (sdlvideosink->screen == NULL) {
+    GST_ELEMENT_ERROR (sdlvideosink, LIBRARY, TOO_LAZY, (NULL),
+     ("SDL: Couldn't set %dx%d: %s",
+      GST_VIDEOSINK_WIDTH (sdlvideosink),
+      GST_VIDEOSINK_HEIGHT (sdlvideosink),
+      SDL_GetError ()));
+    return FALSE;
+  }
+
+  /* create a new YUV overlay */
+  sdlvideosink->overlay = SDL_CreateYUVOverlay (sdlvideosink->width,
+            sdlvideosink->height,
+            sdlvideosink->format,
+            sdlvideosink->screen);
+  if (sdlvideosink->overlay == NULL)
+    {
+      GST_ELEMENT_ERROR (sdlvideosink, LIBRARY, TOO_LAZY, (NULL),
+       ("SDL: Couldn't create SDL YUV overlay (%dx%d \'"
+        GST_FOURCC_FORMAT "\'): %s", sdlvideosink->width,
+        sdlvideosink->height,
+        GST_FOURCC_ARGS (sdlvideosink->format),
+        SDL_GetError ()));
+      return FALSE;
+    }
+  else
+    {
+      GST_DEBUG ("Using a %dx%d %dbpp SDL screen with a %dx%d \'"
+     GST_FOURCC_FORMAT "\' YUV overlay",
+     GST_VIDEOSINK_WIDTH (sdlvideosink),
+     GST_VIDEOSINK_HEIGHT (sdlvideosink),
+     sdlvideosink->screen->format->BitsPerPixel,
+     sdlvideosink->width, sdlvideosink->height,
+     GST_FOURCC_ARGS (sdlvideosink->format));
+    }
+
+  sdlvideosink->rect.x = 0;
+  sdlvideosink->rect.y = 0;
+  sdlvideosink->rect.w = GST_VIDEOSINK_WIDTH (sdlvideosink);
+  sdlvideosink->rect.h = GST_VIDEOSINK_HEIGHT (sdlvideosink);
+
+  SDL_DisplayYUVOverlay(sdlvideosink->overlay, &(sdlvideosink->rect));
+  g_mutex_unlock((sdlvideosink->lock));
+
+  GST_DEBUG ("sdlvideosink: setting %08x (" GST_FOURCC_FORMAT ")",
+       sdlvideosink->format, GST_FOURCC_ARGS (sdlvideosink->format));
+
+  return TRUE;
+}
+
+
+/* note that this function does packing conversion and blending at the
+ * same time */
+static void
+gst_videoblender_blend_ayuv_i420 (guint8 * src, gint xpos, gint ypos,
+    gint src_width, gint src_height, gdouble src_alpha,
+    gint dest_width, gint dest_height,
+    GstVideoBlender * sdlvideosink)
+{
+  gint i, j;
+  guint8 *srcU, *srcV, *destU, *destV;
+  gint SCALE;
+  guint8 *dest;
+
+  SCALE = src_alpha;
+  if (SCALE <= 1) {
+    memcpy (sdlvideosink->overlay->pixels[0], src, src_height *
src_width);
+    memcpy (sdlvideosink->overlay->pixels[1], src + src_height *
src_width,
+            src_width * src_height / 4);
+    memcpy (sdlvideosink->overlay->pixels[2], 
+            src + src_height * src_width * 5 / 4,
+            src_width * src_height / 4);
+  } else {
+    dest = sdlvideosink->overlay->pixels[0];
+    srcU = src + src_width * src_height;
+    destU = sdlvideosink->overlay->pixels[1];
+    srcV = srcU + src_width * src_height / 4;
+    destV = sdlvideosink->overlay->pixels[2];
+
+    for (i = 0; i < src_height; i += SCALE) {
+      for (j = 0; j < src_width; j += SCALE) {
+          dest[(i / SCALE) * src_width + (j / SCALE)] = 
+            src[i * src_width + j];
+          if((i < src_height / 2) && (j < src_width / 2)) {
+            destU[(i / SCALE) * src_width / 2 + (j / SCALE)] = 
+              srcU[i * src_width / 2 + j];
+            destV[(i / SCALE) * src_width / 2 + (j / SCALE)] = 
+              srcV[i * src_width / 2 + j];
+          }
+      }
+    }
+  }
+  return;
+}
+
+/* try to get a buffer on all pads. As long as the queued value is
+ * negative, we skip buffers */
+static gboolean
+gst_videoblender_fill_queues (GstVideoBlender * mix)
+{
+  gboolean eos = TRUE;
+  int      padPos = 0;
+  int      breakWhile = 0;
+
+  /* loop over all pads and fill it with a buffer */
+  while (padPos < 3) {
+    GstVideoBlenderPad *pad = NULL;
+    pad = GST_VIDEO_BLENDER_PAD (mix->sinkpads[padPos]);
+    padPos++;
+    if (pad == NULL) continue;
+
+    GST_DEBUG ("looking at pad %s", gst_pad_get_name (GST_PAD (pad)));
+
+    /* don't care about eos pads */
+    if (pad->eos) {
+      GST_DEBUG ("pad %s in eos, skipping", gst_pad_get_name (GST_PAD
(pad)));
+      continue;
+    }
+    if (!GST_PAD_IS_ACTIVE(pad)) {
+      g_print ("pad %s is inactive skipping\n",
gst_pad_get_name(GST_PAD(pad)));
+      continue;
+    }
+    GST_DEBUG ("pad %s: buffer %p, queued %lld  ",
+        gst_pad_get_name (GST_PAD (pad)), pad->buffer, pad->queued);
+
+    /* this pad is in need of a new buffer */
+    if (pad->buffer == NULL) {
+      GstData *data;
+      GstBuffer *buffer;
+
+      /* as long as not enough buffers have been queued */
+      while (pad->queued <= 0 && !pad->eos) {
+        data = gst_pad_pull (GST_PAD (pad));
+        if (GST_IS_EVENT (data)) {
+          GstEvent *event = GST_EVENT (data);
+
+          breakWhile = 0;
+          switch (GST_EVENT_TYPE (event)) {
+            case GST_EVENT_EOS:
+              GST_DEBUG ("videoblender: EOS on pad %s",
+                  gst_pad_get_name (GST_PAD (pad)));
+              /* mark pad eos */
+              pad->eos = TRUE;
+              gst_event_unref (event);
+              break;
+            case GST_EVENT_INTERRUPT:
+              pad->eos = FALSE;
+              eos = FALSE;
+              gst_event_unref (event);
+              breakWhile = 1;
+              break;
+            default:
+              gst_pad_event_default (GST_PAD (pad), GST_EVENT (data));
+          }
+          if (breakWhile) break;
+        } else {
+          guint64 duration;
+          buffer = GST_BUFFER (data);
+          duration = GST_BUFFER_DURATION (buffer);
+          /* no duration on the buffer, use the framerate */
+          if (duration == -1) {
+            if (pad->in_framerate == 0.0) {
+              duration = G_MAXINT64;
+            } else {
+              duration = GST_SECOND / pad->in_framerate;
+            }
+          }
+          pad->queued += duration;
+          /* this buffer will need to be mixed */
+          if ((pad->queued > 0 )|| (!GST_PAD_IS_ACTIVE(pad))) {
+            pad->buffer = buffer;
+          } else {
+            /* skip buffer, it's too old */
+            gst_buffer_unref (buffer);
+          }
+        }
+        GST_DEBUG ("pad %s: in loop, buffer %p, queued %lld  ",
+            gst_pad_get_name (GST_PAD (pad)), pad->buffer,
pad->queued);
+      }
+    }
+    if (pad->buffer != NULL && (!breakWhile)) {
+      /* got a buffer somewhere so were not eos */
+      eos = FALSE;
+    }
+  }
+
+  return eos;
+}
+
+/* blend all buffers present on the pads */
+static void
+gst_videoblender_blend_buffers (GstVideoBlender * mix, GstBuffer *
outbuf)
+{
+  gint SCALE = 1;
+  int  padPos = 0;
+
+  int  zPadPos[3] = {0, 1, 2};
+  GstVideoBlenderPad *pads[3];
+
+  pads[0] = (GstVideoBlenderPad *)mix->sinkpads[0];  
+  pads[1] = (GstVideoBlenderPad *)mix->sinkpads[1];
+  pads[2] = (GstVideoBlenderPad *)mix->sinkpads[2];
+
+  if (pads[0]) { // i.e. Channel-A exists
+    if (pads[1]) { // i.e. Channel-B ==> Channel-2
+      if (pads[0]->zorder > pads[1]->zorder) {
+        zPadPos[0] = 1;
+        zPadPos[1] = 0;
+        zPadPos[2] = 2;
+      }
+    }
+    else if (pads[2]) { // i.e. Channel-B ==> Channel-3
+      if (pads[0]->zorder > pads[2]->zorder) {
+        zPadPos[0] = 2;
+        zPadPos[1] = 0;
+        zPadPos[2] = 1;
+      }
+    }
+    else {
+    }
+  }
+  else {
+  }
+
+  g_mutex_lock((mix->lock));
+  while (padPos < 3) {
+    GstVideoBlenderPad *pad = GST_VIDEO_BLENDER_PAD
(mix->sinkpads[zPadPos[padPos]]);
+    padPos++;
+    if (pad == NULL) continue;
+
+    GST_DEBUG ("looking at pad %s", gst_pad_get_name (GST_PAD (pad)));
+    if (pad->buffer != NULL) {
+
+      if (GST_BUFFER_FLAG_IS_SET(pad->buffer, GST_BUFFER_KEY_UNIT)) {
+        MPEG_KEYFRAME tmp;
+        tmp.offset = GST_BUFFER_OFFSET(pad->buffer);
+        tmp.timestamp = GST_BUFFER_TIMESTAMP(pad->buffer);
+        g_signal_emit(G_OBJECT(mix),
+          gst_videoblender_pad_signals[SIGNAL_HANDLE_KEYFRAME], 0,
+          gst_pad_get_name(GST_PAD(pad)), (void *)&tmp);
+      }
+
+      if(pad->toTakeOld)
+        memcpy(pad->oldBuffer,
+           GST_BUFFER_DATA(pad->buffer), SCREEN_WIDTH * SCREEN_HEIGHT *
3 / 2);
+
+      gst_videoblender_blend_ayuv_i420 (GST_BUFFER_DATA (pad->buffer),
+                      pad->xpos, pad->ypos,
+                      pad->in_width, pad->in_height,
+                      /*pad->alpha*/ SCALE,
+                      mix->out_width, mix->out_height,
+                      mix);
+      if (GST_VIDEOBLENDER_CLOCK(mix) &&
+          GST_BUFFER_TIMESTAMP_IS_VALID(pad->buffer)) {
+        guint64 a=gst_element_get_time( GST_ELEMENT(mix));
+        guint64 b= GST_BUFFER_TIMESTAMP(pad->buffer);
+        guint64 c=(GST_SECOND / 10);
+        if (llabs(a - b) > c) {
+          gst_element_set_time(GST_ELEMENT(mix),
+                 GST_BUFFER_TIMESTAMP(pad->buffer));
+        }
+        gst_element_wait(GST_ELEMENT(mix),
GST_BUFFER_TIMESTAMP(pad->buffer));
+      }
+
+      SCALE = SCALE_DOWN_FACTOR;
+    }
+    else {
+      if (pad->toTakeOld) {
+        gst_videoblender_blend_ayuv_i420 (pad->oldBuffer,
+          pad->xpos, pad->ypos,
+          pad->in_width, pad->in_height,
+          /*pad->alpha*/ SCALE,
+          mix->out_width, mix->out_height,
+          mix);
+        SCALE = 4;
+      }
+    }
+  }
+  SDL_DisplayYUVOverlay (mix->overlay, &(mix->rect));
+  g_mutex_unlock((mix->lock));
+}
+
+/* remove buffers from the queue that were expired in the
+ * interval of the master, we also prepare the queued value
+ * in the pad so that we can skip and fill buffers later on */
+static void
+gst_videoblender_update_queues (GstVideoBlender * mix)
+{
+  guint64 interval;
+  int     padPos = 0;
+
+  interval = mix->master->queued;
+  if (interval <= 0) {
+    if (mix->in_framerate == 0.0) {
+      interval = G_MAXINT64;
+    } else {
+      interval = GST_SECOND / mix->in_framerate;
+    }
+  }
+
+  while (padPos < 3) {
+    GstVideoBlenderPad *pad = GST_VIDEO_BLENDER_PAD
(mix->sinkpads[padPos]);
+    padPos++;
+    if (pad == NULL) continue;
+
+    GST_DEBUG ("looking at pad %s", gst_pad_get_name (GST_PAD (pad)));
+    if (pad->buffer != NULL) {
+      pad->queued -= interval;
+      GST_DEBUG ("queued now %s %lld", gst_pad_get_name (GST_PAD
(pad)),
+          pad->queued);
+      if (pad->queued <= 0) {
+        gst_buffer_unref (pad->buffer);
+        pad->buffer = NULL;
+      }
+    }
+  }
+}
+
+/*
+ * The basic idea is to get a buffer on all pads and mix them together.
+ * Based on the framerate, buffers are removed from the queues to make
room
+ * for a new buffer. 
+ */
+static void
+gst_videoblender_loop (GstElement * element)
+{
+  GstVideoBlender *mix;
+  GstBuffer *outbuf = NULL;
+  gint outsize;
+  gint new_width, new_height;
+  gboolean eos;
+
+  mix = GST_VIDEO_BLENDER (element);
+
+  eos = gst_videoblender_fill_queues (mix);
+  if (eos) {
+    //gst_pad_push (mix->srcpad, GST_DATA (gst_event_new
(GST_EVENT_EOS)));
+    gst_element_set_eos (GST_ELEMENT (mix));
+    return;
+  }
+
+  new_width = mix->in_width;
+  new_height = mix->in_height;
+
+  if (new_width != mix->out_width ||
+      new_height != mix->out_height || !GST_PAD_CAPS (mix->srcpad)) {
+
+    mix->out_width = new_width;
+    mix->out_height = new_height;
+  }
+
+  outsize = 3 * (mix->out_width * mix->out_height) / 2;
+
+  gst_videoblender_blend_buffers (mix, outbuf);
+
+  gst_videoblender_update_queues (mix);
+
+  //gst_pad_push (mix->srcpad, GST_DATA (outbuf));
+}
+
+static void
+gst_videoblender_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec)
+{
+  GstVideoBlender *mix = GST_VIDEO_BLENDER (object);
+
+  switch (prop_id) {
+    case ARG_BACKGROUND:
+      g_value_set_enum (value, mix->background);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_videoblender_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+  GstVideoBlender *mix = GST_VIDEO_BLENDER (object);
+
+  switch (prop_id) {
+    case ARG_BACKGROUND:
+      mix->background = g_value_get_enum (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static GstElementStateReturn
+gst_videoblender_change_state (GstElement * element)
+{
+  GstVideoBlender *mix;
+  gint transition = GST_STATE_TRANSITION (element);
+
+  g_return_val_if_fail (GST_IS_VIDEO_BLENDER (element),
GST_STATE_FAILURE);
+
+  mix = GST_VIDEO_BLENDER (element);
+
+  switch (transition) {
+    case GST_STATE_NULL_TO_READY:
+    case GST_STATE_READY_TO_PAUSED:
+      break;
+    case GST_STATE_PAUSED_TO_PLAYING:
+    case GST_STATE_PLAYING_TO_PAUSED:
+    case GST_STATE_PAUSED_TO_READY:
+    case GST_STATE_READY_TO_NULL:
+      break;
+  }
+
+  if (GST_ELEMENT_CLASS (parent_class)->change_state)
+    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+
+  return GST_STATE_SUCCESS;
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_videoblender_debug, "videoblender", 0,
+      "video blender");
+
+  return gst_element_register (plugin, "videoblender",
GST_RANK_PRIMARY,
+      GST_TYPE_VIDEO_BLENDER);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "videoblender",
+    "Video blender", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE,
GST_ORIGIN)
diff -Naur gst-plugins-0.8.8/gst-plugins.spec.in
gst-plugins-0.8.8-ipvr/gst-plugins.spec.in
--- gst-plugins-0.8.8/gst-plugins.spec.in	2005-03-05
23:49:03.000000000 +0530
+++ gst-plugins-0.8.8-ipvr/gst-plugins.spec.in	2005-04-19
18:01:12.151702992 +0530
@@ -230,6 +230,7 @@
 %{_libdir}/gstreamer-%{majorminor}/libgstmultipart.so
 %{_libdir}/gstreamer-%{majorminor}/libgstplaybin.so
 %{_libdir}/gstreamer-%{majorminor}/libgstvideobox.so
+%{_libdir}/gstreamer-%{majorminor}/libgstvideoblender.so
 %{_libdir}/gstreamer-%{majorminor}/libgstvideomixer.so
 %{_libdir}/gstreamer-%{majorminor}/libgstvideorate.so
 %{_libdir}/gstreamer-%{majorminor}/libgsttheora.so




More information about the gstreamer-devel mailing list