b0963cbdbf5c018aa06c4d695bb9d923af650d72
[platform/upstream/gstreamer.git] / sys / pvr2d / gstpvrvideosink.c
1 /* GStreamer
2  *
3  * Copyright (C) 2011 Collabora Ltda
4  * Copyright (C) 2011 Texas Instruments
5  *  @author: Luciana Fujii Pontello <luciana.fujii@collabora.co.uk>
6  *  @author: Edward Hervey <edward@collabora.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /* Object header */
25 #include "gstpvrvideosink.h"
26 #include "gstpvrbufferpool.h"
27
28 #include <gst/video/gstvideosink.h>
29 #include <gst/video/videooverlay.h>
30 /* Helper functions */
31 #include <gst/video/gstvideometa.h>
32
33 /* Debugging category */
34 #include <gst/gstinfo.h>
35
36 #define LINUX
37 #include <dri2_ws.h>
38 #include <services.h>
39 #include <img_defs.h>
40 #include <servicesext.h>
41
42 #define DEFAULT_QUEUE_SIZE 12
43
44 GST_DEBUG_CATEGORY_EXTERN (gst_debug_pvrvideosink);
45 #define GST_CAT_DEFAULT gst_debug_pvrvideosink
46
47 #define PVR2DMEMINFO_INITIALISE(d, s) \
48 { \
49   (d)->hPrivateData = (IMG_VOID *)(s); \
50   (d)->hPrivateMapData = (IMG_VOID *)(s->hKernelMemInfo); \
51   (d)->ui32DevAddr = (IMG_UINT32) (s)->sDevVAddr.uiAddr; \
52   (d)->ui32MemSize = (s)->uAllocSize; \
53   (d)->pBase = (s)->pvLinAddr;\
54   (d)->ulFlags = (s)->ui32Flags;\
55 }
56
57 /* end of internal definitions */
58
59 static void gst_pvrvideosink_reset (GstPVRVideoSink * pvrvideosink);
60 static void gst_pvrvideosink_xwindow_draw_borders (GstPVRVideoSink *
61     pvrvideosink, GstXWindow * xwindow, GstVideoRectangle rect);
62 static void gst_pvrvideosink_expose (GstVideoOverlay * overlay);
63 static void gst_pvrvideosink_xwindow_destroy (GstPVRVideoSink * pvrvideosink,
64     GstXWindow * xwindow);
65 static void gst_pvrvideosink_dcontext_free (GstDrawContext * dcontext);
66
67 static void gst_pvrvideosink_videooverlay_init (GstVideoOverlayInterface *
68     iface);
69
70 #define gst_pvrvideosink_parent_class parent_class
71 G_DEFINE_TYPE_WITH_CODE (GstPVRVideoSink, gst_pvrvideosink, GST_TYPE_VIDEO_SINK,
72     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
73         gst_pvrvideosink_videooverlay_init));
74
75
76 static GstStaticPadTemplate gst_pvrvideosink_sink_template_factory =
77 GST_STATIC_PAD_TEMPLATE ("sink",
78     GST_PAD_SINK,
79     GST_PAD_ALWAYS,
80     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("NV12")));
81
82 enum
83 {
84   PROP_0,
85   PROP_FORCE_ASPECT_RATIO,
86   PROP_WINDOW_WIDTH,
87   PROP_WINDOW_HEIGHT
88 };
89
90 /* ============================================================= */
91 /*                                                               */
92 /*                       Private Methods                         */
93 /*                                                               */
94 /* ============================================================= */
95
96 /* pvrvideo buffers */
97
98 static void
99 gst_pvrvideosink_xwindow_update_geometry (GstPVRVideoSink * pvrvideosink)
100 {
101   XWindowAttributes attr;
102   WSEGLError glerror;
103   WSEGLDrawableParams source_params;
104   PVRSRV_CLIENT_MEM_INFO *client_mem_info;
105
106   /* Update the window geometry */
107   g_mutex_lock (pvrvideosink->dcontext->x_lock);
108   if (G_UNLIKELY (pvrvideosink->xwindow == NULL)) {
109     g_mutex_unlock (pvrvideosink->dcontext->x_lock);
110     return;
111   }
112   pvrvideosink->redraw_borders = TRUE;
113
114   XGetWindowAttributes (pvrvideosink->dcontext->x_display,
115       pvrvideosink->xwindow->window, &attr);
116
117   pvrvideosink->xwindow->width = attr.width;
118   pvrvideosink->xwindow->height = attr.height;
119
120   if (!pvrvideosink->have_render_rect) {
121     pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0;
122     pvrvideosink->render_rect.w = attr.width;
123     pvrvideosink->render_rect.h = attr.height;
124   }
125   if (pvrvideosink->dcontext != NULL) {
126     glerror =
127         pvrvideosink->dcontext->wsegl_table->
128         pfnWSEGL_DeleteDrawable (pvrvideosink->dcontext->drawable_handle);
129     if (glerror != WSEGL_SUCCESS) {
130       GST_ERROR_OBJECT (pvrvideosink, "Error destroying drawable");
131       return;
132     }
133     glerror =
134         pvrvideosink->dcontext->wsegl_table->
135         pfnWSEGL_CreateWindowDrawable (pvrvideosink->dcontext->display_handle,
136         pvrvideosink->dcontext->glconfig,
137         &pvrvideosink->dcontext->drawable_handle,
138         (NativeWindowType) pvrvideosink->xwindow->window,
139         &pvrvideosink->dcontext->rotation);
140     if (glerror != WSEGL_SUCCESS) {
141       GST_ERROR_OBJECT (pvrvideosink, "Error creating drawable");
142       return;
143     }
144     glerror =
145         pvrvideosink->dcontext->wsegl_table->
146         pfnWSEGL_GetDrawableParameters (pvrvideosink->dcontext->drawable_handle,
147         &source_params, &pvrvideosink->render_params);
148     if (glerror != WSEGL_SUCCESS) {
149       GST_ERROR_OBJECT (pvrvideosink, "Error getting Drawable params");
150       return;
151     }
152
153     client_mem_info =
154         (PVRSRV_CLIENT_MEM_INFO *) pvrvideosink->render_params.hPrivateData;
155     PVR2DMEMINFO_INITIALISE (&pvrvideosink->dcontext->dst_mem, client_mem_info);
156   }
157
158   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
159 }
160
161 /* This function handles XEvents that might be in the queue. It generates
162    GstEvent that will be sent upstream in the pipeline to handle interactivity
163    and navigation. It will also listen for configure events on the window to
164    trigger caps renegotiation so on the fly software scaling can work. */
165 static void
166 gst_pvrvideosink_handle_xevents (GstPVRVideoSink * pvrvideosink)
167 {
168   XEvent e;
169   gboolean exposed = FALSE;
170   gboolean configured = FALSE;
171
172   g_mutex_lock (pvrvideosink->flow_lock);
173   g_mutex_lock (pvrvideosink->dcontext->x_lock);
174
175   /* Handle Expose */
176   while (XCheckWindowEvent (pvrvideosink->dcontext->x_display,
177           pvrvideosink->xwindow->window, ExposureMask | StructureNotifyMask,
178           &e)) {
179     switch (e.type) {
180       case Expose:
181         exposed = TRUE;
182         break;
183       case ConfigureNotify:
184         g_mutex_unlock (pvrvideosink->dcontext->x_lock);
185         gst_pvrvideosink_xwindow_update_geometry (pvrvideosink);
186         g_mutex_lock (pvrvideosink->dcontext->x_lock);
187         configured = TRUE;
188         break;
189       default:
190         break;
191     }
192   }
193
194   if (exposed || configured) {
195     g_mutex_unlock (pvrvideosink->dcontext->x_lock);
196     g_mutex_unlock (pvrvideosink->flow_lock);
197
198     gst_pvrvideosink_expose (GST_VIDEO_OVERLAY (pvrvideosink));
199
200     g_mutex_lock (pvrvideosink->flow_lock);
201     g_mutex_lock (pvrvideosink->dcontext->x_lock);
202   }
203
204   /* Handle Display events */
205   while (XPending (pvrvideosink->dcontext->x_display)) {
206     XNextEvent (pvrvideosink->dcontext->x_display, &e);
207
208     switch (e.type) {
209       case ClientMessage:{
210         Atom wm_delete;
211
212         wm_delete = XInternAtom (pvrvideosink->dcontext->x_display,
213             "WM_DELETE_WINDOW", True);
214         if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
215           /* Handle window deletion by posting an error on the bus */
216           GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, NOT_FOUND,
217               ("Output window was closed"), (NULL));
218
219           g_mutex_unlock (pvrvideosink->dcontext->x_lock);
220           gst_pvrvideosink_xwindow_destroy (pvrvideosink,
221               pvrvideosink->xwindow);
222           pvrvideosink->xwindow = NULL;
223           g_mutex_lock (pvrvideosink->dcontext->x_lock);
224         }
225         break;
226       }
227       default:
228         break;
229     }
230   }
231
232   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
233   g_mutex_unlock (pvrvideosink->flow_lock);
234 }
235
236 static gpointer
237 gst_pvrvideosink_event_thread (GstPVRVideoSink * pvrvideosink)
238 {
239   GST_OBJECT_LOCK (pvrvideosink);
240   while (pvrvideosink->running) {
241     GST_OBJECT_UNLOCK (pvrvideosink);
242
243     if (pvrvideosink->xwindow) {
244       gst_pvrvideosink_handle_xevents (pvrvideosink);
245     }
246     g_usleep (G_USEC_PER_SEC / 20);
247
248     GST_OBJECT_LOCK (pvrvideosink);
249   }
250   GST_OBJECT_UNLOCK (pvrvideosink);
251
252   return NULL;
253 }
254
255 static void
256 gst_pvrvideosink_manage_event_thread (GstPVRVideoSink * pvrvideosink)
257 {
258   GThread *thread = NULL;
259
260   /* don't start the thread too early */
261   if (pvrvideosink->dcontext == NULL) {
262     return;
263   }
264
265   GST_OBJECT_LOCK (pvrvideosink);
266   if (!pvrvideosink->event_thread) {
267     /* Setup our event listening thread */
268     GST_DEBUG_OBJECT (pvrvideosink, "run xevent thread");
269     pvrvideosink->running = TRUE;
270     pvrvideosink->event_thread = g_thread_create (
271         (GThreadFunc) gst_pvrvideosink_event_thread, pvrvideosink, TRUE, NULL);
272   }
273   GST_OBJECT_UNLOCK (pvrvideosink);
274
275   /* Wait for our event thread to finish */
276   if (thread)
277     g_thread_join (thread);
278 }
279
280
281 static GstDrawContext *
282 gst_pvrvideosink_get_dcontext (GstPVRVideoSink * pvrvideosink)
283 {
284   GstDrawContext *dcontext = NULL;
285   PVR2DERROR pvr_error;
286   gint refresh_rate;
287   DRI2WSDisplay *displayImpl;
288   WSEGLError glerror;
289   const WSEGLCaps *glcaps;
290
291   GST_DEBUG_OBJECT (pvrvideosink, "Getting draw context");
292
293   dcontext = g_new0 (GstDrawContext, 1);
294   dcontext->x_lock = g_mutex_new ();
295
296   dcontext->p_blt_info = g_new0 (PVR2D_3DBLT_EXT, 1);
297   if (!dcontext->p_blt_info)
298     goto p_blt_info_alloc_failed;
299
300   dcontext->p_blt2d_info = g_new0 (PVR2DBLTINFO, 1);
301
302   GST_LOG_OBJECT (pvrvideosink, "Opening X Display");
303   dcontext->x_display = XOpenDisplay (NULL);
304
305   if (dcontext->x_display == NULL)
306     goto fail_open_display;
307
308   GST_LOG_OBJECT (pvrvideosink, "WSEGL_GetFunctionTablePointer()");
309   dcontext->wsegl_table = WSEGL_GetFunctionTablePointer ();
310
311   GST_LOG_OBJECT (pvrvideosink, "pfnWSEGL_IsDisplayValid()");
312   glerror = dcontext->wsegl_table->pfnWSEGL_IsDisplayValid (
313       (NativeDisplayType) dcontext->x_display);
314
315   if (glerror != WSEGL_SUCCESS)
316     goto display_invalid;
317
318   GST_LOG_OBJECT (pvrvideosink, "pfnWSEGL_InitialiseDisplay()");
319
320   glerror = dcontext->wsegl_table->pfnWSEGL_InitialiseDisplay (
321       (NativeDisplayType) dcontext->x_display, &dcontext->display_handle,
322       &glcaps, &dcontext->glconfig);
323   if (glerror != WSEGL_SUCCESS)
324     goto display_init_failed;
325
326   displayImpl = (DRI2WSDisplay *) dcontext->display_handle;
327   dcontext->pvr_context = displayImpl->hContext;
328
329   GST_LOG_OBJECT (pvrvideosink, "PVR2DGetScreenMode()");
330
331   pvr_error = PVR2DGetScreenMode (dcontext->pvr_context,
332       &dcontext->display_format, &dcontext->display_width,
333       &dcontext->display_height, &dcontext->stride, &refresh_rate);
334   if (pvr_error != PVR2D_OK)
335     goto screen_mode_failed;
336
337   GST_DEBUG_OBJECT (pvrvideosink,
338       "Got format:%d, width:%d, height:%d, stride:%d, refresh_rate:%d",
339       dcontext->display_format, dcontext->display_width,
340       dcontext->display_height, dcontext->stride, refresh_rate);
341
342   dcontext->screen_num = DefaultScreen (dcontext->x_display);
343   dcontext->black = XBlackPixel (dcontext->x_display, dcontext->screen_num);
344
345   GST_DEBUG_OBJECT (pvrvideosink, "Returning dcontext %p", dcontext);
346
347   return dcontext;
348
349 p_blt_info_alloc_failed:
350   {
351     GST_ERROR_OBJECT (pvrvideosink, "Alloc of bltinfo failed");
352     gst_pvrvideosink_dcontext_free (dcontext);
353     return NULL;
354   }
355
356 fail_open_display:
357   {
358     GST_ERROR_OBJECT (pvrvideosink, "Failed to open X Display");
359     gst_pvrvideosink_dcontext_free (dcontext);
360     return NULL;
361   }
362
363 display_invalid:
364   {
365     GST_ERROR_OBJECT (pvrvideosink, "Display is not valid (glerror:%d)",
366         glerror);
367     gst_pvrvideosink_dcontext_free (dcontext);
368     return NULL;
369   }
370
371 display_init_failed:
372   {
373     GST_ERROR_OBJECT (pvrvideosink, "Error initializing display (glerror:%d)",
374         glerror);
375     gst_pvrvideosink_dcontext_free (dcontext);
376     return NULL;
377   }
378
379 screen_mode_failed:
380   {
381     GST_ERROR_OBJECT (pvrvideosink, "Failed to get screen mode. error : %s",
382         gst_pvr2d_error_get_string (pvr_error));
383     gst_pvrvideosink_dcontext_free (dcontext);
384     return NULL;
385   }
386 }
387
388 static void
389 gst_pvrvideosink_xwindow_set_title (GstPVRVideoSink * pvrvideosink,
390     GstXWindow * xwindow, const gchar * media_title)
391 {
392   if (media_title) {
393     g_free (pvrvideosink->media_title);
394     pvrvideosink->media_title = g_strdup (media_title);
395   }
396   if (xwindow) {
397     /* we have a window */
398     if (xwindow->internal) {
399       XTextProperty xproperty;
400       const gchar *app_name;
401       const gchar *title = NULL;
402       gchar *title_mem = NULL;
403
404       /* set application name as a title */
405       app_name = g_get_application_name ();
406
407       if (app_name && pvrvideosink->media_title) {
408         title = title_mem = g_strconcat (pvrvideosink->media_title, " : ",
409             app_name, NULL);
410       } else if (app_name) {
411         title = app_name;
412       } else if (pvrvideosink->media_title) {
413         title = pvrvideosink->media_title;
414       }
415
416       if (title) {
417         if ((XStringListToTextProperty (((char **) &title), 1,
418                     &xproperty)) != 0) {
419           XSetWMName (pvrvideosink->dcontext->x_display, xwindow->window,
420               &xproperty);
421           XFree (xproperty.value);
422         }
423
424         g_free (title_mem);
425       }
426     }
427   }
428 }
429
430 static GstXWindow *
431 gst_pvrvideosink_create_window (GstPVRVideoSink * pvrvideosink, gint width,
432     gint height)
433 {
434   WSEGLError glerror;
435   WSEGLDrawableParams source_params;
436   Window root;
437   GstXWindow *xwindow;
438   GstDrawContext *dcontext;
439   XGCValues values;
440   Atom wm_delete;
441   PVRSRV_CLIENT_MEM_INFO *client_mem_info;
442
443   GST_DEBUG_OBJECT (pvrvideosink, "begin");
444
445   dcontext = pvrvideosink->dcontext;
446   xwindow = g_new0 (GstXWindow, 1);
447
448   xwindow->internal = TRUE;
449   pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0;
450   pvrvideosink->render_rect.w = width;
451   pvrvideosink->render_rect.h = height;
452   xwindow->width = width;
453   xwindow->height = height;
454   xwindow->internal = TRUE;
455
456   g_mutex_lock (pvrvideosink->dcontext->x_lock);
457
458   root = DefaultRootWindow (dcontext->x_display);
459   xwindow->window = XCreateSimpleWindow (dcontext->x_display, root, 0, 0,
460       width, height, 2, 2, pvrvideosink->dcontext->black);
461   XSelectInput (dcontext->x_display, xwindow->window,
462       ExposureMask | StructureNotifyMask);
463
464   /* Tell the window manager we'd like delete client messages instead of
465    * being killed */
466   wm_delete = XInternAtom (pvrvideosink->dcontext->x_display,
467       "WM_DELETE_WINDOW", True);
468   if (wm_delete != None) {
469     (void) XSetWMProtocols (pvrvideosink->dcontext->x_display, xwindow->window,
470         &wm_delete, 1);
471   }
472
473   XMapWindow (dcontext->x_display, xwindow->window);
474
475   /* We have to do that to prevent X from redrawing the background on
476    * ConfigureNotify. This takes away flickering of video when resizing. */
477   XSetWindowBackgroundPixmap (pvrvideosink->dcontext->x_display,
478       xwindow->window, None);
479
480   gst_pvrvideosink_xwindow_set_title (pvrvideosink, xwindow, NULL);
481
482   xwindow->gc = XCreateGC (pvrvideosink->dcontext->x_display,
483       xwindow->window, 0, &values);
484
485   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
486
487   glerror =
488       dcontext->wsegl_table->
489       pfnWSEGL_CreateWindowDrawable (dcontext->display_handle,
490       dcontext->glconfig, &(dcontext->drawable_handle),
491       (NativeWindowType) xwindow->window, &(dcontext->rotation));
492
493   if (glerror != WSEGL_SUCCESS) {
494     GST_ERROR_OBJECT (pvrvideosink, "Error creating drawable");
495     return NULL;
496   }
497   glerror =
498       dcontext->wsegl_table->
499       pfnWSEGL_GetDrawableParameters (dcontext->drawable_handle, &source_params,
500       &pvrvideosink->render_params);
501   client_mem_info =
502       (PVRSRV_CLIENT_MEM_INFO *) pvrvideosink->render_params.hPrivateData;
503   PVR2DMEMINFO_INITIALISE (&dcontext->dst_mem, client_mem_info);
504
505   GST_DEBUG_OBJECT (pvrvideosink, "end");
506   return xwindow;
507 }
508
509 static void
510 gst_pvrvideosink_blit (GstPVRVideoSink * pvrvideosink, GstBuffer * buffer)
511 {
512   PVR2DERROR pvr_error;
513   GstDrawContext *dcontext = pvrvideosink->dcontext;
514   gint video_width;
515   gint video_height;
516   gboolean draw_border = FALSE;
517   PPVR2D_3DBLT_EXT p_blt_3d;
518   PVR2DMEMINFO *src_mem;
519   PVR2DFORMAT pvr_format;
520   GstVideoRectangle result;
521   GstPVRMeta *meta;
522   GstVideoCropMeta *cropmeta;
523
524   GST_DEBUG_OBJECT (pvrvideosink, "buffer %p", buffer);
525
526   pvr_format =
527       GST_VIDEO_INFO_FORMAT (&pvrvideosink->info) ==
528       GST_VIDEO_FORMAT_NV12 ? PVR2D_YUV420_2PLANE : PVR2D_ARGB8888;
529
530   g_mutex_lock (pvrvideosink->flow_lock);
531   if (buffer == NULL)
532     buffer = pvrvideosink->current_buffer;
533
534   if (buffer == NULL)
535     goto done;
536
537   meta = gst_buffer_get_pvr_meta (buffer);
538   if (G_UNLIKELY (meta == NULL))
539     goto no_pvr_meta;
540
541   src_mem = meta->src_mem;
542   p_blt_3d = dcontext->p_blt_info;
543
544   video_width = GST_VIDEO_SINK_WIDTH (pvrvideosink);
545   video_height = GST_VIDEO_SINK_HEIGHT (pvrvideosink);
546
547   g_mutex_lock (pvrvideosink->dcontext->x_lock);
548
549   /* Draw borders when displaying the first frame. After this
550      draw borders only on expose event or after a size change. */
551   if (!(pvrvideosink->current_buffer) || pvrvideosink->redraw_borders) {
552     draw_border = TRUE;
553   }
554
555   /* Store a reference to the last image we put, lose the previous one */
556   if (buffer && pvrvideosink->current_buffer != buffer) {
557     if (pvrvideosink->current_buffer) {
558       GST_LOG_OBJECT (pvrvideosink, "unreffing %p",
559           pvrvideosink->current_buffer);
560       gst_buffer_unref (GST_BUFFER_CAST (pvrvideosink->current_buffer));
561     }
562     GST_LOG_OBJECT (pvrvideosink, "reffing %p as our current buffer", buffer);
563     pvrvideosink->current_buffer = gst_buffer_ref (buffer);
564   }
565
566   if (pvrvideosink->keep_aspect) {
567     GstVideoRectangle src, dst;
568
569     src.w = GST_VIDEO_SINK_WIDTH (pvrvideosink);
570     src.h = GST_VIDEO_SINK_HEIGHT (pvrvideosink);
571     dst.w = pvrvideosink->render_rect.w;
572     dst.h = pvrvideosink->render_rect.h;
573     gst_video_sink_center_rect (src, dst, &result, TRUE);
574     result.x += pvrvideosink->render_rect.x;
575     result.y += pvrvideosink->render_rect.y;
576   } else {
577     memcpy (&result, &pvrvideosink->render_rect, sizeof (GstVideoRectangle));
578   }
579
580   p_blt_3d->sDst.pSurfMemInfo = &dcontext->dst_mem;
581   p_blt_3d->sDst.SurfOffset = 0;
582   p_blt_3d->sDst.Stride = 4 * pvrvideosink->render_params.ui32Stride;
583   p_blt_3d->sDst.Format = PVR2D_ARGB8888;
584   p_blt_3d->sDst.SurfWidth = pvrvideosink->xwindow->width;
585   p_blt_3d->sDst.SurfHeight = pvrvideosink->xwindow->height;
586
587   p_blt_3d->rcDest.left = result.x;
588   p_blt_3d->rcDest.top = result.y;
589   p_blt_3d->rcDest.right = result.w + result.x;
590   p_blt_3d->rcDest.bottom = result.h + result.y;
591
592   p_blt_3d->sSrc.pSurfMemInfo = src_mem;
593   p_blt_3d->sSrc.SurfOffset = 0;
594   p_blt_3d->sSrc.Stride = GST_VIDEO_INFO_COMP_STRIDE (&pvrvideosink->info, 0);
595   p_blt_3d->sSrc.Format = pvr_format;
596   p_blt_3d->sSrc.SurfWidth = video_width;
597   p_blt_3d->sSrc.SurfHeight = video_height;
598
599   /* If buffer has crop information, use that */
600   if ((cropmeta = gst_buffer_get_video_crop_meta (buffer))) {
601     p_blt_3d->rcSource.left = cropmeta->x;
602     p_blt_3d->rcSource.top = cropmeta->y;
603     p_blt_3d->rcSource.right = cropmeta->x + cropmeta->width;
604     p_blt_3d->rcSource.bottom = cropmeta->y + cropmeta->height;
605   } else {
606     p_blt_3d->rcSource.left = 0;
607     p_blt_3d->rcSource.top = 0;
608     p_blt_3d->rcSource.right = video_width;
609     p_blt_3d->rcSource.bottom = video_height;
610   }
611
612   p_blt_3d->hUseCode = NULL;
613
614   if (GST_VIDEO_INFO_FORMAT (&pvrvideosink->info) == GST_VIDEO_FORMAT_NV12)
615     p_blt_3d->bDisableDestInput = TRUE;
616   else
617     /* blit fails for RGB without this... not sure why yet... */
618     p_blt_3d->bDisableDestInput = FALSE;
619
620   GST_DEBUG_OBJECT (pvrvideosink, "about to blit");
621
622   pvr_error = PVR2DBlt3DExt (pvrvideosink->dcontext->pvr_context,
623       dcontext->p_blt_info);
624
625   if (pvr_error != PVR2D_OK) {
626     GST_ERROR_OBJECT (pvrvideosink, "Failed to blit. Error : %s",
627         gst_pvr2d_error_get_string (pvr_error));
628     goto done;
629   }
630   dcontext->wsegl_table->pfnWSEGL_SwapDrawable (dcontext->drawable_handle, 1);
631
632   if (draw_border) {
633     gst_pvrvideosink_xwindow_draw_borders (pvrvideosink, pvrvideosink->xwindow,
634         result);
635     pvrvideosink->redraw_borders = FALSE;
636   }
637   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
638
639 done:
640   GST_DEBUG_OBJECT (pvrvideosink, "end");
641   g_mutex_unlock (pvrvideosink->flow_lock);
642   return;
643
644   /* Error cases */
645
646 no_pvr_meta:
647   {
648     g_mutex_unlock (pvrvideosink->flow_lock);
649     GST_ERROR_OBJECT (pvrvideosink, "Got a buffer without GstPVRMeta");
650     return;
651   }
652 }
653
654 static void
655 gst_pvrvideosink_destroy_drawable (GstPVRVideoSink * pvrvideosink)
656 {
657   GST_DEBUG_OBJECT (pvrvideosink, "dcontext : %p", pvrvideosink->dcontext);
658
659   if (pvrvideosink->dcontext != NULL) {
660     if (pvrvideosink->dcontext->drawable_handle) {
661       GST_DEBUG_OBJECT (pvrvideosink, "Deleting Drawable (drawable_handle:%p)",
662           pvrvideosink->dcontext->drawable_handle);
663       pvrvideosink->dcontext->wsegl_table->
664           pfnWSEGL_DeleteDrawable (pvrvideosink->dcontext->drawable_handle);
665     }
666
667     GST_DEBUG_OBJECT (pvrvideosink, "Closing display (display_handle:%p)",
668         pvrvideosink->dcontext->display_handle);
669     pvrvideosink->dcontext->wsegl_table->
670         pfnWSEGL_CloseDisplay (pvrvideosink->dcontext->display_handle);
671   }
672 }
673
674 /* We are called with the x_lock taken */
675 static void
676 gst_pvrvideosink_pvrfill_rectangle (GstPVRVideoSink * pvrvideosink,
677     GstVideoRectangle rect)
678 {
679   PVR2DERROR pvr_error;
680   PPVR2DBLTINFO p_blt2d_info = 0;
681   GstDrawContext *dcontext = pvrvideosink->dcontext;
682
683   GST_DEBUG_OBJECT (pvrvideosink, "begin");
684
685   p_blt2d_info = dcontext->p_blt2d_info;
686
687   p_blt2d_info->pDstMemInfo = &dcontext->dst_mem;
688   p_blt2d_info->BlitFlags = PVR2D_BLIT_DISABLE_ALL;
689   p_blt2d_info->DstOffset = 0;
690   p_blt2d_info->CopyCode = PVR2DROPclear;
691   p_blt2d_info->DstStride = 4 * pvrvideosink->render_params.ui32Stride;
692   p_blt2d_info->DstFormat = PVR2D_ARGB8888;
693   p_blt2d_info->DstSurfWidth = pvrvideosink->xwindow->width;
694   p_blt2d_info->DstSurfHeight = pvrvideosink->xwindow->height;
695   p_blt2d_info->DstX = rect.x;
696   p_blt2d_info->DstY = rect.y;
697   p_blt2d_info->DSizeX = rect.w;
698   p_blt2d_info->DSizeY = rect.h;
699
700   pvr_error = PVR2DBlt (pvrvideosink->dcontext->pvr_context, p_blt2d_info);
701
702   if (pvr_error != PVR2D_OK) {
703     GST_ERROR_OBJECT (pvrvideosink, "Failed to blit. Error : %s",
704         gst_pvr2d_error_get_string (pvr_error));
705     goto done;
706   }
707   dcontext->wsegl_table->pfnWSEGL_SwapDrawable (dcontext->drawable_handle, 1);
708
709 done:
710   GST_DEBUG_OBJECT (pvrvideosink, "end");
711 }
712
713 /* We are called with the x_lock taken */
714 static void
715 gst_pvrvideosink_xwindow_draw_borders (GstPVRVideoSink * pvrvideosink,
716     GstXWindow * xwindow, GstVideoRectangle rect)
717 {
718   gint t1, t2;
719   GstVideoRectangle result;
720
721   g_return_if_fail (GST_IS_PVRVIDEOSINK (pvrvideosink));
722   g_return_if_fail (xwindow != NULL);
723
724   /* Left border */
725   result.x = pvrvideosink->render_rect.x;
726   result.y = pvrvideosink->render_rect.y;
727   result.w = rect.x - pvrvideosink->render_rect.x;
728   result.h = pvrvideosink->render_rect.h;
729   if (rect.x > pvrvideosink->render_rect.x)
730     gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result);
731
732   /* Right border */
733   t1 = rect.x + rect.w;
734   t2 = pvrvideosink->render_rect.x + pvrvideosink->render_rect.w;
735   result.x = t1;
736   result.y = pvrvideosink->render_rect.y;
737   result.w = t2 - t1;
738   result.h = pvrvideosink->render_rect.h;
739   if (t1 < t2)
740     gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result);
741
742   /* Top border */
743   result.x = pvrvideosink->render_rect.x;
744   result.y = pvrvideosink->render_rect.y;
745   result.w = pvrvideosink->render_rect.w;
746   result.h = rect.y - pvrvideosink->render_rect.y;
747   if (rect.y > pvrvideosink->render_rect.y)
748     gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result);
749
750   /* Bottom border */
751   t1 = rect.y + rect.h;
752   t2 = pvrvideosink->render_rect.y + pvrvideosink->render_rect.h;
753   result.x = pvrvideosink->render_rect.x;
754   result.y = t1;
755   result.w = pvrvideosink->render_rect.w;
756   result.h = t2 - t1;
757   if (t1 < t2)
758     gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result);
759 }
760
761 /* Element stuff */
762
763 static gboolean
764 gst_pvrvideosink_setcaps (GstBaseSink * bsink, GstCaps * caps)
765 {
766   GstPVRVideoSink *pvrvideosink;
767   GstVideoInfo info;
768   GstStructure *structure;
769   GstBufferPool *oldpool, *newpool;
770
771   pvrvideosink = GST_PVRVIDEOSINK (bsink);
772
773   GST_DEBUG_OBJECT (pvrvideosink,
774       "sinkconnect possible caps with given caps %" GST_PTR_FORMAT, caps);
775
776   if (!gst_video_info_from_caps (&info, caps))
777     goto invalid_format;
778
779   GST_VIDEO_SINK_WIDTH (pvrvideosink) = info.width;
780   GST_VIDEO_SINK_HEIGHT (pvrvideosink) = info.height;
781
782   /* Notify application to set xwindow id now */
783   g_mutex_lock (pvrvideosink->flow_lock);
784   if (!pvrvideosink->xwindow) {
785     g_mutex_unlock (pvrvideosink->flow_lock);
786     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (pvrvideosink));
787   } else {
788     g_mutex_unlock (pvrvideosink->flow_lock);
789   }
790
791   g_mutex_lock (pvrvideosink->flow_lock);
792   if (!pvrvideosink->xwindow)
793     pvrvideosink->xwindow = gst_pvrvideosink_create_window (pvrvideosink,
794         GST_VIDEO_SINK_WIDTH (pvrvideosink),
795         GST_VIDEO_SINK_HEIGHT (pvrvideosink));
796   g_mutex_unlock (pvrvideosink->flow_lock);
797
798   pvrvideosink->info = info;
799
800   /* After a resize, we want to redraw the borders in case the new frame size
801    * doesn't cover the same area */
802   pvrvideosink->redraw_borders = TRUE;
803
804   /* create a new pool for the new configuration */
805   newpool = gst_pvr_buffer_pool_new (GST_ELEMENT_CAST (pvrvideosink));
806
807   /* PVR needs at least 3 buffers */
808   structure = gst_buffer_pool_get_config (newpool);
809   gst_buffer_pool_config_set (structure, caps, GST_VIDEO_INFO_SIZE (&info), 3,
810       0, 0, 15);
811   if (!gst_buffer_pool_set_config (newpool, structure))
812     goto config_failed;
813
814   oldpool = pvrvideosink->pool;
815   pvrvideosink->pool = newpool;
816   g_mutex_unlock (pvrvideosink->flow_lock);
817
818   /* unref the old sink */
819   if (oldpool) {
820     /* we don't deactivate, some elements might still be using it, it will
821      * be deactivated when the last ref is gone */
822     gst_object_unref (oldpool);
823   }
824
825   return TRUE;
826
827 config_failed:
828   {
829     GST_ERROR_OBJECT (pvrvideosink, "failed to set config.");
830     g_mutex_unlock (pvrvideosink->flow_lock);
831     return FALSE;
832   }
833
834 invalid_format:
835   {
836     GST_DEBUG_OBJECT (pvrvideosink,
837         "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
838     return FALSE;
839   }
840 }
841
842 static GstCaps *
843 gst_pvrvideosink_getcaps (GstBaseSink * bsink, GstCaps * filter)
844 {
845   GstPVRVideoSink *pvrvideosink;
846   GstCaps *caps;
847
848   GST_DEBUG_OBJECT (bsink, "filter:%" GST_PTR_FORMAT, filter);
849
850   pvrvideosink = GST_PVRVIDEOSINK (bsink);
851
852   /* FIXME : If we have curently configured caps, we should return those
853    * intersected with the filter*/
854
855   caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (pvrvideosink)->sinkpad);
856   if (filter) {
857     GstCaps *intersection;
858
859     intersection =
860         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
861     gst_caps_unref (caps);
862     caps = intersection;
863   }
864
865   GST_DEBUG_OBJECT (bsink, "Returning %" GST_PTR_FORMAT, caps);
866
867   return caps;
868 }
869
870 static GstStateChangeReturn
871 gst_pvrvideosink_change_state (GstElement * element, GstStateChange transition)
872 {
873   GstPVRVideoSink *pvrvideosink;
874   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
875   GstDrawContext *dcontext;
876
877   pvrvideosink = GST_PVRVIDEOSINK (element);
878
879   switch (transition) {
880     case GST_STATE_CHANGE_NULL_TO_READY:
881       if (pvrvideosink->dcontext == NULL) {
882         dcontext = gst_pvrvideosink_get_dcontext (pvrvideosink);
883         if (dcontext == NULL)
884           return GST_STATE_CHANGE_FAILURE;
885         GST_OBJECT_LOCK (pvrvideosink);
886         pvrvideosink->dcontext = dcontext;
887         GST_OBJECT_UNLOCK (pvrvideosink);
888       }
889       gst_pvrvideosink_manage_event_thread (pvrvideosink);
890       break;
891     case GST_STATE_CHANGE_READY_TO_PAUSED:
892       break;
893     case GST_STATE_CHANGE_PAUSED_TO_READY:
894       break;
895     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
896       break;
897     default:
898       break;
899   }
900
901   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
902
903   switch (transition) {
904     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
905       break;
906     case GST_STATE_CHANGE_PAUSED_TO_READY:
907       GST_VIDEO_SINK_WIDTH (pvrvideosink) = 0;
908       GST_VIDEO_SINK_HEIGHT (pvrvideosink) = 0;
909       break;
910     case GST_STATE_CHANGE_READY_TO_NULL:
911       gst_pvrvideosink_reset (pvrvideosink);
912       break;
913     default:
914       break;
915   }
916
917   return ret;
918 }
919
920 static void
921 gst_pvrvideosink_get_times (GstBaseSink * bsink, GstBuffer * buf,
922     GstClockTime * start, GstClockTime * end)
923 {
924   GstPVRVideoSink *pvrvideosink;
925
926   pvrvideosink = GST_PVRVIDEOSINK (bsink);
927
928   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
929     *start = GST_BUFFER_TIMESTAMP (buf);
930     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
931       *end = *start + GST_BUFFER_DURATION (buf);
932     } else {
933       gint fps_n, fps_d;
934       fps_n = GST_VIDEO_INFO_FPS_N (&pvrvideosink->info);
935       fps_d = GST_VIDEO_INFO_FPS_D (&pvrvideosink->info);
936       if (fps_n > 0) {
937         *end = *start + gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
938       }
939     }
940   }
941 }
942
943 static GstFlowReturn
944 gst_pvrvideosink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
945 {
946   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (vsink);
947   GstPVRMeta *meta;
948
949   GST_DEBUG_OBJECT (pvrvideosink, "render buffer: %p", buf);
950
951   meta = gst_buffer_get_pvr_meta (buf);
952
953   if (G_UNLIKELY (meta == NULL)) {
954     meta = gst_buffer_add_pvr_meta (buf, GST_ELEMENT_CAST (pvrvideosink));
955     if (meta == NULL)
956       goto meta_failure;
957   }
958
959   gst_pvrvideosink_blit (pvrvideosink, buf);
960
961   return GST_FLOW_OK;
962
963 meta_failure:
964   {
965     GST_WARNING_OBJECT (pvrvideosink, "Failed to map incoming buffer");
966     return GST_FLOW_ERROR;
967   }
968 }
969
970 static gboolean
971 gst_pvrvideosink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
972 {
973   GstPVRVideoSink *pvrvideosink = (GstPVRVideoSink *) bsink;
974   GstBufferPool *pool;
975   GstStructure *config;
976   GstCaps *caps;
977   guint size;
978   gboolean need_pool;
979
980   gst_query_parse_allocation (query, &caps, &need_pool);
981
982   if (caps == NULL)
983     goto no_caps;
984
985   g_mutex_lock (pvrvideosink->flow_lock);
986   if ((pool = pvrvideosink->pool))
987     gst_object_ref (pool);
988   g_mutex_unlock (pvrvideosink->flow_lock);
989
990   if (pool != NULL) {
991     const GstCaps *pcaps;
992
993     /* we had a pool, check caps */
994     GST_DEBUG_OBJECT (pvrvideosink, "check existing pool caps");
995     config = gst_buffer_pool_get_config (pool);
996     gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL);
997
998     if (!gst_caps_is_equal (caps, pcaps)) {
999       GST_DEBUG_OBJECT (pvrvideosink, "pool has different caps");
1000       /* different caps, we can't use this pool */
1001       gst_object_unref (pool);
1002       pool = NULL;
1003     }
1004   }
1005
1006   if (pool == NULL && need_pool) {
1007     GstVideoInfo info;
1008
1009     GST_DEBUG_OBJECT (pvrvideosink, "create new pool");
1010     pool = gst_pvr_buffer_pool_new (GST_ELEMENT_CAST (pvrvideosink));
1011
1012     if (!gst_video_info_from_caps (&info, caps))
1013       goto invalid_caps;
1014
1015     /* the normal size of a frame */
1016     size = info.size;
1017
1018     config = gst_buffer_pool_get_config (pool);
1019     gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0);
1020     if (!gst_buffer_pool_set_config (pool, config))
1021       goto config_failed;
1022   }
1023   /* we need at least 3 buffers */
1024   gst_query_set_allocation_params (query, size, 3, 0, 0, 0, pool);
1025
1026   /* we also support various metadata */
1027   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API);
1028
1029   gst_object_unref (pool);
1030
1031   return TRUE;
1032
1033   /* ERRORS */
1034 no_caps:
1035   {
1036     GST_DEBUG_OBJECT (bsink, "no caps specified");
1037     return FALSE;
1038   }
1039 invalid_caps:
1040   {
1041     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1042     return FALSE;
1043   }
1044 config_failed:
1045   {
1046     GST_DEBUG_OBJECT (bsink, "failed setting config");
1047     return FALSE;
1048   }
1049 }
1050
1051 /* Interfaces stuff */
1052
1053 /* This function destroys a GstXWindow */
1054 static void
1055 gst_pvrvideosink_xwindow_destroy (GstPVRVideoSink * pvrvideosink,
1056     GstXWindow * xwindow)
1057 {
1058   g_return_if_fail (xwindow != NULL);
1059
1060   g_mutex_lock (pvrvideosink->dcontext->x_lock);
1061
1062   /* If we did not create that window we just free the GC and let it live */
1063   if (xwindow->internal)
1064     XDestroyWindow (pvrvideosink->dcontext->x_display, xwindow->window);
1065   else
1066     XSelectInput (pvrvideosink->dcontext->x_display, xwindow->window, 0);
1067
1068   XFreeGC (pvrvideosink->dcontext->x_display, xwindow->gc);
1069
1070   XSync (pvrvideosink->dcontext->x_display, FALSE);
1071
1072   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
1073
1074   g_free (xwindow);
1075 }
1076
1077 static void
1078 gst_pvrvideosink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1079 {
1080   XID window_handle = id;
1081   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay);
1082   GstXWindow *xwindow = NULL;
1083
1084   g_return_if_fail (GST_IS_PVRVIDEOSINK (pvrvideosink));
1085
1086   g_mutex_lock (pvrvideosink->flow_lock);
1087
1088   /* If we already use that window return */
1089   if (pvrvideosink->xwindow && (window_handle == pvrvideosink->xwindow->window)) {
1090     g_mutex_unlock (pvrvideosink->flow_lock);
1091     return;
1092   }
1093
1094   /* If the element has not initialized the X11 context try to do so */
1095   if (!pvrvideosink->dcontext && !(pvrvideosink->dcontext =
1096           gst_pvrvideosink_get_dcontext (pvrvideosink))) {
1097     g_mutex_unlock (pvrvideosink->flow_lock);
1098     /* we have thrown a GST_ELEMENT_ERROR now */
1099     return;
1100   }
1101
1102   /* If a window is there already we destroy it */
1103   if (pvrvideosink->xwindow) {
1104     gst_pvrvideosink_xwindow_destroy (pvrvideosink, pvrvideosink->xwindow);
1105     pvrvideosink->xwindow = NULL;
1106   }
1107
1108   /* If the xid is 0 we will create an internal one in buffer_alloc */
1109   if (window_handle != 0) {
1110     XWindowAttributes attr;
1111     WSEGLError glerror;
1112     WSEGLDrawableParams source_params;
1113     PVRSRV_CLIENT_MEM_INFO *client_mem_info;
1114
1115     xwindow = g_new0 (GstXWindow, 1);
1116     xwindow->window = window_handle;
1117
1118     /* Set the event we want to receive and create a GC */
1119     g_mutex_lock (pvrvideosink->dcontext->x_lock);
1120
1121     XGetWindowAttributes (pvrvideosink->dcontext->x_display, xwindow->window,
1122         &attr);
1123
1124     xwindow->width = attr.width;
1125     xwindow->height = attr.height;
1126     xwindow->internal = FALSE;
1127     if (!pvrvideosink->have_render_rect) {
1128       pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0;
1129       pvrvideosink->render_rect.w = attr.width;
1130       pvrvideosink->render_rect.h = attr.height;
1131     }
1132     XSelectInput (pvrvideosink->dcontext->x_display, xwindow->window,
1133         ExposureMask | StructureNotifyMask);
1134
1135     XSetWindowBackgroundPixmap (pvrvideosink->dcontext->x_display,
1136         xwindow->window, None);
1137
1138     XMapWindow (pvrvideosink->dcontext->x_display, xwindow->window);
1139     xwindow->gc = XCreateGC (pvrvideosink->dcontext->x_display,
1140         xwindow->window, 0, NULL);
1141     g_mutex_unlock (pvrvideosink->dcontext->x_lock);
1142
1143     glerror =
1144         pvrvideosink->dcontext->wsegl_table->
1145         pfnWSEGL_CreateWindowDrawable (pvrvideosink->dcontext->display_handle,
1146         pvrvideosink->dcontext->glconfig,
1147         &(pvrvideosink->dcontext->drawable_handle),
1148         (NativeWindowType) xwindow->window,
1149         &(pvrvideosink->dcontext->rotation));
1150
1151     if (glerror != WSEGL_SUCCESS) {
1152       GST_ERROR_OBJECT (pvrvideosink, "Error creating drawable");
1153       return;
1154     }
1155     glerror =
1156         pvrvideosink->dcontext->wsegl_table->
1157         pfnWSEGL_GetDrawableParameters (pvrvideosink->dcontext->drawable_handle,
1158         &source_params, &pvrvideosink->render_params);
1159
1160     client_mem_info =
1161         (PVRSRV_CLIENT_MEM_INFO *) pvrvideosink->render_params.hPrivateData;
1162     PVR2DMEMINFO_INITIALISE (&pvrvideosink->dcontext->dst_mem, client_mem_info);
1163   }
1164
1165   if (xwindow)
1166     pvrvideosink->xwindow = xwindow;
1167
1168   g_mutex_unlock (pvrvideosink->flow_lock);
1169 }
1170
1171 static void
1172 gst_pvrvideosink_expose (GstVideoOverlay * overlay)
1173 {
1174   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay);
1175
1176   gst_pvrvideosink_blit (pvrvideosink, NULL);
1177 }
1178
1179 static void
1180 gst_pvrvideosink_set_event_handling (GstVideoOverlay * overlay,
1181     gboolean handle_events)
1182 {
1183   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay);
1184
1185   g_mutex_lock (pvrvideosink->flow_lock);
1186
1187   if (G_UNLIKELY (!pvrvideosink->xwindow)) {
1188     g_mutex_unlock (pvrvideosink->flow_lock);
1189     return;
1190   }
1191
1192   g_mutex_lock (pvrvideosink->dcontext->x_lock);
1193
1194   XSelectInput (pvrvideosink->dcontext->x_display,
1195       pvrvideosink->xwindow->window, ExposureMask | StructureNotifyMask);
1196
1197   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
1198
1199   g_mutex_unlock (pvrvideosink->flow_lock);
1200 }
1201
1202 static void
1203 gst_pvrvideosink_set_render_rectangle (GstVideoOverlay * overlay, gint x,
1204     gint y, gint width, gint height)
1205 {
1206   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay);
1207
1208   /* FIXME: how about some locking? */
1209   if (width >= 0 && height >= 0) {
1210     pvrvideosink->render_rect.x = x;
1211     pvrvideosink->render_rect.y = y;
1212     pvrvideosink->render_rect.w = width;
1213     pvrvideosink->render_rect.h = height;
1214     pvrvideosink->have_render_rect = TRUE;
1215   } else {
1216     pvrvideosink->render_rect.x = 0;
1217     pvrvideosink->render_rect.y = 0;
1218     pvrvideosink->render_rect.w = pvrvideosink->xwindow->width;
1219     pvrvideosink->render_rect.h = pvrvideosink->xwindow->height;
1220     pvrvideosink->have_render_rect = FALSE;
1221   }
1222 }
1223
1224 static void
1225 gst_pvrvideosink_videooverlay_init (GstVideoOverlayInterface * iface)
1226 {
1227   iface->set_window_handle = gst_pvrvideosink_set_window_handle;
1228   iface->expose = gst_pvrvideosink_expose;
1229   iface->handle_events = gst_pvrvideosink_set_event_handling;
1230   iface->set_render_rectangle = gst_pvrvideosink_set_render_rectangle;
1231 }
1232
1233 /* =========================================== */
1234 /*                                             */
1235 /*              Init & Class init              */
1236 /*                                             */
1237 /* =========================================== */
1238
1239 static void
1240 gst_pvrvideosink_set_property (GObject * object, guint prop_id,
1241     const GValue * value, GParamSpec * pspec)
1242 {
1243   GstPVRVideoSink *pvrvideosink;
1244
1245   g_return_if_fail (GST_IS_PVRVIDEOSINK (object));
1246
1247   pvrvideosink = GST_PVRVIDEOSINK (object);
1248
1249   switch (prop_id) {
1250     case PROP_FORCE_ASPECT_RATIO:
1251       pvrvideosink->keep_aspect = g_value_get_boolean (value);
1252       break;
1253     default:
1254       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1255       break;
1256   }
1257 }
1258
1259 static void
1260 gst_pvrvideosink_get_property (GObject * object, guint prop_id,
1261     GValue * value, GParamSpec * pspec)
1262 {
1263   GstPVRVideoSink *pvrvideosink;
1264
1265   g_return_if_fail (GST_IS_PVRVIDEOSINK (object));
1266
1267   pvrvideosink = GST_PVRVIDEOSINK (object);
1268
1269   switch (prop_id) {
1270     case PROP_FORCE_ASPECT_RATIO:
1271       g_value_set_boolean (value, pvrvideosink->keep_aspect);
1272       break;
1273     default:
1274       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1275       break;
1276   }
1277 }
1278
1279 void
1280 gst_pvrvideosink_track_buffer (GstPVRVideoSink * pvrsink, GstBuffer * buffer)
1281 {
1282   GST_DEBUG_OBJECT (pvrsink, "Adding buffer %p to tracked buffers", buffer);
1283   pvrsink->metabuffers = g_list_prepend (pvrsink->metabuffers, buffer);
1284 }
1285
1286 void
1287 gst_pvrvideosink_untrack_buffer (GstPVRVideoSink * pvrsink, GstBuffer * buffer)
1288 {
1289   GST_DEBUG_OBJECT (pvrsink, "Removing buffer %p from tracked buffers", buffer);
1290   pvrsink->metabuffers = g_list_remove_all (pvrsink->metabuffers, buffer);
1291 }
1292
1293 static void
1294 gst_pvrvideosink_release_pvr_metas (GstPVRVideoSink * pvrsink)
1295 {
1296   GstBuffer *buf;
1297   GstPVRMeta *pvrmeta;
1298
1299   GST_DEBUG_OBJECT (pvrsink, "Releasing pending PVR metas");
1300
1301   while (pvrsink->metabuffers) {
1302     buf = (GstBuffer *) pvrsink->metabuffers->data;
1303
1304     pvrmeta = gst_buffer_get_pvr_meta (buf);
1305     if (pvrmeta)
1306       gst_buffer_remove_meta (buf, (GstMeta *) pvrmeta);
1307   }
1308
1309   GST_DEBUG_OBJECT (pvrsink, "Done");
1310 }
1311
1312 static void
1313 gst_pvrvideosink_dcontext_free (GstDrawContext * dcontext)
1314 {
1315   GST_DEBUG ("Freeing dcontext %p", dcontext);
1316
1317   if (dcontext->p_blt_info)
1318     g_free (dcontext->p_blt_info);
1319
1320   if (dcontext->p_blt2d_info)
1321     g_free (dcontext->p_blt2d_info);
1322
1323   if (dcontext->x_lock)
1324     g_mutex_lock (dcontext->x_lock);
1325   if (dcontext->x_display) {
1326     GST_LOG ("Closing display");
1327     XCloseDisplay (dcontext->x_display);
1328   }
1329   if (dcontext->x_lock) {
1330     g_mutex_unlock (dcontext->x_lock);
1331     g_mutex_free (dcontext->x_lock);
1332   }
1333
1334   g_free (dcontext);
1335 }
1336
1337 static void
1338 gst_pvrvideosink_dcontext_clear (GstPVRVideoSink * pvrvideosink)
1339 {
1340   GstDrawContext *dcontext;
1341
1342   GST_DEBUG_OBJECT (pvrvideosink, "Clearing dcontext %p",
1343       pvrvideosink->dcontext);
1344
1345   GST_OBJECT_LOCK (pvrvideosink);
1346   if (!pvrvideosink->dcontext) {
1347     GST_OBJECT_UNLOCK (pvrvideosink);
1348     return;
1349   }
1350
1351   dcontext = pvrvideosink->dcontext;
1352   pvrvideosink->dcontext = NULL;
1353   GST_OBJECT_UNLOCK (pvrvideosink);
1354
1355   gst_pvrvideosink_dcontext_free (dcontext);
1356 }
1357
1358 static void
1359 gst_pvrvideosink_reset (GstPVRVideoSink * pvrvideosink)
1360 {
1361   GThread *thread;
1362
1363   GST_DEBUG_OBJECT (pvrvideosink, "Resetting");
1364
1365   GST_OBJECT_LOCK (pvrvideosink);
1366   pvrvideosink->running = FALSE;
1367   thread = pvrvideosink->event_thread;
1368   pvrvideosink->event_thread = NULL;
1369   GST_OBJECT_UNLOCK (pvrvideosink);
1370
1371   if (thread)
1372     g_thread_join (thread);
1373
1374   if (pvrvideosink->current_buffer) {
1375     GST_LOG_OBJECT (pvrvideosink, "Removing cached buffer");
1376     gst_buffer_unref (pvrvideosink->current_buffer);
1377     pvrvideosink->current_buffer = NULL;
1378   }
1379
1380   if (pvrvideosink->pool) {
1381     GST_LOG_OBJECT (pvrvideosink, "Unreffing pool");
1382     gst_object_unref (pvrvideosink->pool);
1383     pvrvideosink->pool = NULL;
1384   }
1385   memset (&pvrvideosink->render_params, 0, sizeof (WSEGLDrawableParams));
1386
1387   pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0;
1388   pvrvideosink->render_rect.w = pvrvideosink->render_rect.h = 0;
1389   pvrvideosink->have_render_rect = FALSE;
1390
1391   gst_pvrvideosink_release_pvr_metas (pvrvideosink);
1392
1393   gst_pvrvideosink_destroy_drawable (pvrvideosink);
1394
1395   if (pvrvideosink->xwindow) {
1396     gst_pvrvideosink_xwindow_destroy (pvrvideosink, pvrvideosink->xwindow);
1397     pvrvideosink->xwindow = NULL;
1398   }
1399
1400   gst_pvrvideosink_dcontext_clear (pvrvideosink);
1401 }
1402
1403 static void
1404 gst_pvrvideosink_finalize (GObject * object)
1405 {
1406   GstPVRVideoSink *pvrvideosink;
1407
1408   pvrvideosink = GST_PVRVIDEOSINK (object);
1409
1410   gst_pvrvideosink_reset (pvrvideosink);
1411
1412   if (pvrvideosink->flow_lock) {
1413     g_mutex_free (pvrvideosink->flow_lock);
1414     pvrvideosink->flow_lock = NULL;
1415   }
1416
1417   G_OBJECT_CLASS (parent_class)->finalize (object);
1418 }
1419
1420 static void
1421 gst_pvrvideosink_init (GstPVRVideoSink * pvrvideosink)
1422 {
1423   pvrvideosink->running = FALSE;
1424
1425   pvrvideosink->flow_lock = g_mutex_new ();
1426   pvrvideosink->pool = NULL;
1427
1428   pvrvideosink->keep_aspect = FALSE;
1429   pvrvideosink->current_caps = NULL;
1430   pvrvideosink->dcontext = NULL;
1431   pvrvideosink->xwindow = NULL;
1432   pvrvideosink->redraw_borders = TRUE;
1433   pvrvideosink->current_buffer = NULL;
1434   pvrvideosink->event_thread = NULL;
1435   memset (&pvrvideosink->render_params, 0, sizeof (WSEGLDrawableParams));
1436 }
1437
1438 static void
1439 gst_pvrvideosink_class_init (GstPVRVideoSinkClass * klass)
1440 {
1441   GObjectClass *gobject_class;
1442   GstElementClass *gstelement_class;
1443   GstBaseSinkClass *gstbasesink_class;
1444   GstVideoSinkClass *videosink_class;
1445
1446   gobject_class = (GObjectClass *) klass;
1447   gstelement_class = (GstElementClass *) klass;
1448   gstbasesink_class = (GstBaseSinkClass *) klass;
1449   videosink_class = (GstVideoSinkClass *) klass;
1450
1451   parent_class = g_type_class_peek_parent (klass);
1452
1453   gobject_class->finalize = gst_pvrvideosink_finalize;
1454   gobject_class->set_property = gst_pvrvideosink_set_property;
1455   gobject_class->get_property = gst_pvrvideosink_get_property;
1456
1457   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1458       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1459           "When enabled, reverse caps negotiation (scaling) will respect "
1460           "original aspect ratio", TRUE,
1461           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1462
1463   gst_element_class_set_details_simple (gstelement_class,
1464       "PVR Video sink", "Sink/Video",
1465       "A PVR videosink",
1466       "Luciana Fujii Pontello <luciana.fujii@collabora.co.uk");
1467
1468   gst_element_class_add_pad_template (gstelement_class,
1469       gst_static_pad_template_get (&gst_pvrvideosink_sink_template_factory));
1470
1471   gstelement_class->change_state = gst_pvrvideosink_change_state;
1472
1473   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_pvrvideosink_setcaps);
1474   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_pvrvideosink_getcaps);
1475   gstbasesink_class->propose_allocation =
1476       GST_DEBUG_FUNCPTR (gst_pvrvideosink_propose_allocation);
1477   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_pvrvideosink_get_times);
1478
1479   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_pvrvideosink_show_frame);
1480 }