ges: Move TimeOverlayClip out of GESTestClip

This was complexifying the implementation for very little gain.
Each source type should ideally have its own API.

In that patch we make it so we do not have to subclass anything
but instead use GESAsset to pass information about how the pipeline
should look like.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/merge_requests/175>
This commit is contained in:
Thibault Saunier 2020-04-22 13:39:21 -04:00 committed by GStreamer Merge Bot
parent b79e5e3508
commit 5e448921d8
19 changed files with 459 additions and 231 deletions

View file

@ -0,0 +1,201 @@
#### `alpha`
alpha of the stream
Value type: #gdouble
#### `background-color`
Background color to use (big-endian ARGB)
Value type: #guint
See #videotestsrc:background-color
#### `font-desc`
Pango font description of font to be used for rendering. See documentation of
pango_font_description_from_string for syntax.
Value type: #gchararray
See #timeoverlay:font-desc
#### `foreground-color`
Foreground color to use (big-endian ARGB)
Value type: #guint
See #videotestsrc:foreground-color
#### `freq`
Frequency of test signal. The sample rate needs to be at least 2 times higher.
Value type: #gdouble
See #audiotestsrc:freq
#### `halignment`
Horizontal alignment of the text
Valid values:
- **left** (0) left
- **center** (1) center
- **right** (2) right
- **position** (4) Absolute position clamped to canvas
- **absolute** (5) Absolute position
See #timeoverlay:halignment
#### `height`
height of the source
Value type: #gint
#### `mute`
mute channel
Value type: #gboolean
See #volume:mute
#### `pattern`
Type of test pattern to generate
Valid values:
- **SMPTE 100% color bars** (0) smpte
- **Random (television snow)** (1) snow
- **100% Black** (2) black
- **100% White** (3) white
- **Red** (4) red
- **Green** (5) green
- **Blue** (6) blue
- **Checkers 1px** (7) checkers-1
- **Checkers 2px** (8) checkers-2
- **Checkers 4px** (9) checkers-4
- **Checkers 8px** (10) checkers-8
- **Circular** (11) circular
- **Blink** (12) blink
- **SMPTE 75% color bars** (13) smpte75
- **Zone plate** (14) zone-plate
- **Gamut checkers** (15) gamut
- **Chroma zone plate** (16) chroma-zone-plate
- **Solid color** (17) solid-color
- **Moving ball** (18) ball
- **SMPTE 100% color bars** (19) smpte100
- **Bar** (20) bar
- **Pinwheel** (21) pinwheel
- **Spokes** (22) spokes
- **Gradient** (23) gradient
- **Colors** (24) colors
See #videotestsrc:pattern
#### `posx`
x position of the stream
Value type: #gint
#### `posy`
y position of the stream
Value type: #gint
#### `text-width`
Resulting width of font rendering
Value type: #guint
See #timeoverlay:text-width
#### `text-x`
Resulting X position of font rendering.
Value type: #gint
See #timeoverlay:text-x
#### `text-y`
Resulting X position of font rendering.
Value type: #gint
See #timeoverlay:text-y
#### `time-mode`
What time to show
Valid values:
- **buffer-time** (0) buffer-time
- **stream-time** (1) stream-time
- **running-time** (2) running-time
- **time-code** (3) time-code
See #timeoverlay:time-mode
#### `valignment`
Vertical alignment of the text
Valid values:
- **baseline** (0) baseline
- **bottom** (1) bottom
- **top** (2) top
- **position** (3) Absolute position clamped to canvas
- **center** (4) center
- **absolute** (5) Absolute position
See #timeoverlay:valignment
#### `video-direction`
Video direction: rotation and flipping
Valid values:
- **GST_VIDEO_ORIENTATION_IDENTITY** (0) identity
- **GST_VIDEO_ORIENTATION_90R** (1) 90r
- **GST_VIDEO_ORIENTATION_180** (2) 180
- **GST_VIDEO_ORIENTATION_90L** (3) 90l
- **GST_VIDEO_ORIENTATION_HORIZ** (4) horiz
- **GST_VIDEO_ORIENTATION_VERT** (5) vert
- **GST_VIDEO_ORIENTATION_UL_LR** (6) ul-lr
- **GST_VIDEO_ORIENTATION_UR_LL** (7) ur-ll
- **GST_VIDEO_ORIENTATION_AUTO** (8) auto
- **GST_VIDEO_ORIENTATION_CUSTOM** (9) custom
See #videoflip:video-direction
#### `volume`
Volume of test signal
Value type: #gdouble
See #audiotestsrc:volume
#### `volume`
volume factor, 1.0=100%
Value type: #gdouble
See #volume:volume
#### `width`
width of the source
Value type: #gint

20
docs/libs/document-children-props.py Executable file → Normal file
View file

@ -33,24 +33,32 @@ if __name__ == "__main__":
layer = tl.append_layer() layer = tl.append_layer()
elements = [] elements = []
def add_clip(c, add=True): def add_clip(c, add=True, override_name=None):
c.props.duration = Gst.SECOND c.props.duration = Gst.SECOND
c.props.start = layer.get_duration() c.props.start = layer.get_duration()
layer.add_clip(c) layer.add_clip(c)
if add: if add:
elements.extend(c.children) elements.extend(c.children)
else:
if override_name:
elements.append((c, override_name))
else:
elements.append(c)
add_clip(GES.UriClipAsset.request_sync(Gst.filename_to_uri(os.path.join("../../", "tests/check/assets/audio_video.ogg"))).extract()) add_clip(GES.UriClipAsset.request_sync(Gst.filename_to_uri(os.path.join("../../", "tests/check/assets/audio_video.ogg"))).extract())
add_clip(GES.TestClip.new()) add_clip(GES.TestClip.new())
add_clip(GES.TitleClip.new()) add_clip(GES.TitleClip.new())
transition = GES.TransitionClip.new_for_nick("crossfade")
add_clip(transition, False) add_clip(GES.SourceClip.new_time_overlay(), False, "GESTimeOverlaySourceClip")
elements.append(transition) add_clip(GES.TransitionClip.new_for_nick("crossfade"), False)
for element in elements: for element in elements:
gtype = element.__gtype__ if isinstance(element, tuple):
element, gtype = element
else:
gtype = element.__gtype__.name
print(gtype) print(gtype)
with open(gtype.name + '-children-props.md', 'w') as f: with open(gtype + '-children-props.md', 'w') as f:
for prop in GES.TimelineElement.list_children_properties(element): for prop in GES.TimelineElement.list_children_properties(element):
prefix = '#### `%s`\n\n' % (prop.name) prefix = '#### `%s`\n\n' % (prop.name)

View file

@ -6,6 +6,7 @@ gi-index
ges-uri-clip.h ges-uri-clip.h
ges-title-clip.h ges-title-clip.h
ges-test-clip.h ges-test-clip.h
ges-time-overlay-clip.h
ges-effect-clip.h ges-effect-clip.h
ges-transition-clip.h ges-transition-clip.h
ges-pipeline.h ges-pipeline.h

View file

@ -504,6 +504,8 @@ ges_video_uri_source_get_natural_size(GESVideoSource* source, gint* width, gint*
G_GNUC_INTERNAL gboolean ges_test_clip_asset_get_natural_size (GESAsset *self, G_GNUC_INTERNAL gboolean ges_test_clip_asset_get_natural_size (GESAsset *self,
gint *width, gint *width,
gint *height); gint *height);
G_GNUC_INTERNAL gchar *ges_test_source_asset_check_id (GType type, const gchar *id,
GError **error);
/************************ /************************
* Our property masks * * Our property masks *

View file

@ -53,10 +53,52 @@ enum
PROP_0, PROP_0,
}; };
static GESExtractableInterface *parent_extractable_iface = NULL;
static gchar *
extractable_check_id (GType type, const gchar * id, GError ** error)
{
if (type == GES_TYPE_SOURCE_CLIP) {
g_set_error (error, GES_ERROR, GES_ERROR_ASSET_WRONG_ID,
"Only `time-overlay` is supported as an ID for type: `GESSourceClip`,"
" got: '%s'", id);
return NULL;
}
return parent_extractable_iface->check_id (type, id, error);
}
static GType
extractable_get_real_extractable_type (GType wanted_type, const gchar * id)
{
GstStructure *structure;
if (!id || (wanted_type != GES_TYPE_SOURCE_CLIP
&& wanted_type != GES_TYPE_TEST_CLIP))
return wanted_type;
structure = gst_structure_new_from_string (id);
if (!structure)
return wanted_type;
if (gst_structure_has_name (structure, "time-overlay"))
/* Just reusing TestClip to create a GESTimeOverlayClip
* as it already does the job! */
wanted_type = GES_TYPE_TEST_CLIP;
gst_structure_free (structure);
return wanted_type;
}
static void static void
extractable_interface_init (GESExtractableInterface * iface) extractable_interface_init (GESExtractableInterface * iface)
{ {
iface->asset_type = GES_TYPE_SOURCE_CLIP_ASSET; iface->asset_type = GES_TYPE_SOURCE_CLIP_ASSET;
iface->get_real_extractable_type = extractable_get_real_extractable_type;
iface->check_id = extractable_check_id;
parent_extractable_iface = g_type_interface_peek_parent (iface);
} }
G_DEFINE_TYPE_WITH_CODE (GESSourceClip, ges_source_clip, G_DEFINE_TYPE_WITH_CODE (GESSourceClip, ges_source_clip,

View file

@ -1,6 +1,8 @@
/* GStreamer Editing Services /* GStreamer Editing Services
* Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk> * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
* 2009 Nokia Corporation * 2009 Nokia Corporation
* Copyright (C) 2020 Igalia S.L
* Author: 2020 Thibault Saunier <tsaunier@igalia.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -28,7 +30,7 @@
* ## Asset * ## Asset
* *
* The default asset ID is GESTestClip, but the framerate and video * The default asset ID is GESTestClip, but the framerate and video
* size can be overriden using an ID of the form: * size can be overridden using an ID of the form:
* *
* ``` * ```
* framerate=60/1, width=1920, height=1080, max-duration=5.0 * framerate=60/1, width=1920, height=1080, max-duration=5.0
@ -167,15 +169,21 @@ typedef struct
GType type; GType type;
} ValidField; } ValidField;
static gchar * gchar *
ges_extractable_check_id (GType type, const gchar * id, GError ** error) ges_test_source_asset_check_id (GType type, const gchar * id, GError ** error)
{ {
if (id && g_strcmp0 (id, g_type_name (type))) { 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; gchar *res = NULL;
GstStructure *structure = gst_structure_from_string (struct_str, NULL); GstStructure *structure = gst_structure_from_string (id, NULL);
GST_DEBUG ("Structure is %s %" GST_PTR_FORMAT, struct_str, structure); if (!structure) {
gchar *struct_str = g_strdup_printf ("%s,%s", g_type_name (type), id);
structure = gst_structure_from_string (struct_str, NULL);
g_free (struct_str);
}
GST_INFO ("Test source ID: %" GST_PTR_FORMAT, structure);
if (!structure) { if (!structure) {
g_set_error (error, GES_ERROR, GES_ERROR_ASSET_WRONG_ID, g_set_error (error, GES_ERROR, GES_ERROR_ASSET_WRONG_ID,
"GESTestClipAsset ID should be in the form: `framerate=30/1, " "GESTestClipAsset ID should be in the form: `framerate=30/1, "
@ -186,6 +194,7 @@ ges_extractable_check_id (GType type, const gchar * id, GError ** error)
{"height", G_TYPE_INT}, {"height", G_TYPE_INT},
{"framerate", G_TYPE_NONE}, /* GST_TYPE_FRACTION is not constant */ {"framerate", G_TYPE_NONE}, /* GST_TYPE_FRACTION is not constant */
{"max-duration", GST_TYPE_CLOCK_TIME}, {"max-duration", GST_TYPE_CLOCK_TIME},
{"disable-timecodestamper", G_TYPE_BOOLEAN},
}; };
gint i; gint i;
@ -209,7 +218,6 @@ ges_extractable_check_id (GType type, const gchar * id, GError ** error)
field.name)), g_type_name (type)); field.name)), g_type_name (type));
gst_structure_free (structure); gst_structure_free (structure);
g_free (struct_str);
return FALSE; return FALSE;
} }
@ -219,7 +227,6 @@ ges_extractable_check_id (GType type, const gchar * id, GError ** error)
gst_structure_free (structure); gst_structure_free (structure);
} }
g_free (struct_str);
return res; return res;
} }
@ -230,7 +237,7 @@ static void
ges_extractable_interface_init (GESExtractableInterface * iface) ges_extractable_interface_init (GESExtractableInterface * iface)
{ {
iface->asset_type = GES_TYPE_TEST_CLIP_ASSET; iface->asset_type = GES_TYPE_TEST_CLIP_ASSET;
iface->check_id = ges_extractable_check_id; iface->check_id = ges_test_source_asset_check_id;
} }
G_DEFINE_TYPE_WITH_CODE (GESTestClip, ges_test_clip, GES_TYPE_SOURCE_CLIP, G_DEFINE_TYPE_WITH_CODE (GESTestClip, ges_test_clip, GES_TYPE_SOURCE_CLIP,
@ -517,7 +524,25 @@ ges_test_clip_create_track_element (GESClip * clip, GESTrackType type)
ges_track_type_name (type)); ges_track_type_name (type));
if (type == GES_TRACK_TYPE_VIDEO) { if (type == GES_TRACK_TYPE_VIDEO) {
res = (GESTrackElement *) ges_video_test_source_new (); gchar *id = NULL;
GESAsset *videoasset;
if (asset) {
GstStructure *structure =
gst_structure_from_string (ges_asset_get_id (asset), NULL);
if (structure) {
id = g_strdup (gst_structure_get_name (structure));
gst_structure_free (structure);
}
}
/* Our asset ID has been verified and thus this should not fail ever */
videoasset = ges_asset_request (GES_TYPE_VIDEO_TEST_SOURCE, id, NULL);
g_assert (videoasset);
g_free (id);
res = (GESTrackElement *) ges_asset_extract (videoasset, NULL);
gst_object_unref (videoasset);
ges_video_test_source_set_pattern ( ges_video_test_source_set_pattern (
(GESVideoTestSource *) res, priv->vpattern); (GESVideoTestSource *) res, priv->vpattern);
} else if (type == GES_TRACK_TYPE_AUDIO) { } else if (type == GES_TRACK_TYPE_AUDIO) {

View file

@ -1,6 +1,8 @@
/* GStreamer Editing Services /* GStreamer Editing Services
* Copyright (C) 2009 Brandon Lewis <brandon.lewis@collabora.co.uk> * Copyright (C) 2009 Brandon Lewis <brandon.lewis@collabora.co.uk>
* 2009 Nokia Corporation * 2009 Nokia Corporation
* Copyright (C) 2020 Igalia S.L
* Author: 2020 Thibault Saunier <tsaunier@igalia.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public

View file

@ -0,0 +1,53 @@
/**
* SECTION:gestimeoverlayclip
* @title: GESTimeOverlayClip
* @short_description: Source with a time overlay on top
* @symbols:
* - ges_source_clip_new_time_overlay
*
* A #GESSourceClip that overlays timing information on top.
*
* ## Asset
*
* The default asset ID is "time-overlay" (of type #GES_TYPE_SOURCE_CLIP),
* but the framerate and video size can be overridden using an ID of the form:
*
* ```
* time-overlay, framerate=60/1, width=1920, height=1080, max-duration=5.0
* ```
*
* ## Children properties
*
* {{ libs/GESTimeOverlayClip-children-props.md }}
*
* ## Symbols
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ges-asset.h"
#include "ges-time-overlay-clip.h"
/**
* ges_source_clip_new_time_overlay:
*
* Creates a new #GESSourceClip that renders a time overlay on top
*
* Returns: (transfer floating) (nullable): The newly created #GESSourceClip,
* or %NULL if there was an error.
*/
GESSourceClip *
ges_source_clip_new_time_overlay (void)
{
GESSourceClip *new_clip;
GESAsset *asset = ges_asset_request (GES_TYPE_SOURCE_CLIP,
"time-overlay", NULL);
new_clip = GES_SOURCE_CLIP (ges_asset_extract (asset, NULL));
gst_object_unref (asset);
return new_clip;
}

View file

@ -0,0 +1,30 @@
/* GStreamer Editing Services
* Copyright (C) 2020 Igalia S.L
* Author: 2020 Thibault Saunier <tsaunier@igalia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include "ges-source-clip.h"
G_BEGIN_DECLS
GES_API
GESSourceClip* ges_source_clip_new_time_overlay (void);
G_END_DECLS

View file

@ -63,6 +63,8 @@ static void
ges_extractable_interface_init (GESExtractableInterface * iface) ges_extractable_interface_init (GESExtractableInterface * iface)
{ {
iface->set_asset = ges_video_source_set_asset; iface->set_asset = ges_video_source_set_asset;
parent_extractable_iface = g_type_interface_peek_parent (iface);
} }
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESVideoSource, ges_video_source, G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESVideoSource, ges_video_source,
@ -233,16 +235,6 @@ ges_video_source_class_init (GESVideoSourceClass * klass)
static void static void
ges_video_source_init (GESVideoSource * self) ges_video_source_init (GESVideoSource * self)
{ {
if (g_once_init_enter (&parent_extractable_iface)) {
GESExtractableInterface *iface, *parent_iface;
iface =
G_TYPE_INSTANCE_GET_INTERFACE (self, GES_TYPE_EXTRACTABLE,
GESExtractableInterface);
parent_iface = g_type_interface_peek_parent (iface);
g_once_init_leave (&parent_extractable_iface, parent_iface);
}
self->priv = ges_video_source_get_instance_private (self); self->priv = ges_video_source_get_instance_private (self);
self->priv->positioner = NULL; self->priv->positioner = NULL;
self->priv->capsfilter = NULL; self->priv->capsfilter = NULL;

View file

@ -1,6 +1,8 @@
/* GStreamer Editing Services /* GStreamer Editing Services
* Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk> * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
* 2010 Nokia Corporation * 2010 Nokia Corporation
* Copyright (C) 2020 Igalia S.L
* Author: 2020 Thibault Saunier <tsaunier@igalia.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -38,18 +40,30 @@ struct _GESVideoTestSourcePrivate
{ {
GESVideoTestPattern pattern; GESVideoTestPattern pattern;
gboolean use_overlay;
GstElement *overlay;
GstPad *is_passthrough_pad;
GstPad *os_passthrough_pad;
GstPad *is_overlay_pad;
GstPad *os_overlay_pad;
GstElement *capsfilter; GstElement *capsfilter;
}; };
G_DEFINE_TYPE_WITH_PRIVATE (GESVideoTestSource, ges_video_test_source, static void
GES_TYPE_VIDEO_SOURCE); ges_extractable_interface_init (GESExtractableInterface * iface)
{
iface->check_id = ges_test_source_asset_check_id;
iface->asset_type = GES_TYPE_TRACK_ELEMENT_ASSET;
}
static GstStructure *
ges_video_test_source_asset_get_config (GESAsset * asset)
{
const gchar *id = ges_asset_get_id (asset);
if (g_strcmp0 (id, g_type_name (ges_asset_get_extractable_type (asset))))
return gst_structure_from_string (ges_asset_get_id (asset), NULL);
return NULL;
}
G_DEFINE_TYPE_WITH_CODE (GESVideoTestSource, ges_video_test_source,
GES_TYPE_VIDEO_SOURCE, G_ADD_PRIVATE (GESVideoTestSource)
G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE,
ges_extractable_interface_init));
static GstElement *ges_video_test_source_create_source (GESTrackElement * self); static GstElement *ges_video_test_source_create_source (GESTrackElement * self);
@ -74,101 +88,6 @@ get_natural_size (GESVideoSource * source, gint * width, gint * height)
return TRUE; return TRUE;
} }
enum
{
PROP_0,
PROP_USE_TIME_OVERLAY,
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
static void
get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
GESVideoTestSource *self = GES_VIDEO_TEST_SOURCE (object);
switch (property_id) {
case PROP_USE_TIME_OVERLAY:
g_value_set_boolean (value, self->priv->use_overlay);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
GESVideoTestSource *self = GES_VIDEO_TEST_SOURCE (object);
switch (property_id) {
case PROP_USE_TIME_OVERLAY:
{
GstElement *os, *is;
gboolean used_overlay = self->priv->use_overlay;
if (!self->priv->overlay) {
GST_ERROR_OBJECT (object, "Overlaying is disabled, you are probably"
"missing some GStreamer plugin");
return;
}
self->priv->use_overlay = g_value_get_boolean (value);
if (used_overlay == self->priv->use_overlay)
return;
is = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (self->
priv->is_overlay_pad)));
if (!is)
return;
os = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (self->
priv->os_overlay_pad)));
if (!os) {
gst_object_unref (is);
return;
}
g_object_set (is, "active-pad",
self->priv->use_overlay ? self->priv->is_overlay_pad : self->
priv->is_passthrough_pad, NULL);
g_object_set (os, "active-pad",
self->priv->use_overlay ? self->priv->os_overlay_pad : self->
priv->os_passthrough_pad, NULL);
gst_object_unref (is);
gst_object_unref (os);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
_set_child_property (GESTimelineElement * self, GObject * child,
GParamSpec * pspec, GValue * value)
{
GstElementFactory *f =
GST_IS_ELEMENT (child) ? gst_element_get_factory (GST_ELEMENT (child)) :
NULL;
if (!f || g_strcmp0 (GST_OBJECT_NAME (f), "timeoverlay"))
goto done;
GST_INFO_OBJECT (self, "Activating time overlay as time mode is being set");
g_object_set (self, "use-time-overlay", TRUE, NULL);
done:
GES_TIMELINE_ELEMENT_CLASS (ges_video_test_source_parent_class)
->set_child_property (self, child, pspec, value);
}
static gboolean static gboolean
_set_parent (GESTimelineElement * element, GESTimelineElement * parent) _set_parent (GESTimelineElement * element, GESTimelineElement * parent)
{ {
@ -231,13 +150,6 @@ _get_natural_framerate (GESTimelineElement * element, gint * fps_n,
static void static void
dispose (GObject * object) dispose (GObject * object)
{ {
GESVideoTestSourcePrivate *priv = GES_VIDEO_TEST_SOURCE (object)->priv;
gst_clear_object (&priv->is_overlay_pad);
gst_clear_object (&priv->is_passthrough_pad);
gst_clear_object (&priv->os_overlay_pad);
gst_clear_object (&priv->os_passthrough_pad);
G_OBJECT_CLASS (ges_video_test_source_parent_class)->dispose (object); G_OBJECT_CLASS (ges_video_test_source_parent_class)->dispose (object);
} }
@ -250,33 +162,11 @@ ges_video_test_source_class_init (GESVideoTestSourceClass * klass)
source_class->create_source = ges_video_test_source_create_source; source_class->create_source = ges_video_test_source_create_source;
source_class->ABI.abi.get_natural_size = get_natural_size; source_class->ABI.abi.get_natural_size = get_natural_size;
/**
* GESVideoTestSource:use-overlay:
*
* Whether to overlay the test video source with a clock time.
* This property is also registered as a child property for the video
* test source, so you can set it like any other child property.
*
* The properties of the corresponding #timeoverlay are also registered
* as children properties. If you set any child property from the underlying
* `timeoverlay`, this property will be set to %TRUE by default.
*/
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);
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->dispose = dispose; 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)->set_parent = _set_parent;
GES_TIMELINE_ELEMENT_CLASS (klass)->get_natural_framerate = GES_TIMELINE_ELEMENT_CLASS (klass)->get_natural_framerate =
_get_natural_framerate; _get_natural_framerate;
g_object_class_install_properties (object_class, PROP_LAST, properties);
} }
static void static void
@ -287,63 +177,40 @@ ges_video_test_source_init (GESVideoTestSource * self)
self->priv->pattern = DEFAULT_VPATTERN; self->priv->pattern = DEFAULT_VPATTERN;
} }
#define CREATE_ELEMENT(var, ename) \ static GstStructure *
if (!(var = gst_element_factory_make(ename, NULL))) { \ ges_video_test_source_get_config (GESVideoTestSource * self)
GST_ERROR_OBJECT(self, "Could not create %s", ename); \ {
goto done; \ GESAsset *asset = ges_extractable_get_asset (GES_EXTRACTABLE (self));
} \ if (asset)
gst_object_ref(var); return ges_video_test_source_asset_get_config (asset);
return NULL;
}
static GstElement * static GstElement *
ges_video_test_source_create_overlay (GESVideoTestSource * self) ges_video_test_source_create_overlay (GESVideoTestSource * self)
{ {
GstElement *bin = NULL, *os = NULL, *is = NULL, *toverlay = NULL, *tcstamper = const gchar *bindesc = NULL;
NULL; GstStructure *config = ges_video_test_source_get_config (self);
GstPad *tmppad;
CREATE_ELEMENT (os, "output-selector"); if (!config)
gst_util_set_object_arg (G_OBJECT (os), "pad-negotiation-mode", "active"); return NULL;
CREATE_ELEMENT (is, "input-selector");
CREATE_ELEMENT (tcstamper, "timecodestamper");
CREATE_ELEMENT (toverlay, "timeoverlay");
bin = gst_bin_new (NULL); if (gst_structure_has_name (config, "time-overlay")) {
gboolean disable_timecodestamper;
gst_bin_add_many (GST_BIN (bin), os, is, tcstamper, toverlay, NULL); if (gst_structure_get_boolean (config, "disable-timecodestamper",
self->priv->os_passthrough_pad = gst_element_get_request_pad (os, "src_%u"); &disable_timecodestamper))
self->priv->is_passthrough_pad = gst_element_get_request_pad (is, "sink_%u"); bindesc = "timeoverlay";
else
bindesc = "timecodestamper ! timeoverlay";
}
gst_structure_free (config);
gst_pad_link_full (self->priv->os_passthrough_pad, if (!bindesc)
self->priv->is_passthrough_pad, GST_PAD_LINK_CHECK_NOTHING); return NULL;
gst_element_link (tcstamper, toverlay);
self->priv->os_overlay_pad = gst_element_get_request_pad (os, "src_%u"); return gst_parse_bin_from_description (bindesc, TRUE, NULL);
tmppad = gst_element_get_static_pad (tcstamper, "sink");
gst_pad_link_full (self->priv->os_overlay_pad, tmppad,
GST_PAD_LINK_CHECK_NOTHING);
gst_object_unref (tmppad);
tmppad = gst_element_get_static_pad (toverlay, "src");
self->priv->is_overlay_pad = gst_element_get_request_pad (is, "sink_%u");
gst_pad_link_full (tmppad, self->priv->is_overlay_pad,
GST_PAD_LINK_CHECK_NOTHING);
gst_object_unref (tmppad);
tmppad = gst_element_get_static_pad (os, "sink");
gst_element_add_pad (GST_ELEMENT (bin), gst_ghost_pad_new ("sink", tmppad));
gst_object_unref (tmppad);
tmppad = gst_element_get_static_pad (is, "src");
gst_element_add_pad (GST_ELEMENT (bin), gst_ghost_pad_new ("src", tmppad));
gst_object_unref (tmppad);
done:
gst_clear_object (&os);
gst_clear_object (&is);
gst_clear_object (&tcstamper);
gst_clear_object (&toverlay);
return bin;
} }
static GstElement * static GstElement *
@ -352,6 +219,7 @@ ges_video_test_source_create_source (GESTrackElement * element)
GstCaps *caps; GstCaps *caps;
gint pattern; gint pattern;
GstElement *testsrc, *res; GstElement *testsrc, *res;
GstElement *overlay;
const gchar *props[] = const gchar *props[] =
{ "pattern", "background-color", "foreground-color", NULL }; { "pattern", "background-color", "foreground-color", NULL };
GPtrArray *elements; GPtrArray *elements;
@ -374,18 +242,16 @@ ges_video_test_source_create_source (GESTrackElement * element)
g_object_set (self->priv->capsfilter, "caps", caps, NULL); g_object_set (self->priv->capsfilter, "caps", caps, NULL);
gst_caps_unref (caps); gst_caps_unref (caps);
self->priv->overlay = ges_video_test_source_create_overlay (self); overlay = ges_video_test_source_create_overlay (self);
if (self->priv->overlay) { if (overlay) {
const gchar *overlay_props[] = const gchar *overlay_props[] =
{ "time-mode", "text-y", "text-x", "text-width", "test-height", { "time-mode", "text-y", "text-x", "text-width", "test-height",
"halignment", "valignment", "font-desc", NULL "halignment", "valignment", "font-desc", NULL
}; };
ges_track_element_add_children_props (element, self->priv->overlay, NULL, ges_track_element_add_children_props (element, overlay, NULL,
NULL, overlay_props); NULL, overlay_props);
ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT (self), g_ptr_array_add (elements, overlay);
properties[PROP_USE_TIME_OVERLAY], G_OBJECT (self));
g_ptr_array_add (elements, self->priv->overlay);
} }
ges_track_element_add_children_props (element, testsrc, NULL, NULL, props); ges_track_element_add_children_props (element, testsrc, NULL, NULL, props);
@ -440,7 +306,8 @@ ges_video_test_source_get_pattern (GESVideoTestSource * source)
return g_value_get_enum (&val); return g_value_get_enum (&val);
} }
/* ges_video_test_source_new: /**
* ges_video_test_source_new:
* *
* Creates a new #GESVideoTestSource. * Creates a new #GESVideoTestSource.
* *

View file

@ -1,6 +1,8 @@
/* GStreamer Editing Services /* GStreamer Editing Services
* Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk> * Copyright (C) 2010 Brandon Lewis <brandon.lewis@collabora.co.uk>
* 2010 Nokia Corporation * 2010 Nokia Corporation
* Copyright (C) 2020 Igalia S.L
* Author: 2020 Thibault Saunier <tsaunier@igalia.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public

View file

@ -33,6 +33,7 @@
#include <ges/ges-clip.h> #include <ges/ges-clip.h>
#include <ges/ges-pipeline.h> #include <ges/ges-pipeline.h>
#include <ges/ges-source-clip.h> #include <ges/ges-source-clip.h>
#include <ges/ges-time-overlay-clip.h>
#include <ges/ges-test-clip.h> #include <ges/ges-test-clip.h>
#include <ges/ges-title-clip.h> #include <ges/ges-title-clip.h>
#include <ges/ges-operation-clip.h> #include <ges/ges-operation-clip.h>

View file

@ -14,6 +14,7 @@ ges_sources = files([
'ges-operation-clip.c', 'ges-operation-clip.c',
'ges-base-transition-clip.c', 'ges-base-transition-clip.c',
'ges-transition-clip.c', 'ges-transition-clip.c',
'ges-time-overlay-clip.c',
'ges-test-clip.c', 'ges-test-clip.c',
'ges-title-clip.c', 'ges-title-clip.c',
'ges-overlay-clip.c', 'ges-overlay-clip.c',
@ -86,6 +87,7 @@ ges_headers = files([
'ges-base-transition-clip.h', 'ges-base-transition-clip.h',
'ges-transition-clip.h', 'ges-transition-clip.h',
'ges-test-clip.h', 'ges-test-clip.h',
'ges-time-overlay-clip.h',
'ges-title-clip.h', 'ges-title-clip.h',
'ges-overlay-clip.h', 'ges-overlay-clip.h',
'ges-text-overlay-clip.h', 'ges-text-overlay-clip.h',

View file

@ -3999,7 +3999,7 @@ GST_START_TEST (test_copy_paste_children_properties)
timeline = ges_timeline_new_audio_video (); timeline = ges_timeline_new_audio_video ();
layer = ges_timeline_append_layer (timeline); layer = ges_timeline_append_layer (timeline);
clip = GES_TIMELINE_ELEMENT (ges_test_clip_new ()); clip = GES_TIMELINE_ELEMENT (ges_source_clip_new_time_overlay ());
assert_set_duration (clip, 50); assert_set_duration (clip, 50);
fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip))); fail_unless (ges_layer_add_clip (layer, GES_CLIP (clip)));

View file

@ -5,7 +5,7 @@ description, handles-states=true,
"--videosink=fakesink"\ "--videosink=fakesink"\
} }
add-clip, name=clip, asset-id="framerate=120/1", layer-priority=0, type=GESTestClip, pattern=blue, duration=f240, inpoint=f100 add-clip, name=clip, asset-id="time-overlay,framerate=120/1", layer-priority=0, type=GESSourceClip, pattern=blue, duration=f240, inpoint=f100
set-child-properties, element-name=clip, time-mode=time-code set-child-properties, element-name=clip, time-mode=time-code
pause pause

View file

@ -12,11 +12,11 @@ meta,
"$(validateflow), pad=videosink:sink, record-buffers=true, ignored-fields=\"stream-start={stream-id,group-id,stream}\"", "$(validateflow), pad=videosink:sink, record-buffers=true, ignored-fields=\"stream-start={stream-id,group-id,stream}\"",
} }
add-clip, name=c0, asset-id=GESTestClip, layer-priority=0, type=GESTestClip, start=0, duration=1.0 add-clip, name=c0, asset-id=time-overlay, layer-priority=0, type=GESSourceClip, start=0, duration=1.0
set-child-properties, element-name=c0, pattern=blue, time-mode=time-code, valignment=center, halignment=center set-child-properties, element-name=c0, pattern=blue, valignment=center, halignment=center, time-mode=time-code
add-clip, name=c1, asset-id=GESTestClip, layer-priority=0, type=GESTestClip, start=1.0, duration=1.0 add-clip, name=c1, asset-id=time-overlay, layer-priority=0, type=GESSourceClip, start=1.0, duration=1.0
set-child-properties, element-name=c1, pattern=red, time-mode=time-code, valignment=center, halignment=center set-child-properties, element-name=c1, pattern=red, valignment=center, halignment=center, time-mode=time-code
commit; commit;
play play

View file

@ -12,11 +12,11 @@ meta,
"$(validateflow), pad=videosink:sink, record-buffers=true, ignored-fields=\"stream-start={stream-id,group-id,stream}\"", "$(validateflow), pad=videosink:sink, record-buffers=true, ignored-fields=\"stream-start={stream-id,group-id,stream}\"",
} }
add-clip, name=c0, asset-id=GESTestClip, layer-priority=0, type=GESTestClip, start=0, duration=1.0 add-clip, name=c0, asset-id=time-overlay, layer-priority=0, type=GESSourceClip, start=0, duration=1.0
set-child-properties, element-name=c0, pattern=blue, time-mode=time-code, valignment=center, halignment=center set-child-properties, element-name=c0, pattern=blue, valignment=center, halignment=center, time-mode=time-code
add-clip, name=c1, asset-id=GESTestClip, layer-priority=0, type=GESTestClip, start=1.0, duration=1.0 add-clip, name=c1, asset-id=time-overlay, layer-priority=0, type=GESSourceClip, start=1.0, duration=1.0
set-child-properties, element-name=c1, pattern=red, time-mode=time-code, valignment=center, halignment=center set-child-properties, element-name=c1, pattern=red, valignment=center, halignment=center, time-mode=time-code
commit; commit;
play play

View file

@ -11,11 +11,11 @@ meta,
"$(validateflow), pad=audiosink:sink, record-buffers=true, ignored-fields=\"stream-start={stream-id,group-id,stream}, segment={position,}\"", "$(validateflow), pad=audiosink:sink, record-buffers=true, ignored-fields=\"stream-start={stream-id,group-id,stream}, segment={position,}\"",
} }
add-clip, name=c0, asset-id=GESTestClip, layer-priority=0, type=GESTestClip, start=0, duration=1.0 add-clip, name=c0, asset-id=time-overlay, layer-priority=0, type=GESSourceClip, start=0, duration=1.0
set-child-properties, element-name=c0, pattern=blue, time-mode=time-code, valignment=center, halignment=center set-child-properties, element-name=c0, pattern=blue, valignment=center, halignment=center, time-mode=time-code
add-clip, name=c1, asset-id=GESTestClip, layer-priority=0, type=GESTestClip, start=1.0, duration=1.0 add-clip, name=c1, asset-id=time-overlay, layer-priority=0, type=GESSourceClip, start=1.0, duration=1.0
set-child-properties, element-name=c1, pattern=red, time-mode=time-code, valignment=center, halignment=center set-child-properties, element-name=c1, pattern=red, valignment=center, halignment=center, time-mode=time-code
pause pause
seek, start=0.0, stop=0.5, flags=accurate+flush seek, start=0.0, stop=0.5, flags=accurate+flush