markerlist: implement GESMarkerList
[platform/upstream/gst-editing-services.git] / ges / ges.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
3  *               2009 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: ges.h
23  * @title: Initialization
24  * @short_description: GStreamer editing services initialization functions
25  *
26  * GES needs to be initialized after GStreamer itself. This section
27  * contains the various functions to do so.
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <stdlib.h>
35 #include <ges/ges.h>
36 #include "ges/gstframepositioner.h"
37 #include "ges-internal.h"
38
39 #ifndef DISABLE_XPTV
40 #include <ges/ges-pitivi-formatter.h>
41 #endif
42
43 #define GES_GNONLIN_VERSION_NEEDED_MAJOR 1
44 #define GES_GNONLIN_VERSION_NEEDED_MINOR 2
45 #define GES_GNONLIN_VERSION_NEEDED_MICRO 0
46
47 GST_DEBUG_CATEGORY (_ges_debug);
48
49 G_LOCK_DEFINE_STATIC (init_lock);
50
51 /* (without holding ref) thread object for thread_self() validation
52  * between init/deinit
53  */
54 static GThread *initialized_thread = NULL;
55
56 static gboolean
57 ges_init_pre (GOptionContext * context, GOptionGroup * group, gpointer data,
58     GError ** error)
59 {
60   if (initialized_thread) {
61     GST_DEBUG ("already initialized");
62     return TRUE;
63   }
64
65   /* initialize debugging category */
66   GST_DEBUG_CATEGORY_INIT (_ges_debug, "ges", GST_DEBUG_FG_YELLOW,
67       "GStreamer Editing Services");
68
69   return TRUE;
70 }
71
72 static gboolean
73 ges_init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
74     GError ** error)
75 {
76   GESUriClipAssetClass *uriasset_klass = NULL;
77   GstElementFactory *nlecomposition_factory = NULL;
78   static GstValueTable gstvtable = {
79     G_TYPE_NONE,
80     (GstValueCompareFunc) NULL,
81     (GstValueSerializeFunc) ges_marker_list_serialize,
82     (GstValueDeserializeFunc) ges_marker_list_deserialize
83   };
84   static gboolean marker_list_registered = FALSE;
85
86   if (initialized_thread) {
87     GST_DEBUG ("already initialized ges");
88     return TRUE;
89   }
90
91   uriasset_klass = g_type_class_ref (GES_TYPE_URI_CLIP_ASSET);
92
93   _init_formatter_assets ();
94   if (!_ges_uri_asset_ensure_setup (uriasset_klass)) {
95     GST_ERROR ("cannot setup uri asset");
96     goto failed;
97   }
98
99   nlecomposition_factory = gst_element_factory_find ("nlecomposition");
100   if (!nlecomposition_factory) {
101     GST_ERROR ("The `nlecomposition` object was not found.");
102     if (error)
103       *error = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN,
104           "The `nle` plugin is missing.");
105
106     goto failed;
107   }
108   gst_object_unref (nlecomposition_factory);
109
110   /* register clip classes with the system */
111
112   g_type_class_ref (GES_TYPE_TEST_CLIP);
113   g_type_class_ref (GES_TYPE_URI_CLIP);
114   g_type_class_ref (GES_TYPE_TITLE_CLIP);
115   g_type_class_ref (GES_TYPE_TRANSITION_CLIP);
116   g_type_class_ref (GES_TYPE_OVERLAY_CLIP);
117   g_type_class_ref (GES_TYPE_OVERLAY_TEXT_CLIP);
118
119   g_type_class_ref (GES_TYPE_GROUP);
120
121   /* Register track elements */
122   g_type_class_ref (GES_TYPE_EFFECT);
123
124   ges_asset_cache_init ();
125
126   gst_element_register (NULL, "framepositioner", 0, GST_TYPE_FRAME_POSITIONNER);
127   gst_element_register (NULL, "gespipeline", 0, GES_TYPE_PIPELINE);
128
129   /* TODO: user-defined types? */
130   initialized_thread = g_thread_self ();
131   g_type_class_unref (uriasset_klass);
132
133   if (!marker_list_registered) {
134     gstvtable.type = GES_TYPE_MARKER_LIST;
135     gst_value_register (&gstvtable);
136     marker_list_registered = TRUE;
137   }
138
139   GST_DEBUG ("GStreamer Editing Services initialized");
140
141   return TRUE;
142
143 failed:
144   if (uriasset_klass)
145     g_type_class_unref (uriasset_klass);
146
147   GST_ERROR ("Could not initialize GES.");
148
149   return FALSE;
150 }
151
152 /**
153  * ges_init:
154  *
155  * Initialize the GStreamer Editing Service. Call this before any usage of
156  * GES. You should take care of initilizing GStreamer before calling this
157  * function.
158  *
159  * MT safety.
160  * GStreamer Editing Services do not guarantee MT safety.
161  * An application is required to use GES APIs (including ges_deinit())
162  * in the thread where ges_init() was called.
163  */
164
165 gboolean
166 ges_init (void)
167 {
168   gboolean ret;
169
170   G_LOCK (init_lock);
171   ges_init_pre (NULL, NULL, NULL, NULL);
172
173   ret = ges_init_post (NULL, NULL, NULL, NULL);
174   G_UNLOCK (init_lock);
175
176   return ret;
177 }
178
179 /**
180  * ges_deinit:
181  *
182  * Clean up any resources created by GES in ges_init().
183  *
184  * It is normally not needed to call this function in a normal application as the
185  * resources will automatically be freed when the program terminates.
186  * This function is therefore mostly used by testsuites and other memory profiling tools.
187  * This function should be called from the thread where ges_init() was called.
188  *
189  * After this call GES should not be used until another ges_init() call.
190  */
191 void
192 ges_deinit (void)
193 {
194   G_LOCK (init_lock);
195
196   GST_INFO ("deinitializing GES");
197
198   if (!initialized_thread) {
199     GST_DEBUG ("nothing to deinitialize");
200     G_UNLOCK (init_lock);
201     return;
202   }
203
204   /* Allow deinit only from a thread where ges_init() was called */
205   g_assert (initialized_thread == g_thread_self ());
206
207   _ges_uri_asset_cleanup ();
208
209   g_type_class_unref (g_type_class_peek (GES_TYPE_TEST_CLIP));
210   g_type_class_unref (g_type_class_peek (GES_TYPE_URI_CLIP));
211   g_type_class_unref (g_type_class_peek (GES_TYPE_TITLE_CLIP));
212   g_type_class_unref (g_type_class_peek (GES_TYPE_TRANSITION_CLIP));
213   g_type_class_unref (g_type_class_peek (GES_TYPE_OVERLAY_CLIP));
214   g_type_class_unref (g_type_class_peek (GES_TYPE_OVERLAY_TEXT_CLIP));
215
216   g_type_class_unref (g_type_class_peek (GES_TYPE_GROUP));
217   /* Register track elements */
218   g_type_class_unref (g_type_class_peek (GES_TYPE_EFFECT));
219
220   ges_asset_cache_deinit ();
221   ges_xml_formatter_deinit ();
222
223   initialized_thread = NULL;
224   G_UNLOCK (init_lock);
225
226   GST_INFO ("deinitialized GES");
227
228   return;
229 }
230
231 #ifndef GST_DISABLE_OPTION_PARSING
232 static gboolean
233 parse_goption_arg (const gchar * s_opt,
234     const gchar * arg, gpointer data, GError ** err)
235 {
236   if (g_strcmp0 (s_opt, "--ges-version") == 0) {
237     g_print ("GStreamer Editing Services version %s\n", PACKAGE_VERSION);
238     exit (0);
239   } else if (g_strcmp0 (s_opt, "--ges-sample-paths") == 0) {
240     ges_add_missing_uri_relocation_uri (arg, FALSE);
241   } else if (g_strcmp0 (s_opt, "--ges-sample-path-recurse") == 0) {
242     ges_add_missing_uri_relocation_uri (arg, TRUE);
243   }
244
245   return TRUE;
246 }
247 #endif
248
249 /**
250  * ges_init_get_option_group: (skip)
251  *
252  * Returns a #GOptionGroup with GES's argument specifications. The
253  * group is set up to use standard GOption callbacks, so when using this
254  * group in combination with GOption parsing methods, all argument parsing
255  * and initialization is automated.
256  *
257  * This function is useful if you want to integrate GES with other
258  * libraries that use GOption (see g_option_context_add_group() ).
259  *
260  * If you use this function, you should make sure you initialise the GStreamer
261  * as one of the very first things in your program. That means you need to
262  * use gst_init_get_option_group() and add it to the option context before
263  * using the ges_init_get_option_group() result.
264  *
265  * Returns: (transfer full): a pointer to GES's option group.
266  */
267 GOptionGroup *
268 ges_init_get_option_group (void)
269 {
270 #ifndef GST_DISABLE_OPTION_PARSING
271
272   GOptionGroup *group;
273   static const GOptionEntry ges_args[] = {
274     {"ges-version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
275           (gpointer) parse_goption_arg,
276           "Print the GStreamer Editing Services version",
277         NULL},
278     {"ges-sample-paths", 0, 0, G_OPTION_ARG_CALLBACK,
279           (gpointer) parse_goption_arg,
280         "List of pathes to look assets in if they were moved"},
281     {"ges-sample-path-recurse", 0, 0, G_OPTION_ARG_CALLBACK,
282           (gpointer) parse_goption_arg,
283         "Same as above, but recursing into the folder"},
284     {NULL}
285   };
286
287   group = g_option_group_new ("GES", "GStreamer Editing Services Options",
288       "Show GES Options", NULL, NULL);
289   g_option_group_set_parse_hooks (group, (GOptionParseFunc) ges_init_pre,
290       (GOptionParseFunc) ges_init_post);
291   g_option_group_add_entries (group, ges_args);
292
293   return group;
294
295 #else
296   return NULL;
297 #endif
298 }
299
300 /**
301  * ges_version:
302  * @major: (out): pointer to a guint to store the major version number
303  * @minor: (out): pointer to a guint to store the minor version number
304  * @micro: (out): pointer to a guint to store the micro version number
305  * @nano:  (out): pointer to a guint to store the nano version number
306  *
307  * Gets the version number of the GStreamer Editing Services library.
308  */
309 void
310 ges_version (guint * major, guint * minor, guint * micro, guint * nano)
311 {
312   g_return_if_fail (major);
313   g_return_if_fail (minor);
314   g_return_if_fail (micro);
315   g_return_if_fail (nano);
316
317   *major = GES_VERSION_MAJOR;
318   *minor = GES_VERSION_MINOR;
319   *micro = GES_VERSION_MICRO;
320   *nano = GES_VERSION_NANO;
321 }
322
323 /**
324  * ges_init_check:
325  * @argc: (inout) (allow-none): pointer to application's argc
326  * @argv: (inout) (array length=argc) (allow-none): pointer to application's argv
327  * @err: pointer to a #GError to which a message will be posted on error
328  *
329  * Initializes the GStreamer Editing Services library, setting up internal path lists,
330  * and loading evrything needed.
331  *
332  * This function will return %FALSE if GES could not be initialized
333  * for some reason.
334  *
335  * Returns: %TRUE if GES could be initialized.
336  */
337 gboolean
338 ges_init_check (int *argc, char **argv[], GError ** err)
339 {
340 #ifndef GST_DISABLE_OPTION_PARSING
341   GOptionGroup *group;
342   GOptionContext *ctx;
343 #endif
344   gboolean res;
345
346   G_LOCK (init_lock);
347
348   if (initialized_thread) {
349     GST_DEBUG ("already initialized ges");
350     G_UNLOCK (init_lock);
351     return TRUE;
352   }
353 #ifndef GST_DISABLE_OPTION_PARSING
354   ctx = g_option_context_new ("- GStreamer Editing Services initialization");
355   g_option_context_set_ignore_unknown_options (ctx, TRUE);
356   g_option_context_set_help_enabled (ctx, FALSE);
357   group = ges_init_get_option_group ();
358   g_option_context_add_group (ctx, group);
359   res = g_option_context_parse (ctx, argc, argv, err);
360   g_option_context_free (ctx);
361 #endif
362
363   if (!res) {
364     G_UNLOCK (init_lock);
365     return res;
366   }
367
368   ges_init_pre (NULL, NULL, NULL, NULL);
369   res = ges_init_post (NULL, NULL, NULL, NULL);
370
371   G_UNLOCK (init_lock);
372
373   return res;
374 }
375
376 /**
377  * ges_is_initialized:
378  *
379  * Use this function to check if GES has been initialized with ges_init()
380  * or ges_init_check().
381  *
382  * Returns: %TRUE if initialization has been done, %FALSE otherwise.
383  *
384  * Since: 1.16
385  */
386 gboolean
387 ges_is_initialized (void)
388 {
389   return initialized_thread ? TRUE : FALSE;
390 }