1 /* GStreamer
2  *
3  * Copyright (C) 2014-2015 Sebastian Dröge <sebastian@centricular.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:gstplayer-videooverlayvideorenderer
23  * @title: GstPlayerVideoOverlayVideoRenderer
24  * @short_description: Player Video Overlay Video Renderer
25  *
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31 
32 #include "gstplayer-video-overlay-video-renderer.h"
33 #include "gstplayer.h"
34 
35 #include <gst/video/video.h>
36 
37 struct _GstPlayerVideoOverlayVideoRenderer
38 {
39   GObject parent;
40 
41   GstVideoOverlay *video_overlay;
42   gpointer window_handle;
43   gint x, y, width, height;
44 
45   GstElement *video_sink;       /* configured video sink, or NULL      */
46 };
47 
48 struct _GstPlayerVideoOverlayVideoRendererClass
49 {
50   GObjectClass parent_class;
51 };
52 
53 static void
54     gst_player_video_overlay_video_renderer_interface_init
55     (GstPlayerVideoRendererInterface * iface);
56 
57 enum
58 {
59   VIDEO_OVERLAY_VIDEO_RENDERER_PROP_0,
60   VIDEO_OVERLAY_VIDEO_RENDERER_PROP_WINDOW_HANDLE,
61   VIDEO_OVERLAY_VIDEO_RENDERER_PROP_VIDEO_SINK,
62   VIDEO_OVERLAY_VIDEO_RENDERER_PROP_LAST
63 };
64 
65 G_DEFINE_TYPE_WITH_CODE (GstPlayerVideoOverlayVideoRenderer,
66     gst_player_video_overlay_video_renderer, G_TYPE_OBJECT,
67     G_IMPLEMENT_INTERFACE (GST_TYPE_PLAYER_VIDEO_RENDERER,
68         gst_player_video_overlay_video_renderer_interface_init));
69 
70 static GParamSpec
71     * video_overlay_video_renderer_param_specs
72     [VIDEO_OVERLAY_VIDEO_RENDERER_PROP_LAST] = { NULL, };
73 
74 static void
gst_player_video_overlay_video_renderer_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)75 gst_player_video_overlay_video_renderer_set_property (GObject * object,
76     guint prop_id, const GValue * value, GParamSpec * pspec)
77 {
78   GstPlayerVideoOverlayVideoRenderer *self =
79       GST_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER (object);
80 
81   switch (prop_id) {
82     case VIDEO_OVERLAY_VIDEO_RENDERER_PROP_WINDOW_HANDLE:
83       self->window_handle = g_value_get_pointer (value);
84       if (self->video_overlay)
85         gst_video_overlay_set_window_handle (self->video_overlay,
86             (guintptr) self->window_handle);
87       break;
88     case VIDEO_OVERLAY_VIDEO_RENDERER_PROP_VIDEO_SINK:
89       self->video_sink = gst_object_ref_sink (g_value_get_object (value));
90       break;
91     default:
92       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
93       break;
94   }
95 }
96 
97 static void
gst_player_video_overlay_video_renderer_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)98 gst_player_video_overlay_video_renderer_get_property (GObject * object,
99     guint prop_id, GValue * value, GParamSpec * pspec)
100 {
101   GstPlayerVideoOverlayVideoRenderer *self =
102       GST_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER (object);
103 
104   switch (prop_id) {
105     case VIDEO_OVERLAY_VIDEO_RENDERER_PROP_WINDOW_HANDLE:
106       g_value_set_pointer (value, self->window_handle);
107       break;
108     case VIDEO_OVERLAY_VIDEO_RENDERER_PROP_VIDEO_SINK:
109       g_value_set_object (value, self->video_sink);
110       break;
111     default:
112       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
113       break;
114   }
115 }
116 
117 static void
gst_player_video_overlay_video_renderer_finalize(GObject * object)118 gst_player_video_overlay_video_renderer_finalize (GObject * object)
119 {
120   GstPlayerVideoOverlayVideoRenderer *self =
121       GST_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER (object);
122 
123   if (self->video_overlay)
124     gst_object_unref (self->video_overlay);
125 
126   if (self->video_sink)
127     gst_object_unref (self->video_sink);
128 
129   G_OBJECT_CLASS
130       (gst_player_video_overlay_video_renderer_parent_class)->finalize (object);
131 }
132 
133 static void
gst_player_video_overlay_video_renderer_class_init(GstPlayerVideoOverlayVideoRendererClass * klass)134     gst_player_video_overlay_video_renderer_class_init
135     (GstPlayerVideoOverlayVideoRendererClass * klass)
136 {
137   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
138 
139   gobject_class->set_property =
140       gst_player_video_overlay_video_renderer_set_property;
141   gobject_class->get_property =
142       gst_player_video_overlay_video_renderer_get_property;
143   gobject_class->finalize = gst_player_video_overlay_video_renderer_finalize;
144 
145   video_overlay_video_renderer_param_specs
146       [VIDEO_OVERLAY_VIDEO_RENDERER_PROP_WINDOW_HANDLE] =
147       g_param_spec_pointer ("window-handle", "Window Handle",
148       "Window handle to embed the video into",
149       G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
150 
151   video_overlay_video_renderer_param_specs
152       [VIDEO_OVERLAY_VIDEO_RENDERER_PROP_VIDEO_SINK] =
153       g_param_spec_object ("video-sink", "Video Sink",
154       "the video output element to use (NULL = default sink)",
155       GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
156 
157   g_object_class_install_properties (gobject_class,
158       VIDEO_OVERLAY_VIDEO_RENDERER_PROP_LAST,
159       video_overlay_video_renderer_param_specs);
160 }
161 
162 static void
gst_player_video_overlay_video_renderer_init(GstPlayerVideoOverlayVideoRenderer * self)163     gst_player_video_overlay_video_renderer_init
164     (GstPlayerVideoOverlayVideoRenderer * self)
165 {
166   self->x = self->y = self->width = self->height = -1;
167   self->video_sink = NULL;
168 }
169 
gst_player_video_overlay_video_renderer_create_video_sink(GstPlayerVideoRenderer * iface,GstPlayer * player)170 static GstElement *gst_player_video_overlay_video_renderer_create_video_sink
171     (GstPlayerVideoRenderer * iface, GstPlayer * player)
172 {
173   GstElement *video_overlay;
174   GstPlayerVideoOverlayVideoRenderer *self =
175       GST_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER (iface);
176 
177   if (self->video_overlay)
178     gst_object_unref (self->video_overlay);
179 
180   video_overlay = gst_player_get_pipeline (player);
181   g_return_val_if_fail (GST_IS_VIDEO_OVERLAY (video_overlay), NULL);
182 
183   self->video_overlay = GST_VIDEO_OVERLAY (video_overlay);
184 
185   gst_video_overlay_set_window_handle (self->video_overlay,
186       (guintptr) self->window_handle);
187   if (self->width != -1 || self->height != -1)
188     gst_video_overlay_set_render_rectangle (self->video_overlay, self->x,
189         self->y, self->width, self->height);
190 
191   return self->video_sink;
192 }
193 
194 static void
gst_player_video_overlay_video_renderer_interface_init(GstPlayerVideoRendererInterface * iface)195     gst_player_video_overlay_video_renderer_interface_init
196     (GstPlayerVideoRendererInterface * iface)
197 {
198   iface->create_video_sink =
199       gst_player_video_overlay_video_renderer_create_video_sink;
200 }
201 
202 /**
203  * gst_player_video_overlay_video_renderer_new:
204  * @window_handle: (allow-none): Window handle to use or %NULL
205  *
206  * Returns: (transfer full):
207  */
208 GstPlayerVideoRenderer *
gst_player_video_overlay_video_renderer_new(gpointer window_handle)209 gst_player_video_overlay_video_renderer_new (gpointer window_handle)
210 {
211   return g_object_new (GST_TYPE_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER,
212       "window-handle", window_handle, NULL);
213 }
214 
215 /**
216  * gst_player_video_overlay_video_renderer_new_with_sink:
217  * @window_handle: (allow-none): Window handle to use or %NULL
218  * @video_sink: (transfer floating): the custom video_sink element to be set for the video renderer
219  *
220  * Returns: (transfer full):
221  *
222  * Since: 1.12
223  */
224 GstPlayerVideoRenderer *
gst_player_video_overlay_video_renderer_new_with_sink(gpointer window_handle,GstElement * video_sink)225 gst_player_video_overlay_video_renderer_new_with_sink (gpointer window_handle,
226     GstElement * video_sink)
227 {
228   return g_object_new (GST_TYPE_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER,
229       "window-handle", window_handle, "video-sink", video_sink, NULL);
230 }
231 
232 /**
233  * gst_player_video_overlay_video_renderer_set_window_handle:
234  * @self: #GstPlayerVideoRenderer instance
235  * @window_handle: handle referencing to the platform specific window
236  *
237  * Sets the platform specific window handle into which the video
238  * should be rendered
239  **/
gst_player_video_overlay_video_renderer_set_window_handle(GstPlayerVideoOverlayVideoRenderer * self,gpointer window_handle)240 void gst_player_video_overlay_video_renderer_set_window_handle
241     (GstPlayerVideoOverlayVideoRenderer * self, gpointer window_handle)
242 {
243   g_return_if_fail (GST_IS_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER (self));
244 
245   g_object_set (self, "window-handle", window_handle, NULL);
246 }
247 
248 /**
249  * gst_player_video_overlay_video_renderer_get_window_handle:
250  * @self: #GstPlayerVideoRenderer instance
251  *
252  * Returns: (transfer none): The currently set, platform specific window
253  * handle
254  */
255 gpointer
gst_player_video_overlay_video_renderer_get_window_handle(GstPlayerVideoOverlayVideoRenderer * self)256     gst_player_video_overlay_video_renderer_get_window_handle
257     (GstPlayerVideoOverlayVideoRenderer * self) {
258   gpointer window_handle;
259 
260   g_return_val_if_fail (GST_IS_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER (self),
261       NULL);
262 
263   g_object_get (self, "window-handle", &window_handle, NULL);
264 
265   return window_handle;
266 }
267 
268 /**
269  * gst_player_video_overlay_video_renderer_expose:
270  * @self: a #GstPlayerVideoOverlayVideoRenderer instance.
271  *
272  * Tell an overlay that it has been exposed. This will redraw the current frame
273  * in the drawable even if the pipeline is PAUSED.
274  */
gst_player_video_overlay_video_renderer_expose(GstPlayerVideoOverlayVideoRenderer * self)275 void gst_player_video_overlay_video_renderer_expose
276     (GstPlayerVideoOverlayVideoRenderer * self)
277 {
278   g_return_if_fail (GST_IS_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER (self));
279 
280   if (self->video_overlay)
281     gst_video_overlay_expose (self->video_overlay);
282 }
283 
284 /**
285  * gst_player_video_overlay_video_renderer_set_render_rectangle:
286  * @self: a #GstPlayerVideoOverlayVideoRenderer instance
287  * @x: the horizontal offset of the render area inside the window
288  * @y: the vertical offset of the render area inside the window
289  * @width: the width of the render area inside the window
290  * @height: the height of the render area inside the window
291  *
292  * Configure a subregion as a video target within the window set by
293  * gst_player_video_overlay_video_renderer_set_window_handle(). If this is not
294  * used or not supported the video will fill the area of the window set as the
295  * overlay to 100%. By specifying the rectangle, the video can be overlaid to
296  * a specific region of that window only. After setting the new rectangle one
297  * should call gst_player_video_overlay_video_renderer_expose() to force a
298  * redraw. To unset the region pass -1 for the @width and @height parameters.
299  *
300  * This method is needed for non fullscreen video overlay in UI toolkits that
301  * do not support subwindows.
302  *
303  */
gst_player_video_overlay_video_renderer_set_render_rectangle(GstPlayerVideoOverlayVideoRenderer * self,gint x,gint y,gint width,gint height)304 void gst_player_video_overlay_video_renderer_set_render_rectangle
305     (GstPlayerVideoOverlayVideoRenderer * self, gint x, gint y, gint width,
306     gint height)
307 {
308   g_return_if_fail (GST_IS_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER (self));
309 
310   self->x = x;
311   self->y = y;
312   self->width = width;
313   self->height = height;
314 
315   if (self->video_overlay)
316     gst_video_overlay_set_render_rectangle (self->video_overlay,
317         x, y, width, height);
318 }
319 
320 /**
321  * gst_player_video_overlay_video_renderer_get_render_rectangle:
322  * @self: a #GstPlayerVideoOverlayVideoRenderer instance
323  * @x: (out) (allow-none): the horizontal offset of the render area inside the window
324  * @y: (out) (allow-none): the vertical offset of the render area inside the window
325  * @width: (out) (allow-none): the width of the render area inside the window
326  * @height: (out) (allow-none): the height of the render area inside the window
327  *
328  * Return the currently configured render rectangle. See gst_player_video_overlay_video_renderer_set_render_rectangle()
329  * for details.
330  *
331  */
gst_player_video_overlay_video_renderer_get_render_rectangle(GstPlayerVideoOverlayVideoRenderer * self,gint * x,gint * y,gint * width,gint * height)332 void gst_player_video_overlay_video_renderer_get_render_rectangle
333     (GstPlayerVideoOverlayVideoRenderer * self, gint * x, gint * y,
334     gint * width, gint * height)
335 {
336   g_return_if_fail (GST_IS_PLAYER_VIDEO_OVERLAY_VIDEO_RENDERER (self));
337 
338   if (x)
339     *x = self->x;
340   if (y)
341     *y = self->y;
342   if (width)
343     *width = self->width;
344   if (height)
345     *height = self->height;
346 }
347