diff options
author | Lionel Landwerlin <llandwerlin@gmail.com> | 2014-05-29 19:47:10 +0100 |
---|---|---|
committer | Lionel Landwerlin <llandwerlin@gmail.com> | 2014-06-03 23:47:54 +0100 |
commit | 6c23a5fe8d7e2433184f249394e043836ea2f302 (patch) | |
tree | da4e8da677ebe89356abca153232d60ec7906799 | |
parent | 3c3f0ab8907bf971905c6f6fa2b87887e3b48af9 (diff) | |
download | clutter-gst-wip/overlays.tar.gz |
untested/uncompiled overlay support starting pointwip/overlays
-rw-r--r-- | clutter-gst/clutter-gst-aspectratio.c | 102 | ||||
-rw-r--r-- | clutter-gst/clutter-gst-content.c | 221 | ||||
-rw-r--r-- | clutter-gst/clutter-gst-content.h | 4 | ||||
-rw-r--r-- | clutter-gst/clutter-gst-private.h | 8 | ||||
-rw-r--r-- | clutter-gst/clutter-gst-types.c | 91 | ||||
-rw-r--r-- | clutter-gst/clutter-gst-types.h | 55 | ||||
-rw-r--r-- | clutter-gst/clutter-gst-util.c | 11 | ||||
-rw-r--r-- | clutter-gst/clutter-gst-video-sink.c | 142 | ||||
-rw-r--r-- | clutter-gst/clutter-gst-video-sink.h | 3 |
9 files changed, 576 insertions, 61 deletions
diff --git a/clutter-gst/clutter-gst-aspectratio.c b/clutter-gst/clutter-gst-aspectratio.c index 21b3802..f426802 100644 --- a/clutter-gst/clutter-gst-aspectratio.c +++ b/clutter-gst/clutter-gst-aspectratio.c @@ -133,6 +133,35 @@ clutter_gst_aspectratio_get_frame_box (ClutterGstAspectratio *self, } } +static void +clutter_gst_aspectratio_get_overlay_box (ClutterGstAspectratio *self, + ClutterGstBox *input_box, + ClutterGstBox *paint_box, + const ClutterGstBox *frame_input, + const ClutterGstBox *frame_box, + ClutterGstFrame *frame, + ClutterGstOverlay *overlay) +{ + gfloat box_width = frame_box->x2 - frame_box->x1, + box_height = frame_box->y2 - frame_box->y1; + + /* texturing input */ + input_box->x1 = (overlay->position.x1 - MAX (frame_input->x1 * frame->resolution.width, overlay->position.x1)) + / (overlay->position.x2 - overlay->position.x1); + input_box->y1 = (overlay->position.y1 - MAX (frame_input->y1 * frame->resolution.height, overlay->position.y1)) + / (overlay->position.y2 - overlay->position.y1); + input_box->x2 = 1 - (overlay->position.x2 - MIN (frame_input->x2 * frame->resolution.width, overlay->position.x2)) + / (overlay->position.x2 - overlay->position.x1); + input_box->y2 = 1 - (overlay->position.y2 - MIN (frame_input->y2 * frame->resolution.height, overlay->position.y2)) + / (overlay->position.y2 - overlay->position.y1); + + /* vertex output */ + paint_box->x1 = frame_box->x1 + overlay->position.x1 * box_width / frame->resolution.width; + paint_box->y1 = frame_box->y1 + overlay->position.y1 * box_height / frame->resolution.height; + paint_box->x2 = frame_box->x1 + overlay->position.x2 * box_width / frame->resolution.width; + paint_box->y2 = frame_box->y1 + overlay->position.y2 * box_height / frame->resolution.height; +} + /**/ static gboolean @@ -161,8 +190,8 @@ clutter_gst_aspectratio_paint_content (ClutterContent *content, { ClutterGstAspectratio *self = CLUTTER_GST_ASPECTRATIO (content); ClutterGstAspectratioPrivate *priv = self->priv; - ClutterGstFrame *frame = - clutter_gst_content_get_frame (CLUTTER_GST_CONTENT (content)); + ClutterGstContent *gst_content = CLUTTER_GST_CONTENT (content); + ClutterGstFrame *frame = clutter_gst_content_get_frame (gst_content); ClutterGstBox input_box, paint_box; ClutterActorBox content_box; ClutterPaintNode *node; @@ -228,21 +257,66 @@ clutter_gst_aspectratio_paint_content (ClutterContent *content, clutter_paint_node_unref (node); } - cogl_pipeline_set_color4ub (frame->pipeline, - paint_opacity, paint_opacity, - paint_opacity, paint_opacity); + if (clutter_gst_content_get_paint_frame (gst_content)) + { + cogl_pipeline_set_color4ub (frame->pipeline, + paint_opacity, paint_opacity, + paint_opacity, paint_opacity); + + node = clutter_pipeline_node_new (frame->pipeline); + clutter_paint_node_set_name (node, "AspectRatioVideoFrame"); - node = clutter_pipeline_node_new (frame->pipeline); - clutter_paint_node_set_name (node, "AspectRatioVideoFrame"); + clutter_paint_node_add_texture_rectangle_custom (node, + paint_box.x1, paint_box.y1, + paint_box.x2, paint_box.y2, + input_box.x1, input_box.y1, + input_box.x2, input_box.y2); - clutter_paint_node_add_texture_rectangle_custom (node, - paint_box.x1, paint_box.y1, - paint_box.x2, paint_box.y2, - input_box.x1, input_box.y1, - input_box.x2, input_box.y2); + clutter_paint_node_add_child (root, node); + clutter_paint_node_unref (node); + } + + if (clutter_gst_content_get_paint_overlays (gst_content)) + { + ClutterGstOverlays *overlays = clutter_gst_content_get_overlays (gst_content); - clutter_paint_node_add_child (root, node); - clutter_paint_node_unref (node); + if (overlays) + { + guint i; + + for (i = 0; i < overlays->overlays->len; i++) + { + ClutterGstOverlay *overlay = + g_ptr_array_index (overlays->overlays, i); + ClutterGstBox overlay_box; + ClutterGstBox overlay_input_box; + + clutter_gst_aspectratio_get_overlay_box (self, + &overlay_input_box, + &overlay_box, + &input_box, + &paint_box, + frame, + overlay); + + cogl_pipeline_set_color4ub (overlay->pipeline, + paint_opacity, paint_opacity, + paint_opacity, paint_opacity); + + node = clutter_pipeline_node_new (overlay->pipeline); + clutter_paint_node_set_name (node, "AspectRatioVideoOverlay"); + + clutter_paint_node_add_texture_rectangle_custom (node, + overlay_box.x1, overlay_box.y1, + overlay_box.x2, overlay_box.y2, + overlay_input_box.x1, overlay_input_box.y1, + overlay_input_box.x2, overlay_input_box.y2); + + clutter_paint_node_add_child (root, node); + clutter_paint_node_unref (node); + } + } + } } static void diff --git a/clutter-gst/clutter-gst-content.c b/clutter-gst/clutter-gst-content.c index e23a31c..83405a0 100644 --- a/clutter-gst/clutter-gst-content.c +++ b/clutter-gst/clutter-gst-content.c @@ -57,7 +57,12 @@ struct _ClutterGstContentPrivate { ClutterGstVideoSink *sink; ClutterGstPlayer *player; + ClutterGstFrame *current_frame; + ClutterGstOverlays *overlays; + + gboolean paint_frame; + gboolean paint_overlays; }; enum @@ -66,6 +71,8 @@ enum PROP_VIDEO_SINK, PROP_PLAYER, + PROP_PAINT_FRAME, + PROP_PAINT_OVERLAYS, PROP_LAST }; @@ -82,6 +89,56 @@ enum static guint signals[LAST_SIGNAL]; +/**/ + +gboolean +clutter_gst_content_get_paint_frame (ClutterGstContent *self) +{ + return self->priv->paint_frame; +} + +static void +clutter_gst_content_set_paint_frame (ClutterGstContent *self, gboolean value) +{ + if (self->priv->paint_frame == value) + return; + + self->priv->paint_frame = value; + clutter_content_invalidate (CLUTTER_CONTENT (self)); +} + +gboolean +clutter_gst_content_get_paint_overlays (ClutterGstContent *self) +{ + return self->priv->paint_overlays; +} + +static void +clutter_gst_content_set_paint_overlays (ClutterGstContent *self, gboolean value) +{ + if (self->priv->paint_overlays == value) + return; + + self->priv->paint_overlays = value; + clutter_content_invalidate (CLUTTER_CONTENT (self)); +} + +static gboolean +clutter_gst_content_has_painting_content (ClutterGstContent *self) +{ + ClutterGstContentPrivate *priv = self->priv; + + if (priv->paint_frame && priv->current_frame) + return TRUE; + + if (priv->paint_overlays && priv->overlays && priv->overlays->overlays->len > 0) + return TRUE; + + return FALSE; +} + +/**/ + static void update_frame (ClutterGstContent *self, ClutterGstFrame *new_frame) @@ -112,12 +169,38 @@ update_frame (ClutterGstContent *self, } static void +update_overlays (ClutterGstContent *self, + ClutterGstOverlays *new_overlays) +{ + ClutterGstContentPrivate *priv = self->priv; + + if (priv->overlays != NULL) + { + g_boxed_free (CLUTTER_GST_TYPE_OVERLAYS, priv->overlays); + priv->overlays = NULL; + } + if (new_overlays != NULL) + priv->overlays = g_boxed_copy (CLUTTER_GST_TYPE_OVERLAYS, new_overlays); +} + +static void _new_frame_from_pipeline (ClutterGstVideoSink *sink, ClutterGstContent *self) { update_frame (self, clutter_gst_video_sink_get_frame (sink)); - clutter_content_invalidate (CLUTTER_CONTENT (self)); + if (CLUTTER_GST_CONTENT_GET_CLASS (self)->has_painting_content (self)) + clutter_content_invalidate (CLUTTER_CONTENT (self)); +} + +static void +_new_overlays_from_pipeline (ClutterGstVideoSink *sink, + ClutterGstContent *self) +{ + update_overlays (self, clutter_gst_video_sink_get_overlays (sink)); + + if (CLUTTER_GST_CONTENT_GET_CLASS (self)->has_painting_content (self)) + clutter_content_invalidate (CLUTTER_CONTENT (self)); } static void @@ -183,14 +266,15 @@ content_set_sink (ClutterGstContent *self, priv->sink = g_object_ref_sink (sink); g_signal_connect (priv->sink, "new-frame", G_CALLBACK (_new_frame_from_pipeline), self); + g_signal_connect (priv->sink, "new-overlays", + G_CALLBACK (_new_overlays_from_pipeline), self); g_signal_connect (priv->sink, "notify::pixel-aspect-ratio", G_CALLBACK (_pixel_aspect_ratio_changed), self); if (clutter_gst_video_sink_is_ready (priv->sink)) { - ClutterGstFrame *frame = clutter_gst_video_sink_get_frame (priv->sink); - if (frame) - update_frame (self, frame); + update_frame (self, clutter_gst_video_sink_get_frame (priv->sink)); + update_overlays (self, clutter_gst_video_sink_get_overlays (priv->sink)); } } @@ -220,46 +304,82 @@ clutter_gst_content_paint_content (ClutterContent *content, ClutterActor *actor, ClutterPaintNode *root) { - ClutterGstContentPrivate *priv = CLUTTER_GST_CONTENT (content)->priv; + ClutterGstContent *self = CLUTTER_GST_CONTENT (content); + ClutterGstContentPrivate *priv = self->priv; ClutterActorBox box; ClutterPaintNode *node; ClutterContentRepeat repeat; guint8 paint_opacity; - if (!priv->current_frame) + if (!CLUTTER_GST_CONTENT_GET_CLASS (self)->has_painting_content (self)) return; clutter_actor_get_content_box (actor, &box); paint_opacity = clutter_actor_get_paint_opacity (actor); repeat = clutter_actor_get_content_repeat (actor); + if (priv->paint_frame && priv->current_frame) + { + cogl_pipeline_set_color4ub (priv->current_frame->pipeline, + paint_opacity, paint_opacity, + paint_opacity, paint_opacity); - cogl_pipeline_set_color4ub (priv->current_frame->pipeline, - paint_opacity, paint_opacity, - paint_opacity, paint_opacity); + node = clutter_pipeline_node_new (priv->current_frame->pipeline); + clutter_paint_node_set_name (node, "Video"); - node = clutter_pipeline_node_new (priv->current_frame->pipeline); - clutter_paint_node_set_name (node, "Video"); + if (repeat == CLUTTER_REPEAT_NONE) + clutter_paint_node_add_rectangle (node, &box); + else + { + float t_w = 1.f, t_h = 1.f; - if (repeat == CLUTTER_REPEAT_NONE) - clutter_paint_node_add_rectangle (node, &box); - else - { - float t_w = 1.f, t_h = 1.f; + if ((repeat & CLUTTER_REPEAT_X_AXIS) != FALSE) + t_w = (box.x2 - box.x1) / priv->current_frame->resolution.width; - if ((repeat & CLUTTER_REPEAT_X_AXIS) != FALSE) - t_w = (box.x2 - box.x1) / priv->current_frame->resolution.width; + if ((repeat & CLUTTER_REPEAT_Y_AXIS) != FALSE) + t_h = (box.y2 - box.y1) / priv->current_frame->resolution.height; - if ((repeat & CLUTTER_REPEAT_Y_AXIS) != FALSE) - t_h = (box.y2 - box.y1) / priv->current_frame->resolution.height; + clutter_paint_node_add_texture_rectangle (node, &box, + 0.f, 0.f, + t_w, t_h); + } - clutter_paint_node_add_texture_rectangle (node, &box, - 0.f, 0.f, - t_w, t_h); + clutter_paint_node_add_child (root, node); + clutter_paint_node_unref (node); } - clutter_paint_node_add_child (root, node); - clutter_paint_node_unref (node); + if (priv->paint_overlays && priv->overlays) + { + guint i; + + for (i = 0; i < priv->overlays->overlays->len; i++) + { + ClutterGstOverlay *overlay = + g_ptr_array_index (priv->overlays->overlays, i); + gfloat box_width = clutter_actor_box_get_width (&box), + box_height = clutter_actor_box_get_height (&box); + ClutterActorBox obox = { + overlay->position.x1 * box_width / priv->current_frame->resolution.width, + overlay->position.y1 * box_height / priv->current_frame->resolution.height, + overlay->position.x2 * box_width / priv->current_frame->resolution.width, + overlay->position.y2 * box_height / priv->current_frame->resolution.height + }; + + cogl_pipeline_set_color4ub (overlay->pipeline, + paint_opacity, paint_opacity, + paint_opacity, paint_opacity); + + node = clutter_pipeline_node_new (overlay->pipeline); + clutter_paint_node_set_name (node, "AspectRatioVideoOverlay"); + + clutter_paint_node_add_texture_rectangle (node, &obox, + 0, 0, + 1, 1); + + clutter_paint_node_add_child (root, node); + clutter_paint_node_unref (node); + } + } } static void @@ -288,6 +408,14 @@ clutter_gst_content_set_property (GObject *object, CLUTTER_GST_PLAYER (g_value_get_object (value))); break; + case PROP_PAINT_FRAME: + clutter_gst_content_set_paint_frame (self, g_value_get_boolean (value)); + break; + + case PROP_PAINT_OVERLAYS: + clutter_gst_content_set_paint_overlays (self, g_value_get_boolean (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -312,6 +440,14 @@ clutter_gst_content_get_property (GObject *object, g_value_set_object (value, priv->player); break; + case PROP_PAINT_FRAME: + g_value_set_boolean (value, priv->paint_frame); + break; + + case PROP_PAINT_OVERLAYS: + g_value_set_boolean (value, priv->paint_overlays); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -350,6 +486,8 @@ clutter_gst_content_class_init (ClutterGstContentClass *klass) gobject_class->dispose = clutter_gst_content_dispose; gobject_class->finalize = clutter_gst_content_finalize; + klass->has_painting_content = clutter_gst_content_has_painting_content; + g_type_class_add_private (klass, sizeof (ClutterGstContentPrivate)); props[PROP_PLAYER] = @@ -365,6 +503,20 @@ clutter_gst_content_class_init (ClutterGstContentClass *klass) "Cogl Video Sink", CLUTTER_GST_TYPE_VIDEO_SINK, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + + props[PROP_PAINT_FRAME] = + g_param_spec_boolean ("paint-frame", + "Paint Video Overlays", + "Paint Video Overlays", + TRUE, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); + + props[PROP_PAINT_OVERLAYS] = + g_param_spec_boolean ("paint-overlays", + "Paint Video Overlays", + "Paint Video Overlays", + TRUE, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_LAST, props); @@ -398,6 +550,9 @@ clutter_gst_content_init (ClutterGstContent *self) content_set_sink (self, CLUTTER_GST_VIDEO_SINK (clutter_gst_create_video_sink ()), FALSE); + + priv->paint_frame = TRUE; + priv->paint_overlays = TRUE; } @@ -445,6 +600,22 @@ clutter_gst_content_get_frame (ClutterGstContent *self) } /** + * clutter_gst_content_get_overlays: + * @self: A #ClutterGstContent + * + * Returns: (transfer none) (element-type ClutterGst.Overlay): The #ClutterGstFrame currently attached to @self. + * + * Since: 3.0 + */ +ClutterGstOverlays * +clutter_gst_content_get_overlays (ClutterGstContent *self) +{ + g_return_val_if_fail (CLUTTER_GST_IS_CONTENT (self), NULL); + + return self->priv->overlays; +} + +/** * clutter_gst_content_get_sink: * @self: A #ClutterGstContent * diff --git a/clutter-gst/clutter-gst-content.h b/clutter-gst/clutter-gst-content.h index 124b2a8..dfd0633 100644 --- a/clutter-gst/clutter-gst-content.h +++ b/clutter-gst/clutter-gst-content.h @@ -74,6 +74,8 @@ struct _ClutterGstContentClass /*< private >*/ GObjectClass parent_class; + gboolean (* has_painting_content) (ClutterGstContent *self); + void *_padding_dummy[10]; }; @@ -85,6 +87,8 @@ ClutterContent * clutter_gst_content_new_with_sink (ClutterGstVideoSink ClutterGstFrame * clutter_gst_content_get_frame (ClutterGstContent *self); +ClutterGstOverlays * clutter_gst_content_get_overlays (ClutterGstContent *self); + void clutter_gst_content_set_sink (ClutterGstContent *self, ClutterGstVideoSink *sink); diff --git a/clutter-gst/clutter-gst-private.h b/clutter-gst/clutter-gst-private.h index 25dcf5c..c1bbeac 100644 --- a/clutter-gst/clutter-gst-private.h +++ b/clutter-gst/clutter-gst-private.h @@ -70,6 +70,10 @@ ClutterGstFrame *clutter_gst_frame_new (void); ClutterGstFrame *clutter_gst_create_blank_frame (const ClutterColor *color); +ClutterGstOverlay *clutter_gst_overlay_new (void); + +ClutterGstOverlays *clutter_gst_overlays_new (void); + void clutter_gst_player_update_frame (ClutterGstPlayer *player, ClutterGstFrame **frame, ClutterGstFrame *new_frame); @@ -80,6 +84,10 @@ void clutter_gst_frame_update_pixel_aspect_ratio (ClutterGstFrame *frame, void clutter_gst_video_resolution_from_video_info (ClutterGstVideoResolution *resolution, GstVideoInfo *info); + +gboolean clutter_gst_content_get_paint_frame (ClutterGstContent *content); +gboolean clutter_gst_content_get_paint_overlays (ClutterGstContent *content); + G_END_DECLS #endif /* __CLUTTER_GST_PRIVATE_H__ */ diff --git a/clutter-gst/clutter-gst-types.c b/clutter-gst/clutter-gst-types.c index bdfd827..a4ec6f4 100644 --- a/clutter-gst/clutter-gst-types.c +++ b/clutter-gst/clutter-gst-types.c @@ -70,6 +70,97 @@ G_DEFINE_BOXED_TYPE (ClutterGstFrame, clutter_gst_frame_copy, clutter_gst_frame_free); + +ClutterGstOverlay * +clutter_gst_overlay_new (void) +{ + return g_slice_new0 (ClutterGstOverlay); +} + +static gpointer +clutter_gst_overlay_copy (gpointer data) +{ + if (G_LIKELY (data)) + { + ClutterGstOverlay *overlay = g_slice_dup (ClutterGstOverlay, data); + + if (overlay->pipeline != COGL_INVALID_HANDLE) + overlay->pipeline = cogl_object_ref (overlay->pipeline); + + return overlay; + } + + return NULL; +} + +static void +clutter_gst_overlay_free (gpointer data) +{ + if (G_LIKELY (data)) + { + ClutterGstOverlay *overlay = (ClutterGstOverlay *) data; + + if (overlay->pipeline != COGL_INVALID_HANDLE) + { + cogl_object_unref (overlay->pipeline); + overlay->pipeline = COGL_INVALID_HANDLE; + } + g_slice_free (ClutterGstOverlay, overlay); + } +} + +G_DEFINE_BOXED_TYPE (ClutterGstOverlay, + clutter_gst_overlay, + clutter_gst_overlay_copy, + clutter_gst_overlay_free); + +ClutterGstOverlays * +clutter_gst_overlays_new (void) +{ + ClutterGstOverlays *overlays = g_slice_new0 (ClutterGstOverlays); + + overlays->overlays = g_ptr_array_new_with_free_func (clutter_gst_overlay_free); + + return overlays; +} + +static gpointer +clutter_gst_overlays_copy (gpointer data) +{ + if (G_LIKELY (data)) + { + ClutterGstOverlays *overlays = clutter_gst_overlays_new (); + GPtrArray *src_overlays = ((ClutterGstOverlays *) data)->overlays; + guint i; + + for (i = 0; i < src_overlays->len; i++) + g_ptr_array_add (overlays->overlays, + clutter_gst_overlay_copy (g_ptr_array_index (src_overlays, + i))); + return overlays; + } + + return NULL; +} + +static void +clutter_gst_overlays_free (gpointer data) +{ + if (G_LIKELY (data)) + { + ClutterGstOverlays *overlays = (ClutterGstOverlays *) data; + + g_ptr_array_unref (overlays->overlays); + + g_slice_free (ClutterGstOverlays, overlays); + } +} + +G_DEFINE_BOXED_TYPE (ClutterGstOverlays, + clutter_gst_overlays, + clutter_gst_overlays_copy, + clutter_gst_overlays_free); + static ClutterGstBox * clutter_gst_box_copy (const ClutterGstBox *box) { diff --git a/clutter-gst/clutter-gst-types.h b/clutter-gst/clutter-gst-types.h index 7124d90..f958161 100644 --- a/clutter-gst/clutter-gst-types.h +++ b/clutter-gst/clutter-gst-types.h @@ -36,9 +36,13 @@ #define CLUTTER_GST_TYPE_FRAME (clutter_gst_frame_get_type ()) #define CLUTTER_GST_TYPE_BOX (clutter_gst_box_get_type ()) +#define CLUTTER_GST_TYPE_OVERLAY (clutter_gst_overlay_get_type ()) +#define CLUTTER_GST_TYPE_OVERLAYS (clutter_gst_overlays_get_type ()) typedef struct _ClutterGstBox ClutterGstBox; typedef struct _ClutterGstFrame ClutterGstFrame; +typedef struct _ClutterGstOverlay ClutterGstOverlay; +typedef struct _ClutterGstOverlays ClutterGstOverlays; typedef struct _ClutterGstVideoResolution ClutterGstVideoResolution; /** @@ -72,6 +76,25 @@ typedef enum _ClutterGstBufferingMode } ClutterGstBufferingMode; /** + * ClutterGstBox: + * @x1: X coordinate of the top left corner + * @y1: Y coordinate of the top left corner + * @x2: X coordinate of the bottom right corner + * @y2: Y coordinate of the bottom right corner + * + * Bounding box of an area in a video texture or actor's allocation. + * Coordinates are usually expressed in the [0, 1] interval. + */ +struct _ClutterGstBox +{ + gfloat x1; + gfloat y1; + + gfloat x2; + gfloat y2; +}; + +/** * ClutterGstVideoResolution: * @width: the width, in pixels * @height: the height, in pixels @@ -90,7 +113,7 @@ struct _ClutterGstVideoResolution /** * ClutterGstFrame: * @resolution: a #ClutterGstVideoResolution - * @pipeline: a #CoglHandle to the pipeline to paint a frame + * @pipeline: a #CoglPipeline to paint a frame * * Represents a frame outputted by the #ClutterGstVideoSink. */ @@ -100,29 +123,27 @@ struct _ClutterGstFrame CoglPipeline *pipeline; }; - /** - * ClutterGstBox: - * @x1: X coordinate of the top left corner - * @y1: Y coordinate of the top left corner - * @x2: X coordinate of the bottom right corner - * @y2: Y coordinate of the bottom right corner + * ClutterGstOverlay: + * @position: + * @pipeline: a #CoglPipeline to paint an overlay * - * Bounding box of an area in a video texture or actor's allocation. - * Coordinates are usually expressed in the [0, 1] interval. */ -struct _ClutterGstBox +struct _ClutterGstOverlay { - gfloat x1; - gfloat y1; - - gfloat x2; - gfloat y2; + ClutterGstBox position; + CoglPipeline *pipeline; }; +struct _ClutterGstOverlays +{ + GPtrArray *overlays; +}; -GType clutter_gst_frame_get_type (void) G_GNUC_CONST; -GType clutter_gst_box_get_type (void) G_GNUC_CONST; +GType clutter_gst_frame_get_type (void) G_GNUC_CONST; +GType clutter_gst_box_get_type (void) G_GNUC_CONST; +GType clutter_gst_overlay_get_type (void) G_GNUC_CONST; +GType clutter_gst_overlays_get_type (void) G_GNUC_CONST; gfloat clutter_gst_box_get_width (const ClutterGstBox *box); gfloat clutter_gst_box_get_height (const ClutterGstBox *box); diff --git a/clutter-gst/clutter-gst-util.c b/clutter-gst/clutter-gst-util.c index fb25abb..c045142 100644 --- a/clutter-gst/clutter-gst-util.c +++ b/clutter-gst/clutter-gst-util.c @@ -271,11 +271,12 @@ clutter_gst_create_blank_frame (const ClutterColor *color) const guint8 *color_ptr = color != NULL ? (const guint8 *) color : (const guint8 *) &black_color; - texture = cogl_texture_new_from_data (1, 1, COGL_TEXTURE_NONE, - COGL_PIXEL_FORMAT_RGBA_8888, - COGL_PIXEL_FORMAT_RGBA_8888, - 1, - color_ptr); + texture = cogl_texture_2d_new_from_data (clutter_gst_get_cogl_context (), + 1, 1, + COGL_PIXEL_FORMAT_RGBA_8888, + 1, + color_ptr, + NULL); frame->pipeline = cogl_pipeline_new (clutter_gst_get_cogl_context ()); cogl_pipeline_set_layer_texture (frame->pipeline, 0, texture); diff --git a/clutter-gst/clutter-gst-video-sink.c b/clutter-gst/clutter-gst-video-sink.c index 1959c4e..539319d 100644 --- a/clutter-gst/clutter-gst-video-sink.c +++ b/clutter-gst/clutter-gst-video-sink.c @@ -130,6 +130,8 @@ enum PIPELINE_READY, NEW_FRAME, + NEW_OVERLAYS, + LAST_SIGNAL }; @@ -231,8 +233,111 @@ struct _ClutterGstVideoSinkPrivate guint8 *tabley; guint8 *tableu; guint8 *tablev; + + /**/ + GstVideoOverlayComposition *last_composition; + ClutterGstOverlays *overlays; }; +/* Overlays */ + +static void +clutter_gst_video_sink_upload_overlay (ClutterGstVideoSink *sink, GstBuffer *buffer) +{ + ClutterGstVideoSinkPrivate *priv = sink->priv; + + GstVideoOverlayComposition *composition = NULL; + GstVideoOverlayCompositionMeta *composition_meta; + guint i, nb_rectangle; + + composition_meta = gst_buffer_get_video_overlay_composition_meta (buffer); + if (composition_meta) + composition = composition_meta->overlay; + + if (composition == NULL) + { + if (priv->last_composition != NULL) + { + gst_video_overlay_composition_unref (priv->last_composition); + priv->last_composition = NULL; + + if (priv->overlays) + g_boxed_free (CLUTTER_GST_TYPE_OVERLAYS, priv->overlays); + priv->overlays = clutter_gst_overlays_new (); + + g_signal_emit (sink, video_sink_signals[NEW_OVERLAYS], 0); + } + return; + } + + g_clear_pointer (&priv->last_composition, gst_video_overlay_composition_unref); + priv->last_composition = gst_video_overlay_composition_ref (composition); + if (priv->overlays) + g_boxed_free (CLUTTER_GST_TYPE_OVERLAYS, priv->overlays); + priv->overlays = clutter_gst_overlays_new (); + + nb_rectangle = gst_video_overlay_composition_n_rectangles (composition); + for (i = 0; i < nb_rectangle; i++) + { + GstVideoOverlayRectangle *rectangle; + GstBuffer *comp_buffer; + GstMapInfo info; + GstVideoMeta *vmeta; + gpointer data; + gint comp_x, comp_y, stride; + guint comp_width, comp_height; + CoglTexture *tex; + CoglError *error; + + rectangle = gst_video_overlay_composition_get_rectangle (composition, i); + comp_buffer = + gst_video_overlay_rectangle_get_pixels_unscaled_argb (rectangle, + GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA); + + gst_video_overlay_rectangle_get_render_rectangle (rectangle, + &comp_x, &comp_y, &comp_width, &comp_height); + + vmeta = gst_buffer_get_video_meta (comp_buffer); + gst_video_meta_map (vmeta, 0, &info, &data, &stride, GST_MAP_READ); + + tex = + cogl_texture_2d_new_from_data (priv->ctx, + comp_width, + comp_height, + COGL_PIXEL_FORMAT_BGRA_8888, + stride, data, + &error); + + gst_video_meta_unmap (vmeta, 0, &info); + + if (tex != NULL) + { + ClutterGstOverlay *overlay = clutter_gst_overlay_new (); + + overlay->position.x1 = comp_x; + overlay->position.y1 = comp_y; + overlay->position.x2 = comp_x + comp_width; + overlay->position.y2 = comp_y + comp_height; + + overlay->pipeline = cogl_pipeline_new (priv->ctx); + cogl_pipeline_set_layer_texture (overlay->pipeline, 0, tex); + + cogl_object_unref (tex); + + g_ptr_array_add (priv->overlays->overlays, overlay); + } + else + { + GST_WARNING_OBJECT (sink, + "Cannot upload overlay texture : %s", + error->message); + cogl_error_free (error); + } + } + + g_signal_emit (sink, video_sink_signals[NEW_OVERLAYS], 0); +} + /* Snippet cache */ static SnippetCacheEntry * @@ -1822,6 +1927,8 @@ clutter_gst_source_dispatch (GSource *source, if (buffer) { + clutter_gst_video_sink_upload_overlay (gst_source->sink, buffer); + if (gst_buffer_get_video_gl_texture_upload_meta (buffer) != NULL) { if (!priv->renderer->upload_gl (gst_source->sink, buffer)) { goto fail_upload; @@ -1919,6 +2026,7 @@ clutter_gst_video_sink_init (ClutterGstVideoSink *sink) priv->ctx = clutter_gst_get_cogl_context (); priv->renderers = clutter_gst_build_renderers_list (priv->ctx); priv->caps = clutter_gst_build_caps (priv->renderers); + priv->overlays = clutter_gst_overlays_new (); } static GstFlowReturn @@ -2092,6 +2200,8 @@ clutter_gst_video_sink_propose_allocation (GstBaseSink *base_sink, GstQuery *que GST_VIDEO_META_API_TYPE, NULL); gst_query_add_allocation_meta (query, GST_VIDEO_GL_TEXTURE_UPLOAD_META_API_TYPE, NULL); + gst_query_add_allocation_meta (query, + GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL); return TRUE; } @@ -2194,6 +2304,29 @@ clutter_gst_video_sink_class_init (ClutterGstVideoSinkClass *klass) g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 /* n_params */); + + /** + * ClutterGstVideoSink::new-overlays: + * @sink: the #ClutterGstVideoSink + * + * The sink will emit this signal whenever there are new textures + * available for set of overlays on the video. After this signal is + * emitted, an application can call + * clutter_gst_video_sink_get_overlays() to get a set of pipelines + * suitable for rendering overlays on a video frame. + * + * Since: 3.0 + */ + video_sink_signals[NEW_OVERLAYS] = + g_signal_new ("new-overlays", + CLUTTER_GST_TYPE_VIDEO_SINK, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ClutterGstVideoSinkClass, new_overlays), + NULL, /* accumulator */ + NULL, /* accu_data */ + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0 /* n_params */); } /** @@ -2395,3 +2528,12 @@ clutter_gst_video_sink_setup_pipeline (ClutterGstVideoSink *sink, priv->renderer->setup_pipeline (sink, priv->pipeline); } } + + +ClutterGstOverlays * +clutter_gst_video_sink_get_overlays (ClutterGstVideoSink *sink) +{ + g_return_val_if_fail (CLUTTER_GST_IS_VIDEO_SINK (sink), NULL); + + return sink->priv->overlays; +} diff --git a/clutter-gst/clutter-gst-video-sink.h b/clutter-gst/clutter-gst-video-sink.h index 505a6cd..692d3ad 100644 --- a/clutter-gst/clutter-gst-video-sink.h +++ b/clutter-gst/clutter-gst-video-sink.h @@ -97,6 +97,7 @@ struct _ClutterGstVideoSinkClass /*< public >*/ void (* new_frame) (ClutterGstVideoSink *sink); void (* pipeline_ready) (ClutterGstVideoSink *sink); + void (* new_overlays) (ClutterGstVideoSink *sink); /*< private >*/ void *_padding_dummy[8]; @@ -116,6 +117,8 @@ CoglPipeline * clutter_gst_video_sink_get_pipeline (ClutterGstVideoSink void clutter_gst_video_sink_setup_pipeline (ClutterGstVideoSink *sink, CoglPipeline *pipeline); +ClutterGstOverlays * clutter_gst_video_sink_get_overlays (ClutterGstVideoSink *sink); + G_END_DECLS #endif |