mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
ges: Properly position video sources in the scene by default
We try to do our best to have the video frames scaled the best way to fill most space on the final frames, keeping aspect ratio. The user can later on rescale or move the sources as usual but it makes the default behaviour a better and more natural especially now that we set default restriction caps to the video tracks. And fix the unit test to take that change into account
This commit is contained in:
parent
7b3ac927dc
commit
53637ad749
5 changed files with 116 additions and 17 deletions
|
@ -53,6 +53,7 @@
|
||||||
#include "ges-video-source.h"
|
#include "ges-video-source.h"
|
||||||
#include "ges-layer.h"
|
#include "ges-layer.h"
|
||||||
#include "gstframepositioner.h"
|
#include "gstframepositioner.h"
|
||||||
|
#include "ges-extractable.h"
|
||||||
|
|
||||||
#define parent_class ges_video_source_parent_class
|
#define parent_class ges_video_source_parent_class
|
||||||
|
|
||||||
|
@ -62,8 +63,26 @@ struct _GESVideoSourcePrivate
|
||||||
GstElement *capsfilter;
|
GstElement *capsfilter;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GESVideoSource, ges_video_source,
|
static void
|
||||||
GES_TYPE_SOURCE);
|
ges_video_source_set_asset (GESExtractable * extractable, GESAsset * asset)
|
||||||
|
{
|
||||||
|
GESVideoSource *self = GES_VIDEO_SOURCE (extractable);
|
||||||
|
|
||||||
|
ges_video_source_get_natural_size (self,
|
||||||
|
&self->priv->positioner->natural_width,
|
||||||
|
&self->priv->positioner->natural_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ges_extractable_interface_init (GESExtractableInterface * iface)
|
||||||
|
{
|
||||||
|
iface->set_asset = ges_video_source_set_asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESVideoSource, ges_video_source,
|
||||||
|
GES_TYPE_SOURCE, G_ADD_PRIVATE (GESVideoSource)
|
||||||
|
G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE,
|
||||||
|
ges_extractable_interface_init));
|
||||||
|
|
||||||
/* TrackElement VMethods */
|
/* TrackElement VMethods */
|
||||||
|
|
||||||
|
@ -169,6 +188,10 @@ ges_video_source_create_element (GESTrackElement * trksrc)
|
||||||
self->priv->positioner = GST_FRAME_POSITIONNER (positioner);
|
self->priv->positioner = GST_FRAME_POSITIONNER (positioner);
|
||||||
self->priv->positioner->scale_in_compositor =
|
self->priv->positioner->scale_in_compositor =
|
||||||
!GES_VIDEO_SOURCE_GET_CLASS (self)->ABI.abi.disable_scale_in_compositor;
|
!GES_VIDEO_SOURCE_GET_CLASS (self)->ABI.abi.disable_scale_in_compositor;
|
||||||
|
ges_video_source_get_natural_size (self,
|
||||||
|
&self->priv->positioner->natural_width,
|
||||||
|
&self->priv->positioner->natural_height);
|
||||||
|
|
||||||
self->priv->capsfilter = capsfilter;
|
self->priv->capsfilter = capsfilter;
|
||||||
|
|
||||||
return topbin;
|
return topbin;
|
||||||
|
@ -226,7 +249,6 @@ ges_video_source_init (GESVideoSource * self)
|
||||||
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -124,7 +124,11 @@ ges_video_uri_source_get_natural_size (GESVideoSource * source, gint * width,
|
||||||
GstDiscovererStreamInfo *info;
|
GstDiscovererStreamInfo *info;
|
||||||
GESAsset *asset = ges_extractable_get_asset (GES_EXTRACTABLE (source));
|
GESAsset *asset = ges_extractable_get_asset (GES_EXTRACTABLE (source));
|
||||||
|
|
||||||
g_assert (GES_IS_URI_SOURCE_ASSET (asset));
|
if (!asset) {
|
||||||
|
GST_DEBUG_OBJECT (source, "No asset set yet");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
info = ges_uri_source_asset_get_stream_info (GES_URI_SOURCE_ASSET (asset));
|
info = ges_uri_source_asset_get_stream_info (GES_URI_SOURCE_ASSET (asset));
|
||||||
|
|
||||||
if (!GST_IS_DISCOVERER_VIDEO_INFO (info)) {
|
if (!GST_IS_DISCOVERER_VIDEO_INFO (info)) {
|
||||||
|
@ -173,7 +177,7 @@ done:
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
rotate:
|
rotate:
|
||||||
GST_INFO_OBJECT (self, "Stream is rotated, taking that into account");
|
GST_INFO_OBJECT (source, "Stream is rotated, taking that into account");
|
||||||
*width =
|
*width =
|
||||||
gst_discoverer_video_info_get_height (GST_DISCOVERER_VIDEO_INFO (info));
|
gst_discoverer_video_info_get_height (GST_DISCOVERER_VIDEO_INFO (info));
|
||||||
*height =
|
*height =
|
||||||
|
@ -193,6 +197,7 @@ ges_extractable_check_id (GType type, const gchar * id, GError ** error)
|
||||||
static void
|
static void
|
||||||
extractable_set_asset (GESExtractable * extractable, GESAsset * asset)
|
extractable_set_asset (GESExtractable * extractable, GESAsset * asset)
|
||||||
{
|
{
|
||||||
|
GESExtractableInterface *piface, *iface;
|
||||||
/* FIXME That should go into #GESTrackElement, but
|
/* FIXME That should go into #GESTrackElement, but
|
||||||
* some work is needed to make sure it works properly */
|
* some work is needed to make sure it works properly */
|
||||||
|
|
||||||
|
@ -202,6 +207,11 @@ extractable_set_asset (GESExtractable * extractable, GESAsset * asset)
|
||||||
ges_track_element_asset_get_track_type (GES_TRACK_ELEMENT_ASSET
|
ges_track_element_asset_get_track_type (GES_TRACK_ELEMENT_ASSET
|
||||||
(asset)));
|
(asset)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iface = G_TYPE_INSTANCE_GET_INTERFACE (extractable, GES_TYPE_EXTRACTABLE,
|
||||||
|
GESExtractableInterface);
|
||||||
|
piface = g_type_interface_peek_parent (iface);
|
||||||
|
piface->set_asset (extractable, asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -81,6 +81,56 @@ _weak_notify_cb (GstFramePositioner * pos, GObject * old)
|
||||||
pos->current_track = NULL;
|
pos->current_track = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
auto_position (GstFramePositioner * self)
|
||||||
|
{
|
||||||
|
gint scaled_width = -1, scaled_height = -1, x, y;
|
||||||
|
|
||||||
|
if (self->user_positioned) {
|
||||||
|
GST_DEBUG_OBJECT (self, "Was positioned by the user, not touching anymore");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->natural_width || !self->natural_height)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!self->track_width || !self->track_height) {
|
||||||
|
GST_INFO_OBJECT (self, "Track doesn't have a proper size, not "
|
||||||
|
"positioning the source");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->track_width == self->natural_width &&
|
||||||
|
self->track_height == self->natural_height)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
scaled_height =
|
||||||
|
gst_util_uint64_scale_int (self->natural_height, self->track_width,
|
||||||
|
self->natural_width);
|
||||||
|
scaled_width = self->track_width;
|
||||||
|
if (scaled_height > self->track_height) {
|
||||||
|
scaled_height = self->track_height;
|
||||||
|
scaled_width =
|
||||||
|
gst_util_uint64_scale_int (self->natural_width, self->track_height,
|
||||||
|
self->natural_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
x = MAX (0, gst_util_uint64_scale_int_round (1,
|
||||||
|
self->track_width - scaled_width, 2));
|
||||||
|
y = MAX (0, gst_util_uint64_scale_int_round (1,
|
||||||
|
self->track_height - scaled_height, 2));
|
||||||
|
|
||||||
|
GST_INFO_OBJECT (self, "Scalling video to match track size from "
|
||||||
|
"%dx%d to %dx%d",
|
||||||
|
self->natural_width, self->natural_height, scaled_width, scaled_height);
|
||||||
|
self->width = scaled_width;
|
||||||
|
self->height = scaled_height;
|
||||||
|
self->posx = x;
|
||||||
|
self->posy = y;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_frame_positioner_update_properties (GstFramePositioner * pos,
|
gst_frame_positioner_update_properties (GstFramePositioner * pos,
|
||||||
gboolean track_mixing, gint old_track_width, gint old_track_height)
|
gboolean track_mixing, gint old_track_width, gint old_track_height)
|
||||||
|
@ -107,19 +157,23 @@ gst_frame_positioner_update_properties (GstFramePositioner * pos,
|
||||||
gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
|
gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
|
||||||
pos->par_n, pos->par_d, NULL);
|
pos->par_n, pos->par_d, NULL);
|
||||||
|
|
||||||
|
if (!auto_position (pos)) {
|
||||||
|
|
||||||
if (old_track_width && pos->width == old_track_width &&
|
if (old_track_width && pos->width == old_track_width &&
|
||||||
old_track_height && pos->height == old_track_height &&
|
old_track_height && pos->height == old_track_height &&
|
||||||
pos->track_height && pos->track_width &&
|
pos->track_height && pos->track_width &&
|
||||||
((float) old_track_width / (float) old_track_height) ==
|
((float) old_track_width / (float) old_track_height) ==
|
||||||
((float) pos->track_width / (float) pos->track_height)) {
|
((float) pos->track_width / (float) pos->track_height)) {
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (pos, "Following track size width old_track: %d -- pos: %d"
|
GST_DEBUG_OBJECT (pos,
|
||||||
" || height, old_track %d -- pos: %d",
|
"Following track size width old_track: %d -- pos: %d"
|
||||||
old_track_width, pos->width, old_track_height, pos->height);
|
" || height, old_track %d -- pos: %d", old_track_width, pos->width,
|
||||||
|
old_track_height, pos->height);
|
||||||
|
|
||||||
pos->width = pos->track_width;
|
pos->width = pos->track_width;
|
||||||
pos->height = pos->track_height;
|
pos->height = pos->track_height;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (caps, "setting caps");
|
GST_DEBUG_OBJECT (caps, "setting caps");
|
||||||
|
|
||||||
|
@ -263,6 +317,9 @@ gst_frame_positioner_class_init (GstFramePositionerClass * klass)
|
||||||
GstBaseTransformClass *base_transform_class =
|
GstBaseTransformClass *base_transform_class =
|
||||||
GST_BASE_TRANSFORM_CLASS (klass);
|
GST_BASE_TRANSFORM_CLASS (klass);
|
||||||
|
|
||||||
|
GST_DEBUG_CATEGORY_INIT (framepositioner, "framepositioner",
|
||||||
|
GST_DEBUG_FG_YELLOW, "ges frame positioner");
|
||||||
|
|
||||||
gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
|
gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
|
||||||
&gst_frame_positioner_src_template);
|
&gst_frame_positioner_src_template);
|
||||||
gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
|
gst_element_class_add_static_pad_template (GST_ELEMENT_CLASS (klass),
|
||||||
|
@ -379,19 +436,23 @@ gst_frame_positioner_set_property (GObject * object, guint property_id,
|
||||||
break;
|
break;
|
||||||
case PROP_POSX:
|
case PROP_POSX:
|
||||||
framepositioner->posx = g_value_get_int (value);
|
framepositioner->posx = g_value_get_int (value);
|
||||||
|
framepositioner->user_positioned = TRUE;
|
||||||
break;
|
break;
|
||||||
case PROP_POSY:
|
case PROP_POSY:
|
||||||
framepositioner->posy = g_value_get_int (value);
|
framepositioner->posy = g_value_get_int (value);
|
||||||
|
framepositioner->user_positioned = TRUE;
|
||||||
break;
|
break;
|
||||||
case PROP_ZORDER:
|
case PROP_ZORDER:
|
||||||
framepositioner->zorder = g_value_get_uint (value);
|
framepositioner->zorder = g_value_get_uint (value);
|
||||||
break;
|
break;
|
||||||
case PROP_WIDTH:
|
case PROP_WIDTH:
|
||||||
|
framepositioner->user_positioned = TRUE;
|
||||||
framepositioner->width = g_value_get_int (value);
|
framepositioner->width = g_value_get_int (value);
|
||||||
gst_frame_positioner_update_properties (framepositioner, track_mixing,
|
gst_frame_positioner_update_properties (framepositioner, track_mixing,
|
||||||
0, 0);
|
0, 0);
|
||||||
break;
|
break;
|
||||||
case PROP_HEIGHT:
|
case PROP_HEIGHT:
|
||||||
|
framepositioner->user_positioned = TRUE;
|
||||||
framepositioner->height = g_value_get_int (value);
|
framepositioner->height = g_value_get_int (value);
|
||||||
gst_frame_positioner_update_properties (framepositioner, track_mixing,
|
gst_frame_positioner_update_properties (framepositioner, track_mixing,
|
||||||
0, 0);
|
0, 0);
|
||||||
|
|
|
@ -51,7 +51,9 @@ struct _GstFramePositioner
|
||||||
gint posy;
|
gint posy;
|
||||||
guint zorder;
|
guint zorder;
|
||||||
gint width;
|
gint width;
|
||||||
|
gint natural_width;
|
||||||
gint height;
|
gint height;
|
||||||
|
gint natural_height;
|
||||||
gint track_width;
|
gint track_width;
|
||||||
gint track_height;
|
gint track_height;
|
||||||
gint fps_n;
|
gint fps_n;
|
||||||
|
@ -60,6 +62,8 @@ struct _GstFramePositioner
|
||||||
gint par_n;
|
gint par_n;
|
||||||
gint par_d;
|
gint par_d;
|
||||||
|
|
||||||
|
gboolean user_positioned;
|
||||||
|
|
||||||
/* This should never be made public, no padding needed */
|
/* This should never be made public, no padding needed */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,9 @@ description, handles-states=true,
|
||||||
|
|
||||||
add-clip, name=clip, asset-id=GESTestClip, layer-priority=0, type=GESTestClip, duration=1.0
|
add-clip, name=clip, asset-id=GESTestClip, layer-priority=0, type=GESTestClip, duration=1.0
|
||||||
|
|
||||||
check-child-properties, element-name=clip, width=0, height=0
|
# VideoTestSource natural size is 1280x720, so keeping aspect ratio, mean
|
||||||
|
# that it will be scaled to 1200x675 (placed at x=0, y=163)
|
||||||
|
check-child-properties, element-name=clip, width=1200, height=675, posx=0, posy=163
|
||||||
|
|
||||||
set-child-properties, element-name=clip, width=1024, height=768
|
set-child-properties, element-name=clip, width=1024, height=768
|
||||||
check-child-properties, element-name=clip, width=1024, height=768
|
check-child-properties, element-name=clip, width=1024, height=768
|
||||||
|
|
Loading…
Reference in a new issue