d3dvideosink: Add support for crop meta
[platform/upstream/gstreamer.git] / sys / d3dvideosink / d3dvideosink.c
1 /* GStreamer
2  * Copyright (C) 2012 Roland Krikava <info@bluedigits.com>
3  * Copyright (C) 2010-2011 David Hoyt <dhoyt@hoytsoft.org>
4  * Copyright (C) 2010 Andoni Morales <ylatuya@gmail.com>
5  * Copyright (C) 2012 Sebastian Dröge <sebastian.droege@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 Library 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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "d3dvideosink.h"
27
28 #define ELEMENT_NAME  "d3dvideosink"
29
30 enum
31 {
32   PROP_0,
33   PROP_FORCE_ASPECT_RATIO,
34   PROP_CREATE_RENDER_WINDOW,
35   PROP_STREAM_STOP_ON_CLOSE,
36   PROP_ENABLE_NAVIGATION_EVENTS,
37   PROP_LAST
38 };
39
40 #define DEFAULT_FORCE_ASPECT_RATIO       TRUE
41 #define DEFAULT_CREATE_RENDER_WINDOW     TRUE
42 #define DEFAULT_STREAM_STOP_ON_CLOSE     TRUE
43 #define DEFAULT_ENABLE_NAVIGATION_EVENTS TRUE
44
45 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
46     GST_PAD_SINK,
47     GST_PAD_ALWAYS,
48     GST_STATIC_CAPS ("video/x-raw, "
49         "format = (string) { I420, YV12, UYVY, YUY2, NV12, BGRx, RGBx, RGBA, BGRA, BGR, RGB16, RGB15 }, "
50         "framerate = (fraction) [ 0, MAX ], "
51         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
52     );
53
54 GST_DEBUG_CATEGORY (gst_d3dvideosink_debug);
55 #define GST_CAT_DEFAULT gst_d3dvideosink_debug
56
57 /** FWD DECLS **/
58 /* GstXOverlay Interface */
59 static void
60 gst_d3dvideosink_video_overlay_interface_init (GstVideoOverlayInterface *
61     iface);
62 static void gst_d3dvideosink_set_window_handle (GstVideoOverlay * overlay,
63     guintptr window_id);
64 static void gst_d3dvideosink_set_render_rectangle (GstVideoOverlay * overlay,
65     gint x, gint y, gint width, gint height);
66 static void gst_d3dvideosink_expose (GstVideoOverlay * overlay);
67 /* GstNavigation Interface */
68 static void gst_d3dvideosink_navigation_interface_init (GstNavigationInterface *
69     iface);
70 static void gst_d3dvideosink_navigation_send_event (GstNavigation * navigation,
71     GstStructure * structure);
72 /* GObject */
73 static void gst_d3dvideosink_set_property (GObject * object, guint prop_id,
74     const GValue * value, GParamSpec * pspec);
75 static void gst_d3dvideosink_get_property (GObject * object, guint prop_id,
76     GValue * value, GParamSpec * pspec);
77 static void gst_d3dvideosink_finalize (GObject * gobject);
78 /* GstBaseSink */
79 static GstCaps *gst_d3dvideosink_get_caps (GstBaseSink * basesink,
80     GstCaps * filter);
81 static gboolean gst_d3dvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps);
82 static gboolean gst_d3dvideosink_start (GstBaseSink * sink);
83 static gboolean gst_d3dvideosink_stop (GstBaseSink * sink);
84 static gboolean gst_d3dvideosink_propose_allocation (GstBaseSink * bsink,
85     GstQuery * query);
86 /* GstVideoSink */
87 static GstFlowReturn gst_d3dvideosink_show_frame (GstVideoSink * vsink,
88     GstBuffer * buffer);
89
90 #define _do_init \
91   G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_d3dvideosink_navigation_interface_init); \
92   G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY, gst_d3dvideosink_video_overlay_interface_init); \
93   GST_DEBUG_CATEGORY_INIT (gst_d3dvideosink_debug, ELEMENT_NAME, 0, "Direct3D Video");
94
95 G_DEFINE_TYPE_WITH_CODE (GstD3DVideoSink, gst_d3dvideosink, GST_TYPE_VIDEO_SINK,
96     _do_init);
97
98 static void
99 gst_d3dvideosink_class_init (GstD3DVideoSinkClass * klass)
100 {
101   GObjectClass *gobject_class;
102   GstElementClass *gstelement_class;
103   GstVideoSinkClass *gstvideosink_class;
104   GstBaseSinkClass *gstbasesink_class;
105
106   gobject_class = (GObjectClass *) klass;
107   gstelement_class = (GstElementClass *) klass;
108   gstvideosink_class = (GstVideoSinkClass *) klass;
109   gstbasesink_class = (GstBaseSinkClass *) klass;
110
111   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_d3dvideosink_finalize);
112   gobject_class->set_property =
113       GST_DEBUG_FUNCPTR (gst_d3dvideosink_set_property);
114   gobject_class->get_property =
115       GST_DEBUG_FUNCPTR (gst_d3dvideosink_get_property);
116
117   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_d3dvideosink_get_caps);
118   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_d3dvideosink_set_caps);
119   gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_d3dvideosink_start);
120   gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_d3dvideosink_stop);
121   gstbasesink_class->propose_allocation =
122       GST_DEBUG_FUNCPTR (gst_d3dvideosink_propose_allocation);
123
124   gstvideosink_class->show_frame =
125       GST_DEBUG_FUNCPTR (gst_d3dvideosink_show_frame);
126
127   /* Add properties */
128   g_object_class_install_property (G_OBJECT_CLASS (klass),
129       PROP_FORCE_ASPECT_RATIO, g_param_spec_boolean ("force-aspect-ratio",
130           "Force aspect ratio",
131           "When enabled, scaling will respect original aspect ratio",
132           DEFAULT_FORCE_ASPECT_RATIO,
133           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
134   g_object_class_install_property (G_OBJECT_CLASS (klass),
135       PROP_CREATE_RENDER_WINDOW, g_param_spec_boolean ("create-render-window",
136           "Create render window",
137           "If no window ID is given, a new render window is created",
138           DEFAULT_CREATE_RENDER_WINDOW,
139           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
140   g_object_class_install_property (G_OBJECT_CLASS (klass),
141       PROP_STREAM_STOP_ON_CLOSE, g_param_spec_boolean ("stream-stop-on-close",
142           "Stop streaming on window close",
143           "If the render window is closed stop stream",
144           DEFAULT_STREAM_STOP_ON_CLOSE,
145           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
146   g_object_class_install_property (G_OBJECT_CLASS (klass),
147       PROP_ENABLE_NAVIGATION_EVENTS,
148       g_param_spec_boolean ("enable-navigation-events",
149           "Enable navigation events",
150           "When enabled, navigation events are sent upstream",
151           DEFAULT_ENABLE_NAVIGATION_EVENTS,
152           (GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
153
154   gst_element_class_set_static_metadata (gstelement_class,
155       "Direct3D video sink", "Sink/Video",
156       "Display data using a Direct3D video renderer",
157       "David Hoyt <dhoyt@hoytsoft.org>, Roland Krikava <info@bluedigits.com>");
158
159   gst_element_class_add_pad_template (gstelement_class,
160       gst_static_pad_template_get (&sink_template));
161
162   g_rec_mutex_init (&klass->lock);
163 }
164
165 static void
166 gst_d3dvideosink_init (GstD3DVideoSink * sink)
167 {
168   GST_DEBUG_OBJECT (sink, " ");
169
170   /* Init Properties */
171   sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
172   sink->create_internal_window = DEFAULT_CREATE_RENDER_WINDOW;
173   sink->stream_stop_on_close = DEFAULT_STREAM_STOP_ON_CLOSE;
174   sink->enable_navigation_events = DEFAULT_ENABLE_NAVIGATION_EVENTS;
175
176   g_rec_mutex_init (&sink->lock);
177 }
178
179 /** GObject Functions **/
180
181 static void
182 gst_d3dvideosink_finalize (GObject * gobject)
183 {
184   GstD3DVideoSink *sink = GST_D3DVIDEOSINK (gobject);
185
186   GST_DEBUG_OBJECT (sink, " ");
187
188   gst_object_replace ((GstObject **) & sink->pool, NULL);
189   gst_object_replace ((GstObject **) & sink->fallback_pool, NULL);
190
191   gst_caps_replace (&sink->supported_caps, NULL);
192
193   g_rec_mutex_clear (&sink->lock);
194
195   G_OBJECT_CLASS (gst_d3dvideosink_parent_class)->finalize (gobject);
196 }
197
198 static void
199 gst_d3dvideosink_set_property (GObject * object, guint prop_id,
200     const GValue * value, GParamSpec * pspec)
201 {
202   GstD3DVideoSink *sink = GST_D3DVIDEOSINK (object);
203
204   switch (prop_id) {
205     case PROP_FORCE_ASPECT_RATIO:
206       sink->force_aspect_ratio = g_value_get_boolean (value);
207       break;
208     case PROP_CREATE_RENDER_WINDOW:
209       sink->create_internal_window = g_value_get_boolean (value);
210       break;
211     case PROP_STREAM_STOP_ON_CLOSE:
212       sink->stream_stop_on_close = g_value_get_boolean (value);
213       break;
214     case PROP_ENABLE_NAVIGATION_EVENTS:
215       sink->enable_navigation_events = g_value_get_boolean (value);
216       break;
217     default:
218       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
219       break;
220   }
221 }
222
223 static void
224 gst_d3dvideosink_get_property (GObject * object, guint prop_id, GValue * value,
225     GParamSpec * pspec)
226 {
227   GstD3DVideoSink *sink = GST_D3DVIDEOSINK (object);
228
229   switch (prop_id) {
230     case PROP_FORCE_ASPECT_RATIO:
231       g_value_set_boolean (value, sink->force_aspect_ratio);
232       break;
233     case PROP_CREATE_RENDER_WINDOW:
234       g_value_set_boolean (value, sink->create_internal_window);
235       break;
236     case PROP_STREAM_STOP_ON_CLOSE:
237       g_value_set_boolean (value, sink->stream_stop_on_close);
238       break;
239     case PROP_ENABLE_NAVIGATION_EVENTS:
240       g_value_set_boolean (value, sink->enable_navigation_events);
241       break;
242     default:
243       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
244       break;
245   }
246 }
247
248 /** GstBaseSinkClass Functions **/
249
250 static GstCaps *
251 gst_d3dvideosink_get_caps (GstBaseSink * basesink, GstCaps * filter)
252 {
253   GstD3DVideoSink *sink = GST_D3DVIDEOSINK (basesink);
254   GstCaps *caps;
255
256   caps = d3d_supported_caps (sink);
257   if (!caps)
258     caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (sink));
259
260   if (caps && filter) {
261     GstCaps *isect;
262     isect = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
263     gst_caps_unref (caps);
264     caps = isect;
265   }
266
267   return caps;
268 }
269
270 static gboolean
271 gst_d3dvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps)
272 {
273   GstD3DVideoSink *sink;
274   GstCaps *sink_caps;
275   gint video_width, video_height;
276   gint video_par_n, video_par_d;        /* video's PAR */
277   gint display_par_n = 1, display_par_d = 1;    /* display's PAR */
278   guint num, den;
279   gchar *tmp = NULL;
280   GstBufferPool *newpool, *oldpool;
281   GstBufferPool *newfbpool, *oldfbpool;
282   GstStructure *config;
283
284   GST_DEBUG_OBJECT (bsink, " ");
285
286   GST_DEBUG_OBJECT (bsink, "Caps: %s", (tmp = gst_caps_to_string (caps)));
287   sink = GST_D3DVIDEOSINK (bsink);
288
289   sink_caps = d3d_supported_caps (sink);
290
291   if (!gst_caps_can_intersect (sink_caps, caps))
292     goto incompatible_caps;
293   gst_caps_replace (&sink_caps, NULL);
294
295   memset (&sink->info, 0, sizeof (GstVideoInfo));
296   if (!gst_video_info_from_caps (&sink->info, caps))
297     goto invalid_format;
298
299   sink->format = sink->info.finfo->format;
300   video_width = sink->info.width;
301   video_height = sink->info.height;
302   video_par_n = sink->info.par_n;
303   video_par_d = sink->info.par_d;
304
305   GST_DEBUG_OBJECT (bsink, "Set Caps Format: %s",
306       gst_video_format_to_string (sink->format));
307
308   /* get aspect ratio from caps if it's present, and
309    * convert video width and height to a display width and height
310    * using wd / hd = wv / hv * PARv / PARd */
311
312   /* TODO: Get display PAR */
313
314   if (!gst_video_calculate_display_ratio (&num, &den, video_width,
315           video_height, video_par_n, video_par_d, display_par_n, display_par_d))
316     goto no_disp_ratio;
317
318   GST_DEBUG_OBJECT (sink,
319       "video width/height: %dx%d, calculated display ratio: %d/%d format: %u",
320       video_width, video_height, num, den, sink->format);
321
322   /* now find a width x height that respects this display ratio.
323    * prefer those that have one of w/h the same as the incoming video
324    * using wd / hd = num / den
325    */
326
327   /* start with same height, because of interlaced video
328    * check hd / den is an integer scale factor, and scale wd with the PAR
329    */
330   if (video_height % den == 0) {
331     GST_DEBUG_OBJECT (sink, "keeping video height");
332     GST_VIDEO_SINK_WIDTH (sink) = (guint)
333         gst_util_uint64_scale_int (video_height, num, den);
334     GST_VIDEO_SINK_HEIGHT (sink) = video_height;
335   } else if (video_width % num == 0) {
336     GST_DEBUG_OBJECT (sink, "keeping video width");
337     GST_VIDEO_SINK_WIDTH (sink) = video_width;
338     GST_VIDEO_SINK_HEIGHT (sink) = (guint)
339         gst_util_uint64_scale_int (video_width, den, num);
340   } else {
341     GST_DEBUG_OBJECT (sink, "approximating while keeping video height");
342     GST_VIDEO_SINK_WIDTH (sink) = (guint)
343         gst_util_uint64_scale_int (video_height, num, den);
344     GST_VIDEO_SINK_HEIGHT (sink) = video_height;
345   }
346   GST_DEBUG_OBJECT (sink, "scaling to %dx%d",
347       GST_VIDEO_SINK_WIDTH (sink), GST_VIDEO_SINK_HEIGHT (sink));
348
349   if (GST_VIDEO_SINK_WIDTH (sink) <= 0 || GST_VIDEO_SINK_HEIGHT (sink) <= 0)
350     goto no_display_size;
351
352   memset (&sink->crop_rect, 0, sizeof (sink->crop_rect));
353   sink->crop_rect.w = sink->info.width;
354   sink->crop_rect.h = sink->info.height;
355
356   sink->width = video_width;
357   sink->height = video_height;
358
359   GST_DEBUG_OBJECT (bsink, "Selected caps: %s", (tmp =
360           gst_caps_to_string (caps)));
361   g_free (tmp);
362
363   if (!d3d_set_render_format (sink))
364     goto incompatible_caps;
365
366   /* Create a window (or start using an application-supplied one, then connect the graph */
367   d3d_prepare_window (sink);
368
369   newpool = gst_d3dsurface_buffer_pool_new (sink);
370   config = gst_buffer_pool_get_config (newpool);
371   /* we need at least 2 buffer because we hold on to the last one */
372   gst_buffer_pool_config_set_params (config, caps, sink->info.size, 2, 0);
373   if (!gst_buffer_pool_set_config (newpool, config)) {
374     gst_object_unref (newpool);
375     GST_ERROR_OBJECT (sink, "Failed to set buffer pool configuration");
376     return FALSE;
377   }
378
379   newfbpool = gst_d3dsurface_buffer_pool_new (sink);
380   config = gst_buffer_pool_get_config (newfbpool);
381   /* we need at least 2 buffer because we hold on to the last one */
382   gst_buffer_pool_config_set_params (config, caps, sink->info.size, 2, 0);
383   /* Fallback pool must use videometa */
384   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
385   if (!gst_buffer_pool_set_config (newfbpool, config)) {
386     gst_object_unref (newfbpool);
387     GST_ERROR_OBJECT (sink, "Failed to set buffer pool configuration");
388     return FALSE;
389   }
390
391   GST_OBJECT_LOCK (sink);
392   oldpool = sink->pool;
393   sink->pool = newpool;
394   oldfbpool = sink->fallback_pool;
395   sink->fallback_pool = newfbpool;
396   GST_OBJECT_UNLOCK (sink);
397
398   if (oldpool)
399     gst_object_unref (oldpool);
400   if (oldfbpool)
401     gst_object_unref (oldfbpool);
402
403   return TRUE;
404   /* ERRORS */
405 incompatible_caps:
406   {
407     GST_ERROR_OBJECT (sink, "caps incompatible");
408     gst_caps_unref (sink_caps);
409     return FALSE;
410   }
411 invalid_format:
412   {
413     gchar *caps_txt = gst_caps_to_string (caps);
414     GST_DEBUG_OBJECT (sink,
415         "Could not locate image format from caps %s", caps_txt);
416     g_free (caps_txt);
417     return FALSE;
418   }
419 no_disp_ratio:
420   {
421     GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL),
422         ("Error calculating the output display ratio of the video."));
423     return FALSE;
424   }
425 no_display_size:
426   {
427     GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION, (NULL),
428         ("Error calculating the output display ratio of the video."));
429     return FALSE;
430   }
431 }
432
433 static gboolean
434 gst_d3dvideosink_start (GstBaseSink * bsink)
435 {
436   GstD3DVideoSink *sink = GST_D3DVIDEOSINK (bsink);
437
438   GST_DEBUG_OBJECT (bsink, "Start() called");
439
440   return d3d_class_init (sink);
441 }
442
443 static gboolean
444 gst_d3dvideosink_stop (GstBaseSink * bsink)
445 {
446   GstD3DVideoSink *sink = GST_D3DVIDEOSINK (bsink);
447
448   GST_DEBUG_OBJECT (bsink, "Stop() called");
449   d3d_stop (sink);
450   d3d_class_destroy (sink);
451
452   return TRUE;
453 }
454
455 static gboolean
456 gst_d3dvideosink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
457 {
458   GstD3DVideoSink *sink = GST_D3DVIDEOSINK (bsink);
459   GstBufferPool *pool;
460   GstStructure *config;
461   GstCaps *caps;
462   guint size;
463   gboolean need_pool;
464
465   gst_query_parse_allocation (query, &caps, &need_pool);
466   if (!caps) {
467     GST_DEBUG_OBJECT (sink, "no caps specified");
468     return FALSE;
469   }
470
471   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
472   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
473
474   GST_OBJECT_LOCK (sink);
475   pool = sink->pool ? gst_object_ref (sink->pool) : NULL;
476   GST_OBJECT_UNLOCK (sink);
477
478   if (pool) {
479     GstCaps *pcaps;
480
481     /* we had a pool, check caps */
482     GST_DEBUG_OBJECT (sink, "check existing pool caps");
483     config = gst_buffer_pool_get_config (pool);
484     gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
485
486     if (!gst_caps_is_equal (caps, pcaps)) {
487       GST_DEBUG_OBJECT (sink, "pool has different caps");
488       /* different caps, we can't use this pool */
489       gst_object_unref (pool);
490       pool = NULL;
491     }
492     gst_structure_free (config);
493   }
494
495   if (pool == NULL && need_pool) {
496     GstVideoInfo info;
497
498     if (!gst_video_info_from_caps (&info, caps)) {
499       GST_ERROR_OBJECT (sink, "allocation query has invalid caps %"
500           GST_PTR_FORMAT, caps);
501       return FALSE;
502     }
503
504     GST_DEBUG_OBJECT (sink, "create new pool");
505     pool = gst_d3dsurface_buffer_pool_new (sink);
506
507     /* the normal size of a frame */
508     size = info.size;
509
510     config = gst_buffer_pool_get_config (pool);
511     /* we need at least 2 buffer because we hold on to the last one */
512     gst_buffer_pool_config_set_params (config, caps, size, 2, 0);
513     if (!gst_buffer_pool_set_config (pool, config)) {
514       gst_object_unref (pool);
515       GST_ERROR_OBJECT (sink, "failed to set pool configuration");
516       return FALSE;
517     }
518   }
519
520   if (pool) {
521     /* we need at least 2 buffer because we hold on to the last one */
522     gst_query_add_allocation_pool (query, pool, size, 2, 0);
523     gst_object_unref (pool);
524   }
525
526   return TRUE;
527 }
528
529 /** PUBLIC FUNCTIONS **/
530
531 /* Iterface Registrations */
532
533 static void
534 gst_d3dvideosink_video_overlay_interface_init (GstVideoOverlayInterface * iface)
535 {
536   iface->set_window_handle = gst_d3dvideosink_set_window_handle;
537   iface->set_render_rectangle = gst_d3dvideosink_set_render_rectangle;
538   iface->expose = gst_d3dvideosink_expose;
539 }
540
541 static void
542 gst_d3dvideosink_navigation_interface_init (GstNavigationInterface * iface)
543 {
544   iface->send_event = gst_d3dvideosink_navigation_send_event;
545 }
546
547 /* Video Render Code */
548
549 static void
550 gst_d3dvideosink_set_window_handle (GstVideoOverlay * overlay,
551     guintptr window_id)
552 {
553   d3d_set_window_handle (GST_D3DVIDEOSINK (overlay), window_id, FALSE);
554 }
555
556 static void
557 gst_d3dvideosink_set_render_rectangle (GstVideoOverlay * overlay, gint x,
558     gint y, gint width, gint height)
559 {
560   GstD3DVideoSink *sink = GST_D3DVIDEOSINK (overlay);
561   sink->render_rect.x = x;
562   sink->render_rect.y = y;
563   sink->render_rect.w = width;
564   sink->render_rect.h = height;
565   d3d_set_render_rectangle (sink);
566 }
567
568 static void
569 gst_d3dvideosink_expose (GstVideoOverlay * overlay)
570 {
571   GstD3DVideoSink *sink = GST_D3DVIDEOSINK (overlay);
572   d3d_expose_window (sink);
573 }
574
575 static GstFlowReturn
576 gst_d3dvideosink_show_frame (GstVideoSink * vsink, GstBuffer * buffer)
577 {
578   GstD3DVideoSink *sink = GST_D3DVIDEOSINK (vsink);
579   return d3d_render_buffer (sink, buffer);
580 }
581
582 /* Video Navigation Events */
583
584 static void
585 gst_d3dvideosink_navigation_send_event (GstNavigation * navigation,
586     GstStructure * structure)
587 {
588   GstD3DVideoSink *sink = GST_D3DVIDEOSINK (navigation);
589   GstEvent *e;
590
591   if ((e = gst_event_new_navigation (structure))) {
592     GstPad *pad;
593     if ((pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink)))) {
594       gst_pad_send_event (pad, e);
595       gst_object_unref (pad);
596     }
597   }
598 }
599
600 /** PRIVATE FUNCTIONS **/
601
602
603 /* Plugin entry point */
604 static gboolean
605 plugin_init (GstPlugin * plugin)
606 {
607   /* PRIMARY: this is the best videosink to use on windows */
608   if (!gst_element_register (plugin, ELEMENT_NAME,
609           GST_RANK_PRIMARY, GST_TYPE_D3DVIDEOSINK))
610     return FALSE;
611
612   return TRUE;
613 }
614
615 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
616     GST_VERSION_MINOR,
617     d3dsinkwrapper,
618     "Direct3D sink wrapper plugin",
619     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)