return FALSE;
gst_structure_set (structure, "type", G_TYPE_STRING, "GESTestClip", NULL);
- gst_structure_set (structure, "asset-id", G_TYPE_STRING,
- gst_structure_get_string (structure, "pattern"), NULL);
+
+ if (!gst_structure_has_field_typed (structure, "asset-id", G_TYPE_STRING))
+ gst_structure_set (structure, "asset-id", G_TYPE_STRING, "GESTestClip",
+ NULL);
return _ges_add_clip_from_struct (timeline, structure, error);
}
static gchar *
extractable_get_id (GESExtractable * self)
{
+ GESAsset *asset;
+
+ if ((asset = ges_extractable_get_asset (self)))
+ return g_strdup (ges_asset_get_id (asset));
+
return g_strdup (g_type_name (G_OBJECT_TYPE (self)));
}
* set, or even that an asset with such an #GESAsset:id does not exist in
* the GES cache. Instead, this should return the #GESAsset:id that is
* _compatible_ with the current state of the object. The default
- * implementation simply returns the type name of the object, which is
- * what is used as the #GESAsset:id by default.
+ * implementation simply returns the currently set asset ID, or the type name
+ * of the object, which is what is used as the #GESAsset:id by default,
+ * if no asset is set.
* @get_real_extractable_type: The method to call to get the actual
* #GESAsset:extractable-type an asset should have set, given the
* requested #GESAsset:id. The default implementation simply returns the
#define GES_FORMAT GES_TIMELINE_ELEMENT_FORMAT
#define GES_ARGS GES_TIMELINE_ELEMENT_ARGS
+#define SUPRESS_UNUSED_WARNING(a) (void)a
+
G_GNUC_INTERNAL gboolean
timeline_ripple_object (GESTimeline *timeline, GESTimelineElement *obj,
gint new_layer_priority,
G_GNUC_INTERNAL gboolean
ges_video_uri_source_get_natural_size(GESVideoSource* source, gint* width, gint* height);
+/**********************************
+ * GESTestClipAsset internal API *
+ **********************************/
+G_GNUC_INTERNAL gboolean ges_test_clip_asset_get_natural_size(GESAsset *self,
+ gint *width,
+ gint *height);
+
/************************
* Our property masks *
************************/
error = g_error_new_literal (GES_ERROR, 0, msg->str);
g_string_free (msg, TRUE);
- GST_ERROR ("BoOOOM ");
return error;
}
*
* Useful for testing purposes.
*
- * You can use the ges_asset_request_simple API to create an Asset
- * capable of extracting GESTestClip-s
+ * ## Asset
+ *
+ * The default asset ID is GESTestClip, but the framerate and video
+ * size can be overriden using an ID of the form:
+ *
+ * ```
+ * framerate=60/1, width=1920, height=1080, max-duration=5.0
+ * ```
+ * Note: `max-duration` can be provided in seconds as float, or as GstClockTime
+ * as guint64 or gint.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#define DEFAULT_VOLUME 1.0
#define DEFAULT_VPATTERN GES_VIDEO_TEST_PATTERN_SMPTE
+G_DECLARE_FINAL_TYPE (GESTestClipAsset, ges_test_clip_asset, GES,
+ TEST_CLIP_ASSET, GESClipAsset);
+
+struct _GESTestClipAsset
+{
+ GESClipAsset parent;
+
+ gint natural_framerate_n;
+ gint natural_framerate_d;
+ gint natural_width;
+ gint natural_height;
+ GstClockTime max_duration;
+};
+
+#define GES_TYPE_TEST_CLIP_ASSET (ges_test_clip_asset_get_type())
+G_DEFINE_TYPE (GESTestClipAsset, ges_test_clip_asset, GES_TYPE_CLIP_ASSET);
+
+static gboolean
+_get_natural_framerate (GESClipAsset * asset, gint * framerate_n,
+ gint * framerate_d)
+{
+ GESTestClipAsset *self = GES_TEST_CLIP_ASSET (asset);
+
+ *framerate_n = self->natural_framerate_n;
+ *framerate_d = self->natural_framerate_d;
+ return TRUE;
+}
+
+static GstClockTime
+ges_test_clip_asset_get_max_duration (GESAsset * asset)
+{
+ GESTestClipAsset *self = GES_TEST_CLIP_ASSET (asset);
+
+ return GES_TEST_CLIP_ASSET (self)->max_duration;
+}
+
+gboolean
+ges_test_clip_asset_get_natural_size (GESAsset * asset, gint * width,
+ gint * height)
+{
+ GESTestClipAsset *self = GES_TEST_CLIP_ASSET (asset);
+
+ *width = self->natural_width;
+ *height = self->natural_height;
+
+ return TRUE;
+}
+
+static void
+ges_test_clip_asset_constructed (GObject * gobject)
+{
+ GESFrameNumber fmax_dur = GES_FRAME_NUMBER_NONE;
+ GESTestClipAsset *self = GES_TEST_CLIP_ASSET (gobject);
+ GstStructure *structure =
+ gst_structure_from_string (ges_asset_get_id (GES_ASSET (self)), NULL);
+
+ g_assert (structure);
+
+ gst_structure_get_int (structure, "width", &self->natural_width);
+ gst_structure_get_int (structure, "height", &self->natural_height);
+ gst_structure_get_fraction (structure, "framerate",
+ &self->natural_framerate_n, &self->natural_framerate_d);
+ ges_util_structure_get_clocktime (structure, "max-duration",
+ &self->max_duration, &fmax_dur);
+ if (GES_FRAME_NUMBER_IS_VALID (fmax_dur))
+ self->max_duration =
+ gst_util_uint64_scale (fmax_dur, self->natural_framerate_d * GST_SECOND,
+ self->natural_framerate_n);
+ gst_structure_free (structure);
+
+ G_OBJECT_CLASS (ges_test_clip_asset_parent_class)->constructed (gobject);
+}
+
+static void
+ges_test_clip_asset_class_init (GESTestClipAssetClass * klass)
+{
+ GESClipAssetClass *clip_asset_class = GES_CLIP_ASSET_CLASS (klass);
+
+ clip_asset_class->get_natural_framerate = _get_natural_framerate;
+ G_OBJECT_CLASS (klass)->constructed = ges_test_clip_asset_constructed;
+}
+
+static void
+ges_test_clip_asset_init (GESTestClipAsset * self)
+{
+ self->natural_width = DEFAULT_WIDTH;
+ self->natural_height = DEFAULT_HEIGHT;
+ self->natural_framerate_n = DEFAULT_FRAMERATE_N;
+ self->natural_framerate_d = DEFAULT_FRAMERATE_D;
+ self->max_duration = GST_CLOCK_TIME_NONE;
+}
+
struct _GESTestClipPrivate
{
gboolean mute;
PROP_VOLUME,
};
-G_DEFINE_TYPE_WITH_PRIVATE (GESTestClip, ges_test_clip, GES_TYPE_SOURCE_CLIP);
+typedef struct
+{
+ const gchar *name;
+ GType type;
+} ValidField;
+
+static gchar *
+ges_extractable_check_id (GType type, const gchar * id, GError ** error)
+{
+ if (id && g_strcmp0 (id, g_type_name (type))) {
+ gchar *struct_str = g_strdup_printf ("%s,%s", g_type_name (type), id);
+ gchar *res = NULL;
+ GstStructure *structure = gst_structure_from_string (struct_str, NULL);
+
+ GST_DEBUG ("Structure is %s %" GST_PTR_FORMAT, struct_str, structure);
+ if (!structure) {
+ g_set_error (error, GES_ERROR, GES_ERROR_ASSET_WRONG_ID,
+ "GESTestClipAsset ID should be in the form: `framerate=30/1, "
+ "width=1920, height=1080, got %s", id);
+ } else {
+ static ValidField valid_fields[] = {
+ {"width", G_TYPE_INT},
+ {"height", G_TYPE_INT},
+ {"framerate", G_TYPE_NONE}, /* GST_TYPE_FRACTION is not constant */
+ {"max-duration", GST_TYPE_CLOCK_TIME},
+ };
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (valid_fields); i++) {
+ if (gst_structure_has_field (structure, valid_fields[i].name)) {
+ GstClockTime ts;
+ GESFrameNumber fn;
+ ValidField field = valid_fields[i];
+ GType type =
+ field.type == G_TYPE_NONE ? GST_TYPE_FRACTION : field.type;
+
+ if (!(gst_structure_has_field_typed (structure, field.name,
+ type) ||
+ (type == GST_TYPE_CLOCK_TIME &&
+ ges_util_structure_get_clocktime (structure, field.name,
+ &ts, &fn)))) {
+
+ g_set_error (error, GES_ERROR, GES_ERROR_ASSET_WRONG_ID,
+ "Field %s has wrong type, %s, expected %s", field.name,
+ g_type_name (gst_structure_get_field_type (structure,
+ field.name)), g_type_name (type));
+
+ gst_structure_free (structure);
+ g_free (struct_str);
+
+ return FALSE;
+ }
+ }
+ }
+ res = gst_structure_to_string (structure);
+ gst_structure_free (structure);
+ }
+
+ g_free (struct_str);
+ return res;
+ }
+
+ return g_strdup (g_type_name (type));
+}
+
+static void
+ges_extractable_interface_init (GESExtractableInterface * iface)
+{
+ iface->asset_type = GES_TYPE_TEST_CLIP_ASSET;
+ iface->check_id = ges_extractable_check_id;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GESTestClip, ges_test_clip, GES_TYPE_SOURCE_CLIP,
+ G_ADD_PRIVATE (GESTestClip)
+ G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE,
+ ges_extractable_interface_init));
+
static GESTrackElement
* ges_test_clip_create_track_element (GESClip * clip, GESTrackType type);
static void
ges_test_clip_init (GESTestClip * self)
{
+ SUPRESS_UNUSED_WARNING (GES_IS_TEST_CLIP_ASSET);
self->priv = ges_test_clip_get_instance_private (self);
self->priv->freq = 0;
static GESTrackElement *
ges_test_clip_create_track_element (GESClip * clip, GESTrackType type)
{
+ GESAsset *asset = ges_extractable_get_asset (GES_EXTRACTABLE (clip));
GESTestClipPrivate *priv = GES_TEST_CLIP (clip)->priv;
GESTrackElement *res = NULL;
ges_audio_test_source_set_volume ((GESAudioTestSource *) res, priv->volume);
}
+ if (asset)
+ ges_timeline_element_set_max_duration (GES_TIMELINE_ELEMENT (res),
+ ges_test_clip_asset_get_max_duration (asset));
+
return res;
}
gboolean use_overlay;
GstElement *overlay;
-
GstPad *is_passthrough_pad;
GstPad *os_passthrough_pad;
-
GstPad *is_overlay_pad;
GstPad *os_overlay_pad;
+
+ GstElement *capsfilter;
};
G_DEFINE_TYPE_WITH_PRIVATE (GESVideoTestSource, ges_video_test_source,
static gboolean
get_natural_size (GESVideoSource * source, gint * width, gint * height)
{
- *width = DEFAULT_WIDTH;
- *height = DEFAULT_HEIGHT;
+ gboolean res = FALSE;
+ GESTimelineElement *parent = GES_TIMELINE_ELEMENT_PARENT (source);
+
+ if (parent) {
+ GESAsset *asset = ges_extractable_get_asset (GES_EXTRACTABLE (parent));
+
+ if (asset)
+ res = ges_test_clip_asset_get_natural_size (asset, width, height);
+ }
+
+ if (!res) {
+ *width = DEFAULT_WIDTH;
+ *height = DEFAULT_HEIGHT;
+ }
return TRUE;
}
->set_child_property (self, child, pspec, value);
}
+static gboolean
+_set_parent (GESTimelineElement * element, GESTimelineElement * parent)
+{
+ GESVideoTestSource *self = GES_VIDEO_TEST_SOURCE (element);
+
+
+ if (parent) {
+ gint width, height, fps_n, fps_d;
+ GstCaps *caps;
+
+ g_assert (self->priv->capsfilter);
+ /* Setting the parent ourself as we need it to get the natural size */
+ element->parent = parent;
+ if (!ges_video_source_get_natural_size (GES_VIDEO_SOURCE (self), &width,
+ &height)) {
+ width = DEFAULT_WIDTH;
+ height = DEFAULT_HEIGHT;
+ }
+ if (!ges_timeline_element_get_natural_framerate (parent, &fps_n, &fps_d)) {
+ fps_n = DEFAULT_FRAMERATE_N;
+ fps_d = DEFAULT_FRAMERATE_D;
+ }
+
+ caps = gst_caps_new_simple ("video/x-raw",
+ "width", G_TYPE_INT, width,
+ "height", G_TYPE_INT, height,
+ "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL);
+ g_object_set (self->priv->capsfilter, "caps", caps, NULL);
+ gst_caps_unref (caps);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_get_natural_framerate (GESTimelineElement * element, gint * fps_n,
+ gint * fps_d)
+{
+ gboolean res = FALSE;
+ GESTimelineElement *parent = GES_TIMELINE_ELEMENT_PARENT (element);
+
+ if (parent) {
+ GESAsset *asset = ges_extractable_get_asset (GES_EXTRACTABLE (parent));
+
+ if (asset) {
+ res =
+ ges_clip_asset_get_natural_framerate (GES_CLIP_ASSET (asset), fps_n,
+ fps_d);
+ }
+ }
+
+ if (!res) {
+ *fps_n = DEFAULT_FRAMERATE_N;
+ *fps_d = DEFAULT_FRAMERATE_D;
+ }
+
+ return TRUE;
+}
+
static void
dispose (GObject * object)
{
*/
properties[PROP_USE_TIME_OVERLAY] =
g_param_spec_boolean ("use-time-overlay", "Use-time-overlay",
- "Use time overlay, setting a child property corresponding to GstTimeOverlay will switch this on"
- " by default.", FALSE, G_PARAM_READWRITE);
+ "Use time overlay, setting a child property corresponding to"
+ "GstTimeOverlay will switch this on by default.", FALSE,
+ G_PARAM_READWRITE);
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->dispose = dispose;
GES_TIMELINE_ELEMENT_CLASS (klass)->set_child_property = _set_child_property;
+ GES_TIMELINE_ELEMENT_CLASS (klass)->set_parent = _set_parent;
+ GES_TIMELINE_ELEMENT_CLASS (klass)->get_natural_framerate =
+ _get_natural_framerate;
g_object_class_install_properties (object_class, PROP_LAST, properties);
}
{
GstCaps *caps;
gint pattern;
- GstElement *testsrc, *capsfilter, *res;
+ GstElement *testsrc, *res;
const gchar *props[] = { "pattern", NULL };
GPtrArray *elements;
GESVideoTestSource *self = GES_VIDEO_TEST_SOURCE (element);
+ g_assert (!GES_TIMELINE_ELEMENT_PARENT (element));
testsrc = gst_element_factory_make ("videotestsrc", NULL);
- capsfilter = gst_element_factory_make ("capsfilter", NULL);
+ self->priv->capsfilter = gst_element_factory_make ("capsfilter", NULL);
pattern = self->priv->pattern;
g_object_set (testsrc, "pattern", pattern, NULL);
elements = g_ptr_array_new ();
- g_ptr_array_add (elements, capsfilter);
+ g_ptr_array_add (elements, self->priv->capsfilter);
caps = gst_caps_new_simple ("video/x-raw",
"width", G_TYPE_INT, DEFAULT_WIDTH,
"height", G_TYPE_INT, DEFAULT_HEIGHT,
"framerate", GST_TYPE_FRACTION, DEFAULT_FRAMERATE_N, DEFAULT_FRAMERATE_D,
NULL);
- g_object_set (capsfilter, "caps", caps, NULL);
+ g_object_set (self->priv->capsfilter, "caps", caps, NULL);
gst_caps_unref (caps);
self->priv->overlay = ges_video_test_source_create_overlay (self);
]
])
+ def test_frame_info(self):
+ self.track_types = [GES.TrackType.VIDEO]
+ super().setUp()
+
+ vtrack, = self.timeline.get_tracks()
+ vtrack.update_restriction_caps(Gst.Caps("video/x-raw,framerate=60/1"))
+ self.assertEqual(self.timeline.get_frame_time(60), Gst.SECOND)
+
+ layer = self.timeline.append_layer()
+ asset = GES.Asset.request(GES.TestClip, "framerate=120/1,height=500,width=500,max-duration=f120")
+ clip = layer.add_asset( asset, 0, 0, Gst.SECOND, GES.TrackType.UNKNOWN)
+ self.assertEqual(clip.get_id(), "GESTestClip, framerate=(fraction)120/1, height=(int)500, width=(int)500, max-duration=(string)f120;")
+
+ test_source, = clip.get_children(True)
+ self.assertEqual(test_source.get_natural_size(), (True, 500, 500))
+ self.assertEqual(test_source.get_natural_framerate(), (True, 120, 1))
+ self.assertEqual(test_source.props.max_duration, Gst.SECOND)
+ self.assertEqual(clip.get_natural_framerate(), (True, 120, 1))
+
+ self.assertEqual(self.timeline.get_frame_at(Gst.SECOND), 60)
+ self.assertEqual(clip.props.max_duration, Gst.SECOND)
+
class TestEditing(common.GESSimpleTimelineTest):