mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-04 13:32:29 +00:00
ges: support test clips assets natural size/framerate
This way we can test this kind of behaviour without requiring real sources. Also add simple tests.
This commit is contained in:
parent
e835042f04
commit
0c87b44fae
8 changed files with 314 additions and 18 deletions
|
@ -386,8 +386,10 @@ _ges_command_line_formatter_add_test_clip (GESTimeline * timeline,
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -81,6 +81,11 @@ G_GNUC_END_IGNORE_DEPRECATIONS; /* End ignoring GParameter deprecation */
|
|||
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)));
|
||||
|
||||
}
|
||||
|
|
|
@ -91,8 +91,9 @@ typedef gchar* (*GESExtractableCheckId) (GType type, const gchar *id,
|
|||
* 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
|
||||
|
|
|
@ -83,6 +83,8 @@ GstDebugCategory * _ges_debug (void);
|
|||
#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,
|
||||
|
@ -489,6 +491,13 @@ G_GNUC_INTERNAL GESMultiFileURI * ges_multi_file_uri_new (const gchar * uri);
|
|||
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 *
|
||||
************************/
|
||||
|
|
|
@ -185,6 +185,5 @@ ges_structure_parser_get_error (GESStructureParser * self)
|
|||
error = g_error_new_literal (GES_ERROR, 0, msg->str);
|
||||
g_string_free (msg, TRUE);
|
||||
|
||||
GST_ERROR ("BoOOOM ");
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -25,8 +25,16 @@
|
|||
*
|
||||
* 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"
|
||||
|
@ -43,6 +51,98 @@
|
|||
#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;
|
||||
|
@ -60,7 +160,83 @@ enum
|
|||
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);
|
||||
|
@ -169,6 +345,7 @@ ges_test_clip_class_init (GESTestClipClass * klass)
|
|||
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;
|
||||
|
@ -331,6 +508,7 @@ ges_test_clip_get_volume (GESTestClip * self)
|
|||
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;
|
||||
|
||||
|
@ -351,6 +529,10 @@ ges_test_clip_create_track_element (GESClip * clip, GESTrackType type)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,12 +40,12 @@ struct _GESVideoTestSourcePrivate
|
|||
|
||||
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,
|
||||
|
@ -56,8 +56,20 @@ static GstElement *ges_video_test_source_create_source (GESTrackElement * self);
|
|||
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;
|
||||
}
|
||||
|
@ -157,6 +169,65 @@ done:
|
|||
->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)
|
||||
{
|
||||
|
@ -192,14 +263,18 @@ ges_video_test_source_class_init (GESVideoTestSourceClass * klass)
|
|||
*/
|
||||
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);
|
||||
}
|
||||
|
@ -275,25 +350,26 @@ ges_video_test_source_create_source (GESTrackElement * element)
|
|||
{
|
||||
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);
|
||||
|
|
|
@ -200,6 +200,28 @@ class TestTimeline(common.GESSimpleTimelineTest):
|
|||
]
|
||||
])
|
||||
|
||||
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):
|
||||
|
||||
|
|
Loading…
Reference in a new issue