ca171f366f872fa53d0b71e0c90c89fdda3621e2
[platform/upstream/gst-editing-services.git] / ges / ges-video-test-source.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
3  *               2010 Nokia Corporation
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:gesvideotestsource
23  * @title: GESVideoTestSource
24  * @short_description: produce solid colors and patterns, possibly with a time
25  * overlay.
26  */
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include "ges-internal.h"
32 #include "ges-track-element.h"
33 #include "ges-video-test-source.h"
34
35 #define DEFAULT_VPATTERN GES_VIDEO_TEST_PATTERN_SMPTE
36
37 struct _GESVideoTestSourcePrivate
38 {
39   GESVideoTestPattern pattern;
40
41   gboolean use_overlay;
42   GstElement *overlay;
43   GstPad *is_passthrough_pad;
44   GstPad *os_passthrough_pad;
45   GstPad *is_overlay_pad;
46   GstPad *os_overlay_pad;
47
48   GstElement *capsfilter;
49 };
50
51 G_DEFINE_TYPE_WITH_PRIVATE (GESVideoTestSource, ges_video_test_source,
52     GES_TYPE_VIDEO_SOURCE);
53
54 static GstElement *ges_video_test_source_create_source (GESTrackElement * self);
55
56 static gboolean
57 get_natural_size (GESVideoSource * source, gint * width, gint * height)
58 {
59   gboolean res = FALSE;
60   GESTimelineElement *parent = GES_TIMELINE_ELEMENT_PARENT (source);
61
62   if (parent) {
63     GESAsset *asset = ges_extractable_get_asset (GES_EXTRACTABLE (parent));
64
65     if (asset)
66       res = ges_test_clip_asset_get_natural_size (asset, width, height);
67   }
68
69   if (!res) {
70     *width = DEFAULT_WIDTH;
71     *height = DEFAULT_HEIGHT;
72   }
73
74   return TRUE;
75 }
76
77 enum
78 {
79   PROP_0,
80   PROP_USE_TIME_OVERLAY,
81   PROP_LAST
82 };
83
84 static GParamSpec *properties[PROP_LAST];
85
86 static void
87 get_property (GObject * object, guint property_id,
88     GValue * value, GParamSpec * pspec)
89 {
90   GESVideoTestSource *self = GES_VIDEO_TEST_SOURCE (object);
91
92   switch (property_id) {
93     case PROP_USE_TIME_OVERLAY:
94       g_value_set_boolean (value, self->priv->use_overlay);
95       break;
96     default:
97       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
98   }
99 }
100
101 static void
102 set_property (GObject * object, guint property_id,
103     const GValue * value, GParamSpec * pspec)
104 {
105   GESVideoTestSource *self = GES_VIDEO_TEST_SOURCE (object);
106
107   switch (property_id) {
108     case PROP_USE_TIME_OVERLAY:
109     {
110       GstElement *os, *is;
111       gboolean used_overlay = self->priv->use_overlay;
112
113       if (!self->priv->overlay) {
114         GST_ERROR_OBJECT (object, "Overlaying is disabled, you are probably"
115             "missing some GStreamer plugin");
116         return;
117       }
118
119       self->priv->use_overlay = g_value_get_boolean (value);
120       if (used_overlay == self->priv->use_overlay)
121         return;
122
123       is = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (self->
124                   priv->is_overlay_pad)));
125
126       if (!is)
127         return;
128
129       os = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (self->
130                   priv->os_overlay_pad)));
131       if (!os) {
132         gst_object_unref (is);
133         return;
134       }
135
136       g_object_set (is, "active-pad",
137           self->priv->use_overlay ? self->priv->is_overlay_pad : self->
138           priv->is_passthrough_pad, NULL);
139       g_object_set (os, "active-pad",
140           self->priv->use_overlay ? self->priv->os_overlay_pad : self->
141           priv->os_passthrough_pad, NULL);
142
143       gst_object_unref (is);
144       gst_object_unref (os);
145
146       break;
147     }
148     default:
149       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
150   }
151 }
152
153 static void
154 _set_child_property (GESTimelineElement * self, GObject * child,
155     GParamSpec * pspec, GValue * value)
156 {
157   GstElementFactory *f =
158       GST_IS_ELEMENT (child) ? gst_element_get_factory (GST_ELEMENT (child)) :
159       NULL;
160
161   if (!f || g_strcmp0 (GST_OBJECT_NAME (f), "timeoverlay"))
162     goto done;
163
164   GST_INFO_OBJECT (self, "Activating time overlay as time mode is being set");
165   g_object_set (self, "use-time-overlay", TRUE, NULL);
166
167 done:
168   GES_TIMELINE_ELEMENT_CLASS (ges_video_test_source_parent_class)
169       ->set_child_property (self, child, pspec, value);
170 }
171
172 static gboolean
173 _set_parent (GESTimelineElement * element, GESTimelineElement * parent)
174 {
175   GESVideoTestSource *self = GES_VIDEO_TEST_SOURCE (element);
176
177
178   if (parent) {
179     gint width, height, fps_n, fps_d;
180     GstCaps *caps;
181
182     g_assert (self->priv->capsfilter);
183     /* Setting the parent ourself as we need it to get the natural size */
184     element->parent = parent;
185     if (!ges_video_source_get_natural_size (GES_VIDEO_SOURCE (self), &width,
186             &height)) {
187       width = DEFAULT_WIDTH;
188       height = DEFAULT_HEIGHT;
189     }
190     if (!ges_timeline_element_get_natural_framerate (parent, &fps_n, &fps_d)) {
191       fps_n = DEFAULT_FRAMERATE_N;
192       fps_d = DEFAULT_FRAMERATE_D;
193     }
194
195     caps = gst_caps_new_simple ("video/x-raw",
196         "width", G_TYPE_INT, width,
197         "height", G_TYPE_INT, height,
198         "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
199     g_object_set (self->priv->capsfilter, "caps", caps, NULL);
200     gst_caps_unref (caps);
201   }
202
203   return TRUE;
204 }
205
206 static gboolean
207 _get_natural_framerate (GESTimelineElement * element, gint * fps_n,
208     gint * fps_d)
209 {
210   gboolean res = FALSE;
211   GESTimelineElement *parent = GES_TIMELINE_ELEMENT_PARENT (element);
212
213   if (parent) {
214     GESAsset *asset = ges_extractable_get_asset (GES_EXTRACTABLE (parent));
215
216     if (asset) {
217       res =
218           ges_clip_asset_get_natural_framerate (GES_CLIP_ASSET (asset), fps_n,
219           fps_d);
220     }
221   }
222
223   if (!res) {
224     *fps_n = DEFAULT_FRAMERATE_N;
225     *fps_d = DEFAULT_FRAMERATE_D;
226   }
227
228   return TRUE;
229 }
230
231 static void
232 dispose (GObject * object)
233 {
234   GESVideoTestSourcePrivate *priv = GES_VIDEO_TEST_SOURCE (object)->priv;
235
236   gst_clear_object (&priv->is_overlay_pad);
237   gst_clear_object (&priv->is_passthrough_pad);
238   gst_clear_object (&priv->os_overlay_pad);
239   gst_clear_object (&priv->os_passthrough_pad);
240
241   G_OBJECT_CLASS (ges_video_test_source_parent_class)->dispose (object);
242 }
243
244 static void
245 ges_video_test_source_class_init (GESVideoTestSourceClass * klass)
246 {
247   GObjectClass *object_class = G_OBJECT_CLASS (klass);
248   GESVideoSourceClass *source_class = GES_VIDEO_SOURCE_CLASS (klass);
249
250   source_class->create_source = ges_video_test_source_create_source;
251   source_class->ABI.abi.get_natural_size = get_natural_size;
252
253   /**
254    * GESVideoTestSource:use-overlay:
255    *
256    * Whether to overlay the test video source with a clock time.
257    * This property is also registered as a child property for the video
258    * test source, so you can set it like any other child property.
259    *
260    * The properties of the corresponding #timeoverlay are also registered
261    * as children properties. If you set any child property from the underlying
262    * `timeoverlay`, this property will be set to %TRUE by default.
263    */
264   properties[PROP_USE_TIME_OVERLAY] =
265       g_param_spec_boolean ("use-time-overlay", "Use-time-overlay",
266       "Use time overlay, setting a child property corresponding to"
267       "GstTimeOverlay will switch this on by default.", FALSE,
268       G_PARAM_READWRITE);
269
270   object_class->get_property = get_property;
271   object_class->set_property = set_property;
272   object_class->dispose = dispose;
273
274   GES_TIMELINE_ELEMENT_CLASS (klass)->set_child_property = _set_child_property;
275   GES_TIMELINE_ELEMENT_CLASS (klass)->set_parent = _set_parent;
276   GES_TIMELINE_ELEMENT_CLASS (klass)->get_natural_framerate =
277       _get_natural_framerate;
278
279   g_object_class_install_properties (object_class, PROP_LAST, properties);
280 }
281
282 static void
283 ges_video_test_source_init (GESVideoTestSource * self)
284 {
285   self->priv = ges_video_test_source_get_instance_private (self);
286
287   self->priv->pattern = DEFAULT_VPATTERN;
288 }
289
290 #define CREATE_ELEMENT(var, ename)                                             \
291   if (!(var = gst_element_factory_make(ename, NULL))) {                        \
292     GST_ERROR_OBJECT(self, "Could not create %s", ename);                      \
293     goto done;                                                                 \
294   }                                                                            \
295   gst_object_ref(var);
296
297 static GstElement *
298 ges_video_test_source_create_overlay (GESVideoTestSource * self)
299 {
300   GstElement *bin = NULL, *os = NULL, *is = NULL, *toverlay = NULL, *tcstamper =
301       NULL;
302   GstPad *tmppad;
303
304   CREATE_ELEMENT (os, "output-selector");
305   CREATE_ELEMENT (is, "input-selector");
306   CREATE_ELEMENT (tcstamper, "timecodestamper");
307   CREATE_ELEMENT (toverlay, "timeoverlay");
308
309   bin = gst_bin_new (NULL);
310
311   gst_bin_add_many (GST_BIN (bin), os, is, tcstamper, toverlay, NULL);
312   self->priv->os_passthrough_pad = gst_element_get_request_pad (os, "src_%u");
313   self->priv->is_passthrough_pad = gst_element_get_request_pad (is, "sink_%u");
314
315   gst_pad_link_full (self->priv->os_passthrough_pad,
316       self->priv->is_passthrough_pad, GST_PAD_LINK_CHECK_NOTHING);
317   gst_element_link (tcstamper, toverlay);
318
319   self->priv->os_overlay_pad = gst_element_get_request_pad (os, "src_%u");
320   tmppad = gst_element_get_static_pad (tcstamper, "sink");
321   gst_pad_link_full (self->priv->os_overlay_pad, tmppad,
322       GST_PAD_LINK_CHECK_NOTHING);
323   gst_object_unref (tmppad);
324
325   tmppad = gst_element_get_static_pad (toverlay, "src");
326   self->priv->is_overlay_pad = gst_element_get_request_pad (is, "sink_%u");
327   gst_pad_link_full (tmppad, self->priv->is_overlay_pad,
328       GST_PAD_LINK_CHECK_NOTHING);
329   gst_object_unref (tmppad);
330
331   tmppad = gst_element_get_static_pad (os, "sink");
332   gst_element_add_pad (GST_ELEMENT (bin), gst_ghost_pad_new ("sink", tmppad));
333   gst_object_unref (tmppad);
334
335   tmppad = gst_element_get_static_pad (is, "src");
336   gst_element_add_pad (GST_ELEMENT (bin), gst_ghost_pad_new ("src", tmppad));
337   gst_object_unref (tmppad);
338
339 done:
340   gst_clear_object (&os);
341   gst_clear_object (&is);
342   gst_clear_object (&tcstamper);
343   gst_clear_object (&toverlay);
344
345   return bin;
346 }
347
348 static GstElement *
349 ges_video_test_source_create_source (GESTrackElement * element)
350 {
351   GstCaps *caps;
352   gint pattern;
353   GstElement *testsrc, *res;
354   const gchar *props[] = { "pattern", NULL };
355   GPtrArray *elements;
356   GESVideoTestSource *self = GES_VIDEO_TEST_SOURCE (element);
357
358   g_assert (!GES_TIMELINE_ELEMENT_PARENT (element));
359   testsrc = gst_element_factory_make ("videotestsrc", NULL);
360   self->priv->capsfilter = gst_element_factory_make ("capsfilter", NULL);
361   pattern = self->priv->pattern;
362
363   g_object_set (testsrc, "pattern", pattern, NULL);
364
365   elements = g_ptr_array_new ();
366   g_ptr_array_add (elements, self->priv->capsfilter);
367   caps = gst_caps_new_simple ("video/x-raw",
368       "width", G_TYPE_INT, DEFAULT_WIDTH,
369       "height", G_TYPE_INT, DEFAULT_HEIGHT,
370       "framerate", GST_TYPE_FRACTION, DEFAULT_FRAMERATE_N, DEFAULT_FRAMERATE_D,
371       NULL);
372   g_object_set (self->priv->capsfilter, "caps", caps, NULL);
373   gst_caps_unref (caps);
374
375   self->priv->overlay = ges_video_test_source_create_overlay (self);
376   if (self->priv->overlay) {
377     const gchar *overlay_props[] =
378         { "time-mode", "text-y", "text-x", "text-width", "test-height",
379       "halignment", "valignment", "font-desc", NULL
380     };
381
382     ges_track_element_add_children_props (element, self->priv->overlay, NULL,
383         NULL, overlay_props);
384     ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT (self),
385         properties[PROP_USE_TIME_OVERLAY], G_OBJECT (self));
386     g_ptr_array_add (elements, self->priv->overlay);
387   }
388
389   ges_track_element_add_children_props (element, testsrc, NULL, NULL, props);
390
391   res = ges_source_create_topbin ("videotestsrc", testsrc, elements);
392   g_ptr_array_free (elements, TRUE);
393
394   return res;
395 }
396
397 /**
398  * ges_video_test_source_set_pattern:
399  * @self: a #GESVideoTestSource
400  * @pattern: a #GESVideoTestPattern
401  *
402  * Sets the source to use the given @pattern.
403  */
404 void
405 ges_video_test_source_set_pattern (GESVideoTestSource
406     * self, GESVideoTestPattern pattern)
407 {
408   GstElement *element =
409       ges_track_element_get_element (GES_TRACK_ELEMENT (self));
410
411   self->priv->pattern = pattern;
412
413   if (element) {
414     GValue val = { 0 };
415
416     g_value_init (&val, GES_VIDEO_TEST_PATTERN_TYPE);
417     g_value_set_enum (&val, pattern);
418     ges_track_element_set_child_property (GES_TRACK_ELEMENT (self), "pattern",
419         &val);
420   }
421 }
422
423 /**
424  * ges_video_test_source_get_pattern:
425  * @source: a #GESVideoTestPattern
426  *
427  * Get the video pattern used by the @source.
428  *
429  * Returns: The video pattern used by the @source.
430  */
431 GESVideoTestPattern
432 ges_video_test_source_get_pattern (GESVideoTestSource * source)
433 {
434   GValue val = { 0 };
435
436   ges_track_element_get_child_property (GES_TRACK_ELEMENT (source), "pattern",
437       &val);
438   return g_value_get_enum (&val);
439 }
440
441 /* ges_video_test_source_new:
442  *
443  * Creates a new #GESVideoTestSource.
444  *
445  * Returns: (transfer floating) (nullable): The newly created
446  * #GESVideoTestSource, or %NULL if there was an error.
447  */
448 GESVideoTestSource *
449 ges_video_test_source_new (void)
450 {
451   GESVideoTestSource *res;
452   GESAsset *asset = ges_asset_request (GES_TYPE_VIDEO_TEST_SOURCE, NULL, NULL);
453
454   res = GES_VIDEO_TEST_SOURCE (ges_asset_extract (asset, NULL));
455   gst_object_unref (asset);
456
457   return res;
458 }