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