From 0c0067a1b01d44017cdffe1be559ee8af1e68f32 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 20 Feb 2006 21:19:59 +0000 Subject: Port ximagesrc to 0.10 (Closes #304795) Original commit message from CVS: * configure.ac: * sys/Makefile.am: * sys/ximagesrc/Makefile.am: * sys/ximagesrc/ximagesrc.c: (gst_ximagesrc_return_buf), (gst_ximagesrc_open_display), (gst_ximagesrc_start), (gst_ximagesrc_stop), (gst_ximagesrc_unlock), (gst_ximagesrc_recalc), (composite_pixel), (gst_ximagesrc_ximage_get), (gst_ximagesrc_create), (gst_ximagesrc_set_property), (gst_ximagesrc_get_property), (gst_ximagesrc_clear_bufpool), (gst_ximagesrc_base_init), (gst_ximagesrc_dispose), (gst_ximagesrc_finalize), (gst_ximagesrc_get_caps), (gst_ximagesrc_set_caps), (gst_ximagesrc_fixate), (gst_ximagesrc_class_init), (gst_ximagesrc_init), (plugin_init): * sys/ximagesrc/ximagesrc.h: * sys/ximagesrc/ximageutil.c: (ximageutil_handle_xerror), (ximageutil_check_xshm_calls), (ximageutil_xcontext_get), (ximageutil_xcontext_clear), (ximageutil_calculate_pixel_aspect_ratio), (gst_ximagesrc_buffer_finalize), (gst_ximage_buffer_free), (gst_ximagesrc_buffer_init), (gst_ximagesrc_buffer_class_init), (gst_ximagesrc_buffer_get_type), (gst_ximageutil_ximage_new), (gst_ximageutil_ximage_destroy): * sys/ximagesrc/ximageutil.h: Port ximagesrc to 0.10 (Closes #304795) --- sys/ximagesrc/ximageutil.c | 482 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 sys/ximagesrc/ximageutil.c (limited to 'sys/ximagesrc/ximageutil.c') diff --git a/sys/ximagesrc/ximageutil.c b/sys/ximagesrc/ximageutil.c new file mode 100644 index 00000000..1928449a --- /dev/null +++ b/sys/ximagesrc/ximageutil.c @@ -0,0 +1,482 @@ +/* GStreamer + * Copyright (C) <2005> Luca Ognibene + * + * 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 "ximageutil.h" + +#ifdef HAVE_XSHM +static gboolean error_caught = FALSE; + +static int +ximageutil_handle_xerror (Display * display, XErrorEvent * xevent) +{ + char error_msg[1024]; + + XGetErrorText (display, xevent->error_code, error_msg, 1024); + GST_DEBUG ("ximageutil failed to use XShm calls. error: %s", error_msg); + error_caught = TRUE; + return 0; +} + +/* This function checks that it is actually really possible to create an image + using XShm */ +gboolean +ximageutil_check_xshm_calls (GstXContext * xcontext) +{ + XImage *ximage; + XShmSegmentInfo SHMInfo; + size_t size; + int (*handler) (Display *, XErrorEvent *); + gboolean result = FALSE; + gboolean did_attach = FALSE; + + g_return_val_if_fail (xcontext != NULL, FALSE); + + /* Sync to ensure any older errors are already processed */ + XSync (xcontext->disp, FALSE); + + /* Set defaults so we don't free these later unnecessarily */ + SHMInfo.shmaddr = ((void *) -1); + SHMInfo.shmid = -1; + + /* Setting an error handler to catch failure */ + error_caught = FALSE; + handler = XSetErrorHandler (ximageutil_handle_xerror); + + /* Trying to create a 1x1 ximage */ + GST_DEBUG ("XShmCreateImage of 1x1"); + + ximage = XShmCreateImage (xcontext->disp, xcontext->visual, + xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1); + + /* Might cause an error, sync to ensure it is noticed */ + XSync (xcontext->disp, FALSE); + if (!ximage || error_caught) { + GST_WARNING ("could not XShmCreateImage a 1x1 image"); + goto beach; + } + size = ximage->height * ximage->bytes_per_line; + + SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777); + if (SHMInfo.shmid == -1) { + GST_WARNING ("could not get shared memory of %d bytes", size); + goto beach; + } + + SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0); + if (SHMInfo.shmaddr == ((void *) -1)) { + GST_WARNING ("Failed to shmat: %s", g_strerror (errno)); + goto beach; + } + + /* Delete the SHM segment. It will actually go away automatically + * when we detach now */ + shmctl (SHMInfo.shmid, IPC_RMID, 0); + + ximage->data = SHMInfo.shmaddr; + SHMInfo.readOnly = FALSE; + + if (XShmAttach (xcontext->disp, &SHMInfo) == 0) { + GST_WARNING ("Failed to XShmAttach"); + goto beach; + } + + /* Sync to ensure we see any errors we caused */ + XSync (xcontext->disp, FALSE); + + if (!error_caught) { + did_attach = TRUE; + /* store whether we succeeded in result */ + result = TRUE; + } +beach: + /* Sync to ensure we swallow any errors we caused and reset error_caught */ + XSync (xcontext->disp, FALSE); + error_caught = FALSE; + XSetErrorHandler (handler); + + if (did_attach) { + XShmDetach (xcontext->disp, &SHMInfo); + XSync (xcontext->disp, FALSE); + } + if (SHMInfo.shmaddr != ((void *) -1)) + shmdt (SHMInfo.shmaddr); + if (ximage) + XDestroyImage (ximage); + return result; +} +#endif /* HAVE_XSHM */ + +/* 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. Note + here that caps for supported format are generated without any window or + image creation */ +GstXContext * +ximageutil_xcontext_get (GstElement * parent, const gchar * display_name) +{ + GstXContext *xcontext = NULL; + XPixmapFormatValues *px_formats = NULL; + gint nb_formats = 0, i; + + xcontext = g_new0 (GstXContext, 1); + + xcontext->disp = XOpenDisplay (display_name); + + if (!xcontext->disp) { + 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); + + xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num); + xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num); + + xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num); + xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num); + + xcontext->caps = NULL; + + GST_DEBUG_OBJECT (parent, "X reports %dx%d pixels and %d mm x %d mm", + xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm); + + ximageutil_calculate_pixel_aspect_ratio (xcontext); + + /* We get supported pixmap formats at supported depth */ + px_formats = XListPixmapFormats (xcontext->disp, &nb_formats); + + if (!px_formats) { + XCloseDisplay (xcontext->disp); + 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 HAVE_XSHM + /* Search for XShm extension support */ + if (XShmQueryExtension (xcontext->disp) && + ximageutil_check_xshm_calls (xcontext)) { + xcontext->use_xshm = TRUE; + GST_DEBUG ("ximageutil is using XShm extension"); + } else { + xcontext->use_xshm = FALSE; + GST_DEBUG ("ximageutil is not using XShm extension"); + } +#endif /* HAVE_XSHM */ + + /* our caps system handles 24/32bpp RGB as big-endian. */ + if ((xcontext->bpp == 24 || xcontext->bpp == 32) && + xcontext->endianness == G_LITTLE_ENDIAN) { + xcontext->endianness = G_BIG_ENDIAN; + xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask); + xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask); + xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask); + if (xcontext->bpp == 24) { + xcontext->visual->red_mask >>= 8; + xcontext->visual->green_mask >>= 8; + xcontext->visual->blue_mask >>= 8; + } + } + + return xcontext; +} + +/* This function cleans the X context. Closing the Display and unrefing the + caps for supported formats. */ +void +ximageutil_xcontext_clear (GstXContext * xcontext) +{ + g_return_if_fail (xcontext != NULL); + + if (xcontext->caps != NULL) + gst_caps_unref (xcontext->caps); + + if (xcontext->par) { + g_value_unset (xcontext->par); + g_free (xcontext->par); + } + + XCloseDisplay (xcontext->disp); + + g_free (xcontext); +} + +/* This function calculates the pixel aspect ratio based on the properties + * in the xcontext structure and stores it there. */ +void +ximageutil_calculate_pixel_aspect_ratio (GstXContext * xcontext) +{ + gint par[][2] = { + {1, 1}, /* regular screen */ + {16, 15}, /* PAL TV */ + {11, 10}, /* 525 line Rec.601 video */ + {54, 59} /* 625 line Rec.601 video */ + }; + gint i; + gint index; + gdouble ratio; + gdouble delta; + +#define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1]))) + + /* first calculate the "real" ratio based on the X values; + * which is the "physical" w/h divided by the w/h in pixels of the display */ + ratio = (gdouble) (xcontext->widthmm * xcontext->height) + / (xcontext->heightmm * xcontext->width); + + /* DirectFB's X in 720x576 reports the physical dimensions wrong, so + * override here */ + if (xcontext->width == 720 && xcontext->height == 576) { + ratio = 4.0 * 576 / (3.0 * 720); + } + GST_DEBUG ("calculated pixel aspect ratio: %f", ratio); + + /* now find the one from par[][2] with the lowest delta to the real one */ + delta = DELTA (0); + index = 0; + + for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) { + gdouble this_delta = DELTA (i); + + if (this_delta < delta) { + index = i; + delta = this_delta; + } + } + + GST_DEBUG ("Decided on index %d (%d/%d)", index, + par[index][0], par[index][1]); + + g_free (xcontext->par); + xcontext->par = g_new0 (GValue, 1); + g_value_init (xcontext->par, GST_TYPE_FRACTION); + gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]); + GST_DEBUG ("set xcontext PAR to %d/%d", + gst_value_get_fraction_numerator (xcontext->par), + gst_value_get_fraction_denominator (xcontext->par)); +} + +static void +gst_ximagesrc_buffer_finalize (GstXImageSrcBuffer * ximage) +{ + GstElement *parent; + + g_return_if_fail (ximage != NULL); + + parent = ximage->parent; + if (parent == NULL) { + g_warning ("XImageSrcBuffer->ximagesrc == NULL"); + goto beach; + } + + if (ximage->return_func) + ximage->return_func (parent, ximage); + +beach: + return; +} + +void +gst_ximage_buffer_free (GstXImageSrcBuffer * ximage) +{ + /* make sure it is not recycled */ + ximage->width = -1; + ximage->height = -1; + gst_buffer_unref (GST_BUFFER (ximage)); +} + +static void +gst_ximagesrc_buffer_init (GstXImageSrcBuffer * ximage_buffer, gpointer g_class) +{ +#ifdef HAVE_XSHM + ximage_buffer->SHMInfo.shmaddr = ((void *) -1); + ximage_buffer->SHMInfo.shmid = -1; +#endif +} + +static void +gst_ximagesrc_buffer_class_init (gpointer g_class, gpointer class_data) +{ + GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class); + + mini_object_class->finalize = (GstMiniObjectFinalizeFunction) + gst_ximagesrc_buffer_finalize; +} + +GType +gst_ximagesrc_buffer_get_type (void) +{ + static GType _gst_ximagesrc_buffer_type; + + if (G_UNLIKELY (_gst_ximagesrc_buffer_type == 0)) { + static const GTypeInfo ximagesrc_buffer_info = { + sizeof (GstBufferClass), + NULL, + NULL, + gst_ximagesrc_buffer_class_init, + NULL, + NULL, + sizeof (GstXImageSrcBuffer), + 0, + (GInstanceInitFunc) gst_ximagesrc_buffer_init, + NULL + }; + _gst_ximagesrc_buffer_type = g_type_register_static (GST_TYPE_BUFFER, + "GstXImageSrcBuffer", &ximagesrc_buffer_info, 0); + } + return _gst_ximagesrc_buffer_type; +} + +/* This function handles GstXImageSrcBuffer creation depending on XShm availability */ +GstXImageSrcBuffer * +gst_ximageutil_ximage_new (GstXContext * xcontext, + GstElement * parent, int width, int height, BufferReturnFunc return_func) +{ + GstXImageSrcBuffer *ximage = NULL; + gboolean succeeded = FALSE; + + ximage = + (GstXImageSrcBuffer *) gst_mini_object_new (GST_TYPE_XIMAGESRC_BUFFER); + + ximage->width = width; + ximage->height = height; + +#ifdef HAVE_XSHM + if (xcontext->use_xshm) { + ximage->ximage = XShmCreateImage (xcontext->disp, + xcontext->visual, xcontext->depth, + ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height); + if (!ximage->ximage) { + goto beach; + } + + /* we have to use the returned bytes_per_line for our shm size */ + ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height; + ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size, + IPC_CREAT | 0777); + if (ximage->SHMInfo.shmid == -1) + goto beach; + + ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, 0, 0); + if (ximage->SHMInfo.shmaddr == ((void *) -1)) + goto beach; + + /* Delete the SHM segment. It will actually go away automatically + * when we detach now */ + shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0); + + ximage->ximage->data = ximage->SHMInfo.shmaddr; + ximage->SHMInfo.readOnly = FALSE; + + if (XShmAttach (xcontext->disp, &ximage->SHMInfo) == 0) + goto beach; + + XSync (xcontext->disp, FALSE); + } else +#endif /* HAVE_XSHM */ + { + ximage->ximage = XCreateImage (xcontext->disp, + xcontext->visual, + xcontext->depth, + ZPixmap, 0, NULL, ximage->width, ximage->height, xcontext->bpp, 0); + if (!ximage->ximage) + goto beach; + + /* we have to use the returned bytes_per_line for our image size */ + ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height; + ximage->ximage->data = g_malloc (ximage->size); + + XSync (xcontext->disp, FALSE); + } + succeeded = TRUE; + + GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data; + GST_BUFFER_SIZE (ximage) = ximage->size; + + /* Keep a ref to our src */ + ximage->parent = gst_object_ref (parent); + ximage->return_func = return_func; +beach: + if (!succeeded) { + gst_ximage_buffer_free (ximage); + ximage = NULL; + } + + return ximage; +} + +/* This function destroys a GstXImageBuffer handling XShm availability */ +void +gst_ximageutil_ximage_destroy (GstXContext * xcontext, + GstXImageSrcBuffer * ximage) +{ + /* We might have some buffers destroyed after changing state to NULL */ + if (!xcontext) + goto beach; + + g_return_if_fail (ximage != NULL); + +#ifdef HAVE_XSHM + if (xcontext->use_xshm) { + if (ximage->SHMInfo.shmaddr != ((void *) -1)) { + XShmDetach (xcontext->disp, &ximage->SHMInfo); + XSync (xcontext->disp, 0); + shmdt (ximage->SHMInfo.shmaddr); + } + if (ximage->ximage) + XDestroyImage (ximage->ximage); + + } else +#endif /* HAVE_XSHM */ + { + if (ximage->ximage) { + XDestroyImage (ximage->ximage); + } + } + + XSync (xcontext->disp, FALSE); +beach: + if (ximage->parent) { + /* Release the ref to our parent */ + gst_object_unref (ximage->parent); + ximage->parent = NULL; + } + + return; +} -- cgit v1.2.1