pvrvideosink: fix leaks on error paths
[gstreamer-omap:gst-plugins-bad.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  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /* Object header */
24 #include "gstpvrvideosink.h"
25 #include "gstpvrbufferpriv.h"
26
27 #include <gst/video/gstvideosink.h>
28 #include <gst/interfaces/xoverlay.h>
29 #include <gst/interfaces/navigation.h>
30
31 /* Debugging category */
32 #include <gst/gstinfo.h>
33
34 #define LINUX
35 #include <dri2_omap_ws.h>
36 #include <services.h>
37 #include <img_defs.h>
38 #include <servicesext.h>
39
40 #include <unistd.h>
41
42 #define DEFAULT_QUEUE_SIZE 12
43 #define DEFAULT_MIN_QUEUED_BUFS 1
44
45 GST_DEBUG_CATEGORY_EXTERN (gst_debug_pvrvideosink);
46 #define GST_CAT_DEFAULT gst_debug_pvrvideosink
47
48 #define PVR2DMEMINFO_INITIALISE(d, s) \
49 { \
50   (d)->hPrivateData = (IMG_VOID *)(s); \
51   (d)->hPrivateMapData = (IMG_VOID *)(s->hKernelMemInfo); \
52   (d)->ui32DevAddr = (IMG_UINT32) (s)->sDevVAddr.uiAddr; \
53   (d)->ui32MemSize = (s)->uAllocSize; \
54   (d)->pBase = (s)->pvLinAddr;\
55   (d)->ulFlags = (s)->ui32Flags;\
56 }
57
58 /* end of internal definitions */
59
60 static void gst_pvrvideosink_reset (GstPVRVideoSink * pvrvideosink);
61 static GstFlowReturn gst_pvrvideosink_buffer_alloc (GstBaseSink * bsink,
62     guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf);
63 static void gst_pvrvideosink_xwindow_draw_borders (GstPVRVideoSink *
64     pvrvideosink, GstXWindow * xwindow, GstVideoRectangle rect);
65 static void gst_pvrvideosink_expose (GstXOverlay * overlay);
66 static void gst_pvrvideosink_xwindow_destroy (GstPVRVideoSink * pvrvideosink,
67     GstXWindow * xwindow);
68 static void gst_pvrvideosink_set_event_handling (GstXOverlay * overlay,
69     gboolean handle_events);
70
71 static GstStaticPadTemplate gst_pvrvideosink_sink_template_factory =
72     GST_STATIC_PAD_TEMPLATE ("sink",
73     GST_PAD_SINK,
74     GST_PAD_ALWAYS,
75     GST_STATIC_CAPS ("video/x-raw-yuv, "
76         "format = (fourcc) NV12, "
77         "width = " GST_VIDEO_SIZE_RANGE ", "
78         "height = " GST_VIDEO_SIZE_RANGE ", "
79         "framerate = " GST_VIDEO_FPS_RANGE ";"
80         "video/x-raw-yuv-strided, "
81         "format = (fourcc) NV12, "
82         "rowstride = (int) 4096, "
83         "width = " GST_VIDEO_SIZE_RANGE ", "
84         "height = " GST_VIDEO_SIZE_RANGE ", "
85         "framerate = " GST_VIDEO_FPS_RANGE));
86
87 enum
88 {
89   PROP_0,
90   PROP_FORCE_ASPECT_RATIO,
91   PROP_WINDOW_WIDTH,
92   PROP_WINDOW_HEIGHT
93 };
94
95 static GstVideoSinkClass *parent_class = NULL;
96
97 /* ============================================================= */
98 /*                                                               */
99 /*                       Private Methods                         */
100 /*                                                               */
101 /* ============================================================= */
102
103 /* pvrvideo buffers */
104
105 #define GST_TYPE_PVRVIDEO_BUFFER (gst_pvrvideo_buffer_get_type())
106
107 #define GST_IS_PVRVIDEO_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PVRVIDEO_BUFFER))
108 #define GST_PVRVIDEO_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PVRVIDEO_BUFFER, GstPVRVideoBuffer))
109 #define GST_PVRVIDEO_BUFFER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PVRVIDEO_BUFFER, GstPVRVideoBufferClass))
110
111
112 static GstDisplayHandle *
113 gst_display_handle_new (WSEGLDrawableHandle dh, const WSEGL_FunctionTable * ft)
114 {
115   GstDisplayHandle *display_handle = g_slice_new0 (GstDisplayHandle);
116   display_handle->refcount = 1;
117   display_handle->wsegl_table = ft;
118   display_handle->display_handle = dh;
119   return display_handle;
120 }
121
122 GstDisplayHandle *
123 gst_display_handle_ref (GstDisplayHandle * display_handle)
124 {
125   g_atomic_int_inc (&display_handle->refcount);
126   return display_handle;
127 }
128
129 void
130 gst_display_handle_unref (GstDisplayHandle * display_handle)
131 {
132   if (g_atomic_int_dec_and_test (&display_handle->refcount)) {
133     display_handle->wsegl_table->pfnWSEGL_CloseDisplay (display_handle->
134         display_handle);
135     g_slice_free (GstDisplayHandle, display_handle);
136   }
137 }
138
139 static const char *
140 pvr2dstrerr (PVR2DERROR err)
141 {
142   switch (err) {
143     case PVR2D_OK:
144       return "Ok";
145     case PVR2DERROR_DEVICE_UNAVAILABLE:
146       return "Failed to blit, device unavailable";
147     case PVR2DERROR_INVALID_CONTEXT:
148       return "Failed to blit, invalid context";
149     case PVR2DERROR_INVALID_PARAMETER:
150       return "Failed to blit, invalid parameter";
151     case PVR2DERROR_HW_FEATURE_NOT_SUPPORTED:
152       return "Failed to blit, hardware feature not supported";
153     case PVR2DERROR_GENERIC_ERROR:
154       return "Failed to blit, generic error";
155     default:
156       return "Unknown error";
157   }
158 }
159
160 static const char *
161 wseglstrerr (WSEGLError err)
162 {
163   switch (err) {
164     case WSEGL_SUCCESS:
165       return "Ok";
166     case WSEGL_CANNOT_INITIALISE:
167       return "Cannot initialize";
168     case WSEGL_BAD_NATIVE_DISPLAY:
169       return "Bad native display";
170     case WSEGL_BAD_NATIVE_WINDOW:
171       return "Bad native window";
172     case WSEGL_BAD_NATIVE_PIXMAP:
173       return "Bad native pixmap";
174     case WSEGL_BAD_NATIVE_ENGINE:
175       return "Bad native engine";
176     case WSEGL_BAD_DRAWABLE:
177       return "Bad drawable";
178     case WSEGL_BAD_MATCH:
179       return "Bad match";
180     case WSEGL_OUT_OF_MEMORY:
181       return "Out of memory";
182     default:
183       return "Unknown error";
184   }
185 }
186
187 /* This function calculates the pixel aspect ratio based on the properties
188  *  * in the xcontext structure and stores it there. */
189 static void
190 gst_pvrvideosink_calculate_pixel_aspect_ratio (GstDrawContext * dcontext)
191 {
192   static const gint par[][2] = {
193     {1, 1},                     /* regular screen */
194     {16, 15},                   /* PAL TV */
195     {11, 10},                   /* 525 line Rec.601 video */
196     {54, 59},                   /* 625 line Rec.601 video */
197     {64, 45},                   /* 1280x1024 on 16:9 display */
198     {5, 3},                     /* 1280x1024 on 4:3 display */
199     {4, 3}                      /* 800x600 on 16:9 display */
200   };
201   gint i;
202   gint index;
203   gdouble ratio;
204   gdouble delta;
205
206 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
207
208   /* first calculate the "real" ratio; which is the "physical" w/h divided
209    * by the w/h in pixels of the display */
210   ratio = (gdouble) (dcontext->physical_width * dcontext->display_height)
211       / (dcontext->physical_height * dcontext->display_width);
212
213   /* XXX */
214   ratio = 1;
215
216   GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
217   /* now find the one from par[][2] with the lowest delta to the real one */
218   delta = DELTA (0);
219   index = 0;
220
221   for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
222     gdouble this_delta = DELTA (i);
223
224     if (this_delta < delta) {
225       index = i;
226       delta = this_delta;
227     }
228   }
229
230   GST_DEBUG ("Decided on index %d (%d/%d)", index,
231       par[index][0], par[index][1]);
232
233   g_free (dcontext->par);
234   dcontext->par = g_new0 (GValue, 1);
235   g_value_init (dcontext->par, GST_TYPE_FRACTION);
236   gst_value_set_fraction (dcontext->par, par[index][0], par[index][1]);
237   GST_DEBUG ("set dcontext PAR to %d/%d",
238       gst_value_get_fraction_numerator (dcontext->par),
239       gst_value_get_fraction_denominator (dcontext->par));
240 }
241
242 static void
243 pvr_recreate_drawable (GstPVRVideoSink * pvrvideosink)
244 {
245   WSEGLError glerror;
246   GstDrawContext *dcontext = pvrvideosink->dcontext;
247
248   if (dcontext->drawable_handle) {
249     glerror =
250         dcontext->wsegl_table->
251         pfnWSEGL_DeleteDrawable (dcontext->drawable_handle);
252     if (glerror) {
253       GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, FAILED,
254           ("error deleting drawable"), ("%s", wseglstrerr (glerror)));
255       return;
256     }
257   }
258
259   glerror =
260       dcontext->wsegl_table->
261       pfnWSEGL_CreateWindowDrawable (dcontext->gst_display_handle->
262       display_handle, dcontext->glconfig, &dcontext->drawable_handle,
263       (NativeWindowType) pvrvideosink->xwindow->window, &dcontext->rotation);
264   if (glerror) {
265     GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, FAILED,
266         ("error creating drawable"), ("%s", wseglstrerr (glerror)));
267   }
268 }
269
270 static void
271 pvr_get_drawable_params (GstPVRVideoSink * pvrvideosink)
272 {
273   WSEGLError glerror;
274   WSEGLDrawableParams source_params;
275   PVRSRV_CLIENT_MEM_INFO *client_mem_info;
276   GstDrawContext *dcontext = pvrvideosink->dcontext;
277
278   glerror =
279       dcontext->wsegl_table->
280       pfnWSEGL_GetDrawableParameters (dcontext->drawable_handle, &source_params,
281       &pvrvideosink->render_params);
282
283   if (glerror == WSEGL_BAD_DRAWABLE) {
284     /* this can happen if window size changes, window is redirected/
285      * unredirected, etc..  when this happens, recreate drawable and
286      * try again.
287      */
288     GST_DEBUG_OBJECT (pvrvideosink, "drawable changed, recreating");
289     pvr_recreate_drawable (pvrvideosink);
290     pvr_get_drawable_params (pvrvideosink);
291     return;
292   }
293
294   if (glerror) {
295     GST_ERROR_OBJECT (pvrvideosink, "%s", wseglstrerr (glerror));
296   }
297
298   client_mem_info =
299       (PVRSRV_CLIENT_MEM_INFO *) pvrvideosink->render_params.hPrivateData;
300
301   PVR2DMEMINFO_INITIALISE (&dcontext->dst_mem, client_mem_info);
302 }
303
304 static void
305 pvr_swap_buffers (GstPVRVideoSink * pvrvideosink)
306 {
307   GstDrawContext *dcontext = pvrvideosink->dcontext;
308
309   dcontext->wsegl_table->pfnWSEGL_SwapDrawable (dcontext->drawable_handle, 1);
310
311   pvr_get_drawable_params (pvrvideosink);
312 }
313
314 /* wait for previous blits to dst surface to complete */
315 static void
316 pvr_query_blits_complete (GstPVRVideoSink * pvrvideosink)
317 {
318   GstDrawContext *dcontext = pvrvideosink->dcontext;
319   PVR2DERROR pvr_error;
320
321   pvr_error = PVR2DQueryBlitsComplete (dcontext->pvr_context,
322       &dcontext->dst_mem, TRUE);
323
324   if (pvr_error) {
325     GST_ERROR_OBJECT (pvrvideosink, "%s (%d)",
326         pvr2dstrerr (pvr_error), pvr_error);
327   }
328 }
329
330 static void
331 gst_pvrvideosink_xwindow_update_geometry (GstPVRVideoSink * pvrvideosink)
332 {
333   XWindowAttributes attr;
334
335   /* Update the window geometry */
336   g_mutex_lock (pvrvideosink->dcontext->x_lock);
337   if (G_UNLIKELY (pvrvideosink->xwindow == NULL)) {
338     g_mutex_unlock (pvrvideosink->dcontext->x_lock);
339     return;
340   }
341   pvrvideosink->redraw_borders = 2;
342
343   XGetWindowAttributes (pvrvideosink->dcontext->x_display,
344       pvrvideosink->xwindow->window, &attr);
345
346   pvrvideosink->xwindow->width = attr.width;
347   pvrvideosink->xwindow->height = attr.height;
348
349   if (!pvrvideosink->have_render_rect) {
350     pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0;
351     pvrvideosink->render_rect.w = attr.width;
352     pvrvideosink->render_rect.h = attr.height;
353   }
354
355   if (pvrvideosink->dcontext != NULL) {
356     pvr_recreate_drawable (pvrvideosink);
357     pvr_get_drawable_params (pvrvideosink);
358   }
359
360   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
361 }
362
363 /* This function handles XEvents that might be in the queue. It generates
364    GstEvent that will be sent upstream in the pipeline to handle interactivity
365    and navigation. It will also listen for configure events on the window to
366    trigger caps renegotiation so on the fly software scaling can work. */
367 static void
368 gst_pvrvideosink_handle_xevents (GstPVRVideoSink * pvrvideosink)
369 {
370   Display *dpy = pvrvideosink->dcontext->x_display;
371   Window win = pvrvideosink->xwindow->window;
372   XEvent e;
373   gboolean exposed = FALSE;
374   gboolean configured = FALSE;
375   guint pointer_x = 0, pointer_y = 0;
376   gboolean pointer_moved = FALSE;
377
378   g_mutex_lock (pvrvideosink->flow_lock);
379   g_mutex_lock (pvrvideosink->dcontext->x_lock);
380
381   /* First get all pointer motion events, only the last position is
382    * interesting so throw out the earlier ones:
383    */
384   while (XCheckWindowEvent (dpy, win, PointerMotionMask, &e)) {
385     switch (e.type) {
386       case MotionNotify:
387         pointer_x = e.xmotion.x;
388         pointer_y = e.xmotion.y;
389         pointer_moved = TRUE;
390         break;
391       default:
392         break;
393     }
394   }
395
396   if (pointer_moved) {
397     GST_DEBUG_OBJECT (pvrvideosink,
398         "pointer moved over window at %d,%d", pointer_x, pointer_y);
399     g_mutex_unlock (pvrvideosink->dcontext->x_lock);
400     gst_navigation_send_mouse_event (GST_NAVIGATION (pvrvideosink),
401         "mouse-move", 0, e.xbutton.x, e.xbutton.y);
402     g_mutex_lock (pvrvideosink->dcontext->x_lock);
403   }
404
405   /* Then handle all the other events: */
406   while (XCheckWindowEvent (pvrvideosink->dcontext->x_display,
407           pvrvideosink->xwindow->window,
408           ExposureMask | StructureNotifyMask |
409           KeyPressMask | KeyReleaseMask |
410           ButtonPressMask | ButtonReleaseMask, &e)) {
411     KeySym keysym;
412     const char *key_str = NULL;
413
414     g_mutex_unlock (pvrvideosink->dcontext->x_lock);
415
416     switch (e.type) {
417       case Expose:
418         exposed = TRUE;
419         break;
420       case ConfigureNotify:
421         gst_pvrvideosink_xwindow_update_geometry (pvrvideosink);
422         configured = TRUE;
423         break;
424       case ButtonPress:
425         GST_DEBUG_OBJECT (pvrvideosink,
426             "button %d pressed over window at %d,%d",
427             e.xbutton.button, e.xbutton.x, e.xbutton.y);
428         gst_navigation_send_mouse_event (GST_NAVIGATION (pvrvideosink),
429             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
430         break;
431       case ButtonRelease:
432         GST_DEBUG_OBJECT (pvrvideosink,
433             "button %d released over window at %d,%d", e.xbutton.button,
434             e.xbutton.x, e.xbutton.y);
435         gst_navigation_send_mouse_event (GST_NAVIGATION (pvrvideosink),
436             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
437         break;
438       case KeyPress:
439       case KeyRelease:
440         g_mutex_lock (pvrvideosink->dcontext->x_lock);
441         keysym = XKeycodeToKeysym (dpy, e.xkey.keycode, 0);
442         if (keysym != NoSymbol) {
443           key_str = XKeysymToString (keysym);
444         } else {
445           key_str = "unknown";
446         }
447         g_mutex_unlock (pvrvideosink->dcontext->x_lock);
448         GST_DEBUG_OBJECT (pvrvideosink,
449             "key %d pressed over window at %d,%d (%s)",
450             e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
451         gst_navigation_send_key_event (GST_NAVIGATION (pvrvideosink),
452             e.type == KeyPress ? "key-press" : "key-release", key_str);
453         break;
454       default:
455         GST_DEBUG_OBJECT (pvrvideosink, "unhandled X event (%d)", e.type);
456         break;
457     }
458
459     g_mutex_lock (pvrvideosink->dcontext->x_lock);
460   }
461
462   if (exposed || configured) {
463     g_mutex_unlock (pvrvideosink->dcontext->x_lock);
464     g_mutex_unlock (pvrvideosink->flow_lock);
465
466     gst_pvrvideosink_expose (GST_X_OVERLAY (pvrvideosink));
467
468     g_mutex_lock (pvrvideosink->flow_lock);
469     g_mutex_lock (pvrvideosink->dcontext->x_lock);
470   }
471
472   /* Handle Display events */
473   while (XPending (pvrvideosink->dcontext->x_display)) {
474     XNextEvent (pvrvideosink->dcontext->x_display, &e);
475
476     switch (e.type) {
477       case ClientMessage:{
478         Atom wm_delete;
479
480         wm_delete = XInternAtom (pvrvideosink->dcontext->x_display,
481             "WM_DELETE_WINDOW", True);
482         if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
483           /* Handle window deletion by posting an error on the bus */
484           GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, NOT_FOUND,
485               ("Output window was closed"), (NULL));
486
487           g_mutex_unlock (pvrvideosink->dcontext->x_lock);
488           gst_pvrvideosink_xwindow_destroy (pvrvideosink,
489               pvrvideosink->xwindow);
490           pvrvideosink->xwindow = NULL;
491           g_mutex_lock (pvrvideosink->dcontext->x_lock);
492         }
493         break;
494       }
495       default:
496         break;
497     }
498   }
499
500   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
501   g_mutex_unlock (pvrvideosink->flow_lock);
502 }
503
504 static gpointer
505 gst_pvrvideosink_event_thread (GstPVRVideoSink * pvrvideosink)
506 {
507   GST_OBJECT_LOCK (pvrvideosink);
508   while (pvrvideosink->running) {
509     GST_OBJECT_UNLOCK (pvrvideosink);
510
511     if (pvrvideosink->xwindow) {
512       gst_pvrvideosink_handle_xevents (pvrvideosink);
513     }
514     g_usleep (G_USEC_PER_SEC / 20);
515
516     GST_OBJECT_LOCK (pvrvideosink);
517   }
518   GST_OBJECT_UNLOCK (pvrvideosink);
519
520   return NULL;
521 }
522
523 static void
524 gst_pvrvideosink_manage_event_thread (GstPVRVideoSink * pvrvideosink)
525 {
526   GThread *thread = NULL;
527
528   /* don't start the thread too early */
529   if (pvrvideosink->dcontext == NULL) {
530     return;
531   }
532
533   GST_OBJECT_LOCK (pvrvideosink);
534   if (!pvrvideosink->event_thread) {
535     /* Setup our event listening thread */
536     GST_DEBUG_OBJECT (pvrvideosink, "run xevent thread");
537     pvrvideosink->running = TRUE;
538     pvrvideosink->event_thread = g_thread_create (
539         (GThreadFunc) gst_pvrvideosink_event_thread, pvrvideosink, TRUE, NULL);
540   }
541   GST_OBJECT_UNLOCK (pvrvideosink);
542
543   /* Wait for our event thread to finish */
544   if (thread)
545     g_thread_join (thread);
546 }
547
548
549 static GstDrawContext *
550 gst_pvrvideosink_get_dcontext (GstPVRVideoSink * pvrvideosink)
551 {
552   GstDrawContext *dcontext;
553   DRI2Display *displayImpl;
554   WSEGLError glerror;
555   const WSEGLCaps *glcaps;
556   PVR2DMISCDISPLAYINFO misc_display_info;
557   Window root;
558   drm_magic_t magic;
559   int eventBase, errorBase, major, minor;
560   char *driver, *device;
561   int fd = -1;
562   WSEGLDisplayHandle display_handle;
563
564   dcontext = g_new0 (GstDrawContext, 1);
565   dcontext->x_lock = g_mutex_new ();
566
567   dcontext->x_display = XOpenDisplay (NULL);
568   if (dcontext->x_display == NULL)
569     goto no_display;
570
571   if (!DRI2InitDisplay (dcontext->x_display, NULL))
572     goto no_dri;
573
574   if (!DRI2QueryExtension (dcontext->x_display, &eventBase, &errorBase))
575     goto no_dri;
576
577   GST_DEBUG_OBJECT (pvrvideosink, "DRI2QueryExtension: "
578       "eventBase=%d, errorBase=%d", eventBase, errorBase);
579
580   if (!DRI2QueryVersion (dcontext->x_display, &major, &minor))
581     goto no_dri;
582
583   GST_DEBUG_OBJECT (pvrvideosink, "DRI2QueryVersion: major=%d, minor=%d",
584       major, minor);
585
586   root = RootWindow (dcontext->x_display, DefaultScreen (dcontext->x_display));
587   if (!DRI2Connect (dcontext->x_display, root, DRI2DriverDRI, &driver, &device))
588     goto dri_connect_error;
589
590   GST_INFO_OBJECT (pvrvideosink, "DRI2Connect: driver=%s, device=%s",
591       driver, device);
592
593   fd = open (device, O_RDWR);
594   if (fd < 0)
595     goto dri_device_error;
596
597   drmGetMagic (fd, &magic);
598   if (!DRI2Authenticate (dcontext->x_display, root, magic))
599     goto dri_auth_error;
600
601   pvrvideosink->drm_fd = fd;
602
603   dcontext->wsegl_table = WSEGL_GetFunctionTablePointer ();
604   glerror = dcontext->wsegl_table->pfnWSEGL_IsDisplayValid (
605       (NativeDisplayType) dcontext->x_display);
606   if (glerror)
607     goto invalid_display;
608
609   glerror = dcontext->wsegl_table->pfnWSEGL_InitialiseDisplay (
610       (NativeDisplayType) dcontext->x_display, &display_handle,
611       &glcaps, &dcontext->glconfig);
612   if (glerror)
613     goto wsegl_init_error;
614
615   dcontext->gst_display_handle =
616       gst_display_handle_new (display_handle, dcontext->wsegl_table);
617
618   displayImpl = (DRI2Display *) display_handle;
619   dcontext->pvr_context = displayImpl->hContext;
620
621 #if 0
622   /* XXX: PVR2DGetScreenMode and PVR2DGetMiscDisplayInfo fail with the new
623    * dri2_omap backend. 
624    */
625   pvr_error = PVR2DGetScreenMode (dcontext->pvr_context,
626       &dcontext->display_format, &dcontext->display_width,
627       &dcontext->display_height, &dcontext->stride, &refresh_rate);
628   if (pvr_error != PVR2D_OK) {
629     GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, READ,
630         ("Failed to get screen mode"), ("returned %d", pvr_error));
631     return NULL;
632   }
633
634   pvr_error = PVR2DGetMiscDisplayInfo (dcontext->pvr_context,
635       &misc_display_info);
636   if (pvr_error != PVR2D_OK) {
637     GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, READ,
638         ("Failed to get display info"), ("returned %d", pvr_error));
639     return NULL;
640   }
641 #endif
642   dcontext->physical_width = misc_display_info.ulPhysicalWidthmm;
643   dcontext->physical_height = misc_display_info.ulPhysicalHeightmm;
644   dcontext->screen_num = DefaultScreen (dcontext->x_display);
645   dcontext->black = XBlackPixel (dcontext->x_display, dcontext->screen_num);
646   gst_pvrvideosink_calculate_pixel_aspect_ratio (dcontext);
647
648   return dcontext;
649
650 no_display:
651   GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, WRITE,
652       ("Could not initialise X output"), ("Could not open display"));
653   goto fail;
654
655 dri_connect_error:
656   GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, WRITE,
657       ("DRI connection failed"), ("%s", strerror (errno)));
658   goto fail;
659
660 no_dri:
661   GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, WRITE,
662       ("DRI call failed"), ("%s", strerror (errno)));
663   goto fail;
664
665 dri_auth_error:
666   GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, WRITE,
667       ("DRI authentication failed"), ("%s", strerror (errno)));
668   goto fail;
669
670 invalid_display:
671   GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, WRITE,
672       ("Display is not valid"), ("%s", wseglstrerr (glerror)));
673   goto fail;
674
675 wsegl_init_error:
676   GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, WRITE,
677       ("Failed to initialize display"), ("%s", wseglstrerr (glerror)));
678   goto fail;
679
680 dri_device_error:
681   GST_ELEMENT_ERROR (pvrvideosink, RESOURCE, WRITE,
682       ("Could not open DRI device"), ("%s", strerror (errno)));
683   goto fail;
684
685 fail:
686   if (dcontext->x_display) {
687     XCloseDisplay (dcontext->x_display);
688     dcontext->x_display = NULL;
689   }
690
691   if (fd) {
692     close (fd);
693     pvrvideosink->drm_fd = -1;
694   }
695
696   g_mutex_free (dcontext->x_lock);
697   g_free (dcontext);
698
699   return NULL;
700 }
701
702 static void
703 gst_pvrvideosink_xwindow_set_title (GstPVRVideoSink * pvrvideosink,
704     GstXWindow * xwindow, const gchar * media_title)
705 {
706   if (media_title) {
707     g_free (pvrvideosink->media_title);
708     pvrvideosink->media_title = g_strdup (media_title);
709   }
710   if (xwindow) {
711     /* we have a window */
712     if (xwindow->internal) {
713       XTextProperty xproperty;
714       const gchar *app_name;
715       const gchar *title = NULL;
716       gchar *title_mem = NULL;
717
718       /* set application name as a title */
719       app_name = g_get_application_name ();
720
721       if (app_name && pvrvideosink->media_title) {
722         title = title_mem = g_strconcat (pvrvideosink->media_title, " : ",
723             app_name, NULL);
724       } else if (app_name) {
725         title = app_name;
726       } else if (pvrvideosink->media_title) {
727         title = pvrvideosink->media_title;
728       }
729
730       if (title) {
731         if ((XStringListToTextProperty (((char **) &title), 1,
732                     &xproperty)) != 0) {
733           XSetWMName (pvrvideosink->dcontext->x_display, xwindow->window,
734               &xproperty);
735           XFree (xproperty.value);
736         }
737
738         g_free (title_mem);
739       }
740     }
741   }
742 }
743
744 static GstXWindow *
745 gst_pvrvideosink_create_window (GstPVRVideoSink * pvrvideosink, gint width,
746     gint height)
747 {
748   Window root;
749   GstXWindow *xwindow;
750   GstDrawContext *dcontext;
751   XGCValues values;
752   Atom wm_delete;
753
754   GST_DEBUG_OBJECT (pvrvideosink, "begin");
755
756   dcontext = pvrvideosink->dcontext;
757   xwindow = g_new0 (GstXWindow, 1);
758
759   xwindow->internal = TRUE;
760
761   g_mutex_lock (dcontext->x_lock);
762
763   root = DefaultRootWindow (dcontext->x_display);
764   xwindow->window = XCreateSimpleWindow (dcontext->x_display, root, 0, 0,
765       width, height, 2, 2, dcontext->black);
766
767   /* Tell the window manager we'd like delete client messages instead of
768    * being killed */
769   wm_delete = XInternAtom (dcontext->x_display, "WM_DELETE_WINDOW", True);
770   if (wm_delete != None) {
771     (void) XSetWMProtocols (dcontext->x_display, xwindow->window,
772         &wm_delete, 1);
773   }
774
775   XMapWindow (dcontext->x_display, xwindow->window);
776
777   /* We have to do that to prevent X from redrawing the background on
778    * ConfigureNotify. This takes away flickering of video when resizing. */
779   XSetWindowBackgroundPixmap (dcontext->x_display, xwindow->window, None);
780
781   gst_pvrvideosink_xwindow_set_title (pvrvideosink, xwindow, NULL);
782
783   xwindow->gc = XCreateGC (dcontext->x_display, xwindow->window, 0, &values);
784
785   g_mutex_unlock (dcontext->x_lock);
786
787   gst_pvrvideosink_xwindow_update_geometry (pvrvideosink);
788
789   GST_DEBUG_OBJECT (pvrvideosink, "end");
790   return xwindow;
791 }
792
793 static void
794 gst_pvrvideosink_blit (GstPVRVideoSink * pvrvideosink, GstBuffer * buffer)
795 {
796   PVR2DERROR pvr_error = PVR2D_OK;
797   GstDrawContext *dcontext = pvrvideosink->dcontext;
798   gint video_width;
799   gint video_height;
800   gboolean draw_border = FALSE;
801   PVR2D_3DBLT_EXT s_blt_3d = { };
802   PPVR2D_3DBLT_EXT p_blt_3d;
803   PVR2DMEMINFO *src_mem;
804   PVR2DFORMAT pvr_format = pvrvideosink->format == GST_VIDEO_FORMAT_NV12 ?
805       PVR2D_YUV420_2PLANE : PVR2D_ARGB8888;
806   GstVideoRectangle result;
807   GstPVRBufferPriv *buf_priv;
808   PVR2DRECT *crop = &pvrvideosink->crop;
809
810   GST_DEBUG_OBJECT (pvrvideosink, "begin");
811
812   g_mutex_lock (pvrvideosink->flow_lock);
813   if (buffer == NULL)
814     buffer = pvrvideosink->current_buffer;
815
816   if (buffer == NULL) {
817     g_mutex_unlock (pvrvideosink->flow_lock);
818     return;
819   }
820
821   buf_priv = gst_pvr_buffer_priv (pvrvideosink, buffer);
822   video_width = pvrvideosink->video_width;
823   video_height = pvrvideosink->video_height;
824   src_mem = buf_priv->mem_info;
825   p_blt_3d = &s_blt_3d;
826
827   g_mutex_lock (dcontext->x_lock);
828
829   /* Draw borders when displaying the first frame. After this
830      draw borders only on expose event or after a size change. */
831   if (!(pvrvideosink->current_buffer) || pvrvideosink->redraw_borders) {
832     draw_border = TRUE;
833   }
834
835   if (!pvrvideosink->xwindow) {
836     goto done;
837   }
838
839   /* Sometimes the application hasn't really given us valid dimensions
840    * when we want to render the first frame, which throws pvr into a
841    * tizzy, so let's just detect it and bail early:
842    */
843   if ((pvrvideosink->xwindow->width <= 1) ||
844       (pvrvideosink->xwindow->height <= 1)) {
845     GST_DEBUG_OBJECT (pvrvideosink, "skipping render due to invalid "
846         "window dimensions: %dx%d", pvrvideosink->xwindow->width,
847         pvrvideosink->xwindow->height);
848     goto done;
849   }
850
851   /* Store a reference to the last image we put, lose the previous one */
852   if (buffer && pvrvideosink->current_buffer != buffer) {
853     if (pvrvideosink->current_buffer) {
854       GST_LOG_OBJECT (pvrvideosink, "unreffing %p",
855           pvrvideosink->current_buffer);
856       gst_buffer_unref (GST_BUFFER_CAST (pvrvideosink->current_buffer));
857     }
858     GST_LOG_OBJECT (pvrvideosink, "reffing %p as our current buffer", buffer);
859     pvrvideosink->current_buffer = gst_buffer_ref (buffer);
860   }
861
862   if (pvrvideosink->keep_aspect) {
863     GstVideoRectangle src, dst;
864
865     src.w = GST_VIDEO_SINK_WIDTH (pvrvideosink);
866     src.h = GST_VIDEO_SINK_HEIGHT (pvrvideosink);
867     dst.w = pvrvideosink->render_rect.w;
868     dst.h = pvrvideosink->render_rect.h;
869     gst_video_sink_center_rect (src, dst, &result, TRUE);
870     result.x += pvrvideosink->render_rect.x;
871     result.y += pvrvideosink->render_rect.y;
872   } else {
873     memcpy (&result, &pvrvideosink->render_rect, sizeof (GstVideoRectangle));
874   }
875
876   p_blt_3d->sDst.pSurfMemInfo = &dcontext->dst_mem;
877   p_blt_3d->sDst.SurfOffset = 0;
878   p_blt_3d->sDst.Stride =
879       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_BGRx, 0,
880       pvrvideosink->render_params.ui32Stride);
881   p_blt_3d->sDst.Format = PVR2D_ARGB8888;
882   p_blt_3d->sDst.SurfWidth = pvrvideosink->xwindow->width;
883   p_blt_3d->sDst.SurfHeight = pvrvideosink->xwindow->height;
884
885   p_blt_3d->rcDest.left = result.x;
886   p_blt_3d->rcDest.top = result.y;
887   p_blt_3d->rcDest.right = result.w + result.x;
888   p_blt_3d->rcDest.bottom = result.h + result.y;
889
890   p_blt_3d->sSrc.pSurfMemInfo = src_mem;
891   p_blt_3d->sSrc.SurfOffset = 0;
892   p_blt_3d->sSrc.Stride = pvrvideosink->rowstride;
893   p_blt_3d->sSrc.Format = pvr_format;
894   p_blt_3d->sSrc.SurfWidth = video_width;
895   p_blt_3d->sSrc.SurfHeight = video_height;
896
897   if (crop->left || crop->top || crop->right || crop->bottom) {
898     p_blt_3d->rcSource = *crop;
899   } else {
900     p_blt_3d->rcSource.left = 0;
901     p_blt_3d->rcSource.top = 0;
902     p_blt_3d->rcSource.right = video_width;
903     p_blt_3d->rcSource.bottom = video_height;
904   }
905
906   GST_DEBUG_OBJECT (pvrvideosink, "blit: %dx%d (%d) -> %dx%d (%d)",
907       p_blt_3d->sSrc.SurfWidth, p_blt_3d->sSrc.SurfHeight,
908       p_blt_3d->sSrc.Stride, p_blt_3d->sDst.SurfWidth,
909       p_blt_3d->sDst.SurfHeight, p_blt_3d->sDst.Stride);
910   GST_DEBUG_OBJECT (pvrvideosink, "crop: %d,%d %d,%d -> %d,%d %d,%d",
911       p_blt_3d->rcSource.left, p_blt_3d->rcSource.top,
912       p_blt_3d->rcSource.right, p_blt_3d->rcSource.bottom,
913       p_blt_3d->rcDest.left, p_blt_3d->rcDest.top,
914       p_blt_3d->rcDest.right, p_blt_3d->rcDest.bottom);
915
916   if (pvrvideosink->format == GST_VIDEO_FORMAT_NV12)
917     p_blt_3d->bDisableDestInput = TRUE;
918   else
919     /* blit fails for RGB without this... not sure why yet... */
920     /* I'd guess because using ARGB format (ie. has alpha channel) */
921     p_blt_3d->bDisableDestInput = FALSE;
922
923   pvr_query_blits_complete (pvrvideosink);
924
925   if (pvrvideosink->interlaced) {
926     /* NOTE: this probably won't look so good if linear (instead
927      * of point) filtering is used.
928      */
929     p_blt_3d->bFilter = FALSE;
930
931     /* for interlaced blits, we split up the image into two blits..
932      * we expect even field on top, odd field on bottom.  We blit
933      * from first top half, then bottom half, doubling up the
934      * stride of the destination buffer.
935      */
936     /* step 1: */
937     p_blt_3d->rcSource.bottom /= 2;
938     p_blt_3d->rcDest.bottom /= 2;
939     p_blt_3d->sDst.Stride *= 2;
940
941     pvr_error = PVR2DBlt3DExt (dcontext->pvr_context, p_blt_3d);
942     if (pvr_error)
943       goto done;
944
945     /* step 2: */
946     p_blt_3d->rcSource.top += video_height / 2;
947     p_blt_3d->rcSource.bottom += video_height / 2;
948     p_blt_3d->sDst.SurfOffset = p_blt_3d->sDst.Stride / 2;
949
950     pvr_error = PVR2DBlt3DExt (dcontext->pvr_context, p_blt_3d);
951
952   } else {
953     p_blt_3d->bFilter = TRUE;
954     pvr_error = PVR2DBlt3DExt (dcontext->pvr_context, p_blt_3d);
955   }
956
957   if (pvr_error)
958     goto done;
959
960   if (draw_border) {
961     gst_pvrvideosink_xwindow_draw_borders (pvrvideosink, pvrvideosink->xwindow,
962         result);
963     pvrvideosink->redraw_borders--;
964     if (pvrvideosink->redraw_borders < 0)
965       pvrvideosink->redraw_borders = 0;
966   } else {
967     pvr_swap_buffers (pvrvideosink);
968   }
969
970 done:
971   if (pvr_error) {
972     GST_ERROR_OBJECT (pvrvideosink, "%s (%d)",
973         pvr2dstrerr (pvr_error), pvr_error);
974   }
975   GST_DEBUG_OBJECT (pvrvideosink, "end");
976   g_mutex_unlock (dcontext->x_lock);
977   g_mutex_unlock (pvrvideosink->flow_lock);
978 }
979
980 static void
981 gst_pvrvideosink_destroy_drawable (GstPVRVideoSink * pvrvideosink)
982 {
983   if (pvrvideosink->dcontext != NULL) {
984     if (pvrvideosink->dcontext->drawable_handle)
985       pvrvideosink->dcontext->
986           wsegl_table->pfnWSEGL_DeleteDrawable (pvrvideosink->dcontext->
987           drawable_handle);
988
989     gst_display_handle_unref (pvrvideosink->dcontext->gst_display_handle);
990     pvrvideosink->dcontext->gst_display_handle = NULL;
991     pvrvideosink->dcontext->pvr_context = NULL;
992   }
993 }
994
995 /* We are called with the x_lock taken */
996 static void
997 gst_pvrvideosink_pvrfill_rectangle (GstPVRVideoSink * pvrvideosink,
998     GstVideoRectangle rect)
999 {
1000   PVR2DERROR pvr_error;
1001   PVR2DBLTINFO s_blt2d_info = { 0 };
1002   PPVR2DBLTINFO p_blt2d_info;
1003   GstDrawContext *dcontext = pvrvideosink->dcontext;
1004
1005   GST_DEBUG_OBJECT (pvrvideosink, "begin");
1006
1007   p_blt2d_info = &s_blt2d_info;
1008
1009   p_blt2d_info->pDstMemInfo = &dcontext->dst_mem;
1010   p_blt2d_info->BlitFlags = PVR2D_BLIT_DISABLE_ALL;
1011   p_blt2d_info->DstOffset = 0;
1012   p_blt2d_info->CopyCode = PVR2DROPclear;
1013   p_blt2d_info->DstStride =
1014       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_BGRx, 0,
1015       pvrvideosink->render_params.ui32Stride);
1016   p_blt2d_info->DstFormat = PVR2D_ARGB8888;
1017   p_blt2d_info->DstSurfWidth = pvrvideosink->xwindow->width;
1018   p_blt2d_info->DstSurfHeight = pvrvideosink->xwindow->height;
1019   p_blt2d_info->DstX = rect.x;
1020   p_blt2d_info->DstY = rect.y;
1021   p_blt2d_info->DSizeX = rect.w;
1022   p_blt2d_info->DSizeY = rect.h;
1023
1024   pvr_error = PVR2DBlt (dcontext->pvr_context, p_blt2d_info);
1025   if (pvr_error) {
1026     GST_ERROR_OBJECT (pvrvideosink, "%s (%d)",
1027         pvr2dstrerr (pvr_error), pvr_error);
1028   }
1029   GST_DEBUG_OBJECT (pvrvideosink, "end");
1030 }
1031
1032 /* We are called with the x_lock taken */
1033 static void
1034 gst_pvrvideosink_xwindow_draw_borders (GstPVRVideoSink * pvrvideosink,
1035     GstXWindow * xwindow, GstVideoRectangle rect)
1036 {
1037   gint t1, t2;
1038   GstVideoRectangle result;
1039
1040   g_return_if_fail (GST_IS_PVRVIDEOSINK (pvrvideosink));
1041   g_return_if_fail (xwindow != NULL);
1042
1043   /* Left border */
1044   result.x = pvrvideosink->render_rect.x;
1045   result.y = pvrvideosink->render_rect.y;
1046   result.w = rect.x - pvrvideosink->render_rect.x;
1047   result.h = pvrvideosink->render_rect.h;
1048   if (rect.x > pvrvideosink->render_rect.x)
1049     gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result);
1050
1051   /* Right border */
1052   t1 = rect.x + rect.w;
1053   t2 = pvrvideosink->render_rect.x + pvrvideosink->render_rect.w;
1054   result.x = t1;
1055   result.y = pvrvideosink->render_rect.y;
1056   result.w = t2 - t1;
1057   result.h = pvrvideosink->render_rect.h;
1058   if (t1 < t2)
1059     gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result);
1060
1061   /* Top border */
1062   result.x = pvrvideosink->render_rect.x;
1063   result.y = pvrvideosink->render_rect.y;
1064   result.w = pvrvideosink->render_rect.w;
1065   result.h = rect.y - pvrvideosink->render_rect.y;
1066   if (rect.y > pvrvideosink->render_rect.y)
1067     gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result);
1068
1069   /* Bottom border */
1070   t1 = rect.y + rect.h;
1071   t2 = pvrvideosink->render_rect.y + pvrvideosink->render_rect.h;
1072   result.x = pvrvideosink->render_rect.x;
1073   result.y = t1;
1074   result.w = pvrvideosink->render_rect.w;
1075   result.h = t2 - t1;
1076   if (t1 < t2)
1077     gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result);
1078
1079   pvr_swap_buffers (pvrvideosink);
1080 }
1081
1082 /* Element stuff */
1083
1084 static gboolean
1085 gst_pvrvideosink_configure_overlay (GstPVRVideoSink * pvrvideosink, gint width,
1086     gint height, gint video_par_n, gint video_par_d, gint display_par_n,
1087     gint display_par_d)
1088 {
1089   guint calculated_par_n;
1090   guint calculated_par_d;
1091
1092   if (!gst_video_calculate_display_ratio (&calculated_par_n, &calculated_par_d,
1093           width, height, video_par_n, video_par_d, display_par_n,
1094           display_par_d)) {
1095     GST_ELEMENT_ERROR (pvrvideosink, CORE, NEGOTIATION, (NULL),
1096         ("Error calculating the output display ratio of the video."));
1097     return FALSE;
1098   }
1099
1100   GST_DEBUG_OBJECT (pvrvideosink,
1101       "video width/height: %dx%d, calculated display ratio: %d/%d",
1102       width, height, calculated_par_n, calculated_par_d);
1103
1104   /* now find a width x height that respects this display ratio.
1105    * prefer those that have one of w/h the same as the incoming video
1106    * using wd / hd = calculated_pad_n / calculated_par_d */
1107
1108   /* start with same height, because of interlaced video */
1109   /* check hd / calculated_par_d is an integer scale factor, and scale wd with the PAR */
1110   if (height % calculated_par_d == 0) {
1111     GST_DEBUG_OBJECT (pvrvideosink, "keeping video height");
1112     GST_VIDEO_SINK_WIDTH (pvrvideosink) = (guint)
1113         gst_util_uint64_scale_int (height, calculated_par_n, calculated_par_d);
1114     GST_VIDEO_SINK_HEIGHT (pvrvideosink) = height;
1115   } else if (width % calculated_par_n == 0) {
1116     GST_DEBUG_OBJECT (pvrvideosink, "keeping video width");
1117     GST_VIDEO_SINK_WIDTH (pvrvideosink) = width;
1118     GST_VIDEO_SINK_HEIGHT (pvrvideosink) = (guint)
1119         gst_util_uint64_scale_int (width, calculated_par_d, calculated_par_n);
1120   } else {
1121     GST_DEBUG_OBJECT (pvrvideosink, "approximating while keeping video height");
1122     GST_VIDEO_SINK_WIDTH (pvrvideosink) = (guint)
1123         gst_util_uint64_scale_int (height, calculated_par_n, calculated_par_d);
1124     GST_VIDEO_SINK_HEIGHT (pvrvideosink) = height;
1125   }
1126   GST_DEBUG_OBJECT (pvrvideosink, "scaling to %dx%d",
1127       GST_VIDEO_SINK_WIDTH (pvrvideosink),
1128       GST_VIDEO_SINK_HEIGHT (pvrvideosink));
1129
1130   return TRUE;
1131 }
1132
1133 static gboolean
1134 gst_pvrvideosink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1135 {
1136   GstPVRVideoSink *pvrvideosink;
1137   gboolean ret = TRUE;
1138   GstStructure *structure;
1139   gint width, height;
1140   const GValue *fps;
1141   const GValue *caps_par;
1142   GstQuery *query;
1143
1144   pvrvideosink = GST_PVRVIDEOSINK (bsink);
1145
1146   GST_DEBUG_OBJECT (pvrvideosink,
1147       "sinkconnect possible caps with given caps %", caps);
1148
1149   if (pvrvideosink->current_caps) {
1150     GST_DEBUG_OBJECT (pvrvideosink, "already have caps set");
1151     if (gst_caps_is_equal (pvrvideosink->current_caps, caps)) {
1152       GST_DEBUG_OBJECT (pvrvideosink, "caps are equal!");
1153       return TRUE;
1154     }
1155     GST_DEBUG_OBJECT (pvrvideosink, "caps are different");
1156   }
1157
1158   structure = gst_caps_get_structure (caps, 0);
1159
1160   ret = gst_video_format_parse_caps_strided (caps, &pvrvideosink->format,
1161       &width, &height, &pvrvideosink->rowstride);
1162   if (pvrvideosink->rowstride == 0)
1163     pvrvideosink->rowstride =
1164         gst_video_format_get_row_stride (pvrvideosink->format, 0, width);
1165   fps = gst_structure_get_value (structure, "framerate");
1166   ret &= (fps != NULL);
1167   if (!ret) {
1168     GST_ERROR_OBJECT (pvrvideosink, "problem at parsing caps");
1169     return FALSE;
1170   }
1171
1172   pvrvideosink->video_width = width;
1173   pvrvideosink->video_height = height;
1174
1175   /* figure out if we are dealing w/ interlaced */
1176   pvrvideosink->interlaced = FALSE;
1177   gst_structure_get_boolean (structure, "interlaced",
1178       &pvrvideosink->interlaced);
1179
1180   /* get video's pixel-aspect-ratio */
1181   caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1182   if (caps_par) {
1183     pvrvideosink->video_par_n = gst_value_get_fraction_numerator (caps_par);
1184     pvrvideosink->video_par_d = gst_value_get_fraction_denominator (caps_par);
1185   } else {
1186     pvrvideosink->video_par_n = 1;
1187     pvrvideosink->video_par_d = 1;
1188   }
1189
1190   /* get display's pixel-aspect-ratio */
1191   if (pvrvideosink->display_par) {
1192     pvrvideosink->display_par_n =
1193         gst_value_get_fraction_numerator (pvrvideosink->display_par);
1194     pvrvideosink->display_par_d =
1195         gst_value_get_fraction_denominator (pvrvideosink->display_par);
1196   } else {
1197     pvrvideosink->display_par_n = 1;
1198     pvrvideosink->display_par_d = 1;
1199   }
1200
1201   if (!gst_pvrvideosink_configure_overlay (pvrvideosink, width, height,
1202           pvrvideosink->video_par_n, pvrvideosink->video_par_d,
1203           pvrvideosink->display_par_n, pvrvideosink->display_par_d))
1204     return FALSE;
1205
1206   g_mutex_lock (pvrvideosink->pool_lock);
1207   if (pvrvideosink->buffer_pool) {
1208     if (gst_drm_buffer_pool_check_caps (pvrvideosink->buffer_pool, caps)) {
1209       GST_INFO_OBJECT (pvrvideosink, "in set caps, pool->caps != caps");
1210       gst_drm_buffer_pool_destroy (pvrvideosink->buffer_pool);
1211       pvrvideosink->buffer_pool = NULL;
1212     }
1213   }
1214   g_mutex_unlock (pvrvideosink->pool_lock);
1215
1216   /* query to find if anyone upstream using these buffers has any
1217    * minimum requirements:
1218    */
1219   query = gst_query_new_buffers (caps);
1220   if (gst_element_query (GST_ELEMENT (pvrvideosink), query)) {
1221     gint min_buffers;
1222
1223     gst_query_parse_buffers_count (query, &min_buffers);
1224
1225     GST_DEBUG_OBJECT (pvrvideosink, "min_buffers=%d", min_buffers);
1226
1227     /* XXX need to account for some buffers used by queue, etc.. probably
1228      * queue should handle query, pass on to sink pad, and then add some
1229      * number of buffers to the min, so this value is dynamic depending
1230      * on the pipeline?
1231      */
1232     if (min_buffers != -1) {
1233       min_buffers += 3 + pvrvideosink->min_queued_bufs;
1234       pvrvideosink->num_buffers_can_change = FALSE;
1235     }
1236
1237     if (min_buffers > pvrvideosink->num_buffers) {
1238       pvrvideosink->num_buffers = min_buffers;
1239     }
1240   }
1241   gst_query_unref (query);
1242
1243   /* Notify application to set xwindow id now */
1244   g_mutex_lock (pvrvideosink->flow_lock);
1245   if (!pvrvideosink->xwindow) {
1246     g_mutex_unlock (pvrvideosink->flow_lock);
1247     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (pvrvideosink));
1248   } else {
1249     g_mutex_unlock (pvrvideosink->flow_lock);
1250   }
1251
1252   g_mutex_lock (pvrvideosink->flow_lock);
1253   if (!pvrvideosink->xwindow) {
1254     pvrvideosink->xwindow = gst_pvrvideosink_create_window (pvrvideosink,
1255         GST_VIDEO_SINK_WIDTH (pvrvideosink),
1256         GST_VIDEO_SINK_HEIGHT (pvrvideosink));
1257     gst_pvrvideosink_xwindow_update_geometry (pvrvideosink);
1258   }
1259
1260   g_mutex_unlock (pvrvideosink->flow_lock);
1261
1262   gst_pvrvideosink_set_event_handling (GST_X_OVERLAY (pvrvideosink), TRUE);
1263
1264   pvrvideosink->fps_n = gst_value_get_fraction_numerator (fps);
1265   pvrvideosink->fps_d = gst_value_get_fraction_denominator (fps);
1266
1267   pvrvideosink->current_caps = gst_caps_ref (caps);
1268
1269   return TRUE;
1270 }
1271
1272 static GstCaps *
1273 gst_pvrvideosink_getcaps (GstBaseSink * bsink)
1274 {
1275   GstPVRVideoSink *pvrvideosink;
1276   GstCaps *caps;
1277
1278   pvrvideosink = GST_PVRVIDEOSINK (bsink);
1279
1280   caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK
1281           (pvrvideosink)->sinkpad));
1282   return caps;
1283 }
1284
1285 static GstStateChangeReturn
1286 gst_pvrvideosink_change_state (GstElement * element, GstStateChange transition)
1287 {
1288   GstPVRVideoSink *pvrvideosink;
1289   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1290   GstDrawContext *dcontext;
1291
1292   pvrvideosink = GST_PVRVIDEOSINK (element);
1293
1294   switch (transition) {
1295     case GST_STATE_CHANGE_NULL_TO_READY:
1296       if (pvrvideosink->dcontext == NULL) {
1297         dcontext = gst_pvrvideosink_get_dcontext (pvrvideosink);
1298         if (dcontext == NULL)
1299           return GST_STATE_CHANGE_FAILURE;
1300         GST_OBJECT_LOCK (pvrvideosink);
1301         pvrvideosink->dcontext = dcontext;
1302         GST_OBJECT_UNLOCK (pvrvideosink);
1303       }
1304
1305       /* update object's pixel-aspect-ratio with calculated one */
1306       if (!pvrvideosink->display_par) {
1307         pvrvideosink->display_par = g_new0 (GValue, 1);
1308         gst_value_init_and_copy (pvrvideosink->display_par,
1309             pvrvideosink->dcontext->par);
1310         GST_DEBUG_OBJECT (pvrvideosink, "set calculated PAR on object's PAR");
1311       }
1312
1313       gst_pvrvideosink_manage_event_thread (pvrvideosink);
1314       break;
1315     case GST_STATE_CHANGE_READY_TO_PAUSED:
1316       g_mutex_lock (pvrvideosink->pool_lock);
1317       pvrvideosink->pool_invalid = FALSE;
1318       g_mutex_unlock (pvrvideosink->pool_lock);
1319       break;
1320     case GST_STATE_CHANGE_PAUSED_TO_READY:
1321       g_mutex_lock (pvrvideosink->pool_lock);
1322       pvrvideosink->pool_invalid = TRUE;
1323       g_mutex_unlock (pvrvideosink->pool_lock);
1324       break;
1325     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1326       break;
1327     default:
1328       break;
1329   }
1330
1331   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1332
1333   switch (transition) {
1334     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1335       break;
1336     case GST_STATE_CHANGE_PAUSED_TO_READY:
1337       pvrvideosink->fps_n = 0;
1338       pvrvideosink->fps_d = 1;
1339       GST_VIDEO_SINK_WIDTH (pvrvideosink) = 0;
1340       GST_VIDEO_SINK_HEIGHT (pvrvideosink) = 0;
1341       break;
1342     case GST_STATE_CHANGE_READY_TO_NULL:
1343       gst_pvrvideosink_reset (pvrvideosink);
1344       break;
1345     default:
1346       break;
1347   }
1348
1349   return ret;
1350 }
1351
1352 static void
1353 gst_pvrvideosink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1354     GstClockTime * start, GstClockTime * end)
1355 {
1356   GstPVRVideoSink *pvrvideosink;
1357
1358   pvrvideosink = GST_PVRVIDEOSINK (bsink);
1359
1360   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1361     *start = GST_BUFFER_TIMESTAMP (buf);
1362     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1363       *end = *start + GST_BUFFER_DURATION (buf);
1364     } else {
1365       if (pvrvideosink->fps_n > 0) {
1366         *end = *start +
1367             gst_util_uint64_scale_int (GST_SECOND, pvrvideosink->fps_d,
1368             pvrvideosink->fps_n);
1369       }
1370     }
1371   }
1372 }
1373
1374 static gboolean
1375 gst_pvrvideosink_event (GstBaseSink * bsink, GstEvent * event)
1376 {
1377   gboolean res;
1378   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (bsink);
1379   GstStructure *structure;
1380   GstMessage *message;
1381
1382   switch (GST_EVENT_TYPE (event)) {
1383     case GST_EVENT_CROP:
1384     {
1385       gint left, top, width, height;
1386
1387       PVR2DRECT *c = &pvrvideosink->crop;
1388       gst_event_parse_crop (event, &top, &left, &width, &height);
1389       c->top = top;
1390       c->left = left;
1391       if (width == -1) {
1392         c->right = GST_VIDEO_SINK_WIDTH (pvrvideosink);
1393         width = GST_VIDEO_SINK_WIDTH (pvrvideosink);
1394       } else {
1395         c->right = left + width;
1396       }
1397
1398       if (height >= 0) {
1399         if (pvrvideosink->current_caps) {
1400           if (pvrvideosink->interlaced)
1401             height *= 2;
1402         }
1403         c->bottom = top + height;
1404       } else {
1405         c->bottom = GST_VIDEO_SINK_HEIGHT (pvrvideosink);
1406         height = GST_VIDEO_SINK_HEIGHT (pvrvideosink);
1407       }
1408
1409       structure = gst_structure_new ("video-size-crop", "width", G_TYPE_INT,
1410           width, "height", G_TYPE_INT, height, NULL);
1411       message = gst_message_new_application (GST_OBJECT (pvrvideosink),
1412           structure);
1413       gst_bus_post (gst_element_get_bus (GST_ELEMENT (pvrvideosink)), message);
1414
1415
1416       if (!gst_pvrvideosink_configure_overlay (pvrvideosink, width, height,
1417               pvrvideosink->video_par_n, pvrvideosink->video_par_d,
1418               pvrvideosink->display_par_n, pvrvideosink->display_par_d))
1419         return FALSE;
1420       break;
1421     }
1422     default:
1423       res = TRUE;
1424   }
1425
1426   return res;
1427 }
1428
1429 static GstFlowReturn
1430 gst_pvrvideosink_show_frame (GstBaseSink * vsink, GstBuffer * buf)
1431 {
1432   GstPVRVideoSink *pvrvideosink;
1433   GstPVRBufferPriv *buf_priv;
1434   GstBuffer *newbuf = NULL;
1435   gboolean try_fallback = TRUE;
1436
1437   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1438
1439   pvrvideosink = GST_PVRVIDEOSINK (vsink);
1440
1441   GST_DEBUG_OBJECT (pvrvideosink, "render buffer: %p", buf);
1442
1443 import_buf:
1444   buf_priv = gst_pvr_buffer_priv (pvrvideosink, buf);
1445   if (buf_priv == NULL && try_fallback) {
1446     GstFlowReturn ret;
1447
1448     /* special case check for sub-buffers:  In certain cases, places like
1449      * GstBaseTransform, which might check that the buffer is writable
1450      * before copying metadata, timestamp, and such, will find that the
1451      * buffer has more than one reference to it.  In these cases, they
1452      * will create a sub-buffer with an offset=0 and length equal to the
1453      * original buffer size.
1454      *
1455      * This could happen in two scenarios: (1) a tee in the pipeline, and
1456      * (2) because the refcnt is incremented in gst_mini_object_free()
1457      * before the finalize function is called, and decremented after it
1458      * returns..  but returning this buffer to the buffer pool in the
1459      * finalize function, could wake up a thread blocked in _buffer_alloc()
1460      * which could run and get a buffer w/ refcnt==2 before the thread
1461      * originally unref'ing the buffer returns from finalize function and
1462      * decrements the refcnt back to 1!
1463      */
1464     if (buf->parent &&
1465         (GST_BUFFER_DATA (buf) == GST_BUFFER_DATA (buf->parent)) &&
1466         (GST_BUFFER_SIZE (buf) == GST_BUFFER_SIZE (buf->parent))) {
1467       GST_DEBUG_OBJECT (pvrvideosink, "I have a sub-buffer!");
1468       return gst_pvrvideosink_show_frame (vsink, buf->parent);
1469     }
1470
1471     GST_DEBUG_OBJECT (pvrvideosink,
1472         "slow-path.. I got a %s so I need to memcpy",
1473         g_type_name (G_OBJECT_TYPE (buf)));
1474
1475     ret = gst_pvrvideosink_buffer_alloc (GST_BASE_SINK (vsink),
1476         GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf), GST_BUFFER_CAPS (buf),
1477         &newbuf);
1478
1479     if (GST_FLOW_OK != ret) {
1480       GST_ERROR_OBJECT (pvrvideosink, "dropping frame!!");
1481       return GST_FLOW_OK;
1482     }
1483
1484     memcpy (GST_BUFFER_DATA (newbuf),
1485         GST_BUFFER_DATA (buf),
1486         MIN (GST_BUFFER_SIZE (newbuf), GST_BUFFER_SIZE (buf)));
1487
1488     GST_DEBUG_OBJECT (pvrvideosink, "render copied buffer: %p", newbuf);
1489
1490     buf = newbuf;
1491     try_fallback = FALSE;
1492     goto import_buf;
1493   }
1494
1495   if (buf_priv == NULL) {
1496     /* FIXME: make this an error */
1497     GST_ERROR_OBJECT (pvrvideosink, "dropping frame");
1498     if (newbuf) {
1499       gst_buffer_unref (newbuf);
1500     }
1501     return GST_FLOW_OK;
1502   }
1503
1504   gst_pvrvideosink_blit (pvrvideosink, buf);
1505
1506   if (newbuf) {
1507     gst_buffer_unref (newbuf);
1508   }
1509
1510   return GST_FLOW_OK;
1511 }
1512
1513
1514 /* Buffer management
1515  *
1516  * The buffer_alloc function must either return a buffer with given size and
1517  * caps or create a buffer with different caps attached to the buffer. This
1518  * last option is called reverse negotiation, ie, where the sink suggests a
1519  * different format from the upstream peer. 
1520  *
1521  * We try to do reverse negotiation when our geometry changes and we like a
1522  * resized buffer.
1523  */
1524 static GstFlowReturn
1525 gst_pvrvideosink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1526     GstCaps * caps, GstBuffer ** buf)
1527 {
1528   GstPVRVideoSink *pvrvideosink;
1529   GstBuffer *pvrvideo = NULL;
1530   GstFlowReturn ret = GST_FLOW_OK;
1531
1532   pvrvideosink = GST_PVRVIDEOSINK (bsink);
1533
1534   GST_DEBUG_OBJECT (pvrvideosink, "begin");
1535
1536   if (G_UNLIKELY (!caps)) {
1537     GST_WARNING_OBJECT (pvrvideosink,
1538         "have no caps, doing fallback allocation");
1539     *buf = NULL;
1540     ret = GST_FLOW_OK;
1541     goto beach;
1542   }
1543
1544   g_mutex_lock (pvrvideosink->pool_lock);
1545   if (G_UNLIKELY (pvrvideosink->pool_invalid)) {
1546     GST_DEBUG_OBJECT (pvrvideosink, "the pool is flushing");
1547     ret = GST_FLOW_WRONG_STATE;
1548     g_mutex_unlock (pvrvideosink->pool_lock);
1549     goto beach;
1550   }
1551
1552   GST_LOG_OBJECT (pvrvideosink,
1553       "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1554       " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1555
1556   /* initialize the buffer pool if not initialized yet */
1557   if (G_UNLIKELY (!pvrvideosink->buffer_pool ||
1558           gst_drm_buffer_pool_size (pvrvideosink->buffer_pool) != size)) {
1559     if (pvrvideosink->buffer_pool) {
1560       GST_INFO_OBJECT (pvrvideosink, "in buffer alloc, pool->size != size");
1561       gst_drm_buffer_pool_destroy (pvrvideosink->buffer_pool);
1562     }
1563
1564     GST_LOG_OBJECT (pvrvideosink, "Creating a buffer pool with %d buffers",
1565         pvrvideosink->num_buffers);
1566     if (!(pvrvideosink->buffer_pool =
1567             gst_drm_buffer_pool_new (GST_ELEMENT (pvrvideosink),
1568                 pvrvideosink->drm_fd, caps, size))) {
1569       g_mutex_unlock (pvrvideosink->pool_lock);
1570       return GST_FLOW_ERROR;
1571     }
1572   }
1573   pvrvideo = gst_drm_buffer_pool_get (pvrvideosink->buffer_pool, FALSE);
1574   g_mutex_unlock (pvrvideosink->pool_lock);
1575
1576   *buf = GST_BUFFER_CAST (pvrvideo);
1577
1578 beach:
1579   return ret;
1580 }
1581
1582 /* Interfaces stuff */
1583
1584 static gboolean
1585 gst_pvrvideosink_interface_supported (GstImplementsInterface * iface,
1586     GType type)
1587 {
1588   if (type == GST_TYPE_X_OVERLAY || type == GST_TYPE_NAVIGATION)
1589     return TRUE;
1590   else
1591     return FALSE;
1592 }
1593
1594 static void
1595 gst_pvrvideosink_interface_init (GstImplementsInterfaceClass * klass)
1596 {
1597   klass->supported = gst_pvrvideosink_interface_supported;
1598 }
1599
1600 /* This function destroys a GstXWindow */
1601 static void
1602 gst_pvrvideosink_xwindow_destroy (GstPVRVideoSink * pvrvideosink,
1603     GstXWindow * xwindow)
1604 {
1605   g_return_if_fail (xwindow != NULL);
1606
1607   g_mutex_lock (pvrvideosink->dcontext->x_lock);
1608
1609   /* If we did not create that window we just free the GC and let it live */
1610   if (xwindow->internal)
1611     XDestroyWindow (pvrvideosink->dcontext->x_display, xwindow->window);
1612   else
1613     XSelectInput (pvrvideosink->dcontext->x_display, xwindow->window, 0);
1614
1615   XFreeGC (pvrvideosink->dcontext->x_display, xwindow->gc);
1616
1617   XSync (pvrvideosink->dcontext->x_display, FALSE);
1618
1619   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
1620
1621   g_free (xwindow);
1622 }
1623
1624 /*
1625  * GstXOverlay Interface:
1626  */
1627
1628 static void
1629 gst_pvrvideosink_set_window_handle (GstXOverlay * overlay, guintptr id)
1630 {
1631   XID xwindow_id = id;
1632   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay);
1633   GstXWindow *xwindow = NULL;
1634
1635   g_return_if_fail (GST_IS_PVRVIDEOSINK (pvrvideosink));
1636
1637   g_mutex_lock (pvrvideosink->flow_lock);
1638
1639   /* If we already use that window return */
1640   if (pvrvideosink->xwindow && (xwindow_id == pvrvideosink->xwindow->window)) {
1641     g_mutex_unlock (pvrvideosink->flow_lock);
1642     return;
1643   }
1644
1645   /* If the element has not initialized the X11 context try to do so */
1646   if (!pvrvideosink->dcontext && !(pvrvideosink->dcontext =
1647           gst_pvrvideosink_get_dcontext (pvrvideosink))) {
1648     g_mutex_unlock (pvrvideosink->flow_lock);
1649     /* we have thrown a GST_ELEMENT_ERROR now */
1650     return;
1651   }
1652
1653   /* Clear image pool as the images are unusable anyway */
1654   g_mutex_lock (pvrvideosink->pool_lock);
1655   if (pvrvideosink->buffer_pool) {
1656     gst_drm_buffer_pool_destroy (pvrvideosink->buffer_pool);
1657     pvrvideosink->buffer_pool = NULL;
1658   }
1659   g_mutex_unlock (pvrvideosink->pool_lock);
1660
1661   /* If a window is there already we destroy it */
1662   if (pvrvideosink->xwindow) {
1663     gst_pvrvideosink_xwindow_destroy (pvrvideosink, pvrvideosink->xwindow);
1664     pvrvideosink->xwindow = NULL;
1665   }
1666
1667   /* If the xid is 0 we will create an internal one in buffer_alloc */
1668   if (xwindow_id != 0) {
1669     XWindowAttributes attr;
1670
1671     xwindow = g_new0 (GstXWindow, 1);
1672     xwindow->window = xwindow_id;
1673
1674     /* Set the event we want to receive and create a GC */
1675     g_mutex_lock (pvrvideosink->dcontext->x_lock);
1676
1677     XGetWindowAttributes (pvrvideosink->dcontext->x_display, xwindow->window,
1678         &attr);
1679
1680     xwindow->width = attr.width;
1681     xwindow->height = attr.height;
1682     xwindow->internal = FALSE;
1683     if (!pvrvideosink->have_render_rect) {
1684       pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0;
1685       pvrvideosink->render_rect.w = attr.width;
1686       pvrvideosink->render_rect.h = attr.height;
1687     }
1688
1689     XSetWindowBackgroundPixmap (pvrvideosink->dcontext->x_display,
1690         xwindow->window, None);
1691
1692     XMapWindow (pvrvideosink->dcontext->x_display, xwindow->window);
1693     xwindow->gc = XCreateGC (pvrvideosink->dcontext->x_display,
1694         xwindow->window, 0, NULL);
1695     g_mutex_unlock (pvrvideosink->dcontext->x_lock);
1696
1697     pvrvideosink->xwindow = xwindow;
1698     pvr_recreate_drawable (pvrvideosink);
1699     pvr_get_drawable_params (pvrvideosink);
1700   }
1701
1702   g_mutex_unlock (pvrvideosink->flow_lock);
1703
1704   gst_pvrvideosink_set_event_handling (overlay, TRUE);
1705 }
1706
1707 static void
1708 gst_pvrvideosink_expose (GstXOverlay * overlay)
1709 {
1710   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay);
1711
1712   gst_pvrvideosink_blit (pvrvideosink, NULL);
1713 }
1714
1715 static void
1716 gst_pvrvideosink_set_event_handling (GstXOverlay * overlay,
1717     gboolean handle_events)
1718 {
1719   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay);
1720   long event_mask;
1721
1722   g_mutex_lock (pvrvideosink->flow_lock);
1723
1724   if (G_UNLIKELY (!pvrvideosink->xwindow)) {
1725     g_mutex_unlock (pvrvideosink->flow_lock);
1726     return;
1727   }
1728
1729   g_mutex_lock (pvrvideosink->dcontext->x_lock);
1730
1731   event_mask = ExposureMask | StructureNotifyMask |
1732       PointerMotionMask | KeyPressMask | KeyReleaseMask;
1733
1734   if (pvrvideosink->xwindow->internal) {
1735     event_mask |= ButtonPressMask | ButtonReleaseMask;
1736   }
1737
1738   XSelectInput (pvrvideosink->dcontext->x_display,
1739       pvrvideosink->xwindow->window, event_mask);
1740
1741   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
1742
1743   g_mutex_unlock (pvrvideosink->flow_lock);
1744 }
1745
1746 static void
1747 gst_pvrvideosink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
1748     gint width, gint height)
1749 {
1750   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay);
1751
1752   /* FIXME: how about some locking? */
1753   if (width >= 0 && height >= 0) {
1754     pvrvideosink->render_rect.x = x;
1755     pvrvideosink->render_rect.y = y;
1756     pvrvideosink->render_rect.w = width;
1757     pvrvideosink->render_rect.h = height;
1758     pvrvideosink->have_render_rect = TRUE;
1759   } else {
1760     pvrvideosink->render_rect.x = 0;
1761     pvrvideosink->render_rect.y = 0;
1762     pvrvideosink->render_rect.w = pvrvideosink->xwindow->width;
1763     pvrvideosink->render_rect.h = pvrvideosink->xwindow->height;
1764     pvrvideosink->have_render_rect = FALSE;
1765   }
1766   GST_DEBUG_OBJECT (pvrvideosink, "render_rect is %dX%d",
1767       pvrvideosink->render_rect.w, pvrvideosink->render_rect.h);
1768 }
1769
1770 static void
1771 gst_pvrvideosink_xoverlay_init (GstXOverlayClass * iface)
1772 {
1773   iface->set_window_handle = gst_pvrvideosink_set_window_handle;
1774   iface->expose = gst_pvrvideosink_expose;
1775   iface->handle_events = gst_pvrvideosink_set_event_handling;
1776   iface->set_render_rectangle = gst_pvrvideosink_set_render_rectangle;
1777 }
1778
1779 /*
1780  * GstNavigation Interface:
1781  */
1782
1783 static void
1784 gst_pvrvideosink_send_event (GstNavigation * navigation,
1785     GstStructure * structure)
1786 {
1787   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (navigation);
1788   GstPad *peer;
1789
1790   if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (pvrvideosink)))) {
1791     GstVideoRectangle result;
1792     gdouble x, y, xscale = 1.0, yscale = 1.0;
1793
1794     if (pvrvideosink->keep_aspect) {
1795       GstVideoRectangle src = {
1796         .w = GST_VIDEO_SINK_WIDTH (pvrvideosink),
1797         .h = GST_VIDEO_SINK_HEIGHT (pvrvideosink),
1798       };
1799       GstVideoRectangle dst = {
1800         .w = pvrvideosink->render_rect.w,
1801         .h = pvrvideosink->render_rect.h,
1802       };
1803
1804       gst_video_sink_center_rect (src, dst, &result, TRUE);
1805       result.x += pvrvideosink->render_rect.x;
1806       result.y += pvrvideosink->render_rect.y;
1807     } else {
1808       result = pvrvideosink->render_rect;
1809     }
1810
1811     /* We calculate scaling using the original video frames geometry to
1812      * include pixel aspect ratio scaling.
1813      */
1814     xscale = (gdouble) pvrvideosink->video_width / result.w;
1815     yscale = (gdouble) pvrvideosink->video_height / result.h;
1816
1817     /* Note: this doesn't account for crop top/left offsets.. which
1818      * is probably not quite right.. OTOH, I don't think the ducati
1819      * decoder elements subtract back out the crop offsets as the
1820      * event propagates upstream, so as long as the one receiving
1821      * the event is upstream of the decoder, the net effect will be
1822      * correct..  although this might be worth fixing correctly at
1823      * some point.
1824      */
1825
1826     /* Converting pointer coordinates to the non scaled geometry */
1827     if (gst_structure_get_double (structure, "pointer_x", &x)) {
1828       x = MIN (x, result.x + result.w);
1829       x = MAX (x - result.x, 0);
1830       gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1831           (gdouble) x * xscale, NULL);
1832     }
1833     if (gst_structure_get_double (structure, "pointer_y", &y)) {
1834       y = MIN (y, result.y + result.h);
1835       y = MAX (y - result.y, 0);
1836       gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1837           (gdouble) y * yscale, NULL);
1838     }
1839
1840     gst_pad_send_event (peer, gst_event_new_navigation (structure));
1841     gst_object_unref (peer);
1842   }
1843 }
1844
1845 static void
1846 gst_pvrvideosink_navigation_init (GstNavigationInterface * iface)
1847 {
1848   iface->send_event = gst_pvrvideosink_send_event;
1849 }
1850
1851 /* =========================================== */
1852 /*                                             */
1853 /*              Init & Class init              */
1854 /*                                             */
1855 /* =========================================== */
1856
1857 static void
1858 gst_pvrvideosink_set_property (GObject * object, guint prop_id,
1859     const GValue * value, GParamSpec * pspec)
1860 {
1861   GstPVRVideoSink *pvrvideosink;
1862
1863   g_return_if_fail (GST_IS_PVRVIDEOSINK (object));
1864
1865   pvrvideosink = GST_PVRVIDEOSINK (object);
1866
1867   switch (prop_id) {
1868     case PROP_FORCE_ASPECT_RATIO:
1869       pvrvideosink->keep_aspect = g_value_get_boolean (value);
1870       break;
1871     default:
1872       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1873       break;
1874   }
1875 }
1876
1877 static void
1878 gst_pvrvideosink_get_property (GObject * object, guint prop_id,
1879     GValue * value, GParamSpec * pspec)
1880 {
1881   GstPVRVideoSink *pvrvideosink;
1882
1883   g_return_if_fail (GST_IS_PVRVIDEOSINK (object));
1884
1885   pvrvideosink = GST_PVRVIDEOSINK (object);
1886
1887   switch (prop_id) {
1888     case PROP_FORCE_ASPECT_RATIO:
1889       g_value_set_boolean (value, pvrvideosink->keep_aspect);
1890       break;
1891     default:
1892       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1893       break;
1894   }
1895 }
1896
1897 static void
1898 gst_pvrvideosink_dcontext_clear (GstPVRVideoSink * pvrvideosink)
1899 {
1900   GstDrawContext *dcontext;
1901
1902   GST_OBJECT_LOCK (pvrvideosink);
1903   if (!pvrvideosink->dcontext) {
1904     GST_OBJECT_UNLOCK (pvrvideosink);
1905     return;
1906   }
1907
1908   dcontext = pvrvideosink->dcontext;
1909   pvrvideosink->dcontext = NULL;
1910   GST_OBJECT_UNLOCK (pvrvideosink);
1911
1912   g_free (dcontext->par);
1913
1914   g_mutex_lock (dcontext->x_lock);
1915   XCloseDisplay (dcontext->x_display);
1916   g_mutex_unlock (dcontext->x_lock);
1917   g_mutex_free (dcontext->x_lock);
1918
1919   g_free (dcontext);
1920 }
1921
1922 static void
1923 gst_pvrvideosink_reset (GstPVRVideoSink * pvrvideosink)
1924 {
1925   GThread *thread;
1926
1927   GST_OBJECT_LOCK (pvrvideosink);
1928   pvrvideosink->running = FALSE;
1929   thread = pvrvideosink->event_thread;
1930   pvrvideosink->event_thread = NULL;
1931   GST_OBJECT_UNLOCK (pvrvideosink);
1932
1933   if (thread)
1934     g_thread_join (thread);
1935
1936   if (pvrvideosink->current_buffer) {
1937     gst_buffer_unref (pvrvideosink->current_buffer);
1938     pvrvideosink->current_buffer = NULL;
1939   }
1940
1941   g_mutex_lock (pvrvideosink->pool_lock);
1942   pvrvideosink->pool_invalid = TRUE;
1943   if (pvrvideosink->buffer_pool) {
1944     gst_drm_buffer_pool_destroy (pvrvideosink->buffer_pool);
1945     pvrvideosink->buffer_pool = NULL;
1946   }
1947   g_mutex_unlock (pvrvideosink->pool_lock);
1948   memset (&pvrvideosink->render_params, 0, sizeof (WSEGLDrawableParams));
1949
1950   pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0;
1951   pvrvideosink->render_rect.w = pvrvideosink->render_rect.h = 0;
1952   pvrvideosink->have_render_rect = FALSE;
1953
1954   gst_pvrvideosink_destroy_drawable (pvrvideosink);
1955
1956   if (pvrvideosink->xwindow) {
1957     gst_pvrvideosink_xwindow_destroy (pvrvideosink, pvrvideosink->xwindow);
1958     pvrvideosink->xwindow = NULL;
1959   }
1960
1961   g_free (pvrvideosink->display_par);
1962   pvrvideosink->display_par = NULL;
1963   gst_pvrvideosink_dcontext_clear (pvrvideosink);
1964   memset (&pvrvideosink->crop, 0, sizeof (PVR2DRECT));
1965 }
1966
1967 static void
1968 gst_pvrvideosink_finalize (GObject * object)
1969 {
1970   GstPVRVideoSink *pvrvideosink;
1971
1972   pvrvideosink = GST_PVRVIDEOSINK (object);
1973
1974   gst_pvrvideosink_reset (pvrvideosink);
1975
1976   if (pvrvideosink->flow_lock) {
1977     g_mutex_free (pvrvideosink->flow_lock);
1978     pvrvideosink->flow_lock = NULL;
1979   }
1980   if (pvrvideosink->pool_lock) {
1981     g_mutex_free (pvrvideosink->pool_lock);
1982     pvrvideosink->pool_lock = NULL;
1983   }
1984
1985   G_OBJECT_CLASS (parent_class)->finalize (object);
1986 }
1987
1988 static void
1989 gst_pvrvideosink_init (GstPVRVideoSink * pvrvideosink)
1990 {
1991   pvrvideosink->running = FALSE;
1992
1993   pvrvideosink->fps_n = 0;
1994   pvrvideosink->fps_d = 1;
1995   pvrvideosink->video_width = 0;
1996   pvrvideosink->video_height = 0;
1997
1998   pvrvideosink->flow_lock = g_mutex_new ();
1999   pvrvideosink->pool_lock = g_mutex_new ();
2000   pvrvideosink->buffer_pool = NULL;
2001   pvrvideosink->pool_invalid = TRUE;
2002
2003   pvrvideosink->keep_aspect = FALSE;
2004   pvrvideosink->current_caps = NULL;
2005   pvrvideosink->num_buffers = DEFAULT_QUEUE_SIZE;
2006   pvrvideosink->num_buffers_can_change = TRUE;
2007   pvrvideosink->min_queued_bufs = DEFAULT_MIN_QUEUED_BUFS;
2008   pvrvideosink->dcontext = NULL;
2009   pvrvideosink->xwindow = NULL;
2010   pvrvideosink->redraw_borders = 2;
2011   pvrvideosink->current_buffer = NULL;
2012   pvrvideosink->event_thread = NULL;
2013   pvrvideosink->display_par = NULL;
2014   memset (&pvrvideosink->render_params, 0, sizeof (WSEGLDrawableParams));
2015   memset (&pvrvideosink->crop, 0, sizeof (PVR2DRECT));
2016 }
2017
2018 static void
2019 gst_pvrvideosink_base_init (gpointer g_class)
2020 {
2021   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2022
2023   gst_element_class_set_details_simple (element_class,
2024       "PVR Video sink", "Sink/Video",
2025       "A PVR videosink",
2026       "Luciana Fujii Pontello <luciana.fujii@collabora.co.uk");
2027
2028   gst_element_class_add_pad_template (element_class,
2029       gst_static_pad_template_get (&gst_pvrvideosink_sink_template_factory));
2030 }
2031
2032 static void
2033 gst_pvrvideosink_class_init (GstPVRVideoSinkClass * klass)
2034 {
2035   GObjectClass *gobject_class;
2036   GstElementClass *gstelement_class;
2037   GstBaseSinkClass *gstbasesink_class;
2038
2039   gobject_class = (GObjectClass *) klass;
2040   gstelement_class = (GstElementClass *) klass;
2041   gstbasesink_class = (GstBaseSinkClass *) klass;
2042
2043   parent_class = g_type_class_peek_parent (klass);
2044
2045   gobject_class->finalize = gst_pvrvideosink_finalize;
2046   gobject_class->set_property = gst_pvrvideosink_set_property;
2047   gobject_class->get_property = gst_pvrvideosink_get_property;
2048
2049   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2050       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2051           "When enabled, reverse caps negotiation (scaling) will respect "
2052           "original aspect ratio", FALSE,
2053           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2054
2055   gstelement_class->change_state = gst_pvrvideosink_change_state;
2056
2057   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_pvrvideosink_setcaps);
2058   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_pvrvideosink_getcaps);
2059   gstbasesink_class->buffer_alloc =
2060       GST_DEBUG_FUNCPTR (gst_pvrvideosink_buffer_alloc);
2061   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_pvrvideosink_get_times);
2062   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_pvrvideosink_event);
2063
2064   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_pvrvideosink_show_frame);
2065 }
2066
2067 /* ============================================================= */
2068 /*                                                               */
2069 /*                       Public Methods                          */
2070 /*                                                               */
2071 /* ============================================================= */
2072
2073 /* =========================================== */
2074 /*                                             */
2075 /*          Object typing & Creation           */
2076 /*                                             */
2077 /* =========================================== */
2078
2079 GType
2080 gst_pvrvideosink_get_type (void)
2081 {
2082   static GType pvrvideosink_type = 0;
2083
2084   if (!pvrvideosink_type) {
2085     static const GTypeInfo pvrvideosink_info = {
2086       sizeof (GstPVRVideoSinkClass),
2087       gst_pvrvideosink_base_init,
2088       NULL,
2089       (GClassInitFunc) gst_pvrvideosink_class_init,
2090       NULL,
2091       NULL,
2092       sizeof (GstPVRVideoSink), 0, (GInstanceInitFunc) gst_pvrvideosink_init,
2093     };
2094     static const GInterfaceInfo iface_info = {
2095       (GInterfaceInitFunc) gst_pvrvideosink_interface_init, NULL, NULL,
2096     };
2097     static const GInterfaceInfo overlay_info = {
2098       (GInterfaceInitFunc) gst_pvrvideosink_xoverlay_init, NULL, NULL,
2099     };
2100     static const GInterfaceInfo navigation_info = {
2101       (GInterfaceInitFunc) gst_pvrvideosink_navigation_init, NULL, NULL,
2102     };
2103
2104     pvrvideosink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2105         "GstPVRVideoSink", &pvrvideosink_info, 0);
2106
2107     g_type_add_interface_static (pvrvideosink_type,
2108         GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
2109     g_type_add_interface_static (pvrvideosink_type, GST_TYPE_X_OVERLAY,
2110         &overlay_info);
2111     g_type_add_interface_static (pvrvideosink_type, GST_TYPE_NAVIGATION,
2112         &navigation_info);
2113   }
2114
2115   return pvrvideosink_type;
2116 }