756aef968c28097fca3aa06e11134b42dfd74485
[platform/upstream/gstreamer.git] / ges / ges-uri-clip.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: gesuriclip
23  * @title: GESUriClip
24  * @short_description: An object for manipulating media files in a GESTimeline
25  *
26  * Represents all the output streams from a particular uri. It is assumed that
27  * the URI points to a file of some type.
28  */
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "ges-internal.h"
34 #include "ges-uri-clip.h"
35 #include "ges-source-clip.h"
36 #include "ges-video-uri-source.h"
37 #include "ges-audio-uri-source.h"
38 #include "ges-uri-asset.h"
39 #include "ges-track-element-asset.h"
40 #include "ges-extractable.h"
41 #include "ges-image-source.h"
42 #include "ges-audio-test-source.h"
43 #include "ges-multi-file-source.h"
44 #include "ges-layer.h"
45
46 static void ges_extractable_interface_init (GESExtractableInterface * iface);
47
48 #define parent_class ges_uri_clip_parent_class
49
50 GESExtractableInterface *parent_extractable_iface;
51
52 struct _GESUriClipPrivate
53 {
54   gchar *uri;
55
56   gboolean mute;
57   gboolean is_image;
58 };
59
60 enum
61 {
62   PROP_0,
63   PROP_URI,
64   PROP_MUTE,
65   PROP_IS_IMAGE,
66   PROP_SUPPORTED_FORMATS,
67 };
68
69 G_DEFINE_TYPE_WITH_CODE (GESUriClip, ges_uri_clip,
70     GES_TYPE_SOURCE_CLIP, G_ADD_PRIVATE (GESUriClip)
71     G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE,
72         ges_extractable_interface_init));
73
74 static GList *ges_uri_clip_create_track_elements (GESClip *
75     clip, GESTrackType type);
76 static void ges_uri_clip_set_uri (GESUriClip * self, gchar * uri);
77
78 gboolean
79 uri_clip_set_max_duration (GESTimelineElement * element,
80     GstClockTime maxduration);
81
82 static void
83 ges_uri_clip_get_property (GObject * object, guint property_id,
84     GValue * value, GParamSpec * pspec)
85 {
86   GESUriClipPrivate *priv = GES_URI_CLIP (object)->priv;
87
88   switch (property_id) {
89     case PROP_URI:
90       g_value_set_string (value, priv->uri);
91       break;
92     case PROP_MUTE:
93       g_value_set_boolean (value, priv->mute);
94       break;
95     case PROP_IS_IMAGE:
96       g_value_set_boolean (value, priv->is_image);
97       break;
98     case PROP_SUPPORTED_FORMATS:
99       g_value_set_flags (value,
100           ges_clip_get_supported_formats (GES_CLIP (object)));
101       break;
102     default:
103       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
104   }
105 }
106
107 static void
108 ges_uri_clip_set_property (GObject * object, guint property_id,
109     const GValue * value, GParamSpec * pspec)
110 {
111   GESUriClip *uriclip = GES_URI_CLIP (object);
112
113   switch (property_id) {
114     case PROP_URI:
115       ges_uri_clip_set_uri (uriclip, g_value_dup_string (value));
116       break;
117     case PROP_MUTE:
118       ges_uri_clip_set_mute (uriclip, g_value_get_boolean (value));
119       break;
120     case PROP_IS_IMAGE:
121       ges_uri_clip_set_is_image (uriclip, g_value_get_boolean (value));
122       break;
123     case PROP_SUPPORTED_FORMATS:
124       ges_clip_set_supported_formats (GES_CLIP (uriclip),
125           g_value_get_flags (value));
126       break;
127     default:
128       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
129   }
130 }
131
132 static void
133 ges_uri_clip_finalize (GObject * object)
134 {
135   GESUriClipPrivate *priv = GES_URI_CLIP (object)->priv;
136
137   if (priv->uri)
138     g_free (priv->uri);
139   G_OBJECT_CLASS (parent_class)->finalize (object);
140 }
141
142 static void
143 ges_uri_clip_class_init (GESUriClipClass * klass)
144 {
145   GObjectClass *object_class = G_OBJECT_CLASS (klass);
146   GESClipClass *clip_class = GES_CLIP_CLASS (klass);
147   GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
148
149   object_class->get_property = ges_uri_clip_get_property;
150   object_class->set_property = ges_uri_clip_set_property;
151   object_class->finalize = ges_uri_clip_finalize;
152
153
154   /**
155    * GESUriClip:uri:
156    *
157    * The location of the file/resource to use.
158    */
159   g_object_class_install_property (object_class, PROP_URI,
160       g_param_spec_string ("uri", "URI", "uri of the resource", NULL,
161           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
162
163   /**
164    * GESUriClip:mute:
165    *
166    * Whether the sound will be played or not.
167    */
168   g_object_class_install_property (object_class, PROP_MUTE,
169       g_param_spec_boolean ("mute", "Mute", "Mute audio track",
170           FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
171
172   /**
173    * GESUriClip:is-image:
174    *
175    * Whether this uri clip represents a still image or not. This must be set
176    * before create_track_elements is called.
177    */
178   g_object_class_install_property (object_class, PROP_IS_IMAGE,
179       g_param_spec_boolean ("is-image", "Is still image",
180           "Whether the clip represents a still image or not",
181           FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
182
183   /* Redefine the supported formats property so the default value is UNKNOWN
184    * and not AUDIO | VIDEO */
185   g_object_class_install_property (object_class, PROP_SUPPORTED_FORMATS,
186       g_param_spec_flags ("supported-formats",
187           "Supported formats", "Formats supported by the file",
188           GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_UNKNOWN,
189           G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
190
191   element_class->set_max_duration = uri_clip_set_max_duration;
192
193   clip_class->create_track_elements = ges_uri_clip_create_track_elements;
194 }
195
196 static gchar *
197 extractable_check_id (GType type, const gchar * id)
198 {
199   if (gst_uri_is_valid (id))
200     return g_strdup (id);
201
202   return NULL;
203 }
204
205 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;       /* Start ignoring GParameter deprecation */
206
207 static GParameter *
208 extractable_get_parameters_from_id (const gchar * id, guint * n_params)
209 {
210   GParameter *params = g_new0 (GParameter, 2);
211
212   params[0].name = "uri";
213   g_value_init (&params[0].value, G_TYPE_STRING);
214   g_value_set_string (&params[0].value, id);
215
216   *n_params = 1;
217
218   return params;
219 }
220
221 G_GNUC_END_IGNORE_DEPRECATIONS; /* End ignoring GParameter deprecation */
222
223 static gchar *
224 extractable_get_id (GESExtractable * self)
225 {
226   return g_strdup (GES_URI_CLIP (self)->priv->uri);
227 }
228
229 static gboolean
230 extractable_set_asset (GESExtractable * self, GESAsset * asset)
231 {
232   gboolean res = TRUE;
233   GESUriClip *uriclip = GES_URI_CLIP (self);
234   GESUriClipAsset *uri_clip_asset;
235   GESClip *clip = GES_CLIP (self);
236   GESLayer *layer = ges_clip_get_layer (clip);
237   GList *tmp;
238   GESTimelineElement *audio_source = NULL, *video_source = NULL;
239
240   g_return_val_if_fail (GES_IS_URI_CLIP_ASSET (asset), FALSE);
241
242   uri_clip_asset = GES_URI_CLIP_ASSET (asset);
243   if (GST_CLOCK_TIME_IS_VALID (GES_TIMELINE_ELEMENT_DURATION (self)) &&
244       ges_uri_clip_asset_get_duration (uri_clip_asset) <
245       GES_TIMELINE_ELEMENT_INPOINT (self) +
246       GES_TIMELINE_ELEMENT_DURATION (self)) {
247     GST_INFO_OBJECT (self,
248         "Can not set asset to %p as its duration is %" GST_TIME_FORMAT
249         " < to inpoint %" GST_TIME_FORMAT " + %" GST_TIME_FORMAT " = %"
250         GST_TIME_FORMAT, asset,
251         GST_TIME_ARGS (ges_uri_clip_asset_get_duration (uri_clip_asset)),
252         GST_TIME_ARGS (GES_TIMELINE_ELEMENT_INPOINT (self)),
253         GST_TIME_ARGS (GES_TIMELINE_ELEMENT_DURATION (self)),
254         GST_TIME_ARGS (GES_TIMELINE_ELEMENT_INPOINT (self) +
255             GES_TIMELINE_ELEMENT_DURATION (self)));
256
257
258     return FALSE;
259   }
260
261   if (GST_CLOCK_TIME_IS_VALID (GES_TIMELINE_ELEMENT_DURATION (clip)) == FALSE)
262     _set_duration0 (GES_TIMELINE_ELEMENT (uriclip),
263         ges_uri_clip_asset_get_duration (uri_clip_asset));
264
265   ges_uri_clip_set_is_image (uriclip,
266       ges_uri_clip_asset_is_image (uri_clip_asset));
267
268   if (ges_clip_get_supported_formats (clip) == GES_TRACK_TYPE_UNKNOWN) {
269     ges_clip_set_supported_formats (clip,
270         ges_clip_asset_get_supported_formats (GES_CLIP_ASSET (uri_clip_asset)));
271   }
272
273   GES_TIMELINE_ELEMENT (uriclip)->asset = asset;
274
275   if (layer) {
276     GList *children = ges_container_get_children (GES_CONTAINER (self), TRUE);
277
278     for (tmp = children; tmp; tmp = tmp->next) {
279       if (GES_IS_SOURCE (tmp->data)) {
280         GESTrack *track = ges_track_element_get_track (tmp->data);
281
282         if (track->type == GES_TRACK_TYPE_AUDIO)
283           audio_source = gst_object_ref (tmp->data);
284         else if (track->type == GES_TRACK_TYPE_VIDEO)
285           video_source = gst_object_ref (tmp->data);
286
287         ges_track_remove_element (track, tmp->data);
288         ges_container_remove (GES_CONTAINER (self), tmp->data);
289       }
290     }
291     g_list_free_full (children, g_object_unref);
292
293     gst_object_ref (clip);
294
295     ges_layer_remove_clip (layer, clip);
296     res = ges_layer_add_clip (layer, clip);
297
298     for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
299       if (GES_IS_SOURCE (tmp->data)) {
300         GESTrack *track = ges_track_element_get_track (tmp->data);
301
302         if (track->type == GES_TRACK_TYPE_AUDIO && audio_source) {
303           ges_track_element_copy_properties (audio_source, tmp->data);
304           ges_track_element_copy_bindings (GES_TRACK_ELEMENT (audio_source),
305               tmp->data, GST_CLOCK_TIME_NONE);
306         } else if (track->type == GES_TRACK_TYPE_VIDEO && video_source) {
307           ges_track_element_copy_properties (video_source, tmp->data);
308           ges_track_element_copy_bindings (GES_TRACK_ELEMENT (video_source),
309               tmp->data, GST_CLOCK_TIME_NONE);
310         }
311       }
312     }
313
314     g_clear_object (&audio_source);
315     g_clear_object (&video_source);
316     gst_object_unref (clip);
317     gst_object_unref (layer);
318   }
319
320   if (res) {
321     g_free (uriclip->priv->uri);
322     uriclip->priv->uri = g_strdup (ges_asset_get_id (asset));
323   }
324
325   if (!GES_CONTAINER_CHILDREN (uriclip))
326     ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (uriclip),
327         ges_uri_clip_asset_get_max_duration (uri_clip_asset));
328
329   return res;
330 }
331
332 static void
333 ges_extractable_interface_init (GESExtractableInterface * iface)
334 {
335   iface->asset_type = GES_TYPE_URI_CLIP_ASSET;
336   iface->check_id = (GESExtractableCheckId) extractable_check_id;
337   iface->get_parameters_from_id = extractable_get_parameters_from_id;
338   iface->get_id = extractable_get_id;
339   iface->get_id = extractable_get_id;
340   iface->can_update_asset = TRUE;
341   iface->set_asset_full = extractable_set_asset;
342 }
343
344 static void
345 ges_uri_clip_init (GESUriClip * self)
346 {
347   self->priv = ges_uri_clip_get_instance_private (self);
348
349   /* Setting the duration to -1 by default. */
350   GES_TIMELINE_ELEMENT (self)->duration = GST_CLOCK_TIME_NONE;
351 }
352
353 /**
354  * ges_uri_clip_set_mute:
355  * @self: the #GESUriClip on which to mute or unmute the audio track
356  * @mute: %TRUE to mute @self audio track, %FALSE to unmute it
357  *
358  * Sets whether the audio track of this clip is muted or not.
359  *
360  */
361 void
362 ges_uri_clip_set_mute (GESUriClip * self, gboolean mute)
363 {
364   GList *tmp;
365
366   GST_DEBUG ("self:%p, mute:%d", self, mute);
367
368   self->priv->mute = mute;
369
370   /* Go over tracked objects, and update 'active' status on all audio objects */
371   for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = g_list_next (tmp)) {
372     GESTrackElement *trackelement = (GESTrackElement *) tmp->data;
373
374     if (ges_track_element_get_track (trackelement)->type ==
375         GES_TRACK_TYPE_AUDIO)
376       ges_track_element_set_active (trackelement, !mute);
377   }
378 }
379
380 gboolean
381 uri_clip_set_max_duration (GESTimelineElement * element,
382     GstClockTime maxduration)
383 {
384   if (_DURATION (element) == GST_CLOCK_TIME_NONE || _DURATION (element) == 0)
385     /* If we don't have a valid duration, use the max duration */
386     /* FIXME: don't do this when we have time effects */
387     _set_duration0 (element, maxduration - _INPOINT (element));
388
389   return
390       GES_TIMELINE_ELEMENT_CLASS (parent_class)->set_max_duration (element,
391       maxduration);
392 }
393
394 /**
395  * ges_uri_clip_set_is_image:
396  * @self: the #GESUriClip
397  * @is_image: %TRUE if @self is a still image, %FALSE otherwise
398  *
399  * Sets whether the clip is a still image or not.
400  */
401 void
402 ges_uri_clip_set_is_image (GESUriClip * self, gboolean is_image)
403 {
404   self->priv->is_image = is_image;
405 }
406
407 /**
408  * ges_uri_clip_is_muted:
409  * @self: the #GESUriClip
410  *
411  * Lets you know if the audio track of @self is muted or not.
412  *
413  * Returns: %TRUE if the audio track of @self is muted, %FALSE otherwise.
414  */
415 gboolean
416 ges_uri_clip_is_muted (GESUriClip * self)
417 {
418   return self->priv->mute;
419 }
420
421 /**
422  * ges_uri_clip_is_image:
423  * @self: the #GESUriClip
424  *
425  * Lets you know if @self is an image or not.
426  *
427  * Returns: %TRUE if @self is a still image %FALSE otherwise.
428  */
429 gboolean
430 ges_uri_clip_is_image (GESUriClip * self)
431 {
432   return self->priv->is_image;
433 }
434
435 /**
436  * ges_uri_clip_get_uri:
437  * @self: the #GESUriClip
438  *
439  * Get the location of the resource.
440  *
441  * Returns: The location of the resource.
442  */
443 const gchar *
444 ges_uri_clip_get_uri (GESUriClip * self)
445 {
446   return self->priv->uri;
447 }
448
449 static GList *
450 ges_uri_clip_create_track_elements (GESClip * clip, GESTrackType type)
451 {
452   GList *res = NULL;
453   const GList *tmp, *stream_assets;
454   GESAsset *asset = GES_TIMELINE_ELEMENT (clip)->asset;
455   GESUriClipAsset *uri_asset;
456   GstClockTime max_duration;
457
458   g_return_val_if_fail (asset, NULL);
459
460   uri_asset = GES_URI_CLIP_ASSET (asset);
461
462   max_duration = ges_uri_clip_asset_get_max_duration (uri_asset);
463   stream_assets = ges_uri_clip_asset_get_stream_assets (uri_asset);
464
465   for (tmp = stream_assets; tmp; tmp = tmp->next) {
466     GESTrackElementAsset *element_asset = GES_TRACK_ELEMENT_ASSET (tmp->data);
467
468     if (ges_track_element_asset_get_track_type (element_asset) == type) {
469       GESTrackElement *element =
470           GES_TRACK_ELEMENT (ges_asset_extract (GES_ASSET (element_asset),
471               NULL));
472       ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (element),
473           max_duration);
474       res = g_list_prepend (res, element);
475     }
476   }
477
478   return res;
479 }
480
481 /**
482  * ges_uri_clip_new:
483  * @uri: the URI the source should control
484  *
485  * Creates a new #GESUriClip for the provided @uri.
486  *
487  * Returns: (transfer floating) (nullable): The newly created #GESUriClip, or
488  * %NULL if there was an error.
489  */
490 GESUriClip *
491 ges_uri_clip_new (const gchar * uri)
492 {
493   GESAsset *asset = GES_ASSET (ges_uri_clip_asset_request_sync (uri, NULL));
494   GESUriClip *res = NULL;
495
496   if (asset) {
497     res = GES_URI_CLIP (ges_asset_extract (asset, NULL));
498     gst_object_unref (asset);
499   } else
500     GST_ERROR ("Could not create asset for uri: %s", uri);
501
502   return res;
503 }
504
505 void
506 ges_uri_clip_set_uri (GESUriClip * self, gchar * uri)
507 {
508   if (GES_CONTAINER_CHILDREN (self)) {
509     /* FIXME handle this case properly */
510     GST_WARNING_OBJECT (self, "Can not change uri when already"
511         "containing TrackElements");
512
513     return;
514   }
515
516   self->priv->uri = uri;
517 }