mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
Adding ximagesink plugin.
Original commit message from CVS: Adding ximagesink plugin. Pure X11 (XShm supported) video output plugin.
This commit is contained in:
parent
7ad3f03e1c
commit
272458b0b0
3 changed files with 1046 additions and 0 deletions
12
sys/ximage/Makefile.am
Normal file
12
sys/ximage/Makefile.am
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
plugin_LTLIBRARIES = libgstximagesink.la
|
||||
|
||||
libgstximagesink_la_SOURCES = ximagesink.c
|
||||
libgstximagesink_la_CFLAGS = $(GST_CFLAGS) $(X_CFLAGS)
|
||||
libgstximagesink_la_LIBADD = $(X_LIBS)
|
||||
libgstximagesink_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) \
|
||||
$(top_builddir)/gst-libs/gst/navigation/libgstnavigation.la \
|
||||
$(top_builddir)/gst-libs/gst/xoverlay/libgstxoverlay.la
|
||||
|
||||
noinst_HEADERS = ximagesink.h
|
||||
|
902
sys/ximage/ximagesink.c
Normal file
902
sys/ximage/ximagesink.c
Normal file
|
@ -0,0 +1,902 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2003> Julien Moutte <julien@moutte.net>
|
||||
*
|
||||
* 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
|
||||
|
||||
/* Our interfaces */
|
||||
#include <gst-libs/gst/navigation/navigation.h>
|
||||
#include <gst-libs/gst/xoverlay/xoverlay.h>
|
||||
|
||||
/* Object header */
|
||||
#include "ximagesink.h"
|
||||
|
||||
/* ElementFactory information */
|
||||
static GstElementDetails gst_ximagesink_details = GST_ELEMENT_DETAILS (
|
||||
"Video sink",
|
||||
"Sink/Video",
|
||||
"A standard X based videosink",
|
||||
"Julien Moutte <julien@moutte.net>"
|
||||
);
|
||||
|
||||
/* Default template - initiated with class struct to allow gst-register to work
|
||||
without X running */
|
||||
GST_PAD_TEMPLATE_FACTORY (gst_ximagesink_sink_template_factory,
|
||||
"sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_CAPS_NEW ("ximagesink_rgbsink", "video/x-raw-rgb",
|
||||
"framerate", GST_PROPS_FLOAT_RANGE (0, G_MAXFLOAT),
|
||||
"width", GST_PROPS_INT_RANGE (0, G_MAXINT),
|
||||
"height", GST_PROPS_INT_RANGE (0, G_MAXINT))
|
||||
)
|
||||
|
||||
static GstElementClass *parent_class = NULL;
|
||||
|
||||
/* ============================================================= */
|
||||
/* */
|
||||
/* Private Methods */
|
||||
/* */
|
||||
/* ============================================================= */
|
||||
|
||||
/* Interfaces stuff */
|
||||
|
||||
static gboolean
|
||||
gst_ximagesink_interface_supported (GstInterface *iface, GType type)
|
||||
{
|
||||
g_assert (type == GST_TYPE_NAVIGATION ||
|
||||
type == GST_TYPE_X_OVERLAY);
|
||||
|
||||
return (GST_STATE (iface) != GST_STATE_NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_interface_init (GstInterfaceClass *klass)
|
||||
{
|
||||
klass->supported = gst_ximagesink_interface_supported;
|
||||
}
|
||||
|
||||
/* X11 stuff */
|
||||
|
||||
static GstXImage *
|
||||
gst_ximagesink_ximage_new (GstXImageSink *ximagesink, gint width, gint height)
|
||||
{
|
||||
GstXImage *ximage = NULL;
|
||||
|
||||
g_return_val_if_fail (ximagesink != NULL, NULL);
|
||||
g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
|
||||
|
||||
ximage = g_new0 (GstXImage, 1);
|
||||
|
||||
ximage->width = width;
|
||||
ximage->height = height;
|
||||
ximage->data = NULL;
|
||||
|
||||
g_mutex_lock (ximagesink->x_lock);
|
||||
|
||||
ximage->size = (ximagesink->xcontext->bpp / 8) * ximage->width * ximage->height;
|
||||
|
||||
#ifdef USE_SHM
|
||||
if (ximagesink->xcontext->use_xshm)
|
||||
{
|
||||
ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
|
||||
ximagesink->xcontext->visual,
|
||||
ximagesink->xcontext->depth,
|
||||
ZPixmap, NULL, &ximage->SHMInfo,
|
||||
ximage->width, ximage->height);
|
||||
|
||||
ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
|
||||
IPC_CREAT | 0777);
|
||||
|
||||
ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, 0, 0);
|
||||
ximage->ximage->data = ximage->SHMInfo.shmaddr;
|
||||
|
||||
ximage->SHMInfo.readOnly = FALSE;
|
||||
|
||||
XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo);
|
||||
|
||||
GST_DEBUG ("ximagesink creating an image with XShm");
|
||||
}
|
||||
else
|
||||
{
|
||||
ximage->data = g_malloc (ximage->size);
|
||||
|
||||
ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
|
||||
ximagesink->xcontext->visual,
|
||||
ximagesink->xcontext->depth,
|
||||
ZPixmap, 0, ximage->data,
|
||||
ximage->width, ximage->height,
|
||||
ximagesink->xcontext->bpp,
|
||||
ximage->width * (ximagesink->xcontext->bpp / 8));
|
||||
|
||||
GST_DEBUG ("ximagesink creating an image without XShm but with SHM defined");
|
||||
}
|
||||
#else
|
||||
ximage->data = g_malloc (ximage->size);
|
||||
|
||||
ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
|
||||
ximagesink->xcontext->visual,
|
||||
ximagesink->xcontext->depth,
|
||||
ZPixmap, 0, ximage->data,
|
||||
ximage->width, ximage->height,
|
||||
ximagesink->xcontext->bpp,
|
||||
ximage->width * (ximagesink->xcontext->bpp / 8));
|
||||
|
||||
GST_DEBUG ("ximagesink creating an image without XShm");
|
||||
#endif /* USE_SHM */
|
||||
|
||||
if (ximage->ximage)
|
||||
{
|
||||
XSync(ximagesink->xcontext->disp, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ximage->data)
|
||||
g_free (ximage->data);
|
||||
|
||||
g_free (ximage);
|
||||
|
||||
ximage = NULL;
|
||||
}
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
|
||||
return ximage;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_ximage_destroy (GstXImageSink *ximagesink, GstXImage *ximage)
|
||||
{
|
||||
g_return_if_fail (ximage != NULL);
|
||||
g_return_if_fail (ximagesink != NULL);
|
||||
g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
|
||||
|
||||
g_mutex_lock (ximagesink->x_lock);
|
||||
|
||||
#ifdef USE_SHM
|
||||
if (ximagesink->xcontext->use_xshm)
|
||||
{
|
||||
if (ximage->SHMInfo.shmaddr)
|
||||
XShmDetach(ximagesink->xcontext->disp, &ximage->SHMInfo);
|
||||
|
||||
if (ximage->ximage)
|
||||
XDestroyImage(ximage->ximage);
|
||||
|
||||
if (ximage->SHMInfo.shmaddr)
|
||||
shmdt(ximage->SHMInfo.shmaddr);
|
||||
|
||||
if (ximage->SHMInfo.shmid > 0)
|
||||
shmctl(ximage->SHMInfo.shmid, IPC_RMID, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ximage->ximage)
|
||||
XDestroyImage(ximage->ximage);
|
||||
}
|
||||
#else
|
||||
if (ximage->ximage)
|
||||
XDestroyImage(ximage->ximage);
|
||||
#endif /* USE_SHM */
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
|
||||
g_free (ximage);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_ximage_put (GstXImageSink *ximagesink, GstXImage *ximage)
|
||||
{
|
||||
gint x, y;
|
||||
XWindowAttributes attr;
|
||||
|
||||
g_return_if_fail (ximage != NULL);
|
||||
g_return_if_fail (ximagesink != NULL);
|
||||
g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
|
||||
|
||||
g_mutex_lock (ximagesink->x_lock);
|
||||
|
||||
/* We center the image in the window */
|
||||
XGetWindowAttributes (ximagesink->xcontext->disp,
|
||||
ximagesink->xwindow->win, &attr);
|
||||
|
||||
x = MAX (0, (attr.width - ximage->width) / 2);
|
||||
y = MAX (0, (attr.height - ximage->height) / 2);
|
||||
|
||||
#ifdef USE_SHM
|
||||
if (ximagesink->xcontext->use_xshm)
|
||||
{
|
||||
XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
|
||||
ximagesink->xwindow->gc, ximage->ximage,
|
||||
0, 0, x, y, ximage->width, ximage->height,
|
||||
FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
|
||||
ximagesink->xwindow->gc, ximage->ximage,
|
||||
0, 0, x, y, ximage->width, ximage->height);
|
||||
}
|
||||
#else
|
||||
XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
|
||||
ximagesink->xwindow->gc, ximage->ximage,
|
||||
0, 0, x, y, ximage->width, ximage->height);
|
||||
#endif /* USE_SHM */
|
||||
|
||||
XSync(ximagesink->xcontext->disp, FALSE);
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
}
|
||||
|
||||
static GstXWindow *
|
||||
gst_ximagesink_xwindow_new (GstXImageSink *ximagesink, gint width, gint height)
|
||||
{
|
||||
GstXWindow *xwindow = NULL;
|
||||
XGCValues values;
|
||||
|
||||
g_return_val_if_fail (ximagesink != NULL, NULL);
|
||||
g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
|
||||
|
||||
xwindow = g_new0 (GstXWindow, 1);
|
||||
|
||||
xwindow->width = width;
|
||||
xwindow->height = height;
|
||||
|
||||
g_mutex_lock (ximagesink->x_lock);
|
||||
|
||||
xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
|
||||
ximagesink->xcontext->root,
|
||||
0, 0, xwindow->width, xwindow->height,
|
||||
0, 0, ximagesink->xcontext->black);
|
||||
|
||||
xwindow->gc = XCreateGC (ximagesink->xcontext->disp,
|
||||
xwindow->win, 0, &values);
|
||||
|
||||
XMapRaised (ximagesink->xcontext->disp, xwindow->win);
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
|
||||
return xwindow;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_xwindow_destroy (GstXImageSink *ximagesink, GstXWindow *xwindow)
|
||||
{
|
||||
g_return_if_fail (xwindow != NULL);
|
||||
g_return_if_fail (ximagesink != NULL);
|
||||
g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
|
||||
|
||||
g_mutex_lock (ximagesink->x_lock);
|
||||
|
||||
XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
|
||||
|
||||
XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
|
||||
g_free (xwindow);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_xwindow_resize (GstXImageSink *ximagesink, GstXWindow *xwindow,
|
||||
gint width, gint height)
|
||||
{
|
||||
g_return_if_fail (xwindow != NULL);
|
||||
g_return_if_fail (ximagesink != NULL);
|
||||
g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
|
||||
|
||||
g_mutex_lock (ximagesink->x_lock);
|
||||
|
||||
XResizeWindow (ximagesink->xcontext->disp, xwindow->win, width, height);
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
}
|
||||
|
||||
/* This function get the X Display and global infos about it. Everything is
|
||||
stored in our object and will be cleaned when the object is disposed. Note
|
||||
here that caps for supported format are generated without any window or
|
||||
image creation */
|
||||
static GstXContext *
|
||||
gst_ximagesink_xcontext_get (GstXImageSink *ximagesink)
|
||||
{
|
||||
GstXContext *xcontext = NULL;
|
||||
XPixmapFormatValues *px_formats = NULL;
|
||||
gint nb_formats = 0, i;
|
||||
|
||||
xcontext = g_new0 (GstXContext, 1);
|
||||
|
||||
g_mutex_lock (ximagesink->x_lock);
|
||||
|
||||
xcontext->disp = XOpenDisplay (NULL);
|
||||
|
||||
if (!xcontext->disp) {
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
g_free (xcontext);
|
||||
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->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
|
||||
xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
|
||||
xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
|
||||
|
||||
/* We get supported pixmap formats at supported depth */
|
||||
px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
|
||||
|
||||
if (!px_formats)
|
||||
{
|
||||
XCloseDisplay (xcontext->disp);
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
g_free (xcontext);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We get bpp value corresponding to our running depth */
|
||||
for (i=0; i<nb_formats; i++)
|
||||
{
|
||||
if (px_formats[i].depth == xcontext->depth)
|
||||
xcontext->bpp = px_formats[i].bits_per_pixel;
|
||||
}
|
||||
|
||||
XFree (px_formats);
|
||||
|
||||
xcontext->endianness = (ImageByteOrder (xcontext->disp) == LSBFirst) ? G_LITTLE_ENDIAN:G_BIG_ENDIAN;
|
||||
|
||||
#ifdef USE_SHM
|
||||
/* Search for XShm extension support */
|
||||
if (XQueryExtension (xcontext->disp, "MIT-SHM", &i, &i, &i))
|
||||
{
|
||||
xcontext->use_xshm = TRUE;
|
||||
GST_DEBUG ("ximagesink is using XShm extension");
|
||||
}
|
||||
else
|
||||
{
|
||||
xcontext->use_xshm = FALSE;
|
||||
GST_DEBUG ("ximagesink is not using XShm extension");
|
||||
}
|
||||
#endif /* USE_SHM */
|
||||
|
||||
xcontext->caps = GST_CAPS_NEW ("ximagesink_ximage_caps", "video/x-raw-rgb",
|
||||
"bpp", GST_PROPS_INT (xcontext->bpp),
|
||||
"depth", GST_PROPS_INT (xcontext->depth),
|
||||
"endianness", GST_PROPS_INT (xcontext->endianness),
|
||||
"red_mask", GST_PROPS_INT (xcontext->visual->red_mask),
|
||||
"green_mask", GST_PROPS_INT (xcontext->visual->green_mask),
|
||||
"blue_mask", GST_PROPS_INT (xcontext->visual->blue_mask),
|
||||
"width", GST_PROPS_INT_RANGE (0, G_MAXINT),
|
||||
"height", GST_PROPS_INT_RANGE (0, G_MAXINT),
|
||||
"framerate", GST_PROPS_FLOAT_RANGE (0, G_MAXFLOAT));
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
|
||||
/* We make this caps non floating. This way we keep it during our whole life */
|
||||
gst_caps_ref (xcontext->caps);
|
||||
gst_caps_sink (xcontext->caps);
|
||||
|
||||
return xcontext;
|
||||
}
|
||||
|
||||
/* This function cleans the X context. Closing the Display and unrefing the
|
||||
caps for supported formats. */
|
||||
static void
|
||||
gst_ximagesink_xcontext_clear (GstXImageSink *ximagesink)
|
||||
{
|
||||
g_return_if_fail (ximagesink != NULL);
|
||||
g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
|
||||
|
||||
gst_caps_unref (ximagesink->xcontext->caps);
|
||||
|
||||
g_mutex_lock (ximagesink->x_lock);
|
||||
|
||||
XCloseDisplay (ximagesink->xcontext->disp);
|
||||
|
||||
g_mutex_unlock (ximagesink->x_lock);
|
||||
|
||||
ximagesink->xcontext = NULL;
|
||||
}
|
||||
|
||||
/* Element stuff */
|
||||
|
||||
static GstCaps *
|
||||
gst_ximagesink_getcaps (GstPad *pad, GstCaps *caps)
|
||||
{
|
||||
GstXImageSink *ximagesink;
|
||||
|
||||
ximagesink = GST_XIMAGESINK (gst_pad_get_parent (pad));
|
||||
|
||||
if (ximagesink->xcontext)
|
||||
caps = gst_caps_append(caps, gst_caps_copy (ximagesink->xcontext->caps));
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static GstPadLinkReturn
|
||||
gst_ximagesink_sinkconnect (GstPad *pad, GstCaps *caps)
|
||||
{
|
||||
GstXImageSink *ximagesink;
|
||||
GstCaps *icaps;
|
||||
|
||||
ximagesink = GST_XIMAGESINK (gst_pad_get_parent (pad));
|
||||
|
||||
/* we are not going to act on variable caps */
|
||||
if (!GST_CAPS_IS_FIXED (caps))
|
||||
return GST_PAD_LINK_DELAYED;
|
||||
if (GST_CAPS_IS_CHAINED (caps))
|
||||
return GST_PAD_LINK_DELAYED;
|
||||
|
||||
GST_DEBUG ("sinkconnect %s with %s", gst_caps_to_string(caps), gst_caps_to_string(ximagesink->xcontext->caps));
|
||||
|
||||
/* Trying caps intersection */
|
||||
icaps = gst_caps_intersect (caps, ximagesink->xcontext->caps);
|
||||
|
||||
if (!icaps)
|
||||
{
|
||||
GST_DEBUG ("no format found");
|
||||
return GST_PAD_LINK_REFUSED;
|
||||
}
|
||||
|
||||
gst_caps_unref(icaps);
|
||||
|
||||
gst_caps_get_int (caps, "width", &ximagesink->width);
|
||||
gst_caps_get_int (caps, "height", &ximagesink->height);
|
||||
|
||||
if (gst_caps_has_fixed_property (caps, "pixel_width"))
|
||||
gst_caps_get_int (caps, "pixel_width", &ximagesink->pixel_width);
|
||||
else
|
||||
ximagesink->pixel_width = 1;
|
||||
|
||||
if (gst_caps_has_fixed_property (caps, "pixel_height"))
|
||||
gst_caps_get_int (caps, "pixel_height", &ximagesink->pixel_height);
|
||||
else
|
||||
ximagesink->pixel_height = 1;
|
||||
|
||||
/* Creating our window and our image */
|
||||
if (!ximagesink->xwindow)
|
||||
ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
|
||||
ximagesink->width,
|
||||
ximagesink->height);
|
||||
else
|
||||
gst_ximagesink_xwindow_resize (ximagesink, ximagesink->xwindow,
|
||||
ximagesink->width, ximagesink->height);
|
||||
|
||||
if (ximagesink->ximage)
|
||||
gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
|
||||
|
||||
ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
|
||||
ximagesink->width,
|
||||
ximagesink->height);
|
||||
|
||||
return GST_PAD_LINK_OK;
|
||||
}
|
||||
|
||||
static GstElementStateReturn
|
||||
gst_ximagesink_change_state (GstElement *element)
|
||||
{
|
||||
GstXImageSink *ximagesink;
|
||||
|
||||
ximagesink = GST_XIMAGESINK (element);
|
||||
|
||||
switch (GST_STATE_TRANSITION (element)) {
|
||||
case GST_STATE_NULL_TO_READY:
|
||||
/* Initializing the XContext */
|
||||
ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink);
|
||||
if (!ximagesink->xcontext)
|
||||
return GST_STATE_FAILURE;
|
||||
break;
|
||||
case GST_STATE_READY_TO_PAUSED:
|
||||
break;
|
||||
case GST_STATE_PAUSED_TO_PLAYING:
|
||||
break;
|
||||
case GST_STATE_PLAYING_TO_PAUSED:
|
||||
break;
|
||||
case GST_STATE_PAUSED_TO_READY:
|
||||
break;
|
||||
case GST_STATE_READY_TO_NULL:
|
||||
break;
|
||||
}
|
||||
|
||||
if (parent_class->change_state)
|
||||
return parent_class->change_state (element);
|
||||
|
||||
return GST_STATE_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_chain (GstPad *pad, GstData *_data)
|
||||
{
|
||||
GstBuffer *buf = GST_BUFFER (_data);
|
||||
GstClockTime time = GST_BUFFER_TIMESTAMP (buf);
|
||||
GstXImageSink *ximagesink;
|
||||
|
||||
g_return_if_fail (pad != NULL);
|
||||
g_return_if_fail (GST_IS_PAD (pad));
|
||||
g_return_if_fail (buf != NULL);
|
||||
|
||||
ximagesink = GST_XIMAGESINK (gst_pad_get_parent (pad));
|
||||
|
||||
if (GST_IS_EVENT (buf)) {
|
||||
GstEvent *event = GST_EVENT (buf);
|
||||
gint64 offset;
|
||||
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_DISCONTINUOUS:
|
||||
offset = GST_EVENT_DISCONT_OFFSET (event, 0).value;
|
||||
g_print ("ximage discont %" G_GINT64_FORMAT "\n", offset);
|
||||
break;
|
||||
default:
|
||||
gst_pad_event_default (pad, event);
|
||||
return;
|
||||
}
|
||||
gst_event_unref (event);
|
||||
return;
|
||||
}
|
||||
|
||||
GST_DEBUG ("videosink: clock wait: %" G_GUINT64_FORMAT, time);
|
||||
|
||||
if (ximagesink->clock) {
|
||||
GstClockID id = gst_clock_new_single_shot_id (ximagesink->clock, time);
|
||||
gst_element_clock_wait (GST_ELEMENT (ximagesink), id, NULL);
|
||||
gst_clock_id_free (id);
|
||||
}
|
||||
|
||||
/* If we have a pool and the image is from this pool, simply put it. */
|
||||
if ( (ximagesink->bufferpool) &&
|
||||
(GST_BUFFER_BUFFERPOOL (buf) == ximagesink->bufferpool) )
|
||||
gst_ximagesink_ximage_put (ximagesink, GST_BUFFER_POOL_PRIVATE (buf));
|
||||
else /* Else we have to copy the data into our private image, */
|
||||
{ /* if we have one... */
|
||||
if (ximagesink->ximage)
|
||||
{
|
||||
memcpy (ximagesink->ximage->ximage->data,
|
||||
GST_BUFFER_DATA (buf),
|
||||
MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
|
||||
gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage);
|
||||
}
|
||||
else /* No image available. Something went wrong during capsnego ! */
|
||||
{
|
||||
gst_buffer_unref (buf);
|
||||
gst_element_error (GST_ELEMENT (ximagesink), "no image to draw");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gst_buffer_unref (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_set_clock (GstElement *element, GstClock *clock)
|
||||
{
|
||||
GstXImageSink *ximagesink;
|
||||
|
||||
ximagesink = GST_XIMAGESINK (element);
|
||||
|
||||
ximagesink->clock = clock;
|
||||
}
|
||||
|
||||
static GstBuffer*
|
||||
gst_ximagesink_buffer_new (GstBufferPool *pool,
|
||||
gint64 location, guint size, gpointer user_data)
|
||||
{
|
||||
GstXImageSink *ximagesink;
|
||||
GstBuffer *buffer;
|
||||
GstXImage *ximage = NULL;
|
||||
gboolean not_found = TRUE;
|
||||
|
||||
ximagesink = GST_XIMAGESINK (user_data);
|
||||
|
||||
g_mutex_lock (ximagesink->pool_lock);
|
||||
|
||||
/* Walking through the pool cleaning unsuable images and searching for a
|
||||
suitable one */
|
||||
while (not_found && ximagesink->image_pool)
|
||||
{
|
||||
ximage = ximagesink->image_pool->data;
|
||||
|
||||
if (ximage)
|
||||
{
|
||||
/* Removing from the pool */
|
||||
ximagesink->image_pool = g_slist_delete_link (ximagesink->image_pool,
|
||||
ximagesink->image_pool);
|
||||
|
||||
if ( (ximage->width != ximagesink->width) ||
|
||||
(ximage->height != ximagesink->height) )
|
||||
{ /* This image is unusable. Destroying... */
|
||||
gst_ximagesink_ximage_destroy (ximagesink, ximage);
|
||||
ximage = NULL;
|
||||
}
|
||||
else /* We found a suitable image */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_mutex_unlock (ximagesink->pool_lock);
|
||||
|
||||
if (!ximage) /* We found not suitable image in the pool. Creating... */
|
||||
ximage = gst_ximagesink_ximage_new (ximagesink,
|
||||
ximagesink->width,
|
||||
ximagesink->height);
|
||||
|
||||
if (ximage)
|
||||
{
|
||||
buffer = gst_buffer_new ();
|
||||
GST_BUFFER_POOL_PRIVATE (buffer) = ximage;
|
||||
GST_BUFFER_DATA (buffer) = ximage->ximage->data;
|
||||
GST_BUFFER_SIZE (buffer) = ximage->size;
|
||||
return buffer;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_buffer_free (GstBufferPool *pool,
|
||||
GstBuffer *buffer, gpointer user_data)
|
||||
{
|
||||
GstXImageSink *ximagesink;
|
||||
GstXImage *ximage;
|
||||
|
||||
ximagesink = GST_XIMAGESINK (user_data);
|
||||
|
||||
ximage = GST_BUFFER_POOL_PRIVATE (buffer);
|
||||
|
||||
/* If our geometry changed we can't reuse that image. */
|
||||
if ( (ximage->width != ximagesink->width) ||
|
||||
(ximage->height != ximagesink->height) )
|
||||
{
|
||||
gst_ximagesink_ximage_destroy (ximagesink, ximage);
|
||||
}
|
||||
else /* In that case we can reuse the image and add it to our image pool. */
|
||||
{
|
||||
g_mutex_lock (ximagesink->pool_lock);
|
||||
ximagesink->image_pool = g_slist_prepend (ximagesink->image_pool, ximage);
|
||||
g_mutex_unlock (ximagesink->pool_lock);
|
||||
}
|
||||
|
||||
GST_BUFFER_DATA (buffer) = NULL;
|
||||
|
||||
gst_buffer_default_free (buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_imagepool_clear (GstXImageSink *ximagesink)
|
||||
{
|
||||
g_mutex_lock(ximagesink->pool_lock);
|
||||
|
||||
while (ximagesink->image_pool)
|
||||
{
|
||||
GstXImage *ximage = ximagesink->image_pool->data;
|
||||
ximagesink->image_pool = g_slist_delete_link (ximagesink->image_pool,
|
||||
ximagesink->image_pool);
|
||||
gst_ximagesink_ximage_destroy (ximagesink, ximage);
|
||||
}
|
||||
|
||||
g_mutex_unlock(ximagesink->pool_lock);
|
||||
}
|
||||
|
||||
static GstBufferPool*
|
||||
gst_ximagesink_get_bufferpool (GstPad *pad)
|
||||
{
|
||||
GstXImageSink *ximagesink;
|
||||
|
||||
ximagesink = GST_XIMAGESINK (gst_pad_get_parent (pad));
|
||||
|
||||
if (!ximagesink->bufferpool) {
|
||||
GST_DEBUG ("ximagesink: creating bufferpool");
|
||||
|
||||
ximagesink->bufferpool = gst_buffer_pool_new (
|
||||
NULL, /* free */
|
||||
NULL, /* copy */
|
||||
(GstBufferPoolBufferNewFunction) gst_ximagesink_buffer_new,
|
||||
NULL, /* buffer copy, the default is fine */
|
||||
(GstBufferPoolBufferFreeFunction) gst_ximagesink_buffer_free,
|
||||
ximagesink);
|
||||
|
||||
ximagesink->image_pool = NULL;
|
||||
}
|
||||
|
||||
gst_buffer_pool_ref (ximagesink->bufferpool);
|
||||
|
||||
return ximagesink->bufferpool;
|
||||
}
|
||||
|
||||
/* =========================================== */
|
||||
/* */
|
||||
/* Init & Class init */
|
||||
/* */
|
||||
/* =========================================== */
|
||||
|
||||
static void
|
||||
gst_ximagesink_dispose (GObject *object)
|
||||
{
|
||||
GstXImageSink *ximagesink;
|
||||
|
||||
ximagesink = GST_XIMAGESINK (object);
|
||||
|
||||
if (ximagesink->ximage)
|
||||
{
|
||||
gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
|
||||
ximagesink->ximage = NULL;
|
||||
}
|
||||
|
||||
if (ximagesink->image_pool)
|
||||
gst_ximagesink_imagepool_clear (ximagesink);
|
||||
|
||||
if (ximagesink->xwindow)
|
||||
{
|
||||
gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
|
||||
ximagesink->xwindow = NULL;
|
||||
}
|
||||
|
||||
if (ximagesink->xcontext)
|
||||
gst_ximagesink_xcontext_clear (ximagesink);
|
||||
|
||||
g_mutex_free (ximagesink->x_lock);
|
||||
g_mutex_free (ximagesink->pool_lock);
|
||||
|
||||
if (ximagesink->bufferpool)
|
||||
gst_buffer_pool_free (ximagesink->bufferpool);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_init (GstXImageSink *ximagesink)
|
||||
{
|
||||
ximagesink->sinkpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET (
|
||||
gst_ximagesink_sink_template_factory),
|
||||
"sink");
|
||||
gst_element_add_pad (GST_ELEMENT (ximagesink), ximagesink->sinkpad);
|
||||
|
||||
gst_pad_set_chain_function (ximagesink->sinkpad, gst_ximagesink_chain);
|
||||
gst_pad_set_link_function (ximagesink->sinkpad, gst_ximagesink_sinkconnect);
|
||||
gst_pad_set_getcaps_function (ximagesink->sinkpad, gst_ximagesink_getcaps);
|
||||
gst_pad_set_bufferpool_function (ximagesink->sinkpad,
|
||||
gst_ximagesink_get_bufferpool);
|
||||
|
||||
ximagesink->xcontext = NULL;
|
||||
ximagesink->xwindow = NULL;
|
||||
ximagesink->ximage = NULL;
|
||||
ximagesink->clock = NULL;
|
||||
|
||||
ximagesink->width = ximagesink->height = 0;
|
||||
|
||||
ximagesink->x_lock = g_mutex_new ();
|
||||
|
||||
ximagesink->pixel_width = ximagesink->pixel_height = 1;
|
||||
|
||||
ximagesink->image_pool = NULL;
|
||||
ximagesink->pool_lock = g_mutex_new ();
|
||||
|
||||
GST_FLAG_SET(ximagesink, GST_ELEMENT_THREAD_SUGGESTED);
|
||||
GST_FLAG_SET(ximagesink, GST_ELEMENT_EVENT_AWARE);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_base_init (gpointer g_class)
|
||||
{
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
||||
|
||||
gst_element_class_set_details (element_class, &gst_ximagesink_details);
|
||||
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
GST_PAD_TEMPLATE_GET (gst_ximagesink_sink_template_factory));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_ximagesink_class_init (GstXImageSinkClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstElementClass *gstelement_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
|
||||
parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
|
||||
|
||||
gobject_class->dispose = gst_ximagesink_dispose;
|
||||
|
||||
gstelement_class->change_state = gst_ximagesink_change_state;
|
||||
gstelement_class->set_clock = gst_ximagesink_set_clock;
|
||||
}
|
||||
|
||||
/* ============================================================= */
|
||||
/* */
|
||||
/* Public Methods */
|
||||
/* */
|
||||
/* ============================================================= */
|
||||
|
||||
/* =========================================== */
|
||||
/* */
|
||||
/* Object typing & Creation */
|
||||
/* */
|
||||
/* =========================================== */
|
||||
|
||||
GType
|
||||
gst_ximagesink_get_type (void)
|
||||
{
|
||||
static GType ximagesink_type = 0;
|
||||
|
||||
if (!ximagesink_type)
|
||||
{
|
||||
static const GTypeInfo ximagesink_info = {
|
||||
sizeof(GstXImageSinkClass),
|
||||
gst_ximagesink_base_init,
|
||||
NULL,
|
||||
(GClassInitFunc) gst_ximagesink_class_init,
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof(GstXImageSink),
|
||||
0,
|
||||
(GInstanceInitFunc) gst_ximagesink_init,
|
||||
};
|
||||
static const GInterfaceInfo iface_info = {
|
||||
(GInterfaceInitFunc) gst_ximagesink_interface_init,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
/*static const GInterfaceInfo navigation_info = {
|
||||
` (GInterfaceInitFunc) gst_ximagesink_navigation_init,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
static const GInterfaceInfo xoverlay_info = {
|
||||
(GInterfaceInitFunc) gst_ximagesink_xoverlay_init,
|
||||
NULL,
|
||||
NULL,
|
||||
};*/
|
||||
|
||||
ximagesink_type = g_type_register_static(GST_TYPE_ELEMENT,
|
||||
"GstXImageSink",
|
||||
&ximagesink_info,
|
||||
0);
|
||||
|
||||
g_type_add_interface_static (ximagesink_type, GST_TYPE_INTERFACE,
|
||||
&iface_info);
|
||||
/*g_type_add_interface_static (xvimagesink_type, GST_TYPE_NAVIGATION,
|
||||
&navigation_info);
|
||||
g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
|
||||
&xoverlay_info);*/
|
||||
}
|
||||
|
||||
return ximagesink_type;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin *plugin)
|
||||
{
|
||||
if (!gst_element_register (plugin, "ximagesink", GST_RANK_NONE, GST_TYPE_XIMAGESINK))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (
|
||||
GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
"ximagesink",
|
||||
"XFree86 video output plugin based on standard Xlib calls",
|
||||
plugin_init,
|
||||
VERSION,
|
||||
GST_LICENSE,
|
||||
GST_COPYRIGHT,
|
||||
GST_PACKAGE,
|
||||
GST_ORIGIN)
|
132
sys/ximage/ximagesink.h
Normal file
132
sys/ximage/ximagesink.h
Normal file
|
@ -0,0 +1,132 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2003> Julien Moutte <julien@moutte.net>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_XIMAGESINK_H__
|
||||
#define __GST_XIMAGESINK_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#ifdef USE_SHM
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#endif /* USE_SHM */
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#ifdef USE_SHM
|
||||
#include <X11/extensions/XShm.h>
|
||||
#endif /* USE_SHM */
|
||||
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_XIMAGESINK \
|
||||
(gst_ximagesink_get_type())
|
||||
#define GST_XIMAGESINK(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_XIMAGESINK, GstXImageSink))
|
||||
#define GST_XIMAGESINK_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_XIMAGESINK, GstXImageSink))
|
||||
#define GST_IS_XIMAGESINK(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_XIMAGESINK))
|
||||
#define GST_IS_XIMAGESINK_CLASS(obj) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_XIMAGESINK))
|
||||
|
||||
typedef struct _GstXContext GstXContext;
|
||||
typedef struct _GstXWindow GstXWindow;
|
||||
typedef struct _GstXImage GstXImage;
|
||||
|
||||
typedef struct _GstXImageSink GstXImageSink;
|
||||
typedef struct _GstXImageSinkClass GstXImageSinkClass;
|
||||
|
||||
/* Global X Context stuff */
|
||||
struct _GstXContext {
|
||||
Display *disp;
|
||||
|
||||
Screen *screen;
|
||||
gint screen_num;
|
||||
|
||||
Visual *visual;
|
||||
|
||||
Window root;
|
||||
|
||||
gulong white, black;
|
||||
|
||||
gint depth;
|
||||
gint bpp;
|
||||
gint endianness;
|
||||
|
||||
gboolean use_xshm;
|
||||
|
||||
GstCaps *caps;
|
||||
};
|
||||
|
||||
/* XWindow stuff */
|
||||
struct _GstXWindow {
|
||||
Window win;
|
||||
gint width, height;
|
||||
GC gc;
|
||||
};
|
||||
|
||||
/* XImage stuff */
|
||||
struct _GstXImage {
|
||||
XImage *ximage;
|
||||
|
||||
#ifdef USE_SHM
|
||||
XShmSegmentInfo SHMInfo;
|
||||
#endif /* USE_SHM */
|
||||
|
||||
char *data;
|
||||
gint width, height, size;
|
||||
};
|
||||
|
||||
struct _GstXImageSink {
|
||||
/* Our element stuff */
|
||||
GstElement element;
|
||||
|
||||
GstPad *sinkpad;
|
||||
|
||||
GstClock *clock;
|
||||
|
||||
GstXContext *xcontext;
|
||||
GstXWindow *xwindow;
|
||||
GstXImage *ximage;
|
||||
|
||||
gint width, height;
|
||||
GMutex *x_lock;
|
||||
|
||||
/* Unused */
|
||||
gint pixel_width, pixel_height;
|
||||
|
||||
GstBufferPool *bufferpool;
|
||||
GMutex *pool_lock;
|
||||
GSList *image_pool;
|
||||
};
|
||||
|
||||
struct _GstXImageSinkClass {
|
||||
GstElementClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_ximagesink_get_type(void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_XIMAGESINK_H__ */
|
Loading…
Reference in a new issue