From 7c8f0e13cd2558851ab3277a14a8929e14f5428f Mon Sep 17 00:00:00 2001
From: Tim Yamin <plasm@roo.me.uk>
Date: Sat, 25 Apr 2009 01:19:09 -0700
Subject: [PATCH] Implement XOverlay and I420 to 422 colorspace conversion.

---
 Makefile |    8 +-
 omapfb.c |  700 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 omapfb.h |   78 +++++++-
 yuv.S    |  117 +++++++++++
 4 files changed, 878 insertions(+), 25 deletions(-)
 create mode 100644 yuv.S

diff --git a/Makefile b/Makefile
index 0ad129b..37b8746 100644
--- a/Makefile
+++ b/Makefile
@@ -1,12 +1,12 @@
-GST_LIBS := $(shell pkg-config --libs gstreamer-0.10 gstreamer-base-0.10)
-GST_CFLAGS := $(shell pkg-config --cflags gstreamer-0.10 gstreamer-base-0.10)
+GST_LIBS := $(shell pkg-config --libs gstreamer-0.10 gstreamer-base-0.10 gstreamer-interfaces-0.10) -lX11
+GST_CFLAGS := $(shell pkg-config --cflags gstreamer-0.10 gstreamer-base-0.10 gstreamer-interfaces-0.10)
 KERNEL := /data/public/dev/omap/linux-omap
 
-CC := arm-linux-gcc
+CC ?= arm-linux-gcc
-CFLAGS := -Wall -ggdb -ansi -std=c99
+CFLAGS := -Wall -ggdb -O3 -Wall -fomit-frame-pointer -mcpu=cortex-a8 -mfpu=neon
 
 plugin := libgstomapfb.so
-objects := omapfb.o
+objects := omapfb.o yuv.o
 
 plugin_dir := $(DESTDIR)/usr/lib/gstreamer-0.10
 
diff --git a/omapfb.c b/omapfb.c
index cbd6936..020a9bb 100644
--- a/omapfb.c
+++ b/omapfb.c
@@ -1,5 +1,9 @@
 /*
  * Copyright (C) 2008 Felipe Contreras <felipe.contreras@gmail.com>
+ * Copyright (C) 2009 Tim Yamin <plasm@roo.me.uk>
+ *
+ * X code largely copied from ximagesink by Julien Moutte and
+ * vo_omapfb.c by Gregoire Gentil.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -20,10 +24,398 @@
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <malloc.h>
+#include <stdlib.h>
 
 #include "omapfb.h"
+#include <gst/interfaces/xoverlay.h>
 
 static GstVideoSinkClass *parent_class = NULL;
+extern void yuv420_to_yuv422(__uint8_t *yuv, __uint8_t *y, __uint8_t *u, __uint8_t *v,
+                             int w, int h, int yw, int cw, int dw);
+
+static void x11_get_window_abs_position(Display *display, Window window,
+                                        int *wx, int *wy, int *ww, int *wh)
+{
+    Window root, parent;
+    Window *child;
+    unsigned int n_children;
+    XWindowAttributes attribs;
+
+    /* Get window attributes */
+    XGetWindowAttributes(display, window, &attribs);
+
+    /* Get relative position of given window */
+    *wx = attribs.x;
+    *wy = attribs.y;
+    if (ww)
+        *ww = attribs.width;
+    if (wh)
+        *wh = attribs.height;
+
+    /* Query window tree information */
+    XQueryTree(display, window, &root, &parent, &child, &n_children);
+    if (parent)
+    {
+        int x, y;
+
+	/* If we have a parent we must go there and discover his position */
+	x11_get_window_abs_position(display, parent, &x, &y, NULL, NULL);
+	*wx += x;
+	*wy += y;
+    }
+
+    /* If we had children, free them */
+    if(n_children)
+        XFree(child);
+}
+
+static GstXWindow *
+gst_omapfbsink_xwindow_new (GstOmapFbSink * omapfbsink, gint width, gint height)
+{
+    GstXWindow *xwindow = NULL;
+    XGCValues values;
+
+    if(!omapfbsink->xcontext)
+        return NULL;
+
+    xwindow = g_new0 (GstXWindow, 1);
+    xwindow->width = width;
+    xwindow->height = height;
+    xwindow->internal = TRUE;
+
+    g_mutex_lock (omapfbsink->x_lock);
+    xwindow->win = XCreateSimpleWindow (omapfbsink->xcontext->disp,
+					omapfbsink->xcontext->root,
+					0, 0, xwindow->width, xwindow->height,
+					0, 0, omapfbsink->colorKey);
+
+    /* We have to do that to prevent X from redrawing the background on
+       ConfigureNotify. This takes away flickering of video when resizing. */
+    XSetWindowBackgroundPixmap (omapfbsink->xcontext->disp, xwindow->win, None);
+
+    if (omapfbsink->handle_events) {
+        Atom wm_delete;
+
+	XSelectInput (omapfbsink->xcontext->disp, xwindow->win, ExposureMask |
+		      StructureNotifyMask | PointerMotionMask | KeyPressMask |
+		      KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
+
+	/* Tell the window manager we'd like delete client messages instead of
+	 * being killed */
+	wm_delete = XInternAtom (omapfbsink->xcontext->disp,
+				 "WM_DELETE_WINDOW", False);
+	(void) XSetWMProtocols (omapfbsink->xcontext->disp, xwindow->win,
+				&wm_delete, 1);
+    }
+
+    xwindow->gc = XCreateGC (omapfbsink->xcontext->disp, xwindow->win,
+			     0, &values);
+
+    XMapRaised (omapfbsink->xcontext->disp, xwindow->win);
+    XSync (omapfbsink->xcontext->disp, FALSE);
+
+    g_mutex_unlock (omapfbsink->x_lock);
+    gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (omapfbsink), xwindow->win);
+
+    return xwindow;
+}
+
+static gboolean gst_omapfbsink_update_plane (GstOmapFbSink *omapfbsink)
+{
+    int wx, wy, ww, wh;
+    if (!omapfbsink->xcontext)
+    {
+        if (ioctl (omapfbsink->overlay_fd, OMAPFB_SETUP_PLANE, &omapfbsink->plane_info))
+            return FALSE;
+        return TRUE;
+    }
+
+    if (omapfbsink->plane_info.enabled != 1)
+        return FALSE;
+
+    x11_get_window_abs_position(omapfbsink->xcontext->disp,
+				omapfbsink->xwindow->win, &wx, &wy, &ww, &wh);
+
+    if (wx != omapfbsink->xwindow->wx || wy != omapfbsink->xwindow->wy ||
+	wh != omapfbsink->xwindow->height || ww != omapfbsink->xwindow->width) {
+        omapfbsink->plane_info.out_width = omapfbsink->xwindow->width = ww;
+	omapfbsink->plane_info.out_height = omapfbsink->xwindow->height = wh;
+        omapfbsink->plane_info.pos_x = omapfbsink->xwindow->wx = wx;
+        omapfbsink->plane_info.pos_y = omapfbsink->xwindow->wy = wy;
+
+        GST_DEBUG_OBJECT(omapfbsink, "updating geometry to: (%d,%d) %dx%d", wx, wy, ww, wh);
+
+        XSetForeground (omapfbsink->xcontext->disp, omapfbsink->xwindow->gc, omapfbsink->colorKey);
+        XFillRectangle (omapfbsink->xcontext->disp, omapfbsink->xwindow->win, omapfbsink->xwindow->gc, 0, 0, ww, wh);
+
+        if (ioctl (omapfbsink->overlay_fd, OMAPFB_SETUP_PLANE, &omapfbsink->plane_info))
+	  return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+gst_omapfbsink_expose (GstXOverlay * overlay)
+{
+    gst_omapfbsink_update_plane(GST_OMAPFB_SINK (overlay));
+}
+
+static void
+gst_omapfbsink_xwindow_destroy (GstOmapFbSink * omapfbsink,
+    GstXWindow * xwindow)
+{
+    g_return_if_fail (xwindow != NULL);
+    g_mutex_lock (omapfbsink->x_lock);
+
+    /* If we did not create that window we just free the GC and let it live */
+    if (xwindow->internal)
+        XDestroyWindow (omapfbsink->xcontext->disp, xwindow->win);
+    else
+        XSelectInput (omapfbsink->xcontext->disp, xwindow->win, 0);
+
+    XFreeGC (omapfbsink->xcontext->disp, xwindow->gc);
+    XSync (omapfbsink->xcontext->disp, FALSE);
+    g_mutex_unlock (omapfbsink->x_lock);
+    g_free (xwindow);
+}
+
+/* This function handles XEvents that might be in the queue. It generates
+   GstEvent that will be sent upstream in the pipeline to handle interactivity
+   and navigation.*/
+static void
+gst_omapfbsink_handle_xevents (GstOmapFbSink * omapfbsink)
+{
+    XEvent e;
+    g_mutex_lock (omapfbsink->flow_lock);
+    g_mutex_lock (omapfbsink->x_lock);
+
+    while (XCheckWindowEvent (omapfbsink->xcontext->disp,
+           omapfbsink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
+        switch (e.type) {
+            case Expose:
+            case ConfigureNotify:
+                gst_omapfbsink_update_plane (omapfbsink);
+                break;
+            default:
+                break;
+        }
+    }
+
+    /* Handle Display events */
+    while (XPending (omapfbsink->xcontext->disp)) {
+        XNextEvent (omapfbsink->xcontext->disp, &e);
+
+        switch (e.type) {
+            case ClientMessage:{
+                Atom wm_delete;
+
+                wm_delete = XInternAtom (omapfbsink->xcontext->disp,
+                        "WM_DELETE_WINDOW", False);
+                if (wm_delete == (Atom) e.xclient.data.l[0]) {
+                    /* Handle window deletion by posting an error on the bus */
+                    GST_ELEMENT_ERROR (omapfbsink, RESOURCE, NOT_FOUND,
+                            ("Output window was closed"), (NULL));
+
+                    g_mutex_unlock (omapfbsink->x_lock);
+                    gst_omapfbsink_xwindow_destroy (omapfbsink, omapfbsink->xwindow);
+                    omapfbsink->xwindow = NULL;
+                    g_mutex_lock (omapfbsink->x_lock);
+                }
+                break;
+            }
+            default:
+                break;
+        }
+    }
+
+    g_mutex_unlock (omapfbsink->x_lock);
+    g_mutex_unlock (omapfbsink->flow_lock);
+}
+
+static gpointer
+gst_omapfbsink_event_thread (GstOmapFbSink * omapfbsink)
+{
+    GST_OBJECT_LOCK (omapfbsink);
+    while (omapfbsink->running) {
+        GST_OBJECT_UNLOCK (omapfbsink);
+
+	if (omapfbsink->xwindow)
+	  gst_omapfbsink_handle_xevents (omapfbsink);
+	g_usleep (100000);
+
+	GST_OBJECT_LOCK (omapfbsink);
+    }
+    GST_OBJECT_UNLOCK (omapfbsink);
+
+    return NULL;
+}
+
+/* This function gets the X Display and global info about it. Everything is
+   stored in our object and will be cleaned when the object is disposed. */
+static GstXContext *
+gst_omapfbsink_xcontext_get (GstOmapFbSink * omapfbsink)
+{
+    GstXContext *xcontext = g_new0 (GstXContext, 1);
+    g_mutex_lock (omapfbsink->x_lock);
+
+    xcontext->disp = XOpenDisplay (omapfbsink->display_name);
+
+    if (!xcontext->disp) {
+        g_mutex_unlock (omapfbsink->x_lock);
+	g_free (xcontext);
+	GST_ELEMENT_WARNING (omapfbsink, RESOURCE, WRITE,
+			     ("Could not initialise X output"),
+			     ("Could not open display"));
+	return NULL;
+    }
+
+    xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
+    xcontext->screen_num = DefaultScreen (xcontext->disp);
+    xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
+    xcontext->root = DefaultRootWindow (xcontext->disp);
+
+    xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
+    xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
+
+    g_mutex_unlock (omapfbsink->x_lock);
+
+    /* Setup our event listening thread */
+    GST_OBJECT_LOCK (omapfbsink);
+    omapfbsink->running = TRUE;
+    omapfbsink->event_thread = g_thread_create (
+      (GThreadFunc) gst_omapfbsink_event_thread, omapfbsink, TRUE, NULL);
+    GST_OBJECT_UNLOCK (omapfbsink);
+
+    return xcontext;
+}
+
+static void
+gst_omapfbsink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
+{
+    GstOmapFbSink *omapfbsink = GST_OMAPFB_SINK (overlay);
+    GstXWindow *xwindow = NULL;
+    XWindowAttributes attr;
+
+    /* If we already use that window, return */
+    if (omapfbsink->xwindow && (xwindow_id == omapfbsink->xwindow->win))
+        return;
+
+    /* If the element has not initialized the X11 context try to do so */
+    if (!omapfbsink->xcontext &&
+	!(omapfbsink->xcontext = gst_omapfbsink_xcontext_get (omapfbsink))) {
+      g_mutex_unlock (omapfbsink->flow_lock);
+      return;
+    }
+
+    /* If a window is there already we destroy it */
+    if (omapfbsink->xwindow) {
+      gst_omapfbsink_xwindow_destroy (omapfbsink, omapfbsink->xwindow);
+      omapfbsink->xwindow = NULL;
+    }
+
+    /* If the xid is 0 we go back to an internal window */
+    if (xwindow_id == 0) {
+      /* If no width/height caps nego did not happen window will be created
+	 during caps nego then */
+      if (GST_VIDEO_SINK_WIDTH (omapfbsink) && GST_VIDEO_SINK_HEIGHT (omapfbsink)) {
+	xwindow = gst_omapfbsink_xwindow_new (omapfbsink,
+          GST_VIDEO_SINK_WIDTH (omapfbsink),
+          GST_VIDEO_SINK_HEIGHT (omapfbsink));
+      }
+    } else {
+        xwindow = g_new0 (GstXWindow, 1);
+	xwindow->wx = xwindow->wy = -1;
+	xwindow->win = xwindow_id;
+
+	/* We get window geometry, set the event we want to receive,
+	   and create a GC */
+	g_mutex_lock (omapfbsink->x_lock);
+	XGetWindowAttributes (omapfbsink->xcontext->disp, xwindow->win, &attr);
+	xwindow->width = attr.width;
+	xwindow->height = attr.height;
+	xwindow->internal = FALSE;
+	if (omapfbsink->handle_events) {
+	    XSelectInput (omapfbsink->xcontext->disp, xwindow->win, ExposureMask |
+			  StructureNotifyMask | PointerMotionMask | KeyPressMask |
+			  KeyReleaseMask);
+	}
+
+        xwindow->gc = XCreateGC (omapfbsink->xcontext->disp, xwindow->win, 0, NULL);
+	g_mutex_unlock (omapfbsink->x_lock);
+    }
+
+    if (xwindow) {
+        omapfbsink->xwindow = xwindow;
+
+	g_mutex_lock (omapfbsink->x_lock);
+	gst_omapfbsink_update_plane(omapfbsink);
+	g_mutex_unlock (omapfbsink->x_lock);
+    }
+}
+
+static void
+gst_omapfbsink_xwindow_clear (GstOmapFbSink * omapfbsink,
+			      GstXWindow * xwindow)
+{
+    g_return_if_fail (xwindow != NULL);
+    g_mutex_lock (omapfbsink->x_lock);
+
+    XSetForeground (omapfbsink->xcontext->disp, xwindow->gc,
+		    XBlackPixel (omapfbsink->xcontext->disp,
+				 omapfbsink->xcontext->screen_num));
+
+    XFillRectangle (omapfbsink->xcontext->disp, xwindow->win, xwindow->gc,
+		    0, 0, xwindow->width, xwindow->height);
+
+    XSync (omapfbsink->xcontext->disp, FALSE);
+    g_mutex_unlock (omapfbsink->x_lock);
+}
+
+static void
+gst_omapfbsink_set_event_handling (GstXOverlay * overlay,
+    gboolean handle_events)
+{
+    GstOmapFbSink *omapfbsink = GST_OMAPFB_SINK (overlay);
+    omapfbsink->handle_events = handle_events;
+
+    g_mutex_lock (omapfbsink->flow_lock);
+
+    if (G_UNLIKELY (!omapfbsink->xwindow)) {
+        g_mutex_unlock (omapfbsink->flow_lock);
+	return;
+    }
+
+    g_mutex_lock (omapfbsink->x_lock);
+
+    if (handle_events) {
+        if (omapfbsink->xwindow->internal) {
+	  XSelectInput (omapfbsink->xcontext->disp, omapfbsink->xwindow->win,
+			ExposureMask | StructureNotifyMask | PointerMotionMask |
+			KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
+	} else {
+	  XSelectInput (omapfbsink->xcontext->disp, omapfbsink->xwindow->win,
+			ExposureMask | StructureNotifyMask | PointerMotionMask |
+			KeyPressMask | KeyReleaseMask);
+	}
+    } else {
+        XSelectInput (omapfbsink->xcontext->disp, omapfbsink->xwindow->win, 0);
+    }
+
+    g_mutex_unlock (omapfbsink->x_lock);
+    g_mutex_unlock (omapfbsink->flow_lock);
+}
+
+static void
+gst_omapfbsink_xoverlay_init (GstXOverlayClass * iface)
+{
+    iface->set_xwindow_id = gst_omapfbsink_set_xwindow_id;
+    iface->expose = gst_omapfbsink_expose;
+    iface->handle_events = gst_omapfbsink_set_event_handling;
+}
 
 static GstCaps *
 generate_sink_template (void)
@@ -49,15 +441,12 @@ generate_sink_template (void)
         g_value_init (&val, GST_TYPE_FOURCC);
 
 #if 0
-        gst_value_set_fourcc (&val, GST_MAKE_FOURCC ('I', '4', '2', '0'));
-        gst_value_list_append_value (&list, &val);
-
         gst_value_set_fourcc (&val, GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'));
         gst_value_list_append_value (&list, &val);
-
-        gst_value_set_fourcc (&val, GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'));
-        gst_value_list_append_value (&list, &val);
 #else
+	gst_value_set_fourcc (&val, GST_MAKE_FOURCC ('I', '4', '2', '0'));
+	gst_value_list_append_value (&list, &val);
+
         gst_value_set_fourcc (&val, GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'));
         gst_value_list_append_value (&list, &val);
 #endif
@@ -81,28 +470,112 @@ buffer_alloc (GstBaseSink *bsink,
               GstBuffer **buf)
 {
     GstOmapFbSink *self;
-    GstBuffer *buffer;
+    GstBuffer *buffer = NULL;
     GstFlowReturn ret = GST_FLOW_OK;
-
     self = GST_OMAPFB_SINK (bsink);
 
-    buffer = gst_buffer_new ();
-    GST_BUFFER_DATA (buffer) = self->framebuffer;
-    GST_BUFFER_SIZE (buffer) = size;
-    gst_buffer_set_caps (buffer, caps);
+    if(self->row_skip) {
+        buffer = gst_buffer_new ();
+        GST_BUFFER_DATA (buffer) = self->buffer = self->framebuffer;
+        GST_BUFFER_SIZE (buffer) = self->buffer_size = size;
+        gst_buffer_set_caps (buffer, caps);
+    } else {
+        if(self->buffer && size == self->buffer_size) {
+            buffer = gst_buffer_new ();
+            GST_BUFFER_DATA (buffer) = self->buffer;
+            GST_BUFFER_SIZE (buffer) = size;
+            gst_buffer_set_caps (buffer, caps);
+        } else {
+            if(self->buffer)
+                free(self->buffer);
+            if(posix_memalign(&self->buffer, 16, (size_t) size) == 0) {
+                buffer = gst_buffer_new ();
+                GST_BUFFER_DATA (buffer) = self->buffer;
+                GST_BUFFER_SIZE (buffer) = self->buffer_size = size;
+                gst_buffer_set_caps (buffer, caps);
+            } else {
+                GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ("Could not allocate aligned buf!"),
+                                  ("Could not alloc aligned buf!"));
+            }
+        }
+    }
 
     *buf = buffer;
-
     return ret;
 }
 
+static GstFlowReturn
+render (GstBaseSink * bsink, GstBuffer * buf)
+{
+    int i, w, h;
+    GstOmapFbSink *omapfbsink = GST_OMAPFB_SINK(bsink);
+    __uint8_t *fb = omapfbsink->framebuffer, *data = GST_BUFFER_DATA(buf);
+
+    if(omapfbsink->plane_info.enabled == 2)
+    {
+        omapfbsink->plane_info.enabled = 1;
+
+        g_mutex_lock (omapfbsink->x_lock);
+        gst_omapfbsink_update_plane(omapfbsink);
+        g_mutex_unlock (omapfbsink->x_lock);
+    }
+
+    /* If a buffer which wasn't supplied by us is given to us to render with,
+       we need to copy to our buffer first so that memory alignment constraints
+       are met. */
+    if(data != omapfbsink->buffer && GST_BUFFER_SIZE(buf) <= omapfbsink->buffer_size)
+    {
+        memcpy(omapfbsink->buffer, data, GST_BUFFER_SIZE(buf));
+        data = omapfbsink->buffer;
+    }
+
+    /* buffer_alloc gave a direct buffer, so we have nothing to
+       do here... */
+    if(omapfbsink->row_skip)
+        return GST_FLOW_OK;
+
+    switch(omapfbsink->image_format) {
+        case GST_MAKE_FOURCC('I', '4', '2', '0'):
+            /* Convert to YUV422 and send to FB */
+
+            h = GST_VIDEO_SINK_HEIGHT (omapfbsink);
+            w = GST_VIDEO_SINK_WIDTH (omapfbsink);
+
+            __uint8_t *y, *u, *v;
+            y = data;
+            u = y + w * h;
+            v = u + w / 2 * h / 2;
+
+            yuv420_to_yuv422(fb, y, u, v, w & ~15, h, w, w / 2, omapfbsink->fixinfo.line_length);
+            break;
+
+        case GST_MAKE_FOURCC('U', 'Y', 'V', 'Y'):
+            /* Send to FB, taking into account line_length */
+
+            w = 2 * GST_VIDEO_SINK_WIDTH (omapfbsink);
+            for(i = 0; i < GST_VIDEO_SINK_HEIGHT (omapfbsink); i++)
+            {
+                memcpy(fb, data, w);
+
+                fb += omapfbsink->fixinfo.line_length;
+                data += w;
+            }
+
+            break;
+    }
+
+    return GST_FLOW_OK;
+}
+
 static gboolean
 setcaps (GstBaseSink *bsink,
          GstCaps *vscapslist)
 {
     GstOmapFbSink *self;
     GstStructure *structure;
+
     gint width, height;
+    struct omapfb_color_key color_key;
 
     self = GST_OMAPFB_SINK (bsink);
 
@@ -118,22 +591,54 @@ setcaps (GstBaseSink *bsink,
 
     self->overlay_info.xoffset = 0;
     self->overlay_info.yoffset = 0;
-    self->overlay_info.nonstd = OMAPFB_COLOR_YUV422;
+
+    gst_structure_get_fourcc (structure, "format", &self->image_format);
+    switch(self->image_format) {
+        case GST_MAKE_FOURCC('I', '4', '2', '0'):
+            self->row_skip = FALSE; /* Colorspace conversion required */
+            self->overlay_info.nonstd = OMAPFB_COLOR_YUY422;
+            break;
+        case GST_MAKE_FOURCC('U', 'Y', 'V', 'Y'):
+            /* Can data be pushed straight to the FB or do we need to interleave? */
+            if (self->fixinfo.line_length != 2 * width)
+                self->row_skip = FALSE;
+            else
+                self->row_skip = TRUE;
+            self->overlay_info.nonstd = OMAPFB_COLOR_YUV422;
+            break;
+    }
 
     if (ioctl (self->overlay_fd, FBIOPUT_VSCREENINFO, &self->overlay_info))
         return FALSE;
 
-    self->plane_info.enabled = 1;
+    GST_VIDEO_SINK_WIDTH (self) = width;
+    GST_VIDEO_SINK_HEIGHT (self) = height;
+    if (!self->xwindow) {
+        self->xwindow = gst_omapfbsink_xwindow_new (self,
+            GST_VIDEO_SINK_WIDTH (self), GST_VIDEO_SINK_HEIGHT (self));
+    }
+
+    color_key.channel_out = OMAPFB_CHANNEL_OUT_LCD;
+    color_key.background = 0x0;
+    color_key.trans_key = self->colorKey;
+    if (self->xwindow)
+        color_key.key_type = OMAPFB_COLOR_KEY_GFX_DST;
+    else
+        color_key.key_type = OMAPFB_COLOR_KEY_DISABLED;
+
+    if (ioctl (self->overlay_fd, OMAPFB_SET_COLOR_KEY, &color_key))
+        return FALSE;
+
     self->plane_info.pos_x = 0;
     self->plane_info.pos_y = 0;
     self->plane_info.out_width = self->overlay_info.xres;
     self->plane_info.out_height = self->overlay_info.yres;
+    self->plane_info.enabled = 2;
 
-    if (ioctl (self->overlay_fd, OMAPFB_SETUP_PLANE, &self->plane_info))
+    if (ioctl (self->overlay_fd, FBIOGET_FSCREENINFO, &self->fixinfo))
         return FALSE;
 
     self->enabled = TRUE;
-
     return TRUE;
 }
 
@@ -204,20 +709,126 @@ stop (GstBaseSink *bsink)
     return TRUE;
 }
 
+/* This function cleans the X context. Closing the Display and unrefing the
+   caps for supported formats. */
+static void
+gst_omapfbsink_xcontext_clear (GstOmapFbSink * omapfbsink)
+{
+    GstXContext *xcontext;
+    GST_OBJECT_LOCK (omapfbsink);
+    if (omapfbsink->xcontext == NULL) {
+      GST_OBJECT_UNLOCK (omapfbsink);
+      return;
+    }
+
+    xcontext = omapfbsink->xcontext;
+    omapfbsink->xcontext = NULL;
+
+    GST_OBJECT_UNLOCK (omapfbsink);
+    g_mutex_lock (omapfbsink->x_lock);
+
+    XCloseDisplay (xcontext->disp);
+    g_mutex_unlock (omapfbsink->x_lock);
+    g_free (xcontext);
+}
+
+static void
+gst_omapfbsink_reset (GstOmapFbSink *omapfbsink)
+{
+    GThread *thread;
+
+    GST_OBJECT_LOCK (omapfbsink);
+    omapfbsink->running = FALSE;
+    /* grab thread and mark it as NULL */
+    thread = omapfbsink->event_thread;
+    omapfbsink->event_thread = NULL;
+    GST_OBJECT_UNLOCK (omapfbsink);
+
+    /* Wait for our event thread to finish before we clean up our stuff. */
+    if (thread)
+      g_thread_join (thread);
+
+    g_mutex_lock (omapfbsink->flow_lock);
+    if (omapfbsink->xwindow) {
+        gst_omapfbsink_xwindow_clear (omapfbsink, omapfbsink->xwindow);
+        gst_omapfbsink_xwindow_destroy (omapfbsink, omapfbsink->xwindow);
+        omapfbsink->xwindow = NULL;
+    }
+    g_mutex_unlock (omapfbsink->flow_lock);
+    gst_omapfbsink_xcontext_clear (omapfbsink);
+}
+
+static GstStateChangeReturn
+gst_omapfbsink_change_state (GstElement * element, GstStateChange transition)
+{
+    GstOmapFbSink *omapfbsink;
+    GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+    GstXContext *xcontext = NULL;
+
+    omapfbsink = GST_OMAPFB_SINK (element);
+
+    switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+
+        /* Initializing the XContext */
+        if (omapfbsink->xcontext == NULL) {
+            xcontext = gst_omapfbsink_xcontext_get (omapfbsink);
+
+            GST_OBJECT_LOCK (omapfbsink);
+            omapfbsink->xcontext = xcontext;
+            GST_OBJECT_UNLOCK (omapfbsink);
+        }
+        break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+        g_mutex_lock (omapfbsink->flow_lock);
+        if (omapfbsink->xwindow)
+            gst_omapfbsink_xwindow_clear (omapfbsink, omapfbsink->xwindow);
+        g_mutex_unlock (omapfbsink->flow_lock);
+        break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+        break;
+    default:
+        break;
+    }
+
+    ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+    switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+        break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+        GST_VIDEO_SINK_WIDTH (omapfbsink) = 0;
+        GST_VIDEO_SINK_HEIGHT (omapfbsink) = 0;
+        break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+        gst_omapfbsink_reset (omapfbsink);
+        break;
+    default:
+        break;
+    }
+
+    return ret;
+}
+
 static void
 type_class_init (gpointer g_class,
                  gpointer class_data)
 {
+    GstElementClass *element_class;
     GstBaseSinkClass *base_sink_class;
 
+    element_class = (GstElementClass *) g_class;
     base_sink_class = (GstBaseSinkClass *) g_class;
 
-    parent_class = g_type_class_ref (GST_OMAPFB_SINK_TYPE);
+    parent_class = g_type_class_peek_parent (g_class);
 
     base_sink_class->set_caps = GST_DEBUG_FUNCPTR (setcaps);
     base_sink_class->buffer_alloc = GST_DEBUG_FUNCPTR (buffer_alloc);
+    base_sink_class->render = GST_DEBUG_FUNCPTR (render);
     base_sink_class->start = GST_DEBUG_FUNCPTR (start);
     base_sink_class->stop = GST_DEBUG_FUNCPTR (stop);
+
+    element_class->change_state = gst_omapfbsink_change_state;
 }
 
 static void
@@ -247,6 +858,42 @@ type_base_init (gpointer g_class)
     }
 }
 
+static gboolean
+gst_omapfbsink_interface_supported (GstImplementsInterface * iface, GType type)
+{
+    g_assert (type == GST_TYPE_X_OVERLAY);
+    return TRUE;
+}
+
+static void
+gst_omapfbsink_interface_init (GstImplementsInterfaceClass * klass)
+{
+    klass->supported = gst_omapfbsink_interface_supported;
+}
+
+static void
+gst_omapfbsink_init (GstOmapFbSink * omapfbsink)
+{
+    omapfbsink->display_name = NULL;
+    omapfbsink->xcontext = NULL;
+    omapfbsink->xwindow = NULL;
+
+    omapfbsink->event_thread = NULL;
+    omapfbsink->running = FALSE;
+
+    omapfbsink->x_lock = g_mutex_new ();
+    omapfbsink->flow_lock = g_mutex_new ();
+
+    omapfbsink->handle_events = TRUE;
+    omapfbsink->colorKey = 0xff0;
+
+    omapfbsink->plane_info.enabled = 0;
+    omapfbsink->row_skip = FALSE;
+
+    omapfbsink->buffer = NULL;
+    omapfbsink->buffer_size = 0;
+}
+
 GType
 gst_omapfbsink_get_type (void)
 {
@@ -255,14 +902,27 @@ gst_omapfbsink_get_type (void)
     if (G_UNLIKELY (type == 0))
     {
         GTypeInfo *type_info;
+        static const GInterfaceInfo iface_info = {
+            (GInterfaceInitFunc) gst_omapfbsink_interface_init,
+            NULL,
+            NULL,
+        };
+        static const GInterfaceInfo overlay_info = {
+            (GInterfaceInitFunc) gst_omapfbsink_xoverlay_init,
+            NULL,
+            NULL,
+        };
 
         type_info = g_new0 (GTypeInfo, 1);
         type_info->class_size = sizeof (GstOmapFbSinkClass);
         type_info->base_init = type_base_init;
         type_info->class_init = type_class_init;
         type_info->instance_size = sizeof (GstOmapFbSink);
+        type_info->instance_init = (GInstanceInitFunc) gst_omapfbsink_init;
 
         type = g_type_register_static (GST_TYPE_BASE_SINK, "GstOmapFbSink", type_info, 0);
+        g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
+        g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &overlay_info);
 
         g_free (type_info);
     }
@@ -273,13 +933,13 @@ gst_omapfbsink_get_type (void)
 static gboolean
 plugin_init (GstPlugin *plugin)
 {
-    if (!gst_element_register (plugin, "omapfbsink", GST_RANK_NONE, GST_OMAPFB_SINK_TYPE))
+    if (!gst_element_register (plugin, "omapfbsink", GST_RANK_PRIMARY, GST_OMAPFB_SINK_TYPE))
         return FALSE;
 
     return TRUE;
 }
 
-GstPluginDesc gst_plugin_desc =
+GST_PLUGIN_EXPORT GstPluginDesc gst_plugin_desc =
 {
     GST_VERSION_MAJOR,
     GST_VERSION_MINOR,
diff --git a/omapfb.h b/omapfb.h
index 8dd91da..3e6af7f 100644
--- a/omapfb.h
+++ b/omapfb.h
@@ -23,21 +23,79 @@
 #include <gst/video/gstvideosink.h>
 #include <gst/video/video.h>
 
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
 #include <linux/fb.h>
-#include <mach/omapfb.h>
+#include <linux/omapfb.h>
 
 G_BEGIN_DECLS
 
 #define GST_OMAPFB_SINK_TYPE (gst_omapfbsink_get_type ())
 #define GST_OMAPFB_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_OMAPFB_SINK_TYPE, GstOmapFbSink))
 
+typedef struct GstXContext GstXContext;
+typedef struct GstXWindow GstXWindow;
 typedef struct GstOmapFbSink GstOmapFbSink;
 typedef struct GstOmapFbSinkClass GstOmapFbSinkClass;
 
+/**
+ * GstXWindow:
+ * @win: the Window ID of this X11 window
+ * @width: the width in pixels of Window @win
+ * @height: the height in pixels of Window @win
+ * @internal: used to remember if Window @win was created internally or passed
+ * through the #GstXOverlay interface
+ * @gc: the Graphical Context of Window @win
+ *
+ * Structure used to store informations about a Window.
+ */
+struct GstXWindow {
+    Window win;
+    gint width, height;
+    gboolean internal;
+    GC gc;
+
+    gint wx, wy;
+};
+
+/**
+ * GstXContext:
+ * @disp: the X11 Display of this context
+ * @screen: the default Screen of Display @disp
+ * @screen_num: the Screen number of @screen
+ * @visual: the default Visual of Screen @screen
+ * @root: the root Window of Display @disp
+ * @white: the value of a white pixel on Screen @screen
+ * @black: the value of a black pixel on Screen @screen
+ * @depth: the color depth of Display @disp
+ * @bpp: the number of bits per pixel on Display @disp
+ * @endianness: the endianness of image bytes on Display @disp
+ * @width: the width in pixels of Display @disp
+ * @height: the height in pixels of Display @disp
+ *
+ * Structure used to store various informations collected/calculated for a
+ * Display.
+ */
+struct GstXContext {
+    Display *disp;
+    Screen *screen;
+    gint screen_num;
+
+    Visual *visual;
+    Window root;
+
+    gint depth;
+    gint bpp;
+
+    gint width, height;
+};
+
 struct GstOmapFbSink
 {
     GstVideoSink videosink;
 
+    struct fb_fix_screeninfo fixinfo;
     struct fb_var_screeninfo varinfo;
     struct fb_var_screeninfo overlay_info;
     struct omapfb_mem_info mem_info;
@@ -46,6 +104,24 @@ struct GstOmapFbSink
     int overlay_fd;
     unsigned char *framebuffer;
     gboolean enabled;
+
+    GMutex *x_lock;
+    GMutex *flow_lock;
+
+    GstXContext *xcontext;
+    GstXWindow *xwindow;
+
+    gulong colorKey;
+    char *display_name;
+    GThread *event_thread;
+
+    void *buffer;
+    guint buffer_size;
+    guint image_format;
+
+    gboolean row_skip;
+    gboolean handle_events;
+    gboolean running;
 };
 
 struct GstOmapFbSinkClass
diff --git a/yuv.S b/yuv.S
new file mode 100644
index 0000000..52113fa
--- /dev/null
+++ b/yuv.S
@@ -0,0 +1,117 @@
+/*
+    Copyright (C) 2008 Mans Rullgard
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+*/
+
+        .fpu neon
+        .text
+
+@ yuv420_to_yuv422(uint8_t *yuv, uint8_t *y, uint8_t *u, uint8_t *v,
+@                  int w, int h, int yw, int cw, int dw)
+
+#define yuv  r0
+#define y    r1
+#define u    r2
+#define v    r3
+#define w    r4
+#define h    r5
+#define yw   r6
+#define cw   r7
+#define dw   r8
+
+#define tyuv r9
+#define ty   r10
+#define tu   r11
+#define tv   r12
+#define i    lr
+
+        .global yuv420_to_yuv422
+        .func   yuv420_to_yuv422
+yuv420_to_yuv422:
+        push            {r4-r11,lr}
+        add             r4,  sp,  #36
+        ldm             r4, {r4-r8}
+1:
+        mov             tu,   u
+        mov             tv,   v
+        vld1.64         {d2}, [u,:64], cw               @ u0
+        vld1.64         {d3}, [v,:64], cw               @ v0
+        mov             tyuv, yuv
+        mov             ty,   y
+        vzip.8          d2,   d3                        @ u0v0
+        mov             i,    #16
+2:
+        pld             [y, #64]
+        vld1.64         {d0, d1},   [y,:128], yw        @ y0
+        pld             [u, #64]
+        subs            i,    i,    #4
+        vld1.64         {d6},       [u,:64],  cw        @ u2
+        pld             [y, #64]
+        vld1.64         {d4, d5},   [y,:128], yw        @ y1
+        pld             [v, #64]
+        vld1.64         {d7},       [v,:64],  cw        @ v2
+        pld             [y, #64]
+        vld1.64         {d16,d17},  [y,:128], yw        @ y2
+        vzip.8          d6,   d7                        @ u2v2
+        pld             [u, #64]
+        vld1.64         {d22},      [u,:64],  cw        @ u4
+        pld             [v, #64]
+        vld1.64         {d23},      [v,:64],  cw        @ v4
+        pld             [y, #64]
+        vld1.64         {d20,d21},  [y,:128], yw        @ y3
+        vmov            q9,   q3                        @ u2v2
+        vzip.8          d22,  d23                       @ u4v4
+        vrhadd.u8       q3,   q1,   q3                  @ u1v1
+        vzip.8          q0,   q1                        @ y0u0y0v0
+        vmov            q12,  q11                       @ u4v4
+        vzip.8          q2,   q3                        @ y1u1y1v1
+        vrhadd.u8       q11,  q9,   q11                 @ u3v3
+        vst1.64         {d0-d3},    [yuv,:128], dw      @ y0u0y0v0
+        vzip.8          q8,   q9                        @ y2u2y2v2
+        vst1.64         {d4-d7},    [yuv,:128], dw      @ y1u1y1v1
+        vzip.8          q10,  q11                       @ y3u3y3v3
+        vst1.64         {d16-d19},  [yuv,:128], dw      @ y2u2y2v2
+        vmov            q1,   q12
+        vst1.64         {d20-d23},  [yuv,:128], dw      @ y3u3y3v3
+        bgt             2b
+        subs            w,    w,    #16
+        add             yuv,  tyuv, #32
+        add             y,    ty,   #16
+        add             u,    tu,   #8
+        add             v,    tv,   #8
+        bgt             1b
+
+        ldr             w,    [sp, #36]
+        subs            h,    h,    #16
+        add             yuv,  yuv,  dw, lsl #4
+        sub             yuv,  yuv,  w,  lsl #1
+        add             y,    y,    yw, lsl #4
+        sub             y,    y,    w
+        add             u,    u,    cw, lsl #3
+        sub             u,    u,    w,  asr #1
+        add             v,    v,    cw, lsl #3
+        sub             v,    v,    w,  asr #1
+        bgt             1b
+
+        pop             {r4-r11,pc}
+        .endfunc
+
-- 
1.5.6.3