source: trunk/third/gst-plugins/sys/glsink/glimagesink.c @ 21443

Revision 21443, 50.4 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21442, which included commits to RCS files with non-trunk default branches.
Line 
1/* GStreamer
2 * Copyright (C) <2003> Julien Moutte <julien@moutte.net>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif
23
24/* Our interfaces */
25#include <gst/navigation/navigation.h>
26#include <gst/xoverlay/xoverlay.h>
27
28/* Object header */
29#include "glimagesink.h"
30
31/* Debugging category */
32#include <gst/gstinfo.h>
33GST_DEBUG_CATEGORY_STATIC (gst_debug_glimagesink);
34#define GST_CAT_DEFAULT gst_debug_glimagesink
35
36static void gst_glimagesink_buffer_free (GstBuffer * buffer);
37
38/* ElementFactory information */
39static GstElementDetails gst_glimagesink_details =
40GST_ELEMENT_DETAILS ("Video sink",
41    "Sink/Video",
42    "An OpenGL 1.2 based videosink",
43    "Gernot Ziegler <gz@lysator.liu.se>, Julien Moutte <julien@moutte.net>");
44
45/* Default template - initiated with class struct to allow gst-register to work
46   without X running */
47static GstStaticPadTemplate gst_glimagesink_sink_template_factory =
48    GST_STATIC_PAD_TEMPLATE ("sink",
49    GST_PAD_SINK,
50    GST_PAD_ALWAYS,
51    GST_STATIC_CAPS ("video/x-raw-rgb, "
52        "framerate = (double) [ 1.0, 100.0 ], "
53        "width = (int) [ 1, MAX ], "
54        "height = (int) [ 1, MAX ]; "
55        "video/x-raw-yuv, "
56        "framerate = (double) [ 1.0, 100.0 ], "
57        "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
58    );
59
60/* GLImageSink signals and args */
61enum
62{
63  SIGNAL_HANDOFF,
64  SIGNAL_BUFALLOC,
65  LAST_SIGNAL
66      /* FILL ME */
67};
68
69static guint gst_glimagesink_signals[LAST_SIGNAL] = { 0 };
70
71enum
72{
73  ARG_0,
74  ARG_DISPLAY,
75  ARG_SYNCHRONOUS,
76  ARG_SIGNAL_HANDOFFS
77      /* FILL ME */
78};
79
80static GstVideoSinkClass *parent_class = NULL;
81
82/* ============================================================= */
83/*                                                               */
84/*                       Private Methods                         */
85/*                                                               */
86/* ============================================================= */
87
88/* X11 and GLX stuff */
89
90#define TEX_XSIZE 1024
91#define TEX_YSIZE 1024
92
93/* This function handles GstGLImage creation
94   it creates data buffers and corresponding texture IDs */
95static GstGLImage *
96gst_glimagesink_ximage_new (GstGLImageSink * glimagesink, gint width,
97    gint height)
98{
99  GstGLImage *ximage = NULL;
100
101  g_return_val_if_fail (GST_IS_GLIMAGESINK (glimagesink), NULL);
102
103  ximage = g_new0 (GstGLImage, 1);
104
105  ximage->width = width;
106  ximage->height = height;
107  ximage->data = NULL;
108  ximage->glimagesink = glimagesink;
109
110  g_mutex_lock (glimagesink->x_lock);
111
112  ximage->size =
113      (glimagesink->xcontext->bpp / 8) * ximage->width * ximage->height;
114
115  printf ("No ximage_new yet !\n");
116
117  {
118    ximage->data = g_malloc (ximage->size);
119
120    ximage->texid = 1000;
121  }
122
123  if (0)                        // can't fail !
124  {
125    if (ximage->data)
126      g_free (ximage->data);
127
128    g_free (ximage);
129    //ximage = NULL;
130  }
131
132  g_mutex_unlock (glimagesink->x_lock);
133
134  return ximage;
135}
136
137/* This function destroys a GstGLImage handling XShm availability */
138static void
139gst_glimagesink_ximage_destroy (GstGLImageSink * glimagesink,
140    GstGLImage * ximage)
141{
142  g_return_if_fail (ximage != NULL);
143  g_return_if_fail (GST_IS_GLIMAGESINK (glimagesink));
144
145  /* If the destroyed image is the current one we destroy our reference too */
146  if (glimagesink->cur_image == ximage)
147    glimagesink->cur_image = NULL;
148
149  printf ("No ximage_destroy implemented yet !\n");
150
151  g_mutex_lock (glimagesink->x_lock);
152
153  {
154    //if (ximage->ximage) // FIXME: doesnt exist - dealloc textures
155    //  XDestroyImage (ximage->ximage);
156  }
157
158  g_mutex_unlock (glimagesink->x_lock);
159
160  g_free (ximage);
161}
162
163/* This function puts a GstGLImage on a GstGLImagesink's window */
164static void
165gst_glimagesink_ximage_put (GstGLImageSink * glimagesink, GstGLImage * ximage)
166{
167  float xmax;
168  float ymax;
169  float px;
170  float py;
171
172  g_return_if_fail (ximage != NULL);
173  g_return_if_fail (GST_IS_GLIMAGESINK (glimagesink));
174
175  if (glimagesink->signal_handoffs) {
176    g_warning ("Not drawing anything due to signal_handoffs !\n");
177    return;
178  }
179
180  /* Store a reference to the last image we put */
181  if (glimagesink->cur_image != ximage)
182    glimagesink->cur_image = ximage;
183
184  g_mutex_lock (glimagesink->x_lock);
185
186  // both upload the video, and redraw the screen
187
188  //printf("No ximage_put yet !\n");
189
190  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
191
192  glMatrixMode (GL_PROJECTION);
193  glLoadIdentity ();
194
195  glMatrixMode (GL_MODELVIEW);
196  glLoadIdentity ();
197  //glTranslatef(0.0, 0.0, -5.0);
198
199  glEnable (GL_TEXTURE_2D);
200
201  glBindTexture (GL_TEXTURE_2D, ximage->texid);
202  glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, ximage->width, ximage->height,
203      GL_RGB, GL_UNSIGNED_BYTE, ximage->data);
204
205  xmax = (float) ximage->width / TEX_XSIZE;
206  ymax = (float) ximage->height / TEX_YSIZE;
207
208  //float aspect = ximage->width/(float)ximage->height;
209
210  // don't know what to do with pixel aspect yet.
211  //float pixel_aspect = glimagesink->pixel_width/(float)glimagesink->pixel_height;
212
213  //if (aspect != pixel_aspect)
214  //  g_warning("screen aspect %f differs from pixel_aspect %f !", aspect, pixel_aspect);
215
216  glColor4f (1, 1, 1, 1);
217  glBegin (GL_QUADS);
218
219  glNormal3f (0, -1, 0);
220
221  glTexCoord2f (xmax, 0);
222  glVertex3f (1, 1, 0);
223
224  glTexCoord2f (0, 0);
225  glVertex3f (-1, 1, 0);
226
227  glTexCoord2f (0, ymax);
228  glVertex3f (-1, -1, 0);
229
230  glTexCoord2f (xmax, ymax);
231  glVertex3f (1, -1, 0);
232  glEnd ();
233
234#if 1                           // for pointer feedback, later
235  glDisable (GL_TEXTURE_2D);
236  if (glimagesink->pointer_moved)
237    glColor3f (1, 1, 1);
238  else
239    glColor3f (1, 0, 1);
240
241  if (glimagesink->pointer_button[0])
242    glColor3f (1, 0, 0);
243
244  px = 2 * glimagesink->pointer_x / (float) ximage->width - 1.0;
245  py = 2 * glimagesink->pointer_y / (float) ximage->height - 1.0;
246  glPointSize (10);
247  glBegin (GL_POINTS);
248  glVertex2f (px, -py);
249  glEnd ();
250#endif
251
252  glXSwapBuffers (glimagesink->xcontext->disp, glimagesink->window->win);
253
254  g_mutex_unlock (glimagesink->x_lock);
255}
256
257/* This function handles a GstXWindow creation */
258static GstGLWindow *
259gst_glimagesink_xwindow_new (GstGLImageSink * glimagesink, gint width,
260    gint height)
261{
262  GstGLWindow *xwindow = NULL;
263  GstXContext *xcontext = glimagesink->xcontext;
264  Colormap cmap;
265  Atom wmDelete;
266
267  if (glimagesink->signal_handoffs) {
268    g_warning ("NOT CREATING any window due to signal_handoffs !\n");
269    return NULL;
270  }
271
272  g_return_val_if_fail (GST_IS_GLIMAGESINK (glimagesink), NULL);
273
274  xwindow = g_new0 (GstGLWindow, 1);
275
276  xwindow->width = width;
277  xwindow->height = height;
278  xwindow->internal = TRUE;
279
280  g_mutex_lock (glimagesink->x_lock);
281
282  /* create a color map */
283  cmap =
284      XCreateColormap (xcontext->disp, RootWindow (xcontext->disp,
285          xcontext->visualinfo->screen), xcontext->visualinfo->visual,
286      AllocNone);
287  xwindow->attr.colormap = cmap;
288  xwindow->attr.border_pixel = 0;
289
290#if 0
291  /* set sizes */
292  xwindow->x = 0;
293  xwindow->y = 0;
294  xwindow->width = 10;
295  xwindow->height = 10;
296
297  xwindow->rotX = 0;
298  xwindow->rotY = 0;
299  xwindow->zoom = 1;
300  xwindow->zoomdir = 0.01;
301#endif
302
303  xwindow->attr.event_mask =
304      ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask;
305
306  /* create a window in window mode */
307  xwindow->win = XCreateWindow (xcontext->disp, /*xcontext->root, */
308      RootWindow (xcontext->disp, xcontext->visualinfo->screen),
309      0, 0, xwindow->width, xwindow->height, 0, xcontext->visualinfo->depth,
310      InputOutput, xcontext->visualinfo->visual,
311      CWBorderPixel | CWColormap | CWEventMask, &xwindow->attr);
312
313  /* only set window title and handle wm_delete_events if in windowed mode */
314  wmDelete = XInternAtom (xcontext->disp, "WM_DELETE_WINDOW", True);
315  XSetWMProtocols (xcontext->disp, xwindow->win, &wmDelete, 1);
316  XSetStandardProperties (xcontext->disp, xwindow->win, "glsink",
317      "glsink", None, NULL, 0, NULL);
318
319#if 0
320  XSelectInput (xcontext->disp, xwindow->win,
321      ExposureMask | StructureNotifyMask);
322#else // we want more than that
323  XSelectInput (glimagesink->xcontext->disp, xwindow->win, ExposureMask |
324      StructureNotifyMask | PointerMotionMask | KeyPressMask |
325      KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
326#endif
327
328  //xwindow->win = XCreateSimpleWindow (glimagesink->xcontext->disp,
329  //    glimagesink->xcontext->root,
330  //    0, 0, xwindow->width, xwindow->height, 0, 0, glimagesink->xcontext->black);
331
332  XMapRaised (glimagesink->xcontext->disp, xwindow->win);
333
334  /* connect the glx-context to the window */
335  glXMakeCurrent (xcontext->disp, xwindow->win, xcontext->glx);
336
337  printf ("Initializing OpenGL parameters\n");
338  /* initialize OpenGL drawing */
339  glDisable (GL_DEPTH_TEST);
340  //glShadeModel(GL_SMOOTH);
341
342  glDisable (GL_TEXTURE_2D);
343  glDisable (GL_CULL_FACE);
344  glClearDepth (1.0f);
345  glClearColor (0, 0.5, 0, 1);
346
347  // both upload the video, and redraw the screen
348  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
349
350
351  //glEnable(GL_LIGHT0);                                        // Quick And Dirty Lighting (Assumes Light0 Is Set Up)
352  //glEnable(GL_LIGHTING);                                      // Enable Lighting
353  glDisable (GL_COLOR_MATERIAL);        // Enable Material Coloring
354  glEnable (GL_AUTO_NORMAL);    // let OpenGL generate the Normals
355
356  glDisable (GL_BLEND);
357
358  glPolygonMode (GL_FRONT, GL_FILL);
359  glPolygonMode (GL_BACK, GL_FILL);
360
361  glShadeModel (GL_SMOOTH);
362  glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
363
364  glBindTexture (GL_TEXTURE_2D, 1000);
365  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
366  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
367  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
368  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
369  glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
370  glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, TEX_XSIZE, TEX_YSIZE, 0, GL_RGBA,
371      GL_UNSIGNED_BYTE, NULL);
372
373  glXSwapBuffers (xcontext->disp, xwindow->win);
374
375  g_mutex_unlock (glimagesink->x_lock);
376
377  gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (glimagesink), xwindow->win);
378
379  return xwindow;
380}
381
382/* This function destroys a GstGLWindow */
383static void
384gst_glimagesink_xwindow_destroy (GstGLImageSink * glimagesink,
385    GstGLWindow * xwindow)
386{
387  GstXContext *xcontext = glimagesink->xcontext;
388
389  g_return_if_fail (xwindow != NULL);
390  g_return_if_fail (GST_IS_GLIMAGESINK (glimagesink));
391
392  g_mutex_lock (glimagesink->x_lock);
393
394  if (glimagesink->signal_handoffs) {
395    g_warning ("NOT DESTROYING any window due to signal_handoff !\n");
396    return;
397  }
398
399  if (xcontext->glx) {
400    if (!glXMakeCurrent (xcontext->disp, None, NULL)) {
401      printf ("Could not release drawing context.\n");
402    }
403    glXDestroyContext (xcontext->disp, xcontext->glx);
404    xcontext->glx = NULL;
405  }
406#if 0                           // not used: prepared for fs mode
407  /* switch back to original desktop resolution if we were in fs */
408  if (GLWin.fs) {
409    XF86VidModeSwitchToMode (GLWin.dpy, GLWin.screen, &GLWin.deskMode);
410    XF86VidModeSetViewPort (GLWin.dpy, GLWin.screen, 0, 0);
411  }
412#endif
413
414  /* If we did not create that window we just free the GC and let it live */
415  if (xwindow->internal)
416    XDestroyWindow (glimagesink->xcontext->disp, xwindow->win);
417  else
418    XSelectInput (glimagesink->xcontext->disp, xwindow->win, 0);
419
420  printf ("Check Xwindow destroy !\n");
421
422  g_mutex_unlock (glimagesink->x_lock);
423
424  g_free (xwindow);
425}
426
427/* This function resizes a GstGLWindow */
428static void
429gst_glimagesink_xwindow_resize (GstGLImageSink * glimagesink,
430    GstGLWindow * xwindow, guint width, guint height)
431{
432  g_return_if_fail (xwindow != NULL);
433  g_return_if_fail (GST_IS_GLIMAGESINK (glimagesink));
434
435  g_mutex_lock (glimagesink->x_lock);
436
437  xwindow->width = width;
438  xwindow->height = height;
439
440  XResizeWindow (glimagesink->xcontext->disp, xwindow->win,
441      xwindow->width, xwindow->height);
442
443  printf ("No xwindow resize implemented yet !\n");
444
445  g_mutex_unlock (glimagesink->x_lock);
446}
447
448static void
449gst_glimagesink_xwindow_update_geometry (GstGLImageSink * glimagesink,
450    GstGLWindow * xwindow)
451{
452  XWindowAttributes attr;
453
454  g_return_if_fail (xwindow != NULL);
455  g_return_if_fail (GST_IS_GLIMAGESINK (glimagesink));
456
457  /* Update the window geometry */
458  g_mutex_lock (glimagesink->x_lock);
459  XGetWindowAttributes (glimagesink->xcontext->disp,
460      glimagesink->window->win, &attr);
461  g_mutex_unlock (glimagesink->x_lock);
462
463  // FIXME: Need to introduce OpenGL setup here if PROJECTION_MATRIX depends on width/height
464  //printf("No update geometry implemented yet !\n");
465
466  glimagesink->window->width = attr.width;
467  glimagesink->window->height = attr.height;
468}
469
470#if 0
471static void
472gst_glimagesink_renegotiate_size (GstGLImageSink * glimagesink)
473{
474  g_return_if_fail (GST_IS_GLIMAGESINK (glimagesink));
475
476  if (!glimagesink->window)
477    return;
478
479  gst_glimagesink_xwindow_update_geometry (glimagesink, glimagesink->window);
480
481  if (glimagesink->window->width <= 1 || glimagesink->window->height <= 1)
482    return;
483
484  if (GST_PAD_IS_NEGOTIATING (GST_VIDEOSINK_PAD (glimagesink)) ||
485      !gst_pad_is_negotiated (GST_VIDEOSINK_PAD (glimagesink)))
486    return;
487
488  /* Window got resized or moved. We do caps negotiation again to get video
489     scaler to fit that new size only if size of the window differs from our
490     size. */
491
492  if (GST_VIDEOSINK_WIDTH (glimagesink) != glimagesink->window->width ||
493      GST_VIDEOSINK_HEIGHT (glimagesink) != glimagesink->window->height) {
494    GstPadLinkReturn r;
495
496    r = gst_pad_try_set_caps (GST_VIDEOSINK_PAD (glimagesink),
497        gst_caps_new_simple ("video/x-raw-rgb",
498            "bpp", G_TYPE_INT, glimagesink->xcontext->bpp,
499            "depth", G_TYPE_INT, glimagesink->xcontext->depth,
500            "endianness", G_TYPE_INT, glimagesink->xcontext->endianness,
501            "red_mask", G_TYPE_INT, glimagesink->xcontext->visual->red_mask,
502            "green_mask", G_TYPE_INT, glimagesink->xcontext->visual->green_mask,
503            "blue_mask", G_TYPE_INT, glimagesink->xcontext->visual->blue_mask,
504            "width", G_TYPE_INT, glimagesink->window->width,
505            "height", G_TYPE_INT, glimagesink->window->height,
506            "framerate", G_TYPE_DOUBLE, glimagesink->framerate, NULL));
507
508    if ((r == GST_PAD_LINK_OK) || (r == GST_PAD_LINK_DONE)) {
509      /* Renegotiation succeeded, we update our size and image */
510      GST_VIDEOSINK_WIDTH (glimagesink) = glimagesink->window->width;
511      GST_VIDEOSINK_HEIGHT (glimagesink) = glimagesink->window->height;
512
513      if ((glimagesink->glimage) &&
514          ((GST_VIDEOSINK_WIDTH (glimagesink) != glimagesink->glimage->width) ||
515              (GST_VIDEOSINK_HEIGHT (glimagesink) !=
516                  glimagesink->glimage->height))) {
517        /* We renew our ximage only if size changed */
518        gst_glimagesink_ximage_destroy (glimagesink, glimagesink->glimage);
519
520        glimagesink->glimage = gst_glimagesink_ximage_new (glimagesink,
521            GST_VIDEOSINK_WIDTH (glimagesink),
522            GST_VIDEOSINK_HEIGHT (glimagesink));
523      }
524    }
525  }
526}
527#endif
528
529/* This function handles XEvents that might be in the queue. It generates
530   GstEvent that will be sent upstream in the pipeline to handle interactivity
531   and navigation. It will also listen for configure events on the window to
532   trigger caps renegotiation so on the fly software scaling can work. */
533static void
534gst_glimagesink_handle_xevents (GstGLImageSink * glimagesink, GstPad * pad)
535{
536  XEvent e;
537
538  glimagesink->pointer_moved = FALSE;
539
540  g_return_if_fail (GST_IS_GLIMAGESINK (glimagesink));
541
542  //printf("handling xevents\n");
543
544  //gst_glimagesink_renegotiate_size (glimagesink);
545
546  /* Then we get all pointer motion events, only the last position is
547     interesting. */
548  g_mutex_lock (glimagesink->x_lock);
549  while (XCheckWindowEvent (glimagesink->xcontext->disp,
550          glimagesink->window->win, PointerMotionMask, &e)) {
551    g_mutex_unlock (glimagesink->x_lock);
552
553    switch (e.type) {
554      case MotionNotify:
555        glimagesink->pointer_x = e.xmotion.x;
556        glimagesink->pointer_y = e.xmotion.y;
557        glimagesink->pointer_moved = TRUE;
558        break;
559      default:
560        break;
561    }
562
563    g_mutex_lock (glimagesink->x_lock);
564  }
565  g_mutex_unlock (glimagesink->x_lock);
566
567  if (glimagesink->pointer_moved) {
568    GST_DEBUG ("glimagesink pointer moved over window at %d,%d",
569        glimagesink->pointer_x, glimagesink->pointer_y);
570    gst_navigation_send_mouse_event (GST_NAVIGATION (glimagesink),
571        "mouse-move", 0, glimagesink->pointer_x, glimagesink->pointer_y);
572  }
573
574  /* We get all remaining events on our window to throw them upstream */
575  g_mutex_lock (glimagesink->x_lock);
576  while (XCheckWindowEvent (glimagesink->xcontext->disp,
577          glimagesink->window->win,
578          KeyPressMask | KeyReleaseMask |
579          ButtonPressMask | ButtonReleaseMask, &e)) {
580    KeySym keysym;
581
582    /* We lock only for the X function call */
583    g_mutex_unlock (glimagesink->x_lock);
584
585    switch (e.type) {
586      case ButtonPress:
587        /* Mouse button pressed/released over our window. We send upstream
588           events for interactivity/navigation */
589        GST_DEBUG ("glimagesink button %d pressed over window at %d,%d",
590            e.xbutton.button, e.xbutton.x, e.xbutton.x);
591        glimagesink->pointer_button[e.xbutton.button - Button1] = TRUE;
592        gst_navigation_send_mouse_event (GST_NAVIGATION (glimagesink),
593            "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
594        break;
595      case ButtonRelease:
596        GST_DEBUG ("glimagesink button %d release over window at %d,%d",
597            e.xbutton.button, e.xbutton.x, e.xbutton.x);
598        glimagesink->pointer_button[e.xbutton.button - Button1] = FALSE;
599        gst_navigation_send_mouse_event (GST_NAVIGATION (glimagesink),
600            "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
601        break;
602      case KeyPress:
603      case KeyRelease:
604        /* Key pressed/released over our window. We send upstream
605           events for interactivity/navigation */
606        GST_DEBUG ("glimagesink key %d released over window at %d,%d",
607            e.xkey.keycode, e.xkey.x, e.xkey.x);
608        keysym = XKeycodeToKeysym (glimagesink->xcontext->disp,
609            e.xkey.keycode, 0);
610        if (keysym != NoSymbol) {
611          gst_navigation_send_key_event (GST_NAVIGATION (glimagesink),
612              e.type == KeyPress ?
613              "key-press" : "key-release", XKeysymToString (keysym));
614        } else {
615          gst_navigation_send_key_event (GST_NAVIGATION (glimagesink),
616              e.type == KeyPress ? "key-press" : "key-release", "unknown");
617        }
618        break;
619      default:
620        GST_DEBUG ("glimagesink unhandled X event (%d)", e.type);
621    }
622
623    g_mutex_lock (glimagesink->x_lock);
624  }
625  g_mutex_unlock (glimagesink->x_lock);
626}
627
628/* attributes for a single buffered visual in RGBA format with at least
629 * 4 bits per color and a 16 bit depth buffer */
630static int attrListSingle[] = {
631  GLX_RGBA,
632  GLX_RED_SIZE, 4,
633  GLX_GREEN_SIZE, 4,
634  GLX_BLUE_SIZE, 4,
635  GLX_DEPTH_SIZE, 16,
636  None
637};
638
639/* attributes for a double buffered visual in RGBA format with at least
640 * 4 bits per color and a 16 bit depth buffer */
641static int attrListDouble[] = {
642  GLX_RGBA, GLX_DOUBLEBUFFER,
643  GLX_RED_SIZE, 4,
644  GLX_GREEN_SIZE, 4,
645  GLX_BLUE_SIZE, 4,
646  GLX_DEPTH_SIZE, 16,
647  None
648};
649
650/* This function get the X Display and global infos about it. Everything is
651   stored in our object and will be cleaned when the object is finalized. Note
652   here that caps for supported format are generated without any window or
653   image creation */
654static GstXContext *
655gst_glimagesink_xcontext_get (GstGLImageSink * glimagesink)
656{
657  GstXContext *xcontext = NULL;
658  int glxMajorVersion, glxMinorVersion;
659  XPixmapFormatValues *px_formats = NULL;
660  gint nb_formats = 0, i;
661
662  printf ("Acquiring X context\n");
663
664  g_return_val_if_fail (GST_IS_GLIMAGESINK (glimagesink), NULL);
665
666  xcontext = g_new0 (GstXContext, 1);
667
668  g_mutex_lock (glimagesink->x_lock);
669
670  xcontext->disp = XOpenDisplay (glimagesink->display_name);
671
672  if (!xcontext->disp) {
673    g_mutex_unlock (glimagesink->x_lock);
674    g_free (xcontext);
675    GST_ELEMENT_ERROR (glimagesink, RESOURCE, TOO_LAZY, (NULL),
676        ("Could not open display"));
677    return NULL;
678  }
679
680  xcontext->screen_num = DefaultScreen (xcontext->disp);
681  xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
682
683  /* get an appropriate visual */
684  xcontext->visualinfo =
685      glXChooseVisual (xcontext->disp, xcontext->screen_num, attrListDouble);
686  if (xcontext->visualinfo == NULL) {
687    xcontext->visualinfo =
688        glXChooseVisual (xcontext->disp, xcontext->screen_num, attrListSingle);
689    GST_DEBUG ("Only Singlebuffered Visual!\n");
690
691    if (xcontext->visualinfo == NULL)
692      GST_ELEMENT_ERROR (glimagesink, RESOURCE, TOO_LAZY, (NULL),
693          ("Could not open GLX connection"));
694  } else {
695    GST_DEBUG ("Got Doublebuffered Visual!\n");
696  }
697  glXQueryVersion (xcontext->disp, &glxMajorVersion, &glxMinorVersion);
698  GST_DEBUG ("glX-Version %d.%d\n", glxMajorVersion, glxMinorVersion);
699
700  printf ("Creating GLX context\n");
701
702  /* create a GLX context */
703  xcontext->glx =
704      glXCreateContext (xcontext->disp, xcontext->visualinfo, 0, GL_TRUE);
705
706  if (glXIsDirect (xcontext->disp, xcontext->glx))
707    printf ("Congrats, you have Direct Rendering!\n");
708  else
709    printf ("Sorry, no Direct Rendering possible!\n");
710
711  xcontext->endianness =
712      (ImageByteOrder (xcontext->disp) ==
713      LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
714
715  xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
716  xcontext->root = DefaultRootWindow (xcontext->disp);
717
718#if 1
719
720  xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
721  xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
722  xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
723
724  /* We get supported pixmap formats at supported depth */
725  px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
726
727  if (!px_formats) {
728    XCloseDisplay (xcontext->disp);
729    g_mutex_unlock (glimagesink->x_lock);
730    g_free (xcontext);
731    return NULL;
732  }
733
734  /* We get bpp value corresponding to our running depth */
735  for (i = 0; i < nb_formats; i++) {
736    if (px_formats[i].depth == xcontext->depth)
737      xcontext->bpp = px_formats[i].bits_per_pixel;
738  }
739
740  XFree (px_formats);
741#endif
742
743  /* our caps system handles 24/32bpp RGB as big-endian. */
744  if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
745      xcontext->endianness == G_LITTLE_ENDIAN) {
746    xcontext->endianness = G_BIG_ENDIAN;
747    xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
748    xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
749    xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
750    if (xcontext->bpp == 24) {
751      xcontext->visual->red_mask >>= 8;
752      xcontext->visual->green_mask >>= 8;
753      xcontext->visual->blue_mask >>= 8;
754    }
755  }
756
757  xcontext->endianness = G_BIG_ENDIAN;
758  xcontext->visual->red_mask = 0xff0000;
759  xcontext->visual->green_mask = 0xff00;
760  xcontext->visual->blue_mask = 0xff;
761  xcontext->bpp = 24;
762  xcontext->depth = 24;
763
764  //char yuvformat[4] = {'Y', 'V', '1', '2'};
765
766  if (!glimagesink->signal_handoffs)
767    xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
768        "bpp", G_TYPE_INT, xcontext->bpp,
769        "depth", G_TYPE_INT, xcontext->depth,
770        "endianness", G_TYPE_INT, xcontext->endianness,
771        "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
772        "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
773        "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
774        "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
775        "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
776        "framerate", GST_TYPE_DOUBLE_RANGE, 1.0, 100.0, NULL);
777  else
778    xcontext->caps = gst_caps_new_simple ("video/x-raw-yuv",
779        //                                      "format", GST_TYPE_FOURCC, GST_PROPS_FOURCC (GST_STR_FOURCC ("YV12")),
780        "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
781        "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
782        "framerate", GST_TYPE_DOUBLE_RANGE, 1.0, 100.0, NULL);
783
784  g_mutex_unlock (glimagesink->x_lock);
785
786  return xcontext;
787}
788
789/* This function cleans the X context. Closing the Display and unrefing the
790   caps for supported formats. */
791static void
792gst_glimagesink_xcontext_clear (GstGLImageSink * glimagesink)
793{
794  g_return_if_fail (GST_IS_GLIMAGESINK (glimagesink));
795
796  gst_caps_free (glimagesink->xcontext->caps);
797
798  g_mutex_lock (glimagesink->x_lock);
799
800  XCloseDisplay (glimagesink->xcontext->disp);
801
802  g_mutex_unlock (glimagesink->x_lock);
803
804  glimagesink->xcontext = NULL;
805}
806
807static void
808gst_glimagesink_imagepool_clear (GstGLImageSink * glimagesink)
809{
810  g_mutex_lock (glimagesink->pool_lock);
811
812  while (glimagesink->image_pool) {
813    GstGLImage *ximage = glimagesink->image_pool->data;
814
815    glimagesink->image_pool = g_slist_delete_link (glimagesink->image_pool,
816        glimagesink->image_pool);
817    gst_glimagesink_ximage_destroy (glimagesink, ximage);
818  }
819
820  g_mutex_unlock (glimagesink->pool_lock);
821}
822
823/*
824=================
825Element stuff
826=================
827*/
828
829static GstCaps *
830gst_glimagesink_fixate (GstPad * pad, const GstCaps * caps)
831{
832  GstStructure *structure;
833  GstCaps *newcaps;
834
835  printf ("Linking the sink\n");
836
837  if (gst_caps_get_size (caps) > 1)
838    return NULL;
839
840  newcaps = gst_caps_copy (caps);
841  structure = gst_caps_get_structure (newcaps, 0);
842
843  if (gst_caps_structure_fixate_field_nearest_int (structure, "width", 320)) {
844    return newcaps;
845  }
846  if (gst_caps_structure_fixate_field_nearest_int (structure, "height", 240)) {
847    return newcaps;
848  }
849  if (gst_caps_structure_fixate_field_nearest_double (structure, "framerate",
850          30.0)) {
851    return newcaps;
852  }
853
854  gst_caps_free (newcaps);
855  return NULL;
856}
857
858static GstCaps *
859gst_glimagesink_getcaps (GstPad * pad)
860{
861  GstGLImageSink *glimagesink;
862
863  glimagesink = GST_GLIMAGESINK (gst_pad_get_parent (pad));
864
865  if (glimagesink->xcontext)
866    return gst_caps_copy (glimagesink->xcontext->caps);
867
868#if 0
869  if (!glimagesink->signal_handoffs)
870    return gst_caps_from_string ("video/x-raw-rgb, "
871        "framerate = (double) [ 1, 100 ], "
872        "width = (int) [ 0, MAX ], " "height = (int) [ 0, MAX ]");
873  else
874#endif
875    return gst_caps_from_string ("video/x-raw-yuv, "
876        "framerate = (double) [ 1, 100 ], "
877        "width = (int) [ 0, MAX ], " "height = (int) [ 0, MAX ]");
878}
879
880static GstPadLinkReturn
881gst_glimagesink_sink_link (GstPad * pad, const GstCaps * caps)
882{
883  GstGLImageSink *glimagesink;
884  gboolean ret;
885  GstStructure *structure;
886
887  glimagesink = GST_GLIMAGESINK (gst_pad_get_parent (pad));
888
889  if (!glimagesink->xcontext)
890    return GST_PAD_LINK_DELAYED;
891
892  GST_DEBUG_OBJECT (glimagesink,
893      "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
894      GST_PTR_FORMAT, glimagesink->xcontext->caps, caps);
895
896  structure = gst_caps_get_structure (caps, 0);
897  ret = gst_structure_get_int (structure, "width",
898      &(GST_VIDEOSINK_WIDTH (glimagesink)));
899  ret &= gst_structure_get_int (structure, "height",
900      &(GST_VIDEOSINK_HEIGHT (glimagesink)));
901  ret &= gst_structure_get_double (structure,
902      "framerate", &glimagesink->framerate);
903  if (!ret)
904    return GST_PAD_LINK_REFUSED;
905
906  glimagesink->pixel_width = 1;
907  gst_structure_get_int (structure, "pixel_width", &glimagesink->pixel_width);
908
909  glimagesink->pixel_height = 1;
910  gst_structure_get_int (structure, "pixel_height", &glimagesink->pixel_height);
911
912  /* Creating our window and our image */
913  if (!glimagesink->window)
914    glimagesink->window = gst_glimagesink_xwindow_new (glimagesink,
915        GST_VIDEOSINK_WIDTH (glimagesink), GST_VIDEOSINK_HEIGHT (glimagesink));
916  else {
917    if (glimagesink->window->internal)
918      gst_glimagesink_xwindow_resize (glimagesink, glimagesink->window,
919          GST_VIDEOSINK_WIDTH (glimagesink),
920          GST_VIDEOSINK_HEIGHT (glimagesink));
921  }
922
923  if ((glimagesink->glimage) && ((GST_VIDEOSINK_WIDTH (glimagesink) != glimagesink->glimage->width) || (GST_VIDEOSINK_HEIGHT (glimagesink) != glimagesink->glimage->height))) { /* We renew our ximage only if size changed */
924    gst_glimagesink_ximage_destroy (glimagesink, glimagesink->glimage);
925
926    glimagesink->glimage = gst_glimagesink_ximage_new (glimagesink,
927        GST_VIDEOSINK_WIDTH (glimagesink), GST_VIDEOSINK_HEIGHT (glimagesink));
928  } else if (!glimagesink->glimage)     /* If no ximage, creating one */
929    glimagesink->glimage = gst_glimagesink_ximage_new (glimagesink,
930        GST_VIDEOSINK_WIDTH (glimagesink), GST_VIDEOSINK_HEIGHT (glimagesink));
931
932  gst_x_overlay_got_desired_size (GST_X_OVERLAY (glimagesink),
933      GST_VIDEOSINK_WIDTH (glimagesink), GST_VIDEOSINK_HEIGHT (glimagesink));
934
935  return GST_PAD_LINK_OK;
936}
937
938static GstElementStateReturn
939gst_glimagesink_change_state (GstElement * element)
940{
941  GstGLImageSink *glimagesink;
942
943  printf ("change state\n");
944
945  glimagesink = GST_GLIMAGESINK (element);
946
947  switch (GST_STATE_TRANSITION (element)) {
948    case GST_STATE_NULL_TO_READY:
949      /* Initializing the XContext */
950      if (!glimagesink->xcontext) {
951        glimagesink->xcontext = gst_glimagesink_xcontext_get (glimagesink);
952        if (!glimagesink->xcontext)
953          return GST_STATE_FAILURE;
954      }
955      printf ("null to ready done\n");
956      break;
957    case GST_STATE_READY_TO_PAUSED:
958      printf ("ready to paused\n");
959      //if (glimagesink->window) // not needed with OpenGL
960      //  gst_glimagesink_xwindow_clear (glimagesink, glimagesink->window);
961      glimagesink->time = 0;
962      break;
963    case GST_STATE_PAUSED_TO_PLAYING:
964      break;
965    case GST_STATE_PLAYING_TO_PAUSED:
966      break;
967    case GST_STATE_PAUSED_TO_READY:
968      glimagesink->framerate = 0;
969      GST_VIDEOSINK_WIDTH (glimagesink) = 0;
970      GST_VIDEOSINK_HEIGHT (glimagesink) = 0;
971      break;
972    case GST_STATE_READY_TO_NULL:
973      if (glimagesink->glimage) {
974        gst_glimagesink_ximage_destroy (glimagesink, glimagesink->glimage);
975        glimagesink->glimage = NULL;
976      }
977
978      if (glimagesink->image_pool)
979        gst_glimagesink_imagepool_clear (glimagesink);
980
981      if (glimagesink->window) {
982        gst_glimagesink_xwindow_destroy (glimagesink, glimagesink->window);
983        glimagesink->window = NULL;
984      }
985
986      if (glimagesink->xcontext) {
987        gst_glimagesink_xcontext_clear (glimagesink);
988        glimagesink->xcontext = NULL;
989      }
990      break;
991  }
992
993  if (GST_ELEMENT_CLASS (parent_class)->change_state)
994    return GST_ELEMENT_CLASS (parent_class)->change_state (element);
995
996  return GST_STATE_SUCCESS;
997}
998
999static void
1000gst_glimagesink_chain (GstPad * pad, GstData * data)
1001{
1002  GstBuffer *buf = GST_BUFFER (data);
1003  GstGLImageSink *glimagesink;
1004
1005  //printf("CHAIN CALL\n");
1006
1007  g_return_if_fail (GST_IS_PAD (pad));
1008  g_return_if_fail (buf != NULL);
1009
1010  glimagesink = GST_GLIMAGESINK (gst_pad_get_parent (pad));
1011
1012  if (GST_IS_EVENT (data)) {
1013    gst_pad_event_default (pad, GST_EVENT (data));
1014    return;
1015  }
1016
1017  buf = GST_BUFFER (data);
1018  /* update time */
1019  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1020    glimagesink->time = GST_BUFFER_TIMESTAMP (buf);
1021  }
1022
1023  if (glimagesink->signal_handoffs)
1024    g_signal_emit (G_OBJECT (glimagesink),
1025        gst_glimagesink_signals[SIGNAL_HANDOFF], 0, buf, pad);
1026  else {
1027    /* If this buffer has been allocated using our buffer management we simply
1028       put the ximage which is in the PRIVATE pointer */
1029    if (GST_BUFFER_FREE_DATA_FUNC (buf) == gst_glimagesink_buffer_free)
1030      gst_glimagesink_ximage_put (glimagesink, GST_BUFFER_PRIVATE (buf));
1031    else {                      /* Else we have to copy the data into our private image, */
1032      /* if we have one... */
1033      printf ("Non-locally allocated: Sub-optimal buffer transfer!\n");
1034      if (glimagesink->glimage) {
1035#if 0
1036        memcpy (glimagesink->glimage->ximage->data,
1037            GST_BUFFER_DATA (buf),
1038            MIN (GST_BUFFER_SIZE (buf), glimagesink->glimage->size));
1039#endif
1040        gst_glimagesink_ximage_put (glimagesink, glimagesink->glimage);
1041      } else {                  /* No image available. Something went wrong during capsnego ! */
1042
1043        gst_buffer_unref (buf);
1044        GST_ELEMENT_ERROR (glimagesink, CORE, NEGOTIATION, (NULL),
1045            ("no format defined before chain function"));
1046        return;
1047      }
1048    }
1049  }
1050
1051  GST_DEBUG ("clock wait: %" GST_TIME_FORMAT,
1052      GST_TIME_ARGS (glimagesink->time));
1053
1054  /// ah, BTW, I think the gst_element_wait should happen _before_ the ximage is shown
1055  if (GST_VIDEOSINK_CLOCK (glimagesink))
1056    gst_element_wait (GST_ELEMENT (glimagesink), glimagesink->time);
1057
1058  /* set correct time for next buffer */
1059  if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf) && glimagesink->framerate > 0)
1060    glimagesink->time += GST_SECOND / glimagesink->framerate;
1061
1062  gst_buffer_unref (buf);
1063
1064  if (!glimagesink->signal_handoffs)
1065    gst_glimagesink_handle_xevents (glimagesink, pad);
1066}
1067
1068/* Buffer management */
1069
1070static void
1071gst_glimagesink_buffer_free (GstBuffer * buffer)
1072{
1073  GstGLImageSink *glimagesink;
1074  GstGLImage *ximage;
1075
1076  ximage = GST_BUFFER_PRIVATE (buffer);
1077
1078  g_assert (GST_IS_GLIMAGESINK (ximage->glimagesink));
1079  glimagesink = ximage->glimagesink;
1080
1081  /* If our geometry changed we can't reuse that image. */
1082  if ((ximage->width != GST_VIDEOSINK_WIDTH (glimagesink)) ||
1083      (ximage->height != GST_VIDEOSINK_HEIGHT (glimagesink)))
1084    gst_glimagesink_ximage_destroy (glimagesink, ximage);
1085  else {                        /* In that case we can reuse the image and add it to our image pool. */
1086
1087    g_mutex_lock (glimagesink->pool_lock);
1088    glimagesink->image_pool = g_slist_prepend (glimagesink->image_pool, ximage);
1089    g_mutex_unlock (glimagesink->pool_lock);
1090  }
1091}
1092
1093static GstBuffer *
1094gst_glimagesink_buffer_alloc (GstPad * pad, guint64 offset, guint size)
1095{
1096  GstGLImageSink *glimagesink;
1097  GstBuffer *buffer;
1098  GstGLImage *ximage = NULL;
1099  gboolean not_found = TRUE;
1100
1101  //printf("Allocating new data buffer\n");
1102
1103  glimagesink = GST_GLIMAGESINK (gst_pad_get_parent (pad));
1104
1105  g_mutex_lock (glimagesink->pool_lock);
1106
1107  /* Walking through the pool cleaning unsuable images and searching for a
1108     suitable one */
1109  while (not_found && glimagesink->image_pool) {
1110    ximage = glimagesink->image_pool->data;
1111
1112    if (ximage) {
1113      /* Removing from the pool */
1114      glimagesink->image_pool = g_slist_delete_link (glimagesink->image_pool,
1115          glimagesink->image_pool);
1116
1117      if ((ximage->width != GST_VIDEOSINK_WIDTH (glimagesink)) || (ximage->height != GST_VIDEOSINK_HEIGHT (glimagesink))) {     /* This image is unusable. Destroying... */
1118        gst_glimagesink_ximage_destroy (glimagesink, ximage);
1119        ximage = NULL;
1120      } else {                  /* We found a suitable image */
1121
1122        break;
1123      }
1124    }
1125  }
1126
1127  g_mutex_unlock (glimagesink->pool_lock);
1128
1129  if (!ximage) {                /* We found no suitable image in the pool. Creating... */
1130    ximage = gst_glimagesink_ximage_new (glimagesink,
1131        GST_VIDEOSINK_WIDTH (glimagesink), GST_VIDEOSINK_HEIGHT (glimagesink));
1132  }
1133
1134  if (ximage) {
1135    buffer = gst_buffer_new ();
1136
1137    /* Storing some pointers in the buffer */
1138    GST_BUFFER_PRIVATE (buffer) = ximage;
1139
1140    GST_BUFFER_DATA (buffer) = ximage->data;
1141    GST_BUFFER_FREE_DATA_FUNC (buffer) = gst_glimagesink_buffer_free;
1142    GST_BUFFER_SIZE (buffer) = ximage->size;
1143    return buffer;
1144  } else
1145    return NULL;
1146}
1147
1148/* Interfaces stuff */
1149
1150static gboolean
1151gst_glimagesink_interface_supported (GstImplementsInterface * iface, GType type)
1152{
1153  g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1154  return TRUE;
1155}
1156
1157static void
1158gst_glimagesink_interface_init (GstImplementsInterfaceClass * klass)
1159{
1160  klass->supported = gst_glimagesink_interface_supported;
1161}
1162
1163static void
1164gst_glimagesink_navigation_send_event (GstNavigation * navigation,
1165    GstStructure * structure)
1166{
1167  GstGLImageSink *glimagesink = GST_GLIMAGESINK (navigation);
1168  GstEvent *event;
1169  gint x_offset, y_offset;
1170  double x, y;
1171
1172  event = gst_event_new (GST_EVENT_NAVIGATION);
1173  event->event_data.structure.structure = structure;
1174
1175  /* We are not converting the pointer coordinates as there's no hardware
1176     scaling done here. The only possible scaling is done by videoscale and
1177     videoscale will have to catch those events and tranform the coordinates
1178     to match the applied scaling. So here we just add the offset if the image
1179     is centered in the window.  */
1180
1181  x_offset = glimagesink->window->width - GST_VIDEOSINK_WIDTH (glimagesink);
1182  y_offset = glimagesink->window->height - GST_VIDEOSINK_HEIGHT (glimagesink);
1183
1184  if (gst_structure_get_double (structure, "pointer_x", &x)) {
1185    x += x_offset;
1186    gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1187  }
1188  if (gst_structure_get_double (structure, "pointer_y", &y)) {
1189    y += y_offset;
1190    gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1191  }
1192
1193  gst_pad_send_event (gst_pad_get_peer (GST_VIDEOSINK_PAD (glimagesink)),
1194      event);
1195}
1196
1197static void
1198gst_glimagesink_navigation_init (GstNavigationInterface * iface)
1199{
1200  iface->send_event = gst_glimagesink_navigation_send_event;
1201}
1202
1203static void
1204gst_glimagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
1205{
1206  GstGLImageSink *glimagesink = GST_GLIMAGESINK (overlay);
1207  GstGLWindow *xwindow = NULL;
1208  XWindowAttributes attr;
1209
1210  printf ("set_xwindow_id\n");
1211
1212  g_return_if_fail (GST_IS_GLIMAGESINK (glimagesink));
1213
1214  /* If we already use that window return */
1215  if (glimagesink->window && (xwindow_id == glimagesink->window->win))
1216    return;
1217
1218  /* If the element has not initialized the X11 context try to do so */
1219  if (!glimagesink->xcontext)
1220    glimagesink->xcontext = gst_glimagesink_xcontext_get (glimagesink);
1221
1222  if (!glimagesink->xcontext) {
1223    g_warning ("glimagesink was unable to obtain the X11 context.");
1224    return;
1225  }
1226
1227  /* Clear image pool as the images are unusable anyway */
1228  gst_glimagesink_imagepool_clear (glimagesink);
1229
1230  /* Clear the ximage */
1231  if (glimagesink->glimage) {
1232    gst_glimagesink_ximage_destroy (glimagesink, glimagesink->glimage);
1233    glimagesink->glimage = NULL;
1234  }
1235
1236  /* If a window is there already we destroy it */
1237  if (glimagesink->window) {
1238    gst_glimagesink_xwindow_destroy (glimagesink, glimagesink->window);
1239    glimagesink->window = NULL;
1240  }
1241
1242  /* If the xid is 0 we go back to an internal window */
1243  if (xwindow_id == 0) {
1244    /* If no width/height caps nego did not happen window will be created
1245       during caps nego then */
1246    if (GST_VIDEOSINK_WIDTH (glimagesink) && GST_VIDEOSINK_HEIGHT (glimagesink)) {
1247      xwindow = gst_glimagesink_xwindow_new (glimagesink,
1248          GST_VIDEOSINK_WIDTH (glimagesink),
1249          GST_VIDEOSINK_HEIGHT (glimagesink));
1250    }
1251  } else {
1252    GST_ELEMENT_ERROR (glimagesink, RESOURCE, TOO_LAZY, (NULL),
1253        ("glimagesink is incapable of connecting to other X windows !"));
1254    exit (100);
1255
1256    xwindow = g_new0 (GstGLWindow, 1);
1257
1258    xwindow->win = xwindow_id;
1259
1260    /* We get window geometry, set the event we want to receive,
1261       and create a GC */
1262    g_mutex_lock (glimagesink->x_lock);
1263    XGetWindowAttributes (glimagesink->xcontext->disp, xwindow->win, &attr);
1264    xwindow->width = attr.width;
1265    xwindow->height = attr.height;
1266    xwindow->internal = FALSE;
1267    XSelectInput (glimagesink->xcontext->disp, xwindow->win, ExposureMask |
1268        StructureNotifyMask | PointerMotionMask | KeyPressMask |
1269        KeyReleaseMask);
1270
1271    //xwindow->gc = XCreateGC (glimagesink->xcontext->disp, xwindow->win, 0, NULL);
1272    g_mutex_unlock (glimagesink->x_lock);
1273
1274    /* If that new window geometry differs from our one we try to
1275       renegotiate caps */
1276    if (gst_pad_is_negotiated (GST_VIDEOSINK_PAD (glimagesink)) &&
1277        (xwindow->width != GST_VIDEOSINK_WIDTH (glimagesink) ||
1278            xwindow->height != GST_VIDEOSINK_HEIGHT (glimagesink))) {
1279      GstPadLinkReturn r;
1280
1281      r = gst_pad_try_set_caps (GST_VIDEOSINK_PAD (glimagesink),
1282          gst_caps_new_simple ("video/x-raw-rgb",
1283              "bpp", G_TYPE_INT, glimagesink->xcontext->bpp,
1284              "depth", G_TYPE_INT, glimagesink->xcontext->depth,
1285              "endianness", G_TYPE_INT, glimagesink->xcontext->endianness,
1286              "red_mask", G_TYPE_INT, glimagesink->xcontext->visual->red_mask,
1287              "green_mask", G_TYPE_INT,
1288              glimagesink->xcontext->visual->green_mask, "blue_mask",
1289              G_TYPE_INT, glimagesink->xcontext->visual->blue_mask, "width",
1290              G_TYPE_INT, xwindow->width, "height", G_TYPE_INT, xwindow->height,
1291              "framerate", G_TYPE_DOUBLE, glimagesink->framerate, NULL));
1292
1293      /* If caps nego succeded updating our size */
1294      if ((r == GST_PAD_LINK_OK) || (r == GST_PAD_LINK_DONE)) {
1295        GST_VIDEOSINK_WIDTH (glimagesink) = xwindow->width;
1296        GST_VIDEOSINK_HEIGHT (glimagesink) = xwindow->height;
1297      }
1298    }
1299  }
1300
1301  /* Recreating our ximage */
1302  if (!glimagesink->glimage &&
1303      GST_VIDEOSINK_WIDTH (glimagesink) && GST_VIDEOSINK_HEIGHT (glimagesink)) {
1304    glimagesink->glimage = gst_glimagesink_ximage_new (glimagesink,
1305        GST_VIDEOSINK_WIDTH (glimagesink), GST_VIDEOSINK_HEIGHT (glimagesink));
1306  }
1307
1308  if (xwindow)
1309    glimagesink->window = xwindow;
1310}
1311
1312static void
1313gst_glimagesink_get_desired_size (GstXOverlay * overlay,
1314    guint * width, guint * height)
1315{
1316  GstGLImageSink *glimagesink = GST_GLIMAGESINK (overlay);
1317
1318  *width = GST_VIDEOSINK_WIDTH (glimagesink);
1319  *height = GST_VIDEOSINK_HEIGHT (glimagesink);
1320}
1321
1322static void
1323gst_glimagesink_expose (GstXOverlay * overlay)
1324{
1325  GstGLImageSink *glimagesink = GST_GLIMAGESINK (overlay);
1326
1327  if (!glimagesink->window)
1328    return;
1329
1330  gst_glimagesink_xwindow_update_geometry (glimagesink, glimagesink->window);
1331
1332  /* We don't act on internal window from outside that could cause some thread
1333     race with the video sink own thread checking for configure event */
1334  if (glimagesink->window->internal)
1335    return;
1336
1337  //gst_glimagesink_xwindow_clear (glimagesink, glimagesink->window);
1338
1339  if (glimagesink->cur_image)
1340    gst_glimagesink_ximage_put (glimagesink, glimagesink->cur_image);
1341}
1342
1343static void
1344gst_glimagesink_xoverlay_init (GstXOverlayClass * iface)
1345{
1346  iface->set_xwindow_id = gst_glimagesink_set_xwindow_id;
1347  iface->get_desired_size = gst_glimagesink_get_desired_size;
1348  iface->expose = gst_glimagesink_expose;
1349}
1350
1351/* =========================================== */
1352/*                                             */
1353/*              Init & Class init              */
1354/*                                             */
1355/* =========================================== */
1356
1357static void
1358gst_glimagesink_set_property (GObject * object, guint prop_id,
1359    const GValue * value, GParamSpec * pspec)
1360{
1361  GstGLImageSink *glimagesink;
1362
1363  g_return_if_fail (GST_IS_GLIMAGESINK (object));
1364
1365  glimagesink = GST_GLIMAGESINK (object);
1366
1367  switch (prop_id) {
1368    case ARG_DISPLAY:
1369      glimagesink->display_name = g_strdup (g_value_get_string (value));
1370      break;
1371    case ARG_SYNCHRONOUS:
1372      glimagesink->synchronous = g_value_get_boolean (value);
1373      if (glimagesink->xcontext) {
1374        XSynchronize (glimagesink->xcontext->disp, glimagesink->synchronous);
1375    case ARG_SIGNAL_HANDOFFS:
1376        glimagesink->signal_handoffs = g_value_get_boolean (value);
1377        break;
1378      }
1379      break;
1380    default:
1381      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1382      break;
1383  }
1384}
1385
1386static void
1387gst_glimagesink_get_property (GObject * object, guint prop_id,
1388    GValue * value, GParamSpec * pspec)
1389{
1390  GstGLImageSink *glimagesink;
1391
1392  g_return_if_fail (GST_IS_GLIMAGESINK (object));
1393
1394  glimagesink = GST_GLIMAGESINK (object);
1395
1396  switch (prop_id) {
1397    case ARG_DISPLAY:
1398      g_value_set_string (value, g_strdup (glimagesink->display_name));
1399      break;
1400    case ARG_SYNCHRONOUS:
1401      g_value_set_boolean (value, glimagesink->synchronous);
1402      break;
1403    case ARG_SIGNAL_HANDOFFS:
1404      g_value_set_boolean (value, glimagesink->signal_handoffs);
1405      break;
1406    default:
1407      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1408      break;
1409  }
1410}
1411
1412static void
1413gst_glimagesink_finalize (GObject * object)
1414{
1415  GstGLImageSink *glimagesink;
1416
1417  glimagesink = GST_GLIMAGESINK (object);
1418
1419  if (glimagesink->display_name) {
1420    g_free (glimagesink->display_name);
1421    glimagesink->display_name = NULL;
1422  }
1423
1424  g_mutex_free (glimagesink->x_lock);
1425  g_mutex_free (glimagesink->pool_lock);
1426
1427  G_OBJECT_CLASS (parent_class)->finalize (object);
1428}
1429
1430static void
1431gst_glimagesink_init (GstGLImageSink * glimagesink)
1432{
1433  GST_VIDEOSINK_PAD (glimagesink) =
1434      gst_pad_new_from_template (gst_static_pad_template_get
1435      (&gst_glimagesink_sink_template_factory), "sink");
1436
1437  gst_element_add_pad (GST_ELEMENT (glimagesink),
1438      GST_VIDEOSINK_PAD (glimagesink));
1439
1440  gst_pad_set_chain_function (GST_VIDEOSINK_PAD (glimagesink),
1441      gst_glimagesink_chain);
1442  gst_pad_set_link_function (GST_VIDEOSINK_PAD (glimagesink),
1443      gst_glimagesink_sink_link);
1444  gst_pad_set_getcaps_function (GST_VIDEOSINK_PAD (glimagesink),
1445      gst_glimagesink_getcaps);
1446  gst_pad_set_fixate_function (GST_VIDEOSINK_PAD (glimagesink),
1447      gst_glimagesink_fixate);
1448  gst_pad_set_bufferalloc_function (GST_VIDEOSINK_PAD (glimagesink),
1449      gst_glimagesink_buffer_alloc);
1450
1451  glimagesink->display_name = NULL;
1452  glimagesink->xcontext = NULL;
1453  glimagesink->window = NULL;
1454  glimagesink->glimage = NULL;
1455  glimagesink->cur_image = NULL;
1456
1457  glimagesink->framerate = 0;
1458
1459  glimagesink->x_lock = g_mutex_new ();
1460
1461  glimagesink->pixel_width = glimagesink->pixel_height = 1;
1462
1463  glimagesink->image_pool = NULL;
1464  glimagesink->pool_lock = g_mutex_new ();
1465
1466  glimagesink->synchronous = FALSE;
1467  glimagesink->signal_handoffs = FALSE;
1468
1469  GST_FLAG_SET (glimagesink, GST_ELEMENT_THREAD_SUGGESTED);
1470  GST_FLAG_SET (glimagesink, GST_ELEMENT_EVENT_AWARE);
1471}
1472
1473static void
1474gst_glimagesink_base_init (gpointer g_class)
1475{
1476  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1477
1478  gst_element_class_set_details (element_class, &gst_glimagesink_details);
1479
1480  gst_element_class_add_pad_template (element_class,
1481      gst_static_pad_template_get (&gst_glimagesink_sink_template_factory));
1482}
1483
1484static void
1485gst_glimagesink_class_init (GstGLImageSinkClass * klass)
1486{
1487  GObjectClass *gobject_class;
1488  GstElementClass *gstelement_class;
1489
1490  gobject_class = (GObjectClass *) klass;
1491  gstelement_class = (GstElementClass *) klass;
1492
1493  parent_class = g_type_class_ref (GST_TYPE_VIDEOSINK);
1494
1495  g_object_class_install_property (gobject_class, ARG_DISPLAY,
1496      g_param_spec_string ("display", "Display", "X Display name",
1497          NULL, G_PARAM_READWRITE));
1498  g_object_class_install_property (gobject_class, ARG_SYNCHRONOUS,
1499      g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
1500          "the X display in synchronous mode. (used only for debugging)", FALSE,
1501          G_PARAM_READWRITE));
1502  g_object_class_install_property (gobject_class, ARG_SIGNAL_HANDOFFS,
1503      g_param_spec_boolean ("signal-handoffs", "Signal handoffs",
1504          "Send a signal before unreffing the buffer, forces YUV, no GL output",
1505          FALSE, G_PARAM_READWRITE));
1506#if 0                           // needed ?
1507  g_object_class_install_property (gobject_class, ARG_SIGNAL_BUFFER_ALLOC,
1508      g_param_spec_boolean ("signal-bufferalloc", "Signal buffer allocation",
1509          "Asks the application for a buffer allocation", FALSE,
1510          G_PARAM_READWRITE));
1511#endif
1512
1513  gst_glimagesink_signals[SIGNAL_HANDOFF] =
1514      g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
1515      G_STRUCT_OFFSET (GstGLImageSinkClass, handoff), NULL, NULL,
1516      gst_marshal_VOID__POINTER_OBJECT, G_TYPE_NONE, 2,
1517      GST_TYPE_BUFFER, GST_TYPE_PAD);
1518  gst_glimagesink_signals[SIGNAL_BUFALLOC] =
1519      g_signal_new ("bufferalloc", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
1520      G_STRUCT_OFFSET (GstGLImageSinkClass, bufferalloc), NULL, NULL,
1521      gst_marshal_VOID__POINTER_OBJECT, G_TYPE_NONE, 2,
1522      GST_TYPE_BUFFER, GST_TYPE_PAD);
1523
1524  gobject_class->finalize = gst_glimagesink_finalize;
1525  gobject_class->set_property = gst_glimagesink_set_property;
1526  gobject_class->get_property = gst_glimagesink_get_property;
1527
1528  gstelement_class->change_state = gst_glimagesink_change_state;
1529}
1530
1531/* ============================================================= */
1532/*                                                               */
1533/*                       Public Methods                          */
1534/*                                                               */
1535/* ============================================================= */
1536
1537/* =========================================== */
1538/*                                             */
1539/*          Object typing & Creation           */
1540/*                                             */
1541/* =========================================== */
1542
1543GType
1544gst_glimagesink_get_type (void)
1545{
1546  static GType glimagesink_type = 0;
1547
1548  if (!glimagesink_type) {
1549    static const GTypeInfo glimagesink_info = {
1550      sizeof (GstGLImageSinkClass),
1551      gst_glimagesink_base_init,
1552      NULL,
1553      (GClassInitFunc) gst_glimagesink_class_init,
1554      NULL,
1555      NULL,
1556      sizeof (GstGLImageSink),
1557      0,
1558      (GInstanceInitFunc) gst_glimagesink_init,
1559    };
1560    static const GInterfaceInfo iface_info = {
1561      (GInterfaceInitFunc) gst_glimagesink_interface_init,
1562      NULL,
1563      NULL,
1564    };
1565    static const GInterfaceInfo navigation_info = {
1566      (GInterfaceInitFunc) gst_glimagesink_navigation_init,
1567      NULL,
1568      NULL,
1569    };
1570    static const GInterfaceInfo overlay_info = {
1571      (GInterfaceInitFunc) gst_glimagesink_xoverlay_init,
1572      NULL,
1573      NULL,
1574    };
1575
1576    glimagesink_type = g_type_register_static (GST_TYPE_VIDEOSINK,
1577        "GstGLImageSink", &glimagesink_info, 0);
1578
1579    g_type_add_interface_static (glimagesink_type,
1580        GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
1581    g_type_add_interface_static (glimagesink_type, GST_TYPE_NAVIGATION,
1582        &navigation_info);
1583    g_type_add_interface_static (glimagesink_type, GST_TYPE_X_OVERLAY,
1584        &overlay_info);
1585  }
1586
1587  return glimagesink_type;
1588}
1589
1590static gboolean
1591plugin_init (GstPlugin * plugin)
1592{
1593  /* Loading the library containing GstVideoSink, our parent object */
1594  if (!gst_library_load ("gstvideo"))
1595    return FALSE;
1596
1597  if (!gst_element_register (plugin, "glimagesink",
1598          GST_RANK_SECONDARY, GST_TYPE_GLIMAGESINK))
1599    return FALSE;
1600
1601  GST_DEBUG_CATEGORY_INIT (gst_debug_glimagesink, "glimagesink", 0,
1602      "glimagesink element");
1603
1604  return TRUE;
1605}
1606
1607GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1608    GST_VERSION_MINOR,
1609    "glimagesink",
1610    "OpenGL video output plugin based on OpenGL 1.2 calls",
1611    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)
Note: See TracBrowser for help on using the repository browser.