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 ret;
559   gboolean draw_border = FALSE;
560   PPVR2D_3DBLT_EXT p_blt_3d;
561   PVR2DMEMINFO *src_mem;
562   PVR2DFORMAT pvr_format = pvrvideosink->format == GST_VIDEO_FORMAT_NV12 ?
563       PVR2D_YUV420_2PLANE : PVR2D_ARGB8888;
564   PVR2DRECT *crop = &pvrvideosink->crop;
565   GstVideoRectangle result;
566
567   GST_DEBUG_OBJECT (pvrvideosink, "begin");
568
569   g_mutex_lock (pvrvideosink->flow_lock);
570   if (buffer == NULL)
571     buffer = pvrvideosink->current_buffer;
572
573   if (buffer == NULL) {
574     g_mutex_unlock (pvrvideosink->flow_lock);
575     return;
576   }
577
578   video_width = pvrvideosink->video_width;
579   video_height = pvrvideosink->video_height;
580
581   src_mem = gst_ducati_buffer_get_meminfo ((GstDucatiBuffer *) buffer);
582   p_blt_3d = dcontext->p_blt_info;
583
584   g_mutex_lock (pvrvideosink->dcontext->x_lock);
585
586   /* Draw borders when displaying the first frame. After this
587      draw borders only on expose event or after a size change. */
588   if (!(pvrvideosink->current_buffer) || pvrvideosink->redraw_borders) {
589     draw_border = TRUE;
590   }
591
592   /* Sometimes the application hasn't really given us valid dimensions
593    * when we want to render the first frame, which throws pvr into a
594    * tizzy, so let's just detect it and bail early:
595    */
596   if ((pvrvideosink->xwindow->width <= 1) ||
597       (pvrvideosink->xwindow->height <= 1)) {
598     GST_DEBUG_OBJECT (pvrvideosink, "skipping render due to invalid "
599         "window dimentions: %dx%d", pvrvideosink->xwindow->width,
600         pvrvideosink->xwindow->height);
601     goto done;
602   }
603
604   /* Store a reference to the last image we put, lose the previous one */
605   if (buffer && pvrvideosink->current_buffer != buffer) {
606     if (pvrvideosink->current_buffer) {
607       GST_LOG_OBJECT (pvrvideosink, "unreffing %p",
608           pvrvideosink->current_buffer);
609       gst_buffer_unref (GST_BUFFER_CAST (pvrvideosink->current_buffer));
610     }
611     GST_LOG_OBJECT (pvrvideosink, "reffing %p as our current buffer", buffer);
612     pvrvideosink->current_buffer = gst_buffer_ref (buffer);
613   }
614
615   if (pvrvideosink->keep_aspect) {
616     GstVideoRectangle src, dst;
617
618     src.w = GST_VIDEO_SINK_WIDTH (pvrvideosink);
619     src.h = GST_VIDEO_SINK_HEIGHT (pvrvideosink);
620     dst.w = pvrvideosink->render_rect.w;
621     dst.h = pvrvideosink->render_rect.h;
622     gst_video_sink_center_rect (src, dst, &result, TRUE);
623     result.x += pvrvideosink->render_rect.x;
624     result.y += pvrvideosink->render_rect.y;
625   } else {
626     memcpy (&result, &pvrvideosink->render_rect, sizeof (GstVideoRectangle));
627   }
628
629   p_blt_3d->sDst.pSurfMemInfo = &dcontext->dst_mem;
630   p_blt_3d->sDst.SurfOffset = 0;
631   p_blt_3d->sDst.Stride =
632       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_BGRx, 0,
633       pvrvideosink->render_params.ui32Stride);
634   p_blt_3d->sDst.Format = PVR2D_ARGB8888;
635   p_blt_3d->sDst.SurfWidth = pvrvideosink->xwindow->width;
636   p_blt_3d->sDst.SurfHeight = pvrvideosink->xwindow->height;
637
638   p_blt_3d->rcDest.left = result.x;
639   p_blt_3d->rcDest.top = result.y;
640   p_blt_3d->rcDest.right = result.w + result.x;
641   p_blt_3d->rcDest.bottom = result.h + result.y;
642
643   p_blt_3d->sSrc.pSurfMemInfo = src_mem;
644   p_blt_3d->sSrc.SurfOffset = 0;
645   p_blt_3d->sSrc.Stride = pvrvideosink->rowstride;
646   p_blt_3d->sSrc.Format = pvr_format;
647   p_blt_3d->sSrc.SurfWidth = video_width;
648   p_blt_3d->sSrc.SurfHeight = video_height;
649
650   if (crop->left || crop->top || crop->right || crop->bottom) {
651     p_blt_3d->rcSource = *crop;
652   } else {
653     p_blt_3d->rcSource.left = 0;
654     p_blt_3d->rcSource.top = 0;
655     p_blt_3d->rcSource.right = video_width;
656     p_blt_3d->rcSource.bottom = video_height;
657   }
658
659   p_blt_3d->hUseCode = NULL;
660
661   if (pvrvideosink->format == GST_VIDEO_FORMAT_NV12)
662     p_blt_3d->bDisableDestInput = TRUE;
663   else
664     /* blit fails for RGB without this... not sure why yet... */
665     /* I'd guess because using ARGB format (ie. has alpha channel) */
666     p_blt_3d->bDisableDestInput = FALSE;
667
668   if (pvrvideosink->interlaced) {
669     /* NOTE: this probably won't look so good if linear (instead
670      * of point) filtering is used.
671      */
672     p_blt_3d->bFilter = FALSE;
673
674     /* for interlaced blits, we split up the image into two blits..
675      * we expect even field on top, odd field on bottom.  We blit
676      * from first top half, then bottom half, doubling up the
677      * stride of the destination buffer.
678      */
679     /* step 1: */
680     p_blt_3d->rcSource.top /= 2;
681     p_blt_3d->rcSource.bottom /= 2;
682     p_blt_3d->rcDest.top /= 2;
683     p_blt_3d->rcDest.bottom /= 2;
684     p_blt_3d->sDst.Stride *= 2;
685
686     pvr_error = PVR2DBlt3DExt (pvrvideosink->dcontext->pvr_context,
687         dcontext->p_blt_info);
688     if (pvr_error)
689       goto done;
690
691     /* step 2: */
692     p_blt_3d->rcSource.top += video_height / 2;
693     p_blt_3d->rcSource.bottom += video_height / 2;
694     p_blt_3d->sDst.SurfOffset = p_blt_3d->sDst.Stride / 2;
695
696     pvr_error = PVR2DBlt3DExt (pvrvideosink->dcontext->pvr_context,
697         dcontext->p_blt_info);
698
699   } else {
700     p_blt_3d->bFilter = TRUE;
701     pvr_error = PVR2DBlt3DExt (pvrvideosink->dcontext->pvr_context,
702         dcontext->p_blt_info);
703   }
704
705   if (pvr_error)
706     goto done;
707
708   dcontext->wsegl_table->pfnWSEGL_SwapDrawable (dcontext->drawable_handle, 1);
709
710   if (draw_border) {
711     gst_pvrvideosink_xwindow_draw_borders (pvrvideosink, pvrvideosink->xwindow,
712         result);
713     pvrvideosink->redraw_borders = FALSE;
714   }
715
716 done:
717   if (pvr_error) {
718     GST_ERROR_OBJECT (pvrvideosink, "%s (%d)",
719         pvr2dstrerr(pvr_error), pvr_error);
720   }
721   GST_DEBUG_OBJECT (pvrvideosink, "end");
722   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
723   g_mutex_unlock (pvrvideosink->flow_lock);
724 }
725
726 static void
727 gst_pvrvideosink_destroy_drawable (GstPVRVideoSink * pvrvideosink)
728 {
729   if (pvrvideosink->dcontext != NULL) {
730     if (pvrvideosink->dcontext->drawable_handle)
731       pvrvideosink->dcontext->
732           wsegl_table->pfnWSEGL_DeleteDrawable (pvrvideosink->dcontext->
733           drawable_handle);
734
735     pvrvideosink->dcontext->wsegl_table->pfnWSEGL_CloseDisplay (pvrvideosink->
736         dcontext->display_handle);
737   }
738 }
739
740 /* We are called with the x_lock taken */
741 static void
742 gst_pvrvideosink_pvrfill_rectangle (GstPVRVideoSink * pvrvideosink,
743     GstVideoRectangle rect)
744 {
745   PVR2DERROR pvr_error;
746   PPVR2DBLTINFO p_blt2d_info = 0;
747   GstDrawContext *dcontext = pvrvideosink->dcontext;
748
749   GST_DEBUG_OBJECT (pvrvideosink, "begin");
750
751   p_blt2d_info = dcontext->p_blt2d_info;
752
753   p_blt2d_info->pDstMemInfo = &dcontext->dst_mem;
754   p_blt2d_info->BlitFlags = PVR2D_BLIT_DISABLE_ALL;
755   p_blt2d_info->DstOffset = 0;
756   p_blt2d_info->CopyCode = PVR2DROPclear;
757   p_blt2d_info->DstStride =
758       gst_video_format_get_row_stride (GST_VIDEO_FORMAT_BGRx, 0,
759       pvrvideosink->render_params.ui32Stride);
760   p_blt2d_info->DstFormat = PVR2D_ARGB8888;
761   p_blt2d_info->DstSurfWidth = pvrvideosink->xwindow->width;
762   p_blt2d_info->DstSurfHeight = pvrvideosink->xwindow->height;
763   p_blt2d_info->DstX = rect.x;
764   p_blt2d_info->DstY = rect.y;
765   p_blt2d_info->DSizeX = rect.w;
766   p_blt2d_info->DSizeY = rect.h;
767
768   pvr_error = PVR2DBlt (pvrvideosink->dcontext->pvr_context, p_blt2d_info);
769   if (pvr_error)
770     goto done;
771
772   dcontext->wsegl_table->pfnWSEGL_SwapDrawable (dcontext->drawable_handle, 1);
773
774 done:
775   if (pvr_error) {
776     GST_ERROR_OBJECT (pvrvideosink, "%s (%d)",
777         pvr2dstrerr(pvr_error), pvr_error);
778   }
779   GST_DEBUG_OBJECT (pvrvideosink, "end");
780 }
781
782 /* We are called with the x_lock taken */
783 static void
784 gst_pvrvideosink_xwindow_draw_borders (GstPVRVideoSink * pvrvideosink,
785     GstXWindow * xwindow, GstVideoRectangle rect)
786 {
787   gint t1, t2;
788   GstVideoRectangle result;
789
790   g_return_if_fail (GST_IS_PVRVIDEOSINK (pvrvideosink));
791   g_return_if_fail (xwindow != NULL);
792
793   /* Left border */
794   result.x = pvrvideosink->render_rect.x;
795   result.y = pvrvideosink->render_rect.y;
796   result.w = rect.x - pvrvideosink->render_rect.x;
797   result.h = pvrvideosink->render_rect.h;
798   if (rect.x > pvrvideosink->render_rect.x)
799     gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result);
800
801   /* Right border */
802   t1 = rect.x + rect.w;
803   t2 = pvrvideosink->render_rect.x + pvrvideosink->render_rect.w;
804   result.x = t1;
805   result.y = pvrvideosink->render_rect.y;
806   result.w = t2 - t1;
807   result.h = pvrvideosink->render_rect.h;
808   if (t1 < t2)
809     gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result);
810
811   /* Top border */
812   result.x = pvrvideosink->render_rect.x;
813   result.y = pvrvideosink->render_rect.y;
814   result.w = pvrvideosink->render_rect.w;
815   result.h = rect.y - pvrvideosink->render_rect.y;
816   if (rect.y > pvrvideosink->render_rect.y)
817     gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result);
818
819   /* Bottom border */
820   t1 = rect.y + rect.h;
821   t2 = pvrvideosink->render_rect.y + pvrvideosink->render_rect.h;
822   result.x = pvrvideosink->render_rect.x;
823   result.y = t1;
824   result.w = pvrvideosink->render_rect.w;
825   result.h = t2 - t1;
826   if (t1 < t2)
827     gst_pvrvideosink_pvrfill_rectangle (pvrvideosink, result);
828 }
829
830 /* Element stuff */
831
832 static gboolean
833 gst_pvrvideosink_configure_overlay (GstPVRVideoSink * pvrvideosink, gint width,
834     gint height, gint video_par_n, gint video_par_d, gint display_par_n,
835     gint display_par_d)
836 {
837   guint calculated_par_n;
838   guint calculated_par_d;
839
840   if (!gst_video_calculate_display_ratio (&calculated_par_n, &calculated_par_d,
841           width, height, video_par_n, video_par_d, display_par_n,
842           display_par_d)) {
843     GST_ELEMENT_ERROR (pvrvideosink, CORE, NEGOTIATION, (NULL),
844         ("Error calculating the output display ratio of the video."));
845     return FALSE;
846   }
847
848   GST_DEBUG_OBJECT (pvrvideosink,
849       "video width/height: %dx%d, calculated display ratio: %d/%d",
850       width, height, calculated_par_n, calculated_par_d);
851
852   /* now find a width x height that respects this display ratio.
853    * prefer those that have one of w/h the same as the incoming video
854    * using wd / hd = calculated_pad_n / calculated_par_d */
855
856   /* start with same height, because of interlaced video */
857   /* check hd / calculated_par_d is an integer scale factor, and scale wd with the PAR */
858   if (height % calculated_par_d == 0) {
859     GST_DEBUG_OBJECT (pvrvideosink, "keeping video height");
860     GST_VIDEO_SINK_WIDTH (pvrvideosink) = (guint)
861         gst_util_uint64_scale_int (height, calculated_par_n, calculated_par_d);
862     GST_VIDEO_SINK_HEIGHT (pvrvideosink) = height;
863   } else if (width % calculated_par_n == 0) {
864     GST_DEBUG_OBJECT (pvrvideosink, "keeping video width");
865     GST_VIDEO_SINK_WIDTH (pvrvideosink) = width;
866     GST_VIDEO_SINK_HEIGHT (pvrvideosink) = (guint)
867         gst_util_uint64_scale_int (width, calculated_par_d, calculated_par_n);
868   } else {
869     GST_DEBUG_OBJECT (pvrvideosink, "approximating while keeping video height");
870     GST_VIDEO_SINK_WIDTH (pvrvideosink) = (guint)
871         gst_util_uint64_scale_int (height, calculated_par_n, calculated_par_d);
872     GST_VIDEO_SINK_HEIGHT (pvrvideosink) = height;
873   }
874   GST_DEBUG_OBJECT (pvrvideosink, "scaling to %dx%d",
875       GST_VIDEO_SINK_WIDTH (pvrvideosink),
876       GST_VIDEO_SINK_HEIGHT (pvrvideosink));
877
878   return TRUE;
879 }
880
881 static gboolean
882 gst_pvrvideosink_setcaps (GstBaseSink * bsink, GstCaps * caps)
883 {
884   GstPVRVideoSink *pvrvideosink;
885   gboolean ret = TRUE;
886   GstStructure *structure;
887   gint width, height;
888   const GValue *fps;
889   const GValue *caps_par;
890   GstQuery *query;
891
892   pvrvideosink = GST_PVRVIDEOSINK (bsink);
893
894   GST_DEBUG_OBJECT (pvrvideosink,
895       "sinkconnect possible caps with given caps %", caps);
896
897   if (pvrvideosink->current_caps) {
898     GST_DEBUG_OBJECT (pvrvideosink, "already have caps set");
899     if (gst_caps_is_equal (pvrvideosink->current_caps, caps)) {
900       GST_DEBUG_OBJECT (pvrvideosink, "caps are equal!");
901       return TRUE;
902     }
903     GST_DEBUG_OBJECT (pvrvideosink, "caps are different");
904   }
905
906   structure = gst_caps_get_structure (caps, 0);
907
908   ret = gst_video_format_parse_caps_strided (caps, &pvrvideosink->format,
909       &width, &height, &pvrvideosink->rowstride);
910   if (pvrvideosink->rowstride == 0)
911     pvrvideosink->rowstride =
912         gst_video_format_get_row_stride (pvrvideosink->format, 0, width);
913   fps = gst_structure_get_value (structure, "framerate");
914   ret &= (fps != NULL);
915   if (!ret) {
916     GST_ERROR_OBJECT (pvrvideosink, "problem at parsing caps");
917     return FALSE;
918   }
919
920   pvrvideosink->video_width = width;
921   pvrvideosink->video_height = height;
922
923   /* figure out if we are dealing w/ interlaced */
924   pvrvideosink->interlaced = FALSE;
925   gst_structure_get_boolean (structure, "interlaced",
926       &pvrvideosink->interlaced);
927
928   /* get video's pixel-aspect-ratio */
929   caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
930   if (caps_par) {
931     pvrvideosink->video_par_n = gst_value_get_fraction_numerator (caps_par);
932     pvrvideosink->video_par_d = gst_value_get_fraction_denominator (caps_par);
933   } else {
934     pvrvideosink->video_par_n = 1;
935     pvrvideosink->video_par_d = 1;
936   }
937
938   /* get display's pixel-aspect-ratio */
939   if (pvrvideosink->display_par) {
940     pvrvideosink->display_par_n =
941         gst_value_get_fraction_numerator (pvrvideosink->display_par);
942     pvrvideosink->display_par_d =
943         gst_value_get_fraction_denominator (pvrvideosink->display_par);
944   } else {
945     pvrvideosink->display_par_n = 1;
946     pvrvideosink->display_par_d = 1;
947   }
948
949   if (!gst_pvrvideosink_configure_overlay (pvrvideosink, width, height,
950           pvrvideosink->video_par_n, pvrvideosink->video_par_d,
951           pvrvideosink->display_par_n, pvrvideosink->display_par_d))
952     return FALSE;
953
954   g_mutex_lock (pvrvideosink->pool_lock);
955   if (pvrvideosink->buffer_pool) {
956     if (!gst_caps_is_equal (pvrvideosink->buffer_pool->caps, caps)) {
957       GST_INFO_OBJECT (pvrvideosink, "in set caps, pool->caps != caps");
958       gst_pvr_bufferpool_stop_running (pvrvideosink->buffer_pool, FALSE);
959       pvrvideosink->buffer_pool = NULL;
960     }
961   }
962   g_mutex_unlock (pvrvideosink->pool_lock);
963
964   /* query to find if anyone upstream using these buffers has any
965    * minimum requirements:
966    */
967   query = gst_query_new_buffers (caps);
968   if (gst_element_query (GST_ELEMENT (pvrvideosink), query)) {
969     gint min_buffers;
970
971     gst_query_parse_buffers_count (query, &min_buffers);
972
973     GST_DEBUG_OBJECT (pvrvideosink, "min_buffers=%d", min_buffers);
974
975     /* XXX need to account for some buffers used by queue, etc.. probably
976      * queue should handle query, pass on to sink pad, and then add some
977      * number of buffers to the min, so this value is dynamic depending
978      * on the pipeline?
979      */
980     if (min_buffers != -1) {
981       min_buffers += 3 + pvrvideosink->min_queued_bufs;
982       pvrvideosink->num_buffers_can_change = FALSE;
983     }
984
985     if (min_buffers > pvrvideosink->num_buffers) {
986       pvrvideosink->num_buffers = min_buffers;
987     }
988   }
989   gst_query_unref (query);
990
991   /* Notify application to set xwindow id now */
992   g_mutex_lock (pvrvideosink->flow_lock);
993   if (!pvrvideosink->xwindow) {
994     g_mutex_unlock (pvrvideosink->flow_lock);
995     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (pvrvideosink));
996   } else {
997     g_mutex_unlock (pvrvideosink->flow_lock);
998   }
999
1000   g_mutex_lock (pvrvideosink->flow_lock);
1001   if (!pvrvideosink->xwindow)
1002     pvrvideosink->xwindow = gst_pvrvideosink_create_window (pvrvideosink,
1003         GST_VIDEO_SINK_WIDTH (pvrvideosink),
1004         GST_VIDEO_SINK_HEIGHT (pvrvideosink));
1005   g_mutex_unlock (pvrvideosink->flow_lock);
1006
1007   pvrvideosink->fps_n = gst_value_get_fraction_numerator (fps);
1008   pvrvideosink->fps_d = gst_value_get_fraction_denominator (fps);
1009
1010   pvrvideosink->current_caps = gst_caps_ref (caps);
1011
1012   return TRUE;
1013 }
1014
1015 static GstCaps *
1016 gst_pvrvideosink_getcaps (GstBaseSink * bsink)
1017 {
1018   GstPVRVideoSink *pvrvideosink;
1019   GstCaps *caps;
1020
1021   pvrvideosink = GST_PVRVIDEOSINK (bsink);
1022
1023   caps = gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK
1024           (pvrvideosink)->sinkpad));
1025   return caps;
1026 }
1027
1028 static GstStateChangeReturn
1029 gst_pvrvideosink_change_state (GstElement * element, GstStateChange transition)
1030 {
1031   GstPVRVideoSink *pvrvideosink;
1032   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1033   GstDrawContext *dcontext;
1034
1035   pvrvideosink = GST_PVRVIDEOSINK (element);
1036
1037   switch (transition) {
1038     case GST_STATE_CHANGE_NULL_TO_READY:
1039       if (pvrvideosink->dcontext == NULL) {
1040         dcontext = gst_pvrvideosink_get_dcontext (pvrvideosink);
1041         if (dcontext == NULL)
1042           return GST_STATE_CHANGE_FAILURE;
1043         GST_OBJECT_LOCK (pvrvideosink);
1044         pvrvideosink->dcontext = dcontext;
1045         GST_OBJECT_UNLOCK (pvrvideosink);
1046       }
1047
1048       /* update object's pixel-aspect-ratio with calculated one */
1049       if (!pvrvideosink->display_par) {
1050         pvrvideosink->display_par = g_new0 (GValue, 1);
1051         gst_value_init_and_copy (pvrvideosink->display_par,
1052             pvrvideosink->dcontext->par);
1053         GST_DEBUG_OBJECT (pvrvideosink, "set calculated PAR on object's PAR");
1054       }
1055
1056       gst_pvrvideosink_manage_event_thread (pvrvideosink);
1057       break;
1058     case GST_STATE_CHANGE_READY_TO_PAUSED:
1059       g_mutex_lock (pvrvideosink->pool_lock);
1060       pvrvideosink->pool_invalid = FALSE;
1061       g_mutex_unlock (pvrvideosink->pool_lock);
1062       break;
1063     case GST_STATE_CHANGE_PAUSED_TO_READY:
1064       g_mutex_lock (pvrvideosink->pool_lock);
1065       pvrvideosink->pool_invalid = TRUE;
1066       g_mutex_unlock (pvrvideosink->pool_lock);
1067       break;
1068     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1069       break;
1070     default:
1071       break;
1072   }
1073
1074   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1075
1076   switch (transition) {
1077     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1078       break;
1079     case GST_STATE_CHANGE_PAUSED_TO_READY:
1080       pvrvideosink->fps_n = 0;
1081       pvrvideosink->fps_d = 1;
1082       GST_VIDEO_SINK_WIDTH (pvrvideosink) = 0;
1083       GST_VIDEO_SINK_HEIGHT (pvrvideosink) = 0;
1084       break;
1085     case GST_STATE_CHANGE_READY_TO_NULL:
1086       gst_pvrvideosink_reset (pvrvideosink);
1087       break;
1088     default:
1089       break;
1090   }
1091
1092   return ret;
1093 }
1094
1095 static void
1096 gst_pvrvideosink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1097     GstClockTime * start, GstClockTime * end)
1098 {
1099   GstPVRVideoSink *pvrvideosink;
1100
1101   pvrvideosink = GST_PVRVIDEOSINK (bsink);
1102
1103   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1104     *start = GST_BUFFER_TIMESTAMP (buf);
1105     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1106       *end = *start + GST_BUFFER_DURATION (buf);
1107     } else {
1108       if (pvrvideosink->fps_n > 0) {
1109         *end = *start +
1110             gst_util_uint64_scale_int (GST_SECOND, pvrvideosink->fps_d,
1111             pvrvideosink->fps_n);
1112       }
1113     }
1114   }
1115 }
1116
1117 static gboolean
1118 gst_pvrvideosink_event (GstBaseSink * bsink, GstEvent * event)
1119 {
1120   gboolean res;
1121   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (bsink);
1122   GstStructure *structure;
1123   GstMessage *message;
1124
1125   switch (GST_EVENT_TYPE (event)) {
1126     case GST_EVENT_CROP:
1127     {
1128       gint left, top, width, height;
1129
1130       PVR2DRECT *c = &pvrvideosink->crop;
1131       gst_event_parse_crop (event, &top, &left, &width, &height);
1132       c->top = top;
1133       c->left = left;
1134       if (width == -1) {
1135         c->right = GST_VIDEO_SINK_WIDTH (pvrvideosink);
1136         width = GST_VIDEO_SINK_WIDTH (pvrvideosink);
1137       } else {
1138         c->right = left + width;
1139       }
1140
1141       if (height == -1) {
1142         c->bottom = GST_VIDEO_SINK_HEIGHT (pvrvideosink);
1143         height = GST_VIDEO_SINK_HEIGHT (pvrvideosink);
1144       } else {
1145         c->bottom = top + height;
1146       }
1147
1148       structure = gst_structure_new ("video-size-crop", "width", G_TYPE_INT,
1149           width, "height", G_TYPE_INT, height, NULL);
1150       message = gst_message_new_application (GST_OBJECT (pvrvideosink),
1151           structure);
1152       gst_bus_post (gst_element_get_bus (GST_ELEMENT (pvrvideosink)), message);
1153
1154
1155       if (!gst_pvrvideosink_configure_overlay (pvrvideosink, width, height,
1156               pvrvideosink->video_par_n, pvrvideosink->video_par_d,
1157               pvrvideosink->display_par_n, pvrvideosink->display_par_d))
1158         return FALSE;
1159       break;
1160     }
1161     default:
1162       res = TRUE;
1163   }
1164
1165
1166   return res;
1167 }
1168
1169 static GstFlowReturn
1170 gst_pvrvideosink_show_frame (GstBaseSink * vsink, GstBuffer * buf)
1171 {
1172   GstPVRVideoSink *pvrvideosink;
1173   GstBuffer *newbuf = NULL;
1174   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1175
1176   pvrvideosink = GST_PVRVIDEOSINK (vsink);
1177
1178   GST_DEBUG_OBJECT (pvrvideosink, "render buffer: %p", buf);
1179
1180   if (!GST_IS_DUCATIBUFFER (buf)) {
1181     GstFlowReturn ret;
1182
1183     /* special case check for sub-buffers:  In certain cases, places like
1184      * GstBaseTransform, which might check that the buffer is writable
1185      * before copying metadata, timestamp, and such, will find that the
1186      * buffer has more than one reference to it.  In these cases, they
1187      * will create a sub-buffer with an offset=0 and length equal to the
1188      * original buffer size.
1189      *
1190      * This could happen in two scenarios: (1) a tee in the pipeline, and
1191      * (2) because the refcnt is incremented in gst_mini_object_free()
1192      * before the finalize function is called, and decremented after it
1193      * returns..  but returning this buffer to the buffer pool in the
1194      * finalize function, could wake up a thread blocked in _buffer_alloc()
1195      * which could run and get a buffer w/ refcnt==2 before the thread
1196      * originally unref'ing the buffer returns from finalize function and
1197      * decrements the refcnt back to 1!
1198      */
1199     if (buf->parent &&
1200         (GST_BUFFER_DATA (buf) == GST_BUFFER_DATA (buf->parent)) &&
1201         (GST_BUFFER_SIZE (buf) == GST_BUFFER_SIZE (buf->parent))) {
1202       GST_DEBUG_OBJECT (pvrvideosink, "I have a sub-buffer!");
1203       return gst_pvrvideosink_show_frame (vsink, buf->parent);
1204     }
1205
1206     GST_DEBUG_OBJECT (pvrvideosink,
1207         "slow-path.. I got a %s so I need to memcpy",
1208         g_type_name (G_OBJECT_TYPE (buf)));
1209
1210     ret = gst_pvrvideosink_buffer_alloc (GST_BASE_SINK (vsink),
1211         GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf), GST_BUFFER_CAPS (buf),
1212         &newbuf);
1213
1214     if (GST_FLOW_OK != ret) {
1215       GST_DEBUG_OBJECT (pvrvideosink, "dropping frame!!");
1216       return GST_FLOW_OK;
1217     }
1218
1219     memcpy (GST_BUFFER_DATA (newbuf),
1220         GST_BUFFER_DATA (buf),
1221         MIN (GST_BUFFER_SIZE (newbuf), GST_BUFFER_SIZE (buf)));
1222
1223     GST_DEBUG_OBJECT (pvrvideosink, "render copied buffer: %p", newbuf);
1224
1225     buf = newbuf;
1226   }
1227
1228   gst_pvrvideosink_blit (pvrvideosink, buf);
1229
1230   if (newbuf) {
1231     gst_buffer_unref (newbuf);
1232   }
1233
1234   return GST_FLOW_OK;
1235 }
1236
1237
1238 /* Buffer management
1239  *
1240  * The buffer_alloc function must either return a buffer with given size and
1241  * caps or create a buffer with different caps attached to the buffer. This
1242  * last option is called reverse negotiation, ie, where the sink suggests a
1243  * different format from the upstream peer. 
1244  *
1245  * We try to do reverse negotiation when our geometry changes and we like a
1246  * resized buffer.
1247  */
1248 static GstFlowReturn
1249 gst_pvrvideosink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1250     GstCaps * caps, GstBuffer ** buf)
1251 {
1252   GstPVRVideoSink *pvrvideosink;
1253   GstDucatiBuffer *pvrvideo = NULL;
1254   GstFlowReturn ret = GST_FLOW_OK;
1255
1256   pvrvideosink = GST_PVRVIDEOSINK (bsink);
1257
1258   GST_DEBUG_OBJECT (pvrvideosink, "begin");
1259
1260   if (G_UNLIKELY (!caps)) {
1261     GST_WARNING_OBJECT (pvrvideosink,
1262         "have no caps, doing fallback allocation");
1263     *buf = NULL;
1264     ret = GST_FLOW_OK;
1265     goto beach;
1266   }
1267
1268   g_mutex_lock (pvrvideosink->pool_lock);
1269   if (G_UNLIKELY (pvrvideosink->pool_invalid)) {
1270     GST_DEBUG_OBJECT (pvrvideosink, "the pool is flushing");
1271     ret = GST_FLOW_WRONG_STATE;
1272     g_mutex_unlock (pvrvideosink->pool_lock);
1273     goto beach;
1274   } else {
1275     g_mutex_unlock (pvrvideosink->pool_lock);
1276   }
1277
1278   GST_LOG_OBJECT (pvrvideosink,
1279       "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1280       " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1281
1282   g_mutex_lock (pvrvideosink->pool_lock);
1283   /* initialize the buffer pool if not initialized yet */
1284   if (G_UNLIKELY (!pvrvideosink->buffer_pool ||
1285           pvrvideosink->buffer_pool->size != size)) {
1286     if (pvrvideosink->buffer_pool) {
1287       GST_INFO_OBJECT (pvrvideosink, "in buffer alloc, pool->size != size");
1288       gst_pvr_bufferpool_stop_running (pvrvideosink->buffer_pool, FALSE);
1289     }
1290
1291     GST_LOG_OBJECT (pvrvideosink, "Creating a buffer pool with %d buffers",
1292         pvrvideosink->num_buffers);
1293     if (!(pvrvideosink->buffer_pool =
1294             gst_pvr_bufferpool_new (GST_ELEMENT (pvrvideosink),
1295                 caps, 8, size, pvrvideosink->dcontext->pvr_context))) {
1296       g_mutex_unlock (pvrvideosink->pool_lock);
1297       return GST_FLOW_ERROR;
1298     }
1299   }
1300   pvrvideo = gst_pvr_bufferpool_get (pvrvideosink->buffer_pool, NULL);
1301   g_mutex_unlock (pvrvideosink->pool_lock);
1302
1303   *buf = GST_BUFFER_CAST (pvrvideo);
1304
1305 beach:
1306   return ret;
1307 }
1308
1309 /* Interfaces stuff */
1310
1311 static gboolean
1312 gst_pvrvideosink_interface_supported (GstImplementsInterface * iface,
1313     GType type)
1314 {
1315   if (type == GST_TYPE_X_OVERLAY)
1316     return TRUE;
1317   else
1318     return FALSE;
1319 }
1320
1321 static void
1322 gst_pvrvideosink_interface_init (GstImplementsInterfaceClass * klass)
1323 {
1324   klass->supported = gst_pvrvideosink_interface_supported;
1325 }
1326
1327 /* This function destroys a GstXWindow */
1328 static void
1329 gst_pvrvideosink_xwindow_destroy (GstPVRVideoSink * pvrvideosink,
1330     GstXWindow * xwindow)
1331 {
1332   g_return_if_fail (xwindow != NULL);
1333
1334   g_mutex_lock (pvrvideosink->dcontext->x_lock);
1335
1336   /* If we did not create that window we just free the GC and let it live */
1337   if (xwindow->internal)
1338     XDestroyWindow (pvrvideosink->dcontext->x_display, xwindow->window);
1339   else
1340     XSelectInput (pvrvideosink->dcontext->x_display, xwindow->window, 0);
1341
1342   XFreeGC (pvrvideosink->dcontext->x_display, xwindow->gc);
1343
1344   XSync (pvrvideosink->dcontext->x_display, FALSE);
1345
1346   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
1347
1348   g_free (xwindow);
1349 }
1350
1351 static void
1352 gst_pvrvideosink_set_window_handle (GstXOverlay * overlay, guintptr id)
1353 {
1354   XID xwindow_id = id;
1355   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay);
1356   GstXWindow *xwindow = NULL;
1357
1358   g_return_if_fail (GST_IS_PVRVIDEOSINK (pvrvideosink));
1359
1360   g_mutex_lock (pvrvideosink->flow_lock);
1361
1362   /* If we already use that window return */
1363   if (pvrvideosink->xwindow && (xwindow_id == pvrvideosink->xwindow->window)) {
1364     g_mutex_unlock (pvrvideosink->flow_lock);
1365     return;
1366   }
1367
1368   /* If the element has not initialized the X11 context try to do so */
1369   if (!pvrvideosink->dcontext && !(pvrvideosink->dcontext =
1370           gst_pvrvideosink_get_dcontext (pvrvideosink))) {
1371     g_mutex_unlock (pvrvideosink->flow_lock);
1372     /* we have thrown a GST_ELEMENT_ERROR now */
1373     return;
1374   }
1375
1376   /* Clear image pool as the images are unusable anyway */
1377   g_mutex_lock (pvrvideosink->pool_lock);
1378   if (pvrvideosink->buffer_pool) {
1379     gst_pvr_bufferpool_stop_running (pvrvideosink->buffer_pool, FALSE);
1380     pvrvideosink->buffer_pool = NULL;
1381   }
1382   g_mutex_unlock (pvrvideosink->pool_lock);
1383
1384   /* If a window is there already we destroy it */
1385   if (pvrvideosink->xwindow) {
1386     gst_pvrvideosink_xwindow_destroy (pvrvideosink, pvrvideosink->xwindow);
1387     pvrvideosink->xwindow = NULL;
1388   }
1389
1390   /* If the xid is 0 we will create an internal one in buffer_alloc */
1391   if (xwindow_id != 0) {
1392     XWindowAttributes attr;
1393     WSEGLError glerror;
1394     WSEGLDrawableParams source_params;
1395     PVRSRV_CLIENT_MEM_INFO *client_mem_info;
1396
1397     xwindow = g_new0 (GstXWindow, 1);
1398     xwindow->window = xwindow_id;
1399
1400     /* Set the event we want to receive and create a GC */
1401     g_mutex_lock (pvrvideosink->dcontext->x_lock);
1402
1403     XGetWindowAttributes (pvrvideosink->dcontext->x_display, xwindow->window,
1404         &attr);
1405
1406     xwindow->width = attr.width;
1407     xwindow->height = attr.height;
1408     xwindow->internal = FALSE;
1409     if (!pvrvideosink->have_render_rect) {
1410       pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0;
1411       pvrvideosink->render_rect.w = attr.width;
1412       pvrvideosink->render_rect.h = attr.height;
1413     }
1414     XSelectInput (pvrvideosink->dcontext->x_display, xwindow->window,
1415         ExposureMask | StructureNotifyMask);
1416
1417     XSetWindowBackgroundPixmap (pvrvideosink->dcontext->x_display,
1418         xwindow->window, None);
1419
1420     XMapWindow (pvrvideosink->dcontext->x_display, xwindow->window);
1421     xwindow->gc = XCreateGC (pvrvideosink->dcontext->x_display,
1422         xwindow->window, 0, NULL);
1423     g_mutex_unlock (pvrvideosink->dcontext->x_lock);
1424
1425     glerror =
1426         pvrvideosink->dcontext->
1427         wsegl_table->pfnWSEGL_CreateWindowDrawable (pvrvideosink->dcontext->
1428         display_handle, pvrvideosink->dcontext->glconfig,
1429         &(pvrvideosink->dcontext->drawable_handle),
1430         (NativeWindowType) xwindow->window,
1431         &(pvrvideosink->dcontext->rotation));
1432
1433     if (glerror != WSEGL_SUCCESS) {
1434       GST_ERROR_OBJECT (pvrvideosink, "Error creating drawable");
1435       return;
1436     }
1437     glerror =
1438         pvrvideosink->dcontext->
1439         wsegl_table->pfnWSEGL_GetDrawableParameters (pvrvideosink->dcontext->
1440         drawable_handle, &source_params, &pvrvideosink->render_params);
1441
1442     client_mem_info =
1443         (PVRSRV_CLIENT_MEM_INFO *) pvrvideosink->render_params.hPrivateData;
1444     PVR2DMEMINFO_INITIALISE (&pvrvideosink->dcontext->dst_mem, client_mem_info);
1445   }
1446
1447   if (xwindow)
1448     pvrvideosink->xwindow = xwindow;
1449
1450   g_mutex_unlock (pvrvideosink->flow_lock);
1451 }
1452
1453 static void
1454 gst_pvrvideosink_expose (GstXOverlay * overlay)
1455 {
1456   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay);
1457
1458   gst_pvrvideosink_blit (pvrvideosink, NULL);
1459 }
1460
1461 static void
1462 gst_pvrvideosink_set_event_handling (GstXOverlay * overlay,
1463     gboolean handle_events)
1464 {
1465   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay);
1466
1467   g_mutex_lock (pvrvideosink->flow_lock);
1468
1469   if (G_UNLIKELY (!pvrvideosink->xwindow)) {
1470     g_mutex_unlock (pvrvideosink->flow_lock);
1471     return;
1472   }
1473
1474   g_mutex_lock (pvrvideosink->dcontext->x_lock);
1475
1476   XSelectInput (pvrvideosink->dcontext->x_display,
1477       pvrvideosink->xwindow->window, ExposureMask | StructureNotifyMask);
1478
1479   g_mutex_unlock (pvrvideosink->dcontext->x_lock);
1480
1481   g_mutex_unlock (pvrvideosink->flow_lock);
1482 }
1483
1484 static void
1485 gst_pvrvideosink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
1486     gint width, gint height)
1487 {
1488   GstPVRVideoSink *pvrvideosink = GST_PVRVIDEOSINK (overlay);
1489
1490   /* FIXME: how about some locking? */
1491   if (width >= 0 && height >= 0) {
1492     pvrvideosink->render_rect.x = x;
1493     pvrvideosink->render_rect.y = y;
1494     pvrvideosink->render_rect.w = width;
1495     pvrvideosink->render_rect.h = height;
1496     pvrvideosink->have_render_rect = TRUE;
1497   } else {
1498     pvrvideosink->render_rect.x = 0;
1499     pvrvideosink->render_rect.y = 0;
1500     pvrvideosink->render_rect.w = pvrvideosink->xwindow->width;
1501     pvrvideosink->render_rect.h = pvrvideosink->xwindow->height;
1502     pvrvideosink->have_render_rect = FALSE;
1503   }
1504   GST_DEBUG_OBJECT (pvrvideosink, "render_rect is %dX%d",
1505       pvrvideosink->render_rect.w, pvrvideosink->render_rect.h);
1506 }
1507
1508 static void
1509 gst_pvrvideosink_xoverlay_init (GstXOverlayClass * iface)
1510 {
1511   iface->set_window_handle = gst_pvrvideosink_set_window_handle;
1512   iface->expose = gst_pvrvideosink_expose;
1513   iface->handle_events = gst_pvrvideosink_set_event_handling;
1514   iface->set_render_rectangle = gst_pvrvideosink_set_render_rectangle;
1515 }
1516
1517 /* =========================================== */
1518 /*                                             */
1519 /*              Init & Class init              */
1520 /*                                             */
1521 /* =========================================== */
1522
1523 static void
1524 gst_pvrvideosink_set_property (GObject * object, guint prop_id,
1525     const GValue * value, GParamSpec * pspec)
1526 {
1527   GstPVRVideoSink *pvrvideosink;
1528
1529   g_return_if_fail (GST_IS_PVRVIDEOSINK (object));
1530
1531   pvrvideosink = GST_PVRVIDEOSINK (object);
1532
1533   switch (prop_id) {
1534     case PROP_FORCE_ASPECT_RATIO:
1535       pvrvideosink->keep_aspect = g_value_get_boolean (value);
1536       break;
1537     default:
1538       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1539       break;
1540   }
1541 }
1542
1543 static void
1544 gst_pvrvideosink_get_property (GObject * object, guint prop_id,
1545     GValue * value, GParamSpec * pspec)
1546 {
1547   GstPVRVideoSink *pvrvideosink;
1548
1549   g_return_if_fail (GST_IS_PVRVIDEOSINK (object));
1550
1551   pvrvideosink = GST_PVRVIDEOSINK (object);
1552
1553   switch (prop_id) {
1554     case PROP_FORCE_ASPECT_RATIO:
1555       g_value_set_boolean (value, pvrvideosink->keep_aspect);
1556       break;
1557     default:
1558       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1559       break;
1560   }
1561 }
1562
1563 static void
1564 gst_pvrvideosink_dcontext_clear (GstPVRVideoSink * pvrvideosink)
1565 {
1566   GstDrawContext *dcontext;
1567
1568   GST_OBJECT_LOCK (pvrvideosink);
1569   if (!pvrvideosink->dcontext) {
1570     GST_OBJECT_UNLOCK (pvrvideosink);
1571     return;
1572   }
1573
1574   dcontext = pvrvideosink->dcontext;
1575   pvrvideosink->dcontext = NULL;
1576   GST_OBJECT_UNLOCK (pvrvideosink);
1577
1578   if (dcontext->p_blt_info)
1579     g_free (dcontext->p_blt_info);
1580   g_free (dcontext->par);
1581
1582   g_mutex_lock (dcontext->x_lock);
1583   XCloseDisplay (dcontext->x_display);
1584   g_mutex_unlock (dcontext->x_lock);
1585   g_mutex_free (dcontext->x_lock);
1586
1587   g_free (dcontext);
1588 }
1589
1590 static void
1591 gst_pvrvideosink_reset (GstPVRVideoSink * pvrvideosink)
1592 {
1593   GThread *thread;
1594
1595   GST_OBJECT_LOCK (pvrvideosink);
1596   pvrvideosink->running = FALSE;
1597   thread = pvrvideosink->event_thread;
1598   pvrvideosink->event_thread = NULL;
1599   GST_OBJECT_UNLOCK (pvrvideosink);
1600
1601   if (thread)
1602     g_thread_join (thread);
1603
1604   if (pvrvideosink->current_buffer) {
1605     gst_buffer_unref (pvrvideosink->current_buffer);
1606     pvrvideosink->current_buffer = NULL;
1607   }
1608
1609   g_mutex_lock (pvrvideosink->pool_lock);
1610   pvrvideosink->pool_invalid = TRUE;
1611   if (pvrvideosink->buffer_pool) {
1612     gst_pvr_bufferpool_stop_running (pvrvideosink->buffer_pool, TRUE);
1613     pvrvideosink->buffer_pool = NULL;
1614   }
1615   g_mutex_unlock (pvrvideosink->pool_lock);
1616   memset (&pvrvideosink->crop, 0, sizeof (PVR2DRECT));
1617   memset (&pvrvideosink->render_params, 0, sizeof (WSEGLDrawableParams));
1618
1619   pvrvideosink->render_rect.x = pvrvideosink->render_rect.y = 0;
1620   pvrvideosink->render_rect.w = pvrvideosink->render_rect.h = 0;
1621   pvrvideosink->have_render_rect = FALSE;
1622
1623   gst_pvrvideosink_destroy_drawable (pvrvideosink);
1624
1625   if (pvrvideosink->xwindow) {
1626     gst_pvrvideosink_xwindow_destroy (pvrvideosink, pvrvideosink->xwindow);
1627     pvrvideosink->xwindow = NULL;
1628   }
1629
1630   g_free (pvrvideosink->display_par);
1631   gst_pvrvideosink_dcontext_clear (pvrvideosink);
1632 }
1633
1634 static void
1635 gst_pvrvideosink_finalize (GObject * object)
1636 {
1637   GstPVRVideoSink *pvrvideosink;
1638
1639   pvrvideosink = GST_PVRVIDEOSINK (object);
1640
1641   gst_pvrvideosink_reset (pvrvideosink);
1642
1643   if (pvrvideosink->flow_lock) {
1644     g_mutex_free (pvrvideosink->flow_lock);
1645     pvrvideosink->flow_lock = NULL;
1646   }
1647   if (pvrvideosink->pool_lock) {
1648     g_mutex_free (pvrvideosink->pool_lock);
1649     pvrvideosink->pool_lock = NULL;
1650   }
1651
1652   G_OBJECT_CLASS (parent_class)->finalize (object);
1653 }
1654
1655 static void
1656 gst_pvrvideosink_init (GstPVRVideoSink * pvrvideosink)
1657 {
1658   pvrvideosink->running = FALSE;
1659
1660   pvrvideosink->fps_n = 0;
1661   pvrvideosink->fps_d = 1;
1662   pvrvideosink->video_width = 0;
1663   pvrvideosink->video_height = 0;
1664
1665   pvrvideosink->flow_lock = g_mutex_new ();
1666   pvrvideosink->pool_lock = g_mutex_new ();
1667   pvrvideosink->buffer_pool = NULL;
1668   pvrvideosink->pool_invalid = TRUE;
1669
1670   pvrvideosink->keep_aspect = FALSE;
1671   pvrvideosink->current_caps = NULL;
1672   pvrvideosink->num_buffers = DEFAULT_QUEUE_SIZE;
1673   pvrvideosink->num_buffers_can_change = TRUE;
1674   pvrvideosink->min_queued_bufs = DEFAULT_MIN_QUEUED_BUFS;
1675   pvrvideosink->dcontext = NULL;
1676   pvrvideosink->xwindow = NULL;
1677   pvrvideosink->redraw_borders = TRUE;
1678   pvrvideosink->current_buffer = NULL;
1679   pvrvideosink->event_thread = NULL;
1680   memset (&pvrvideosink->crop, 0, sizeof (PVR2DRECT));
1681   memset (&pvrvideosink->render_params, 0, sizeof (WSEGLDrawableParams));
1682 }
1683
1684 static void
1685 gst_pvrvideosink_base_init (gpointer g_class)
1686 {
1687   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
1688
1689   gst_element_class_set_details_simple (element_class,
1690       "PVR Video sink", "Sink/Video",
1691       "A PVR videosink",
1692       "Luciana Fujii Pontello <luciana.fujii@collabora.co.uk");
1693
1694   gst_element_class_add_pad_template (element_class,
1695       gst_static_pad_template_get (&gst_pvrvideosink_sink_template_factory));
1696 }
1697
1698 static void
1699 gst_pvrvideosink_class_init (GstPVRVideoSinkClass * klass)
1700 {
1701   GObjectClass *gobject_class;
1702   GstElementClass *gstelement_class;
1703   GstBaseSinkClass *gstbasesink_class;
1704
1705   gobject_class = (GObjectClass *) klass;
1706   gstelement_class = (GstElementClass *) klass;
1707   gstbasesink_class = (GstBaseSinkClass *) klass;
1708
1709   parent_class = g_type_class_peek_parent (klass);
1710
1711   gobject_class->finalize = gst_pvrvideosink_finalize;
1712   gobject_class->set_property = gst_pvrvideosink_set_property;
1713   gobject_class->get_property = gst_pvrvideosink_get_property;
1714
1715   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1716       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1717           "When enabled, reverse caps negotiation (scaling) will respect "
1718           "original aspect ratio", FALSE,
1719           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1720
1721   gstelement_class->change_state = gst_pvrvideosink_change_state;
1722
1723   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_pvrvideosink_setcaps);
1724   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_pvrvideosink_getcaps);
1725   gstbasesink_class->buffer_alloc =
1726       GST_DEBUG_FUNCPTR (gst_pvrvideosink_buffer_alloc);
1727   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_pvrvideosink_get_times);
1728   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_pvrvideosink_event);
1729
1730   gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_pvrvideosink_show_frame);
1731 }
1732
1733 /* ============================================================= */
1734 /*                                                               */
1735 /*                       Public Methods                          */
1736 /*                                                               */
1737 /* ============================================================= */
1738
1739 /* =========================================== */
1740 /*                                             */
1741 /*          Object typing & Creation           */
1742 /*                                             */
1743 /* =========================================== */
1744
1745 GType
1746 gst_pvrvideosink_get_type (void)
1747 {
1748   static GType pvrvideosink_type = 0;
1749
1750   if (!pvrvideosink_type) {
1751     static const GTypeInfo pvrvideosink_info = {
1752       sizeof (GstPVRVideoSinkClass),
1753       gst_pvrvideosink_base_init,
1754       NULL,
1755       (GClassInitFunc) gst_pvrvideosink_class_init,
1756       NULL,
1757       NULL,
1758       sizeof (GstPVRVideoSink), 0, (GInstanceInitFunc) gst_pvrvideosink_init,
1759     };
1760     static const GInterfaceInfo iface_info = {
1761       (GInterfaceInitFunc) gst_pvrvideosink_interface_init, NULL, NULL,
1762     };
1763     static const GInterfaceInfo overlay_info = {
1764       (GInterfaceInitFunc) gst_pvrvideosink_xoverlay_init, NULL, NULL,
1765     };
1766
1767     pvrvideosink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
1768         "GstPVRVideoSink", &pvrvideosink_info, 0);
1769
1770     g_type_add_interface_static (pvrvideosink_type,
1771         GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
1772     g_type_add_interface_static (pvrvideosink_type, GST_TYPE_X_OVERLAY,
1773         &overlay_info);
1774   }
1775
1776   return pvrvideosink_type;
1777 }