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