diff --git a/ext/gl/gstglbasemixer.c b/ext/gl/gstglbasemixer.c index 87cea2c4b2..0284c8dc2b 100644 --- a/ext/gl/gstglbasemixer.c +++ b/ext/gl/gstglbasemixer.c @@ -28,10 +28,6 @@ #include "gstglbasemixer.h" -#define gst_gl_base_mixer_parent_class parent_class -G_DEFINE_ABSTRACT_TYPE (GstGLBaseMixer, gst_gl_base_mixer, - GST_TYPE_VIDEO_AGGREGATOR); - #define GST_CAT_DEFAULT gst_gl_base_mixer_debug GST_DEBUG_CATEGORY (gst_gl_base_mixer_debug); @@ -45,14 +41,6 @@ static void gst_gl_base_mixer_set_context (GstElement * element, static GstStateChangeReturn gst_gl_base_mixer_change_state (GstElement * element, GstStateChange transition); -enum -{ - PROP_PAD_0 -}; - -#define GST_GL_BASE_MIXER_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_GL_BASE_MIXER, GstGLBaseMixerPrivate)) - struct _GstGLBaseMixerPrivate { gboolean negotiated; @@ -60,6 +48,10 @@ struct _GstGLBaseMixerPrivate GstGLContext *other_context; }; +#define gst_gl_base_mixer_parent_class parent_class +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstGLBaseMixer, gst_gl_base_mixer, + GST_TYPE_VIDEO_AGGREGATOR); + G_DEFINE_TYPE (GstGLBaseMixerPad, gst_gl_base_mixer_pad, GST_TYPE_VIDEO_AGGREGATOR_PAD); @@ -73,7 +65,6 @@ gst_gl_base_mixer_pad_class_init (GstGLBaseMixerPadClass * klass) gobject_class->set_property = gst_gl_base_mixer_pad_set_property; gobject_class->get_property = gst_gl_base_mixer_pad_get_property; - vaggpad_class->set_info = NULL; vaggpad_class->prepare_frame = NULL; vaggpad_class->clean_frame = NULL; } @@ -261,8 +252,6 @@ gst_gl_base_mixer_class_init (GstGLBaseMixerClass * klass) gobject_class = (GObjectClass *) klass; element_class = GST_ELEMENT_CLASS (klass); - g_type_class_add_private (klass, sizeof (GstGLBaseMixerPrivate)); - gobject_class->get_property = gst_gl_base_mixer_get_property; gobject_class->set_property = gst_gl_base_mixer_set_property; @@ -293,7 +282,7 @@ gst_gl_base_mixer_class_init (GstGLBaseMixerClass * klass) static void gst_gl_base_mixer_init (GstGLBaseMixer * mix) { - mix->priv = GST_GL_BASE_MIXER_GET_PRIVATE (mix); + mix->priv = gst_gl_base_mixer_get_instance_private (mix); } static void diff --git a/ext/gl/gstglmixer.c b/ext/gl/gstglmixer.c index 2ea099ee03..682622d6d9 100644 --- a/ext/gl/gstglmixer.c +++ b/ext/gl/gstglmixer.c @@ -26,10 +26,9 @@ #include #include -#include "gstglmixer.h" +#include -#define gst_gl_mixer_parent_class parent_class -G_DEFINE_ABSTRACT_TYPE (GstGLMixer, gst_gl_mixer, GST_TYPE_GL_BASE_MIXER); +#include "gstglmixer.h" #define GST_CAT_DEFAULT gst_gl_mixer_debug GST_DEBUG_CATEGORY (gst_gl_mixer_debug); @@ -38,15 +37,17 @@ static void gst_gl_mixer_pad_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_gl_mixer_pad_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); +static gboolean gst_gl_mixer_pad_prepare_frame (GstVideoAggregatorPad * vpad, + GstVideoAggregator * vagg, GstBuffer * buffer, + GstVideoFrame * prepared_frame); +static void gst_gl_mixer_pad_clean_frame (GstVideoAggregatorPad * vpad, + GstVideoAggregator * vagg, GstVideoFrame * prepared_frame); enum { PROP_PAD_0 }; -#define GST_GL_MIXER_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_GL_MIXER, GstGLMixerPrivate)) - struct _GstGLMixerPrivate { gboolean negotiated; @@ -56,6 +57,10 @@ struct _GstGLMixerPrivate GCond gl_resource_cond; }; +#define gst_gl_mixer_parent_class parent_class +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GstGLMixer, gst_gl_mixer, + GST_TYPE_GL_BASE_MIXER); + G_DEFINE_TYPE (GstGLMixerPad, gst_gl_mixer_pad, GST_TYPE_GL_BASE_MIXER_PAD); static void @@ -68,9 +73,8 @@ gst_gl_mixer_pad_class_init (GstGLMixerPadClass * klass) gobject_class->set_property = gst_gl_mixer_pad_set_property; gobject_class->get_property = gst_gl_mixer_pad_get_property; - vaggpad_class->set_info = NULL; - vaggpad_class->prepare_frame = NULL; - vaggpad_class->clean_frame = NULL; + vaggpad_class->prepare_frame = gst_gl_mixer_pad_prepare_frame; + vaggpad_class->clean_frame = gst_gl_mixer_pad_clean_frame; } static void @@ -95,6 +99,50 @@ gst_gl_mixer_pad_set_property (GObject * object, guint prop_id, } } +static gboolean +gst_gl_mixer_pad_prepare_frame (GstVideoAggregatorPad * vpad, + GstVideoAggregator * vagg, GstBuffer * buffer, + GstVideoFrame * prepared_frame) +{ + GstGLMixerPad *pad = GST_GL_MIXER_PAD (vpad); + GstGLMixer *mix = GST_GL_MIXER (vagg); + GstVideoInfo gl_info; + GstGLSyncMeta *sync_meta; + + pad->current_texture = 0; + + gst_video_info_set_format (&gl_info, + GST_VIDEO_FORMAT_RGBA, + GST_VIDEO_INFO_WIDTH (&vpad->info), GST_VIDEO_INFO_HEIGHT (&vpad->info)); + + sync_meta = gst_buffer_get_gl_sync_meta (buffer); + if (sync_meta) + gst_gl_sync_meta_wait (sync_meta, GST_GL_BASE_MIXER (mix)->context); + + if (!gst_video_frame_map (prepared_frame, &gl_info, buffer, + GST_MAP_READ | GST_MAP_GL)) { + GST_ERROR_OBJECT (pad, "Failed to map input frame"); + return FALSE; + } + + pad->current_texture = *(guint *) prepared_frame->data[0]; + + return TRUE; +} + +static void +gst_gl_mixer_pad_clean_frame (GstVideoAggregatorPad * vpad, + GstVideoAggregator * vagg, GstVideoFrame * prepared_frame) +{ + GstGLMixerPad *pad = GST_GL_MIXER_PAD (vpad); + + pad->current_texture = 0; + if (prepared_frame->buffer) { + gst_video_frame_unmap (prepared_frame); + memset (prepared_frame, 0, sizeof (GstVideoFrame)); + } +} + static gboolean _negotiated_caps (GstAggregator * agg, GstCaps * caps) { @@ -357,8 +405,6 @@ gst_gl_mixer_class_init (GstGLMixerClass * klass) GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "glmixer", 0, "OpenGL mixer"); - g_type_class_add_private (klass, sizeof (GstGLMixerPrivate)); - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_gl_mixer_finalize); gobject_class->get_property = gst_gl_mixer_get_property; @@ -396,7 +442,7 @@ gst_gl_mixer_reset (GstGLMixer * mix) static void gst_gl_mixer_init (GstGLMixer * mix) { - mix->priv = GST_GL_MIXER_GET_PRIVATE (mix); + mix->priv = gst_gl_mixer_get_instance_private (mix); mix->priv->gl_resource_ready = FALSE; g_mutex_init (&mix->priv->gl_resource_lock); @@ -561,42 +607,6 @@ context_error: } } -static gboolean -gst_gl_mixer_upload_frames (GstElement * element, GstPad * sink_pad, - gpointer user_data) -{ - GstVideoAggregatorPad *vaggpad = GST_VIDEO_AGGREGATOR_PAD (sink_pad); - GstGLMixerPad *pad = GST_GL_MIXER_PAD (sink_pad); - GstGLMixer *mix = GST_GL_MIXER (element); - - pad->current_texture = 0; - if (vaggpad->buffer != NULL) { - GstVideoInfo gl_info; - GstVideoFrame gl_frame; - GstGLSyncMeta *sync_meta; - - gst_video_info_set_format (&gl_info, - GST_VIDEO_FORMAT_RGBA, - GST_VIDEO_INFO_WIDTH (&vaggpad->info), - GST_VIDEO_INFO_HEIGHT (&vaggpad->info)); - - sync_meta = gst_buffer_get_gl_sync_meta (vaggpad->buffer); - if (sync_meta) - gst_gl_sync_meta_wait (sync_meta, GST_GL_BASE_MIXER (mix)->context); - - if (!gst_video_frame_map (&gl_frame, &gl_info, vaggpad->buffer, - GST_MAP_READ | GST_MAP_GL)) { - GST_ERROR_OBJECT (pad, "Failed to map input frame"); - return FALSE; - } - - pad->current_texture = *(guint *) gl_frame.data[0]; - gst_video_frame_unmap (&gl_frame); - } - - return TRUE; -} - gboolean gst_gl_mixer_process_textures (GstGLMixer * mix, GstBuffer * outbuf) { @@ -616,12 +626,6 @@ gst_gl_mixer_process_textures (GstGLMixer * mix, GstBuffer * outbuf) out_tex = (GstGLMemory *) out_frame.map[0].memory; - if (!gst_element_foreach_sink_pad (GST_ELEMENT_CAST (mix), - gst_gl_mixer_upload_frames, NULL)) { - res = FALSE; - goto out; - } - g_mutex_lock (&priv->gl_resource_lock); if (!priv->gl_resource_ready) g_cond_wait (&priv->gl_resource_cond, &priv->gl_resource_lock); diff --git a/ext/gl/gstglmixerbin.c b/ext/gl/gstglmixerbin.c index b30e6a8721..47a390d2a0 100644 --- a/ext/gl/gstglmixerbin.c +++ b/ext/gl/gstglmixerbin.c @@ -68,6 +68,7 @@ struct input_chain GstGhostPad *ghost_pad; GstElement *upload; GstElement *in_convert; + GstElement *in_overlay; GstPad *mixer_pad; }; @@ -91,6 +92,12 @@ _free_input_chain (struct input_chain *chain) chain->in_convert = NULL; } + if (chain->in_overlay) { + gst_element_set_state (chain->in_overlay, GST_STATE_NULL); + gst_bin_remove (GST_BIN (chain->self), chain->in_overlay); + chain->in_overlay = NULL; + } + if (chain->mixer_pad) { gst_element_release_request_pad (chain->self->mixer, chain->mixer_pad); gst_object_unref (chain->mixer_pad); @@ -126,9 +133,8 @@ enum static void gst_gl_mixer_bin_child_proxy_init (gpointer g_iface, gpointer iface_data); -#define GST_GL_MIXER_BIN_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE((o), GST_TYPE_GL_MIXER_BIN, GstGLMixerBinPrivate)) G_DEFINE_TYPE_WITH_CODE (GstGLMixerBin, gst_gl_mixer_bin, GST_TYPE_BIN, + G_ADD_PRIVATE (GstGLMixerBin) G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, gst_gl_mixer_bin_child_proxy_init)); @@ -145,7 +151,6 @@ static void gst_gl_mixer_bin_set_property (GObject * object, guint prop_id, static void gst_gl_mixer_bin_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_gl_mixer_bin_dispose (GObject * object); -static void gst_gl_mixer_bin_finalize (GObject * object); static GstPad *gst_gl_mixer_bin_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps); @@ -160,8 +165,6 @@ gst_gl_mixer_bin_class_init (GstGLMixerBinClass * klass) GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstCaps *upload_caps; - g_type_class_add_private (klass, sizeof (GstGLMixerBinPrivate)); - GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "glmixerbin", 0, "opengl mixer bin"); @@ -172,7 +175,6 @@ gst_gl_mixer_bin_class_init (GstGLMixerBinClass * klass) gobject_class->get_property = gst_gl_mixer_bin_get_property; gobject_class->set_property = gst_gl_mixer_bin_set_property; gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_gl_mixer_bin_dispose); - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_gl_mixer_bin_finalize); g_object_class_install_property (gobject_class, PROP_MIXER, g_param_spec_object ("mixer", @@ -183,11 +185,10 @@ gst_gl_mixer_bin_class_init (GstGLMixerBinClass * klass) G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_LATENCY, - g_param_spec_int64 ("latency", "Buffer latency", + g_param_spec_uint64 ("latency", "Buffer latency", "Additional latency in live mode to allow upstream " "to take longer to produce buffers for the current " - "position", 0, - (G_MAXLONG == G_MAXINT64) ? G_MAXINT64 : (G_MAXLONG * GST_SECOND - 1), + "position (in nanoseconds)", 0, G_MAXUINT64, DEFAULT_LATENCY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_START_TIME_SELECTION, @@ -235,7 +236,7 @@ gst_gl_mixer_bin_init (GstGLMixerBin * self) gboolean res = TRUE; GstPad *pad; - self->priv = GST_GL_MIXER_BIN_GET_PRIVATE (self); + self->priv = gst_gl_mixer_bin_get_instance_private (self); self->out_convert = gst_element_factory_make ("glcolorconvert", NULL); self->download = gst_element_factory_make ("gldownload", NULL); @@ -259,17 +260,6 @@ gst_gl_mixer_bin_init (GstGLMixerBin * self) GST_ERROR_OBJECT (self, "failed to create output chain"); } -static void -gst_gl_mixer_bin_finalize (GObject * object) -{ - GstGLMixerBin *self = GST_GL_MIXER_BIN (object); - - if (self->mixer) - gst_object_unref (self->mixer); - - G_OBJECT_CLASS (gst_gl_mixer_bin_parent_class)->finalize (object); -} - static void gst_gl_mixer_bin_dispose (GObject * object) { @@ -308,16 +298,21 @@ _create_input_chain (GstGLMixerBin * self, struct input_chain *chain, chain->upload = gst_element_factory_make ("glupload", NULL); chain->in_convert = gst_element_factory_make ("glcolorconvert", NULL); + chain->in_overlay = gst_element_factory_make ("gloverlaycompositor", NULL); res &= gst_bin_add (GST_BIN (self), chain->in_convert); + res &= gst_bin_add (GST_BIN (self), chain->in_overlay); res &= gst_bin_add (GST_BIN (self), chain->upload); - pad = gst_element_get_static_pad (chain->in_convert, "src"); + pad = gst_element_get_static_pad (chain->in_overlay, "src"); if (gst_pad_link (pad, mixer_pad) != GST_PAD_LINK_OK) { gst_object_unref (pad); return FALSE; } gst_object_unref (pad); + res &= + gst_element_link_pads (chain->in_convert, "src", chain->in_overlay, + "sink"); res &= gst_element_link_pads (chain->upload, "src", chain->in_convert, "sink"); @@ -349,6 +344,7 @@ _create_input_chain (GstGLMixerBin * self, struct input_chain *chain, gst_element_sync_state_with_parent (chain->upload); gst_element_sync_state_with_parent (chain->in_convert); + gst_element_sync_state_with_parent (chain->in_overlay); return TRUE; } @@ -398,39 +394,18 @@ _connect_mixer_element (GstGLMixerBin * self) return res; } -/* - * @mixer: (transfer floating): - */ -static gboolean -gst_gl_mixer_bin_set_mixer (GstGLMixerBin * self, GstElement * mixer) -{ - g_return_val_if_fail (GST_IS_ELEMENT (mixer), FALSE); - - if (self->mixer) { - gst_element_set_locked_state (self->mixer, TRUE); - gst_bin_remove (GST_BIN (self), self->mixer); - gst_element_set_state (self->mixer, GST_STATE_NULL); - gst_object_unref (self->mixer); - self->mixer = NULL; - } - self->mixer = mixer; - - gst_object_ref_sink (mixer); - - if (mixer && !_connect_mixer_element (self)) { - gst_object_unref (self->mixer); - self->mixer = NULL; - return FALSE; - } - - return TRUE; -} - void gst_gl_mixer_bin_finish_init_with_element (GstGLMixerBin * self, GstElement * element) { - gst_gl_mixer_bin_set_mixer (self, element); + g_return_if_fail (GST_IS_ELEMENT (element)); + + self->mixer = element; + + if (!_connect_mixer_element (self)) { + gst_object_unref (self->mixer); + self->mixer = NULL; + } } void @@ -475,7 +450,11 @@ gst_gl_mixer_bin_set_property (GObject * object, GstElement *mixer = g_value_get_object (value); /* FIXME: deal with replacing a mixer */ g_return_if_fail (!self->mixer || (self->mixer == mixer)); - gst_gl_mixer_bin_set_mixer (self, mixer); + self->mixer = mixer; + if (mixer) { + gst_object_ref_sink (mixer); + _connect_mixer_element (self); + } break; } default: @@ -560,12 +539,9 @@ gst_gl_mixer_bin_change_state (GstElement * element, GstStateChange transition) if (klass->create_element) self->mixer = klass->create_element (); - if (!self->mixer) { + if (!self->mixer) g_signal_emit (element, gst_gl_mixer_bin_signals[SIGNAL_CREATE_ELEMENT], 0, &self->mixer); - if (self->mixer && g_object_is_floating (self->mixer)) - gst_object_ref_sink (self->mixer); - } if (!self->mixer) { GST_ERROR_OBJECT (element, "Failed to retrieve element"); diff --git a/ext/gl/gstglmosaic.c b/ext/gl/gstglmosaic.c index 77aec599cd..63075ba5a9 100644 --- a/ext/gl/gstglmosaic.c +++ b/ext/gl/gstglmosaic.c @@ -55,10 +55,15 @@ enum PROP_0, }; +static void gst_gl_mosaic_child_proxy_init (gpointer g_iface, + gpointer iface_data); + #define DEBUG_INIT \ GST_DEBUG_CATEGORY_INIT (gst_gl_mosaic_debug, "glmosaic", 0, "glmosaic element"); G_DEFINE_TYPE_WITH_CODE (GstGLMosaic, gst_gl_mosaic, GST_TYPE_GL_MIXER, + G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, + gst_gl_mosaic_child_proxy_init); DEBUG_INIT); static void gst_gl_mosaic_set_property (GObject * object, guint prop_id, @@ -66,6 +71,10 @@ static void gst_gl_mosaic_set_property (GObject * object, guint prop_id, static void gst_gl_mosaic_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +static GstPad *gst_gl_mosaic_request_new_pad (GstElement * element, + GstPadTemplate * temp, const gchar * req_name, const GstCaps * caps); +static void gst_gl_mosaic_release_pad (GstElement * element, GstPad * pad); + static void gst_gl_mosaic_reset (GstGLMixer * mixer); static gboolean gst_gl_mosaic_init_shader (GstGLMixer * mixer, GstCaps * outcaps); @@ -128,6 +137,10 @@ gst_gl_mosaic_class_init (GstGLMosaicClass * klass) gobject_class->set_property = gst_gl_mosaic_set_property; gobject_class->get_property = gst_gl_mosaic_get_property; + element_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_gl_mosaic_request_new_pad); + element_class->release_pad = GST_DEBUG_FUNCPTR (gst_gl_mosaic_release_pad); + gst_element_class_set_metadata (element_class, "OpenGL mosaic", "Filter/Effect/Video", "OpenGL mosaic", "Julien Isorce "); @@ -171,6 +184,44 @@ gst_gl_mosaic_get_property (GObject * object, guint prop_id, } } +static GstPad * +gst_gl_mosaic_request_new_pad (GstElement * element, GstPadTemplate * templ, + const gchar * req_name, const GstCaps * caps) +{ + GstPad *newpad; + + newpad = (GstPad *) + GST_ELEMENT_CLASS (gst_gl_mosaic_parent_class)->request_new_pad (element, + templ, req_name, caps); + + if (newpad == NULL) + goto could_not_create; + + gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad), + GST_OBJECT_NAME (newpad)); + + return newpad; + +could_not_create: + { + GST_DEBUG_OBJECT (element, "could not create/add pad"); + return NULL; + } +} + +static void +gst_gl_mosaic_release_pad (GstElement * element, GstPad * pad) +{ + GstGLMosaic *gl_mosaic = GST_GL_MOSAIC (element); + + GST_DEBUG_OBJECT (gl_mosaic, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + gst_child_proxy_child_removed (GST_CHILD_PROXY (gl_mosaic), G_OBJECT (pad), + GST_OBJECT_NAME (pad)); + + GST_ELEMENT_CLASS (gst_gl_mosaic_parent_class)->release_pad (element, pad); +} + static void gst_gl_mosaic_reset (GstGLMixer * mixer) { @@ -356,3 +407,43 @@ gst_gl_mosaic_callback (gpointer stuff) return TRUE; } + +/* GstChildProxy implementation */ +static GObject * +gst_gl_mosaic_child_proxy_get_child_by_index (GstChildProxy * child_proxy, + guint index) +{ + GstGLMosaic *gl_mosaic = GST_GL_MOSAIC (child_proxy); + GObject *obj = NULL; + + GST_OBJECT_LOCK (gl_mosaic); + obj = g_list_nth_data (GST_ELEMENT_CAST (gl_mosaic)->sinkpads, index); + if (obj) + gst_object_ref (obj); + GST_OBJECT_UNLOCK (gl_mosaic); + + return obj; +} + +static guint +gst_gl_mosaic_child_proxy_get_children_count (GstChildProxy * child_proxy) +{ + guint count = 0; + GstGLMosaic *gl_mosaic = GST_GL_MOSAIC (child_proxy); + + GST_OBJECT_LOCK (gl_mosaic); + count = GST_ELEMENT_CAST (gl_mosaic)->numsinkpads; + GST_OBJECT_UNLOCK (gl_mosaic); + GST_INFO_OBJECT (gl_mosaic, "Children Count: %d", count); + + return count; +} + +static void +gst_gl_mosaic_child_proxy_init (gpointer g_iface, gpointer iface_data) +{ + GstChildProxyInterface *iface = g_iface; + + iface->get_child_by_index = gst_gl_mosaic_child_proxy_get_child_by_index; + iface->get_children_count = gst_gl_mosaic_child_proxy_get_children_count; +} diff --git a/ext/gl/gstglstereomix.c b/ext/gl/gstglstereomix.c index 69760c8cf1..464fad1354 100644 --- a/ext/gl/gstglstereomix.c +++ b/ext/gl/gstglstereomix.c @@ -78,12 +78,17 @@ gst_gl_stereo_mix_pad_init (GstGLStereoMixPad * pad) { } +static void gst_gl_stereo_mix_child_proxy_init (gpointer g_iface, + gpointer iface_data); + #define gst_gl_stereo_mix_parent_class parent_class -G_DEFINE_TYPE (GstGLStereoMix, gst_gl_stereo_mix, GST_TYPE_GL_MIXER); +G_DEFINE_TYPE_WITH_CODE (GstGLStereoMix, gst_gl_stereo_mix, GST_TYPE_GL_MIXER, + G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, + gst_gl_stereo_mix_child_proxy_init)); static GstCaps *_update_caps (GstVideoAggregator * vagg, GstCaps * caps); static gboolean _negotiated_caps (GstAggregator * aggregator, GstCaps * caps); -gboolean gst_gl_stereo_mix_make_output (GstGLStereoMix * mix); +static gboolean gst_gl_stereo_mix_make_output (GstGLStereoMix * mix); static gboolean gst_gl_stereo_mix_process_frames (GstGLStereoMix * mixer); #define DEFAULT_DOWNMIX GST_GL_STEREO_DOWNMIX_ANAGLYPH_GREEN_MAGENTA_DUBOIS @@ -133,8 +138,12 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u", "; " GST_VIDEO_CAPS_MAKE (GST_GL_COLOR_CONVERT_FORMATS)) ); -static GstFlowReturn gst_gl_stereo_mix_get_output_buffer (GstVideoAggregator * - videoaggregator, GstBuffer ** outbuf); +static GstPad *gst_gl_stereo_mix_request_new_pad (GstElement * element, + GstPadTemplate * temp, const gchar * req_name, const GstCaps * caps); +static void gst_gl_stereo_mix_release_pad (GstElement * element, GstPad * pad); + +static GstFlowReturn gst_gl_stereo_mix_create_output_buffer (GstVideoAggregator + * videoaggregator, GstBuffer ** outbuf); static gboolean gst_gl_stereo_mix_stop (GstAggregator * agg); static gboolean gst_gl_stereo_mix_start (GstAggregator * agg); static gboolean gst_gl_stereo_mix_src_query (GstAggregator * agg, @@ -184,6 +193,11 @@ gst_gl_stereo_mix_class_init (GstGLStereoMixClass * klass) gst_element_class_add_static_pad_template_with_gtype (element_class, &sink_factory, GST_TYPE_GL_STEREO_MIX_PAD); + element_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_gl_stereo_mix_request_new_pad); + element_class->release_pad = + GST_DEBUG_FUNCPTR (gst_gl_stereo_mix_release_pad); + agg_class->stop = gst_gl_stereo_mix_stop; agg_class->start = gst_gl_stereo_mix_start; agg_class->src_query = gst_gl_stereo_mix_src_query; @@ -191,8 +205,8 @@ gst_gl_stereo_mix_class_init (GstGLStereoMixClass * klass) videoaggregator_class->aggregate_frames = gst_gl_stereo_mix_aggregate_frames; videoaggregator_class->update_caps = _update_caps; - videoaggregator_class->get_output_buffer = - gst_gl_stereo_mix_get_output_buffer; + videoaggregator_class->create_output_buffer = + gst_gl_stereo_mix_create_output_buffer; base_mix_class->supported_gl_api = GST_GL_API_GLES2 | GST_GL_API_OPENGL | GST_GL_API_OPENGL3; @@ -249,7 +263,7 @@ gst_gl_stereo_mix_src_query (GstAggregator * agg, GstQuery * query) static GstFlowReturn -gst_gl_stereo_mix_get_output_buffer (GstVideoAggregator * videoaggregator, +gst_gl_stereo_mix_create_output_buffer (GstVideoAggregator * videoaggregator, GstBuffer ** outbuf) { GstGLStereoMix *mix = GST_GL_STEREO_MIX (videoaggregator); @@ -287,7 +301,7 @@ gst_gl_stereo_mix_get_output_buffer (GstVideoAggregator * videoaggregator, return ret; } -gboolean +static gboolean gst_gl_stereo_mix_make_output (GstGLStereoMix * mix) { GList *walk; @@ -302,11 +316,12 @@ gst_gl_stereo_mix_make_output (GstGLStereoMix * mix) while (walk) { GstVideoAggregatorPad *vaggpad = walk->data; GstGLStereoMixPad *pad = walk->data; + GstBuffer *buffer = gst_video_aggregator_pad_get_current_buffer (vaggpad); GST_LOG_OBJECT (mix, "Checking pad %" GST_PTR_FORMAT, vaggpad); - if (vaggpad->buffer != NULL) { - pad->current_buffer = vaggpad->buffer; + if (buffer != NULL) { + pad->current_buffer = buffer; GST_DEBUG_OBJECT (pad, "Got buffer %" GST_PTR_FORMAT, pad->current_buffer); @@ -399,6 +414,42 @@ gst_gl_stereo_mix_set_property (GObject * object, } } +static GstPad * +gst_gl_stereo_mix_request_new_pad (GstElement * element, GstPadTemplate * templ, + const gchar * req_name, const GstCaps * caps) +{ + GstPad *newpad; + + newpad = (GstPad *) + GST_ELEMENT_CLASS (parent_class)->request_new_pad (element, + templ, req_name, caps); + + if (newpad == NULL) + goto could_not_create; + + gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad), + GST_OBJECT_NAME (newpad)); + + return GST_PAD_CAST (newpad); + +could_not_create: + { + GST_DEBUG_OBJECT (element, "could not create/add pad"); + return NULL; + } +} + +static void +gst_gl_stereo_mix_release_pad (GstElement * element, GstPad * pad) +{ + GST_DEBUG_OBJECT (element, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + gst_child_proxy_child_removed (GST_CHILD_PROXY (element), G_OBJECT (pad), + GST_OBJECT_NAME (pad)); + + GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad); +} + static gboolean gst_gl_stereo_mix_start (GstAggregator * agg) { @@ -696,3 +747,43 @@ gst_gl_stereo_mix_process_frames (GstGLStereoMix * mixer) return TRUE; } + +/* GstChildProxy implementation */ +static GObject * +gst_gl_stereo_mix_child_proxy_get_child_by_index (GstChildProxy * child_proxy, + guint index) +{ + GstGLStereoMix *gl_stereo_mix = GST_GL_STEREO_MIX (child_proxy); + GObject *obj = NULL; + + GST_OBJECT_LOCK (gl_stereo_mix); + obj = g_list_nth_data (GST_ELEMENT_CAST (gl_stereo_mix)->sinkpads, index); + if (obj) + gst_object_ref (obj); + GST_OBJECT_UNLOCK (gl_stereo_mix); + + return obj; +} + +static guint +gst_gl_stereo_mix_child_proxy_get_children_count (GstChildProxy * child_proxy) +{ + guint count = 0; + GstGLStereoMix *gl_stereo_mix = GST_GL_STEREO_MIX (child_proxy); + + GST_OBJECT_LOCK (gl_stereo_mix); + count = GST_ELEMENT_CAST (gl_stereo_mix)->numsinkpads; + GST_OBJECT_UNLOCK (gl_stereo_mix); + GST_INFO_OBJECT (gl_stereo_mix, "Children Count: %d", count); + + return count; +} + +static void +gst_gl_stereo_mix_child_proxy_init (gpointer g_iface, gpointer iface_data) +{ + GstChildProxyInterface *iface = g_iface; + + iface->get_child_by_index = gst_gl_stereo_mix_child_proxy_get_child_by_index; + iface->get_children_count = gst_gl_stereo_mix_child_proxy_get_children_count; +} diff --git a/ext/gl/gstglvideomixer.c b/ext/gl/gstglvideomixer.c index 487bf2fe16..0306d22e53 100644 --- a/ext/gl/gstglvideomixer.c +++ b/ext/gl/gstglvideomixer.c @@ -43,6 +43,7 @@ #include "config.h" #endif +#include #include #include #include @@ -136,7 +137,7 @@ gst_gl_video_mixer_blend_function_get_type (void) "One Minus Constant Color", "one-minus-contant-color"}, {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_CONSTANT_ALPHA, "Constant Alpha", "constant-alpha"}, - {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_CONSTANT_COLOR, + {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_CONSTANT_ALPHA, "One Minus Constant Alpha", "one-minus-contant-alpha"}, {GST_GL_VIDEO_MIXER_BLEND_FUNCTION_SRC_ALPHA_SATURATE, "Source Alpha Saturate", "src-alpha-saturate"}, @@ -157,11 +158,11 @@ gst_gl_video_mixer_blend_function_get_type (void) #define DEFAULT_PAD_HEIGHT 0 #define DEFAULT_PAD_ALPHA 1.0 #define DEFAULT_PAD_ZORDER 0 -#define DEFAULT_PAD_IGNORE_EOS FALSE +#define DEFAULT_PAD_REPEAT_AFTER_EOS FALSE #define DEFAULT_PAD_BLEND_EQUATION_RGB GST_GL_VIDEO_MIXER_BLEND_EQUATION_ADD #define DEFAULT_PAD_BLEND_EQUATION_ALPHA GST_GL_VIDEO_MIXER_BLEND_EQUATION_ADD #define DEFAULT_PAD_BLEND_FUNCTION_SRC_RGB GST_GL_VIDEO_MIXER_BLEND_FUNCTION_SRC_ALPHA -#define DEFAULT_PAD_BLEND_FUNCTION_SRC_ALPHA GST_GL_VIDEO_MIXER_BLEND_FUNCTION_SRC_ALPHA +#define DEFAULT_PAD_BLEND_FUNCTION_SRC_ALPHA GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE #define DEFAULT_PAD_BLEND_FUNCTION_DST_RGB GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_SRC_ALPHA #define DEFAULT_PAD_BLEND_FUNCTION_DST_ALPHA GST_GL_VIDEO_MIXER_BLEND_FUNCTION_ONE_MINUS_SRC_ALPHA @@ -184,7 +185,7 @@ enum PROP_INPUT_BLEND_FUNCTION_CONSTANT_COLOR_BLUE, PROP_INPUT_BLEND_FUNCTION_CONSTANT_COLOR_ALPHA, PROP_INPUT_ZORDER, - PROP_INPUT_IGNORE_EOS, + PROP_INPUT_REPEAT_AFTER_EOS, }; static void gst_gl_video_mixer_input_get_property (GObject * object, @@ -225,10 +226,11 @@ gst_gl_video_mixer_input_class_init (GstGLVideoMixerInputClass * klass) g_param_spec_uint ("zorder", "Z-Order", "Z Order of the picture", 0, 10000, DEFAULT_PAD_ZORDER, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_INPUT_IGNORE_EOS, - g_param_spec_boolean ("ignore-eos", "Ignore EOS", "Aggregate the last " + g_object_class_install_property (gobject_class, PROP_INPUT_REPEAT_AFTER_EOS, + g_param_spec_boolean ("repeat-after-eos", "Repeat After EOS", + "Aggregate the last " "frame on pads that are EOS till they are released", - DEFAULT_PAD_IGNORE_EOS, + DEFAULT_PAD_REPEAT_AFTER_EOS, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_INPUT_XPOS, g_param_spec_int ("xpos", "X Position", "X Position of the picture", @@ -239,12 +241,12 @@ gst_gl_video_mixer_input_class_init (GstGLVideoMixerInputClass * klass) G_MININT, G_MAXINT, DEFAULT_PAD_YPOS, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_INPUT_WIDTH, - g_param_spec_int ("width", "Width", "Width of the picture", - G_MININT, G_MAXINT, DEFAULT_PAD_WIDTH, + g_param_spec_int ("width", "Width", "Width of the picture", G_MININT, + G_MAXINT, DEFAULT_PAD_WIDTH, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_INPUT_HEIGHT, - g_param_spec_int ("height", "Height", "Height of the picture", - G_MININT, G_MAXINT, DEFAULT_PAD_HEIGHT, + g_param_spec_int ("height", "Height", "Height of the picture", G_MININT, + G_MAXINT, DEFAULT_PAD_HEIGHT, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_INPUT_ALPHA, g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0, @@ -252,8 +254,7 @@ gst_gl_video_mixer_input_class_init (GstGLVideoMixerInputClass * klass) G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, PROP_INPUT_BLEND_EQUATION_RGB, g_param_spec_enum ("blend-equation-rgb", "Blend Equation RGB", - "Blend Equation for RGB", - GST_TYPE_GL_VIDEO_MIXER_BLEND_EQUATION, + "Blend Equation for RGB", GST_TYPE_GL_VIDEO_MIXER_BLEND_EQUATION, DEFAULT_PAD_BLEND_EQUATION_RGB, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, @@ -288,7 +289,7 @@ gst_gl_video_mixer_input_class_init (GstGLVideoMixerInputClass * klass) PROP_INPUT_BLEND_FUNCTION_DST_ALPHA, g_param_spec_enum ("blend-function-dst-alpha", "Blend Function Destination Alpha", - "Blend Function for Destiniation Alpha", + "Blend Function for Destination Alpha", GST_TYPE_GL_VIDEO_MIXER_BLEND_FUNCTION, DEFAULT_PAD_BLEND_FUNCTION_DST_ALPHA, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); @@ -451,12 +452,16 @@ enum PROP_BACKGROUND, }; +static void gst_gl_video_mixer_child_proxy_init (gpointer g_iface, + gpointer iface_data); + #define DEBUG_INIT \ GST_DEBUG_CATEGORY_INIT (gst_gl_video_mixer_debug, "glvideomixer", 0, "glvideomixer element"); #define gst_gl_video_mixer_parent_class parent_class G_DEFINE_TYPE_WITH_CODE (GstGLVideoMixer, gst_gl_video_mixer, GST_TYPE_GL_MIXER, - DEBUG_INIT); + G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, + gst_gl_video_mixer_child_proxy_init); DEBUG_INIT); static void gst_gl_video_mixer_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); @@ -565,6 +570,7 @@ struct _GstGLVideoMixerPad gboolean geometry_change; GLuint vertex_buffer; + gfloat m_matrix[16]; }; struct _GstGLVideoMixerPadClass @@ -611,6 +617,11 @@ gst_gl_video_mixer_pad_init (GstGLVideoMixerPad * pad) pad->blend_function_src_alpha = DEFAULT_PAD_BLEND_FUNCTION_SRC_ALPHA; pad->blend_function_dst_rgb = DEFAULT_PAD_BLEND_FUNCTION_DST_RGB; pad->blend_function_dst_alpha = DEFAULT_PAD_BLEND_FUNCTION_DST_ALPHA; + memset (pad->m_matrix, 0, sizeof (gfloat) * 4 * 4); + pad->m_matrix[0] = 1.0; + pad->m_matrix[5] = 1.0; + pad->m_matrix[10] = 1.0; + pad->m_matrix[15] = 1.0; } static void @@ -679,7 +690,7 @@ gst_gl_video_mixer_pad_class_init (GstGLVideoMixerPadClass * klass) PROP_INPUT_BLEND_FUNCTION_DST_ALPHA, g_param_spec_enum ("blend-function-dst-alpha", "Blend Function Destination Alpha", - "Blend Function for Destiniation Alpha", + "Blend Function for Destination Alpha", GST_TYPE_GL_VIDEO_MIXER_BLEND_FUNCTION, DEFAULT_PAD_BLEND_FUNCTION_DST_ALPHA, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); @@ -837,18 +848,52 @@ _del_buffer (GstGLContext * context, GLuint * pBuffer) context->gl_vtable->DeleteBuffers (1, pBuffer); } +static GstPad * +gst_gl_video_mixer_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps) +{ + GstPad *newpad; + + newpad = (GstPad *) + GST_ELEMENT_CLASS (parent_class)->request_new_pad (element, + templ, req_name, caps); + + if (newpad == NULL) + goto could_not_create; + + gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad), + GST_OBJECT_NAME (newpad)); + + return newpad; + +could_not_create: + { + GST_DEBUG_OBJECT (element, "could not create/add pad"); + return NULL; + } +} + static void gst_gl_video_mixer_release_pad (GstElement * element, GstPad * p) { GstGLVideoMixerPad *pad = GST_GL_VIDEO_MIXER_PAD (p); + + gst_child_proxy_child_removed (GST_CHILD_PROXY (element), G_OBJECT (pad), + GST_OBJECT_NAME (pad)); + + /* we call the base class first as this will remove the pad from + * the aggregator, thus stopping misc callbacks from being called, + * one of which (process_textures) will recreate the vertex_buffer + * if it is destroyed */ + GST_ELEMENT_CLASS (g_type_class_peek_parent (G_OBJECT_GET_CLASS (element))) + ->release_pad (element, p); + if (pad->vertex_buffer) { GstGLBaseMixer *mix = GST_GL_BASE_MIXER (element); gst_gl_context_thread_add (mix->context, (GstGLContextThreadFunc) _del_buffer, &pad->vertex_buffer); pad->vertex_buffer = 0; } - GST_ELEMENT_CLASS (g_type_class_peek_parent (G_OBJECT_GET_CLASS (element))) - ->release_pad (element, p); } static void @@ -861,6 +906,7 @@ gst_gl_video_mixer_class_init (GstGLVideoMixerClass * klass) gobject_class = (GObjectClass *) klass; element_class = GST_ELEMENT_CLASS (klass); + element_class->request_new_pad = gst_gl_video_mixer_request_new_pad; element_class->release_pad = gst_gl_video_mixer_release_pad; gobject_class->set_property = gst_gl_video_mixer_set_property; @@ -1225,10 +1271,10 @@ _draw_checker_background (GstGLVideoMixer * video_mixer) /* *INDENT-OFF* */ gfloat v_vertices[] = { - -1.0,-1.0,-1.0f, - 1.0,-1.0,-1.0f, - 1.0, 1.0,-1.0f, - -1.0, 1.0,-1.0f, + -1.0,-1.0, 0.0f, + 1.0,-1.0, 0.0f, + 1.0, 1.0, 0.0f, + -1.0, 1.0, 0.0f, }; /* *INDENT-ON* */ @@ -1466,10 +1512,10 @@ gst_gl_video_mixer_callback (gpointer stuff) /* *INDENT-OFF* */ gfloat v_vertices[] = { - -1.0,-1.0,-1.0f, 0.0f, 0.0f, - 1.0,-1.0,-1.0f, 1.0f, 0.0f, - 1.0, 1.0,-1.0f, 1.0f, 1.0f, - -1.0, 1.0,-1.0f, 0.0f, 1.0f, + -1.0,-1.0, 0.0f, 0.0f, 0.0f, + 1.0,-1.0, 0.0f, 1.0f, 0.0f, + 1.0, 1.0, 0.0f, 1.0f, 1.0f, + -1.0, 1.0, 0.0f, 0.0f, 1.0f, }; /* *INDENT-ON* */ @@ -1507,25 +1553,21 @@ gst_gl_video_mixer_callback (gpointer stuff) w = ((gfloat) pad_width / (gfloat) out_width); h = ((gfloat) pad_height / (gfloat) out_height); - /* top-left */ - v_vertices[0] = v_vertices[15] = - 2.0f * (gfloat) pad->xpos / (gfloat) out_width - 1.0f; - /* bottom-left */ - v_vertices[1] = v_vertices[6] = - 2.0f * (gfloat) pad->ypos / (gfloat) out_height - 1.0f; - /* top-right */ - v_vertices[5] = v_vertices[10] = v_vertices[0] + 2.0f * w; - /* bottom-right */ - v_vertices[11] = v_vertices[16] = v_vertices[1] + 2.0f * h; + pad->m_matrix[0] = w; + pad->m_matrix[5] = h; + pad->m_matrix[12] = + 2. * (gfloat) pad->xpos / (gfloat) out_width - (1. - w); + pad->m_matrix[13] = + 2. * (gfloat) pad->ypos / (gfloat) out_height - (1. - h); + GST_TRACE ("processing texture:%u dimensions:%ux%u, at %f,%f %fx%f with " - "alpha:%f", in_tex, in_width, in_height, v_vertices[0], v_vertices[1], - v_vertices[5], v_vertices[11], pad->alpha); + "alpha:%f", in_tex, in_width, in_height, pad->m_matrix[12], + pad->m_matrix[13], pad->m_matrix[0], pad->m_matrix[5], pad->alpha); if (!pad->vertex_buffer) gl->GenBuffers (1, &pad->vertex_buffer); gl->BindBuffer (GL_ARRAY_BUFFER, pad->vertex_buffer); - gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), v_vertices, GL_STATIC_DRAW); @@ -1543,10 +1585,13 @@ gst_gl_video_mixer_callback (gpointer stuff) { GstVideoAffineTransformationMeta *af_meta; gfloat matrix[16]; + gfloat af_matrix[16]; + GstBuffer *buffer = + gst_video_aggregator_pad_get_current_buffer (vagg_pad); - af_meta = - gst_buffer_get_video_affine_transformation_meta (vagg_pad->buffer); - gst_gl_get_affine_transformation_meta_as_ndc_ext (af_meta, matrix); + af_meta = gst_buffer_get_video_affine_transformation_meta (buffer); + gst_gl_get_affine_transformation_meta_as_ndc_ext (af_meta, af_matrix); + gst_gl_multiply_matrix4 (af_matrix, pad->m_matrix, matrix); gst_gl_shader_set_uniform_matrix_4fv (video_mixer->shader, "u_transformation", 1, FALSE, matrix); } @@ -1584,3 +1629,43 @@ gst_gl_video_mixer_callback (gpointer stuff) return TRUE; } + +/* GstChildProxy implementation */ +static GObject * +gst_gl_video_mixer_child_proxy_get_child_by_index (GstChildProxy * child_proxy, + guint index) +{ + GstGLVideoMixer *gl_video_mixer = GST_GL_VIDEO_MIXER (child_proxy); + GObject *obj = NULL; + + GST_OBJECT_LOCK (gl_video_mixer); + obj = g_list_nth_data (GST_ELEMENT_CAST (gl_video_mixer)->sinkpads, index); + if (obj) + gst_object_ref (obj); + GST_OBJECT_UNLOCK (gl_video_mixer); + + return obj; +} + +static guint +gst_gl_video_mixer_child_proxy_get_children_count (GstChildProxy * child_proxy) +{ + guint count = 0; + GstGLVideoMixer *gl_video_mixer = GST_GL_VIDEO_MIXER (child_proxy); + + GST_OBJECT_LOCK (gl_video_mixer); + count = GST_ELEMENT_CAST (gl_video_mixer)->numsinkpads; + GST_OBJECT_UNLOCK (gl_video_mixer); + GST_INFO_OBJECT (gl_video_mixer, "Children Count: %d", count); + + return count; +} + +static void +gst_gl_video_mixer_child_proxy_init (gpointer g_iface, gpointer iface_data) +{ + GstChildProxyInterface *iface = g_iface; + + iface->get_child_by_index = gst_gl_video_mixer_child_proxy_get_child_by_index; + iface->get_children_count = gst_gl_video_mixer_child_proxy_get_children_count; +} diff --git a/gst-libs/gst/video/gstvideoaggregator.c b/gst-libs/gst/video/gstvideoaggregator.c new file mode 100644 index 0000000000..2ade7da9f4 --- /dev/null +++ b/gst-libs/gst/video/gstvideoaggregator.c @@ -0,0 +1,2582 @@ +/* Generic video aggregator plugin + * Copyright (C) 2004, 2008 Wim Taymans + * Copyright (C) 2010 Sebastian Dröge + * + * 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. + */ + +/** + * SECTION:gstvideoaggregator + * @title: GstVideoAggregator + * @short_description: Base class for video aggregators + * + * VideoAggregator can accept AYUV, ARGB and BGRA video streams. For each of the requested + * sink pads it will compare the incoming geometry and framerate to define the + * output parameters. Indeed output video frames will have the geometry of the + * biggest incoming video stream and the framerate of the fastest incoming one. + * + * VideoAggregator will do colorspace conversion. + * + * Zorder for each input stream can be configured on the + * #GstVideoAggregatorPad. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstvideoaggregator.h" + +GST_DEBUG_CATEGORY_STATIC (gst_video_aggregator_debug); +#define GST_CAT_DEFAULT gst_video_aggregator_debug + +/* Needed prototypes */ +static void gst_video_aggregator_reset_qos (GstVideoAggregator * vagg); + +/**************************************** + * GstVideoAggregatorPad implementation * + ****************************************/ + +#define DEFAULT_PAD_ZORDER 0 +#define DEFAULT_PAD_REPEAT_AFTER_EOS FALSE +enum +{ + PROP_PAD_0, + PROP_PAD_ZORDER, + PROP_PAD_REPEAT_AFTER_EOS, +}; + + +struct _GstVideoAggregatorPadPrivate +{ + GstBuffer *buffer; + GstVideoFrame prepared_frame; + + /* properties */ + guint zorder; + gboolean repeat_after_eos; + + /* Subclasses can force an alpha channel in the (input thus output) + * colorspace format */ + gboolean needs_alpha; + + GstClockTime start_time; + GstClockTime end_time; + + GstVideoInfo pending_vinfo; +}; + + +G_DEFINE_TYPE_WITH_PRIVATE (GstVideoAggregatorPad, gst_video_aggregator_pad, + GST_TYPE_AGGREGATOR_PAD); + +static void +gst_video_aggregator_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (object); + + switch (prop_id) { + case PROP_PAD_ZORDER: + g_value_set_uint (value, pad->priv->zorder); + break; + case PROP_PAD_REPEAT_AFTER_EOS: + g_value_set_boolean (value, pad->priv->repeat_after_eos); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static int +pad_zorder_compare (const GstVideoAggregatorPad * pad1, + const GstVideoAggregatorPad * pad2) +{ + return pad1->priv->zorder - pad2->priv->zorder; +} + +static void +gst_video_aggregator_pad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (object); + GstVideoAggregator *vagg = + GST_VIDEO_AGGREGATOR (gst_pad_get_parent (GST_PAD (pad))); + + switch (prop_id) { + case PROP_PAD_ZORDER: + GST_OBJECT_LOCK (vagg); + pad->priv->zorder = g_value_get_uint (value); + GST_ELEMENT (vagg)->sinkpads = g_list_sort (GST_ELEMENT (vagg)->sinkpads, + (GCompareFunc) pad_zorder_compare); + GST_OBJECT_UNLOCK (vagg); + break; + case PROP_PAD_REPEAT_AFTER_EOS: + pad->priv->repeat_after_eos = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + + gst_object_unref (vagg); +} + +static GstFlowReturn +_flush_pad (GstAggregatorPad * aggpad, GstAggregator * aggregator) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (aggregator); + GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (aggpad); + + gst_video_aggregator_reset_qos (vagg); + gst_buffer_replace (&pad->priv->buffer, NULL); + pad->priv->start_time = -1; + pad->priv->end_time = -1; + + return GST_FLOW_OK; +} + +static gboolean +gst_video_aggregator_pad_skip_buffer (GstAggregatorPad * aggpad, + GstAggregator * agg, GstBuffer * buffer) +{ + gboolean ret = FALSE; + GstSegment *agg_segment = &GST_AGGREGATOR_PAD (agg->srcpad)->segment; + + if (agg_segment->position != GST_CLOCK_TIME_NONE + && GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE) { + GstClockTime start_time = + gst_segment_to_running_time (agg_segment, GST_FORMAT_TIME, + GST_BUFFER_PTS (buffer)); + GstClockTime end_time = start_time + GST_BUFFER_DURATION (buffer); + GstClockTime output_start_running_time = + gst_segment_to_running_time (agg_segment, GST_FORMAT_TIME, + agg_segment->position); + + ret = end_time < output_start_running_time; + } + + return ret; +} + +static gboolean +gst_video_aggregator_pad_prepare_frame (GstVideoAggregatorPad * pad, + GstVideoAggregator * vagg, GstBuffer * buffer, + GstVideoFrame * prepared_frame) +{ + if (!gst_video_frame_map (prepared_frame, &pad->info, buffer, GST_MAP_READ)) { + GST_WARNING_OBJECT (vagg, "Could not map input buffer"); + return FALSE; + } + + return TRUE; +} + +static void +gst_video_aggregator_pad_clean_frame (GstVideoAggregatorPad * pad, + GstVideoAggregator * vagg, GstVideoFrame * prepared_frame) +{ + if (prepared_frame->buffer) { + gst_video_frame_unmap (prepared_frame); + memset (prepared_frame, 0, sizeof (GstVideoFrame)); + } +} + +static void +gst_video_aggregator_pad_class_init (GstVideoAggregatorPadClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstAggregatorPadClass *aggpadclass = (GstAggregatorPadClass *) klass; + + gobject_class->set_property = gst_video_aggregator_pad_set_property; + gobject_class->get_property = gst_video_aggregator_pad_get_property; + + g_object_class_install_property (gobject_class, PROP_PAD_ZORDER, + g_param_spec_uint ("zorder", "Z-Order", "Z Order of the picture", + 0, G_MAXUINT, DEFAULT_PAD_ZORDER, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PAD_REPEAT_AFTER_EOS, + g_param_spec_boolean ("repeat-after-eos", "Repeat After EOS", + "Repeat the " "last frame after EOS until all pads are EOS", + DEFAULT_PAD_REPEAT_AFTER_EOS, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + + aggpadclass->flush = GST_DEBUG_FUNCPTR (_flush_pad); + aggpadclass->skip_buffer = + GST_DEBUG_FUNCPTR (gst_video_aggregator_pad_skip_buffer); + klass->prepare_frame = + GST_DEBUG_FUNCPTR (gst_video_aggregator_pad_prepare_frame); + klass->clean_frame = GST_DEBUG_FUNCPTR (gst_video_aggregator_pad_clean_frame); +} + +static void +gst_video_aggregator_pad_init (GstVideoAggregatorPad * vaggpad) +{ + vaggpad->priv = gst_video_aggregator_pad_get_instance_private (vaggpad); + + vaggpad->priv->zorder = DEFAULT_PAD_ZORDER; + vaggpad->priv->repeat_after_eos = DEFAULT_PAD_REPEAT_AFTER_EOS; + memset (&vaggpad->priv->prepared_frame, 0, sizeof (GstVideoFrame)); +} + +/** + * gst_video_aggregator_pad_has_current_buffer: + * @pad: a #GstVideoAggregatorPad + * + * Checks if the pad currently has a buffer queued that is going to be used + * for the current output frame. + * + * This must only be called from the aggregate_frames() virtual method, + * or from the prepare_frame() virtual method of the aggregator pads. + * + * Returns: %TRUE if the pad has currently a buffer queued + */ +gboolean +gst_video_aggregator_pad_has_current_buffer (GstVideoAggregatorPad * pad) +{ + g_return_val_if_fail (GST_IS_VIDEO_AGGREGATOR_PAD (pad), FALSE); + + return pad->priv->buffer != NULL; +} + +/** + * gst_video_aggregator_pad_get_current_buffer: + * @pad: a #GstVideoAggregatorPad + * + * Returns the currently queued buffer that is going to be used + * for the current output frame. + * + * This must only be called from the aggregate_frames() virtual method, + * or from the prepare_frame() virtual method of the aggregator pads. + * + * The return value is only valid until aggregate_frames() or prepare_frames() + * returns. + * + * Returns: (transfer none): The currently queued buffer + */ +GstBuffer * +gst_video_aggregator_pad_get_current_buffer (GstVideoAggregatorPad * pad) +{ + g_return_val_if_fail (GST_IS_VIDEO_AGGREGATOR_PAD (pad), NULL); + + return pad->priv->buffer; +} + +/** + * gst_video_aggregator_pad_get_prepared_frame: + * @pad: a #GstVideoAggregatorPad + * + * Returns the currently prepared video frame that has to be aggregated into + * the current output frame. + * + * This must only be called from the aggregate_frames() virtual method, + * or from the prepare_frame() virtual method of the aggregator pads. + * + * The return value is only valid until aggregate_frames() or prepare_frames() + * returns. + * + * Returns: (transfer none): The currently prepared video frame + */ +GstVideoFrame * +gst_video_aggregator_pad_get_prepared_frame (GstVideoAggregatorPad * pad) +{ + g_return_val_if_fail (GST_IS_VIDEO_AGGREGATOR_PAD (pad), NULL); + + return pad->priv->prepared_frame.buffer ? &pad->priv->prepared_frame : NULL; +} + +/** + * gst_video_aggregator_pad_set_needs_alpha: + * @pad: a #GstVideoAggregatorPad + * @needs_alpha: %TRUE if this pad requires alpha output + * + * Allows selecting that this pad requires an output format with alpha + * + */ +void +gst_video_aggregator_pad_set_needs_alpha (GstVideoAggregatorPad * pad, + gboolean needs_alpha) +{ + g_return_if_fail (GST_IS_VIDEO_AGGREGATOR_PAD (pad)); + + if (needs_alpha != pad->priv->needs_alpha) { + GstAggregator *agg = + GST_AGGREGATOR (gst_object_get_parent (GST_OBJECT (pad))); + pad->priv->needs_alpha = needs_alpha; + if (agg) { + gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (agg)); + gst_object_unref (agg); + } + } +} + +/**************************************** + * GstVideoAggregatorConvertPad implementation * + ****************************************/ + +enum +{ + PROP_CONVERT_PAD_0, + PROP_CONVERT_PAD_CONVERTER_CONFIG, +}; + +struct _GstVideoAggregatorConvertPadPrivate +{ + /* Converter, if NULL no conversion is done */ + GstVideoConverter *convert; + + /* caps used for conversion if needed */ + GstVideoInfo conversion_info; + GstBuffer *converted_buffer; + + GstStructure *converter_config; + gboolean converter_config_changed; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (GstVideoAggregatorConvertPad, + gst_video_aggregator_convert_pad, GST_TYPE_VIDEO_AGGREGATOR_PAD); + +static void +gst_video_aggregator_convert_pad_finalize (GObject * o) +{ + GstVideoAggregatorConvertPad *vaggpad = GST_VIDEO_AGGREGATOR_CONVERT_PAD (o); + + if (vaggpad->priv->convert) + gst_video_converter_free (vaggpad->priv->convert); + vaggpad->priv->convert = NULL; + + if (vaggpad->priv->converter_config) + gst_structure_free (vaggpad->priv->converter_config); + vaggpad->priv->converter_config = NULL; + + G_OBJECT_CLASS (gst_video_aggregator_pad_parent_class)->finalize (o); +} + +static void + gst_video_aggregator_convert_pad_update_conversion_info_internal + (GstVideoAggregatorPad * vpad) +{ + GstVideoAggregatorConvertPad *pad = GST_VIDEO_AGGREGATOR_CONVERT_PAD (vpad); + + pad->priv->converter_config_changed = TRUE; +} + +static gboolean +gst_video_aggregator_convert_pad_prepare_frame (GstVideoAggregatorPad * vpad, + GstVideoAggregator * vagg, GstBuffer * buffer, + GstVideoFrame * prepared_frame) +{ + GstVideoAggregatorConvertPad *pad = GST_VIDEO_AGGREGATOR_CONVERT_PAD (vpad); + GstVideoFrame frame; + + /* Update/create converter as needed */ + if (pad->priv->converter_config_changed) { + GstVideoAggregatorConvertPadClass *klass = + GST_VIDEO_AGGREGATOR_CONVERT_PAD_GET_CLASS (pad); + GstVideoInfo conversion_info; + + gst_video_info_init (&conversion_info); + klass->create_conversion_info (pad, vagg, &conversion_info); + if (conversion_info.finfo == NULL) + return FALSE; + pad->priv->converter_config_changed = FALSE; + + if (!pad->priv->conversion_info.finfo + || !gst_video_info_is_equal (&conversion_info, + &pad->priv->conversion_info)) { + pad->priv->conversion_info = conversion_info; + + if (pad->priv->convert) + gst_video_converter_free (pad->priv->convert); + pad->priv->convert = NULL; + + if (!gst_video_info_is_equal (&vpad->info, &pad->priv->conversion_info)) { + pad->priv->convert = + gst_video_converter_new (&vpad->info, &pad->priv->conversion_info, + pad->priv->converter_config ? gst_structure_copy (pad-> + priv->converter_config) : NULL); + if (!pad->priv->convert) { + GST_WARNING_OBJECT (pad, "No path found for conversion"); + return FALSE; + } + + GST_DEBUG_OBJECT (pad, "This pad will be converted from %d to %d", + GST_VIDEO_INFO_FORMAT (&vpad->info), + GST_VIDEO_INFO_FORMAT (&pad->priv->conversion_info)); + } else { + GST_DEBUG_OBJECT (pad, "This pad will not need conversion"); + } + } + } + + if (!gst_video_frame_map (&frame, &vpad->info, buffer, GST_MAP_READ)) { + GST_WARNING_OBJECT (vagg, "Could not map input buffer"); + return FALSE; + } + + if (pad->priv->convert) { + GstVideoFrame converted_frame; + GstBuffer *converted_buf = NULL; + static GstAllocationParams params = { 0, 15, 0, 0, }; + gint converted_size; + guint outsize; + + /* We wait until here to set the conversion infos, in case vagg->info changed */ + converted_size = pad->priv->conversion_info.size; + outsize = GST_VIDEO_INFO_SIZE (&vagg->info); + converted_size = converted_size > outsize ? converted_size : outsize; + converted_buf = gst_buffer_new_allocate (NULL, converted_size, ¶ms); + + if (!gst_video_frame_map (&converted_frame, &(pad->priv->conversion_info), + converted_buf, GST_MAP_READWRITE)) { + GST_WARNING_OBJECT (vagg, "Could not map converted frame"); + + gst_video_frame_unmap (&frame); + return FALSE; + } + + gst_video_converter_frame (pad->priv->convert, &frame, &converted_frame); + pad->priv->converted_buffer = converted_buf; + gst_video_frame_unmap (&frame); + *prepared_frame = converted_frame; + } else { + *prepared_frame = frame; + } + + return TRUE; +} + +static void +gst_video_aggregator_convert_pad_clean_frame (GstVideoAggregatorPad * vpad, + GstVideoAggregator * vagg, GstVideoFrame * prepared_frame) +{ + GstVideoAggregatorConvertPad *pad = GST_VIDEO_AGGREGATOR_CONVERT_PAD (vpad); + + if (prepared_frame->buffer) { + gst_video_frame_unmap (prepared_frame); + memset (prepared_frame, 0, sizeof (GstVideoFrame)); + } + + if (pad->priv->converted_buffer) { + gst_buffer_unref (pad->priv->converted_buffer); + pad->priv->converted_buffer = NULL; + } +} + +static void + gst_video_aggregator_convert_pad_create_conversion_info + (GstVideoAggregatorConvertPad * pad, GstVideoAggregator * agg, + GstVideoInfo * convert_info) +{ + GstVideoAggregatorPad *vpad = GST_VIDEO_AGGREGATOR_PAD (pad); + gchar *colorimetry, *best_colorimetry; + const gchar *chroma, *best_chroma; + + g_return_if_fail (GST_IS_VIDEO_AGGREGATOR_CONVERT_PAD (pad)); + g_return_if_fail (convert_info != NULL); + + if (!vpad->info.finfo + || GST_VIDEO_INFO_FORMAT (&vpad->info) == GST_VIDEO_FORMAT_UNKNOWN) { + return; + } + + if (!agg->info.finfo + || GST_VIDEO_INFO_FORMAT (&agg->info) == GST_VIDEO_FORMAT_UNKNOWN) { + return; + } + + colorimetry = gst_video_colorimetry_to_string (&vpad->info.colorimetry); + chroma = gst_video_chroma_to_string (vpad->info.chroma_site); + + best_colorimetry = gst_video_colorimetry_to_string (&agg->info.colorimetry); + best_chroma = gst_video_chroma_to_string (agg->info.chroma_site); + + if (GST_VIDEO_INFO_FORMAT (&agg->info) != GST_VIDEO_INFO_FORMAT (&vpad->info) + || g_strcmp0 (colorimetry, best_colorimetry) + || g_strcmp0 (chroma, best_chroma)) { + GstVideoInfo tmp_info; + + /* Initialize with the wanted video format and our original width and + * height as we don't want to rescale. Then copy over the wanted + * colorimetry, and chroma-site and our current pixel-aspect-ratio + * and other relevant fields. + */ + gst_video_info_set_format (&tmp_info, GST_VIDEO_INFO_FORMAT (&agg->info), + vpad->info.width, vpad->info.height); + tmp_info.chroma_site = agg->info.chroma_site; + tmp_info.colorimetry = agg->info.colorimetry; + tmp_info.par_n = vpad->info.par_n; + tmp_info.par_d = vpad->info.par_d; + tmp_info.fps_n = vpad->info.fps_n; + tmp_info.fps_d = vpad->info.fps_d; + tmp_info.flags = vpad->info.flags; + tmp_info.interlace_mode = vpad->info.interlace_mode; + + *convert_info = tmp_info; + } else { + *convert_info = vpad->info; + } + + g_free (colorimetry); + g_free (best_colorimetry); +} + +static void +gst_video_aggregator_convert_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstVideoAggregatorConvertPad *pad = GST_VIDEO_AGGREGATOR_CONVERT_PAD (object); + + switch (prop_id) { + case PROP_CONVERT_PAD_CONVERTER_CONFIG: + GST_OBJECT_LOCK (pad); + if (pad->priv->converter_config) + g_value_set_boxed (value, pad->priv->converter_config); + GST_OBJECT_UNLOCK (pad); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_video_aggregator_convert_pad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstVideoAggregatorConvertPad *pad = GST_VIDEO_AGGREGATOR_CONVERT_PAD (object); + + switch (prop_id) { + case PROP_CONVERT_PAD_CONVERTER_CONFIG: + GST_OBJECT_LOCK (pad); + if (pad->priv->converter_config) + gst_structure_free (pad->priv->converter_config); + pad->priv->converter_config = g_value_dup_boxed (value); + pad->priv->converter_config_changed = TRUE; + GST_OBJECT_UNLOCK (pad); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_video_aggregator_convert_pad_class_init (GstVideoAggregatorConvertPadClass * + klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstVideoAggregatorPadClass *vaggpadclass = + (GstVideoAggregatorPadClass *) klass; + + gobject_class->finalize = gst_video_aggregator_convert_pad_finalize; + gobject_class->get_property = + GST_DEBUG_FUNCPTR (gst_video_aggregator_convert_pad_get_property); + gobject_class->set_property = + GST_DEBUG_FUNCPTR (gst_video_aggregator_convert_pad_set_property); + + g_object_class_install_property (gobject_class, + PROP_CONVERT_PAD_CONVERTER_CONFIG, g_param_spec_boxed ("converter-config", + "Converter configuration", + "A GstStructure describing the configuration that should be used " + "when scaling and converting this pad's video frames", + GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + vaggpadclass->update_conversion_info = + GST_DEBUG_FUNCPTR + (gst_video_aggregator_convert_pad_update_conversion_info_internal); + vaggpadclass->prepare_frame = + GST_DEBUG_FUNCPTR (gst_video_aggregator_convert_pad_prepare_frame); + vaggpadclass->clean_frame = + GST_DEBUG_FUNCPTR (gst_video_aggregator_convert_pad_clean_frame); + + klass->create_conversion_info = + gst_video_aggregator_convert_pad_create_conversion_info; +} + +static void +gst_video_aggregator_convert_pad_init (GstVideoAggregatorConvertPad * vaggpad) +{ + vaggpad->priv = + gst_video_aggregator_convert_pad_get_instance_private (vaggpad); + + vaggpad->priv->converted_buffer = NULL; + vaggpad->priv->convert = NULL; + vaggpad->priv->converter_config = NULL; + vaggpad->priv->converter_config_changed = FALSE; +} + + +/** + * gst_video_aggregator_convert_pad_update_conversion_info: + * @pad: a #GstVideoAggregatorPad + * + * Requests the pad to check and update the converter before the next usage to + * update for any changes that have happened. + * + */ +void gst_video_aggregator_convert_pad_update_conversion_info + (GstVideoAggregatorConvertPad * pad) +{ + g_return_if_fail (GST_IS_VIDEO_AGGREGATOR_CONVERT_PAD (pad)); + + pad->priv->converter_config_changed = TRUE; +} + +/************************************** + * GstVideoAggregator implementation * + **************************************/ + +#define GST_VIDEO_AGGREGATOR_GET_LOCK(vagg) (&GST_VIDEO_AGGREGATOR(vagg)->priv->lock) + +#define GST_VIDEO_AGGREGATOR_LOCK(vagg) G_STMT_START { \ + GST_LOG_OBJECT (vagg, "Taking EVENT lock from thread %p", \ + g_thread_self()); \ + g_mutex_lock(GST_VIDEO_AGGREGATOR_GET_LOCK(vagg)); \ + GST_LOG_OBJECT (vagg, "Took EVENT lock from thread %p", \ + g_thread_self()); \ + } G_STMT_END + +#define GST_VIDEO_AGGREGATOR_UNLOCK(vagg) G_STMT_START { \ + GST_LOG_OBJECT (vagg, "Releasing EVENT lock from thread %p", \ + g_thread_self()); \ + g_mutex_unlock(GST_VIDEO_AGGREGATOR_GET_LOCK(vagg)); \ + GST_LOG_OBJECT (vagg, "Took EVENT lock from thread %p", \ + g_thread_self()); \ + } G_STMT_END + + +struct _GstVideoAggregatorPrivate +{ + /* Lock to prevent the state to change while aggregating */ + GMutex lock; + + /* Current downstream segment */ + GstClockTime ts_offset; + guint64 nframes; + + /* QoS stuff */ + gdouble proportion; + GstClockTime earliest_time; + guint64 qos_processed, qos_dropped; + + /* current caps */ + GstCaps *current_caps; + + gboolean live; +}; + +/* Can't use the G_DEFINE_TYPE macros because we need the + * videoaggregator class in the _init to be able to set + * the sink pad non-alpha caps. Using the G_DEFINE_TYPE there + * seems to be no way of getting the real class being initialized */ +static void gst_video_aggregator_init (GstVideoAggregator * self, + GstVideoAggregatorClass * klass); +static void gst_video_aggregator_class_init (GstVideoAggregatorClass * klass); +static gpointer gst_video_aggregator_parent_class = NULL; +static gint video_aggregator_private_offset = 0; + +GType +gst_video_aggregator_get_type (void) +{ + static volatile gsize g_define_type_id_volatile = 0; + + if (g_once_init_enter (&g_define_type_id_volatile)) { + GType g_define_type_id = g_type_register_static_simple (GST_TYPE_AGGREGATOR, + g_intern_static_string ("GstVideoAggregator"), + sizeof (GstVideoAggregatorClass), + (GClassInitFunc) gst_video_aggregator_class_init, + sizeof (GstVideoAggregator), + (GInstanceInitFunc) gst_video_aggregator_init, + (GTypeFlags) G_TYPE_FLAG_ABSTRACT); + + video_aggregator_private_offset = + g_type_add_instance_private (g_define_type_id, + sizeof (GstVideoAggregatorPrivate)); + + g_once_init_leave (&g_define_type_id_volatile, g_define_type_id); + } + return g_define_type_id_volatile; +} + +static inline GstVideoAggregatorPrivate * +gst_video_aggregator_get_instance_private (GstVideoAggregator * self) +{ + return (G_STRUCT_MEMBER_P (self, video_aggregator_private_offset)); +} + +static void +gst_video_aggregator_find_best_format (GstVideoAggregator * vagg, + GstCaps * downstream_caps, GstVideoInfo * best_info, + gboolean * at_least_one_alpha) +{ + GList *tmp; + GstCaps *possible_caps; + GstVideoAggregatorPad *pad; + gboolean need_alpha = FALSE; + gint best_format_number = 0; + GHashTable *formats_table = g_hash_table_new (g_direct_hash, g_direct_equal); + + GST_OBJECT_LOCK (vagg); + for (tmp = GST_ELEMENT (vagg)->sinkpads; tmp; tmp = tmp->next) { + GstStructure *s; + gint format_number; + + pad = tmp->data; + + if (!pad->info.finfo) + continue; + + if (pad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA) + *at_least_one_alpha = TRUE; + + /* If we want alpha, disregard all the other formats */ + if (need_alpha && !(pad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)) + continue; + + /* This can happen if we release a pad and another pad hasn't been negotiated_caps yet */ + if (GST_VIDEO_INFO_FORMAT (&pad->info) == GST_VIDEO_FORMAT_UNKNOWN) + continue; + + possible_caps = gst_video_info_to_caps (&pad->info); + + s = gst_caps_get_structure (possible_caps, 0); + gst_structure_remove_fields (s, "width", "height", "framerate", + "pixel-aspect-ratio", "interlace-mode", NULL); + + /* Can downstream accept this format ? */ + if (!gst_caps_can_intersect (downstream_caps, possible_caps)) { + gst_caps_unref (possible_caps); + continue; + } + + gst_caps_unref (possible_caps); + + format_number = + GPOINTER_TO_INT (g_hash_table_lookup (formats_table, + GINT_TO_POINTER (GST_VIDEO_INFO_FORMAT (&pad->info)))); + format_number += pad->info.width * pad->info.height; + + g_hash_table_replace (formats_table, + GINT_TO_POINTER (GST_VIDEO_INFO_FORMAT (&pad->info)), + GINT_TO_POINTER (format_number)); + + /* If that pad is the first with alpha, set it as the new best format */ + if (!need_alpha && (pad->priv->needs_alpha + && (!GST_VIDEO_FORMAT_INFO_HAS_ALPHA (pad->info.finfo)))) { + need_alpha = TRUE; + /* Just fallback to ARGB in case we require alpha but the input pad + * does not have alpha. + * Do not increment best_format_number in that case. */ + gst_video_info_set_format (best_info, + GST_VIDEO_FORMAT_ARGB, + GST_VIDEO_INFO_WIDTH (&pad->info), + GST_VIDEO_INFO_HEIGHT (&pad->info)); + } else if (!need_alpha + && (pad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)) { + need_alpha = TRUE; + *best_info = pad->info; + best_format_number = format_number; + } else if (format_number > best_format_number) { + *best_info = pad->info; + best_format_number = format_number; + } + } + GST_OBJECT_UNLOCK (vagg); + + g_hash_table_unref (formats_table); +} + +static GstCaps * +gst_video_aggregator_default_fixate_src_caps (GstAggregator * agg, + GstCaps * caps) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + gint best_width = -1, best_height = -1; + gint best_fps_n = -1, best_fps_d = -1; + gdouble best_fps = -1.; + GstStructure *s; + GList *l; + + GST_OBJECT_LOCK (vagg); + for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) { + GstVideoAggregatorPad *mpad = l->data; + gint fps_n, fps_d; + gint width, height; + gdouble cur_fps; + + fps_n = GST_VIDEO_INFO_FPS_N (&mpad->info); + fps_d = GST_VIDEO_INFO_FPS_D (&mpad->info); + width = GST_VIDEO_INFO_WIDTH (&mpad->info); + height = GST_VIDEO_INFO_HEIGHT (&mpad->info); + + if (width == 0 || height == 0) + continue; + + if (best_width < width) + best_width = width; + if (best_height < height) + best_height = height; + + if (fps_d == 0) + cur_fps = 0.0; + else + gst_util_fraction_to_double (fps_n, fps_d, &cur_fps); + + if (best_fps < cur_fps) { + best_fps = cur_fps; + best_fps_n = fps_n; + best_fps_d = fps_d; + } + } + GST_OBJECT_UNLOCK (vagg); + + if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) { + best_fps_n = 25; + best_fps_d = 1; + best_fps = 25.0; + } + + caps = gst_caps_make_writable (caps); + s = gst_caps_get_structure (caps, 0); + gst_structure_fixate_field_nearest_int (s, "width", best_width); + gst_structure_fixate_field_nearest_int (s, "height", best_height); + gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n, + best_fps_d); + if (gst_structure_has_field (s, "pixel-aspect-ratio")) + gst_structure_fixate_field_nearest_fraction (s, "pixel-aspect-ratio", 1, 1); + caps = gst_caps_fixate (caps); + + return caps; +} + +static GstCaps * +gst_video_aggregator_default_update_caps (GstVideoAggregator * vagg, + GstCaps * caps) +{ + GstVideoAggregatorClass *vagg_klass = GST_VIDEO_AGGREGATOR_GET_CLASS (vagg); + GstCaps *ret, *best_format_caps; + gboolean at_least_one_alpha = FALSE; + GstVideoFormat best_format; + GstVideoInfo best_info; + gchar *color_name; + + best_format = GST_VIDEO_FORMAT_UNKNOWN; + gst_video_info_init (&best_info); + + if (vagg_klass->find_best_format) { + vagg_klass->find_best_format (vagg, caps, &best_info, &at_least_one_alpha); + + best_format = GST_VIDEO_INFO_FORMAT (&best_info); + } + + if (best_format == GST_VIDEO_FORMAT_UNKNOWN) { + GstCaps *tmp = gst_caps_fixate (gst_caps_ref (caps)); + gst_video_info_from_caps (&best_info, tmp); + best_format = GST_VIDEO_INFO_FORMAT (&best_info); + gst_caps_unref (tmp); + } + + color_name = gst_video_colorimetry_to_string (&best_info.colorimetry); + + GST_DEBUG_OBJECT (vagg, + "The output format will now be : %d with chroma : %s and colorimetry %s", + best_format, gst_video_chroma_to_string (best_info.chroma_site), + color_name); + + best_format_caps = gst_caps_copy (caps); + gst_caps_set_simple (best_format_caps, "format", G_TYPE_STRING, + gst_video_format_to_string (best_format), "chroma-site", G_TYPE_STRING, + gst_video_chroma_to_string (best_info.chroma_site), "colorimetry", + G_TYPE_STRING, color_name, NULL); + g_free (color_name); + ret = gst_caps_merge (best_format_caps, gst_caps_ref (caps)); + + return ret; +} + +static GstFlowReturn +gst_video_aggregator_default_update_src_caps (GstAggregator * agg, + GstCaps * caps, GstCaps ** ret) +{ + GstVideoAggregatorClass *vagg_klass = GST_VIDEO_AGGREGATOR_GET_CLASS (agg); + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + gboolean at_least_one_pad_configured = FALSE; + GList *l; + + GST_OBJECT_LOCK (vagg); + for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) { + GstVideoAggregatorPad *mpad = l->data; + + if (GST_VIDEO_INFO_WIDTH (&mpad->info) == 0 + || GST_VIDEO_INFO_HEIGHT (&mpad->info) == 0) + continue; + + at_least_one_pad_configured = TRUE; + } + GST_OBJECT_UNLOCK (vagg); + + if (!at_least_one_pad_configured) { + /* We couldn't decide the output video info because the sinkpads don't have + * all the caps yet, so we mark the pad as needing a reconfigure. This + * allows aggregate() to skip ahead a bit and try again later. */ + GST_DEBUG_OBJECT (vagg, "Couldn't decide output video info"); + gst_pad_mark_reconfigure (agg->srcpad); + return GST_AGGREGATOR_FLOW_NEED_DATA; + } + + g_assert (vagg_klass->update_caps); + + *ret = vagg_klass->update_caps (vagg, caps); + + return GST_FLOW_OK; +} + +static gboolean +_update_conversion_info (GstElement * element, GstPad * pad, gpointer user_data) +{ + GstVideoAggregatorPad *vaggpad = GST_VIDEO_AGGREGATOR_PAD (pad); + GstVideoAggregatorPadClass *vaggpad_klass = + GST_VIDEO_AGGREGATOR_PAD_GET_CLASS (vaggpad); + + if (vaggpad_klass->update_conversion_info) { + vaggpad_klass->update_conversion_info (vaggpad); + } + + return TRUE; +} + +static gboolean +gst_video_aggregator_default_negotiated_src_caps (GstAggregator * agg, + GstCaps * caps) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + gboolean at_least_one_alpha = FALSE; + const GstVideoFormatInfo *finfo; + GstVideoInfo info; + GList *l; + + GST_INFO_OBJECT (agg->srcpad, "set src caps: %" GST_PTR_FORMAT, caps); + + GST_OBJECT_LOCK (vagg); + for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) { + GstVideoAggregatorPad *mpad = l->data; + + if (GST_VIDEO_INFO_WIDTH (&mpad->info) == 0 + || GST_VIDEO_INFO_HEIGHT (&mpad->info) == 0) + continue; + + if (mpad->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA) + at_least_one_alpha = TRUE; + } + GST_OBJECT_UNLOCK (vagg); + + if (!gst_video_info_from_caps (&info, caps)) + return FALSE; + + if (GST_VIDEO_INFO_FPS_N (&vagg->info) != GST_VIDEO_INFO_FPS_N (&info) || + GST_VIDEO_INFO_FPS_D (&vagg->info) != GST_VIDEO_INFO_FPS_D (&info)) { + if (GST_AGGREGATOR_PAD (agg->srcpad)->segment.position != -1) { + vagg->priv->nframes = 0; + /* The timestamp offset will be updated based on the + * segment position the next time we aggregate */ + GST_DEBUG_OBJECT (vagg, + "Resetting frame counter because of framerate change"); + } + gst_video_aggregator_reset_qos (vagg); + } + + vagg->info = info; + + finfo = vagg->info.finfo; + + if (at_least_one_alpha && !(finfo->flags & GST_VIDEO_FORMAT_FLAG_ALPHA)) { + GST_ELEMENT_ERROR (vagg, CORE, NEGOTIATION, + ("At least one of the input pads contains alpha, but configured caps don't support alpha."), + ("Either convert your inputs to not contain alpha or add a videoconvert after the aggregator")); + return FALSE; + } + + /* Then browse the sinks once more, setting or unsetting conversion if needed */ + gst_element_foreach_sink_pad (GST_ELEMENT_CAST (vagg), + _update_conversion_info, NULL); + + if (vagg->priv->current_caps == NULL || + gst_caps_is_equal (caps, vagg->priv->current_caps) == FALSE) { + GstClockTime latency; + + gst_caps_replace (&vagg->priv->current_caps, caps); + + gst_aggregator_set_src_caps (agg, caps); + latency = gst_util_uint64_scale (GST_SECOND, + GST_VIDEO_INFO_FPS_D (&vagg->info), GST_VIDEO_INFO_FPS_N (&vagg->info)); + gst_aggregator_set_latency (agg, latency, latency); + } + + return TRUE; +} + +static gboolean +gst_video_aggregator_get_sinkpads_interlace_mode (GstVideoAggregator * vagg, + GstVideoAggregatorPad * skip_pad, GstVideoInterlaceMode * mode) +{ + GList *walk; + + GST_OBJECT_LOCK (vagg); + for (walk = GST_ELEMENT (vagg)->sinkpads; walk; walk = g_list_next (walk)) { + GstVideoAggregatorPad *vaggpad = walk->data; + + if (skip_pad && vaggpad == skip_pad) + continue; + if (vaggpad->info.finfo + && GST_VIDEO_INFO_FORMAT (&vaggpad->info) != GST_VIDEO_FORMAT_UNKNOWN) { + *mode = GST_VIDEO_INFO_INTERLACE_MODE (&vaggpad->info); + GST_OBJECT_UNLOCK (vagg); + return TRUE; + } + } + GST_OBJECT_UNLOCK (vagg); + return FALSE; +} + +static gboolean +gst_video_aggregator_pad_sink_setcaps (GstPad * pad, GstObject * parent, + GstCaps * caps) +{ + GstVideoAggregator *vagg; + GstVideoAggregatorPad *vaggpad; + GstVideoInfo info; + gboolean ret = FALSE; + + GST_INFO_OBJECT (pad, "Setting caps %" GST_PTR_FORMAT, caps); + + vagg = GST_VIDEO_AGGREGATOR (parent); + vaggpad = GST_VIDEO_AGGREGATOR_PAD (pad); + + if (!gst_video_info_from_caps (&info, caps)) { + GST_DEBUG_OBJECT (pad, "Failed to parse caps"); + goto beach; + } + + GST_VIDEO_AGGREGATOR_LOCK (vagg); + { + GstVideoInterlaceMode pads_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; + gboolean has_mode = FALSE; + + /* get the current output setting or fallback to other pads settings */ + if (GST_VIDEO_INFO_FORMAT (&vagg->info) != GST_VIDEO_FORMAT_UNKNOWN) { + pads_mode = GST_VIDEO_INFO_INTERLACE_MODE (&vagg->info); + has_mode = TRUE; + } else { + has_mode = + gst_video_aggregator_get_sinkpads_interlace_mode (vagg, vaggpad, + &pads_mode); + } + + if (has_mode) { + if (pads_mode != GST_VIDEO_INFO_INTERLACE_MODE (&info)) { + GST_ERROR_OBJECT (pad, + "got input caps %" GST_PTR_FORMAT ", but current caps are %" + GST_PTR_FORMAT, caps, vagg->priv->current_caps); + GST_VIDEO_AGGREGATOR_UNLOCK (vagg); + return FALSE; + } + } + } + + if (!vaggpad->info.finfo || + GST_VIDEO_INFO_FORMAT (&vaggpad->info) == GST_VIDEO_FORMAT_UNKNOWN) { + /* no video info was already set, so this is the first time + * that this pad is getting configured; configure immediately to avoid + * problems with the initial negotiation */ + vaggpad->info = info; + gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (vagg)); + } else { + /* this pad already had caps but received new ones; keep the new caps + * pending until we pick the next buffer from the queue, otherwise we + * might use an old buffer with the new caps and crash */ + vaggpad->priv->pending_vinfo = info; + GST_DEBUG_OBJECT (pad, "delaying caps change"); + } + ret = TRUE; + + GST_VIDEO_AGGREGATOR_UNLOCK (vagg); + +beach: + return ret; +} + +static gboolean +gst_video_aggregator_caps_has_alpha (GstCaps * caps) +{ + guint size = gst_caps_get_size (caps); + guint i; + + for (i = 0; i < size; i++) { + GstStructure *s = gst_caps_get_structure (caps, i); + const GValue *formats = gst_structure_get_value (s, "format"); + + if (formats) { + const GstVideoFormatInfo *info; + + if (GST_VALUE_HOLDS_LIST (formats)) { + guint list_size = gst_value_list_get_size (formats); + guint index; + + for (index = 0; index < list_size; index++) { + const GValue *list_item = gst_value_list_get_value (formats, index); + info = + gst_video_format_get_info (gst_video_format_from_string + (g_value_get_string (list_item))); + if (GST_VIDEO_FORMAT_INFO_HAS_ALPHA (info)) + return TRUE; + } + + } else if (G_VALUE_HOLDS_STRING (formats)) { + info = + gst_video_format_get_info (gst_video_format_from_string + (g_value_get_string (formats))); + if (GST_VIDEO_FORMAT_INFO_HAS_ALPHA (info)) + return TRUE; + + } else { + g_assert_not_reached (); + GST_WARNING ("Unexpected type for video 'format' field: %s", + G_VALUE_TYPE_NAME (formats)); + } + + } else { + return TRUE; + } + } + return FALSE; +} + +static GstCaps * +_get_non_alpha_caps (GstCaps * caps) +{ + GstCaps *result; + guint i, size; + + size = gst_caps_get_size (caps); + result = gst_caps_new_empty (); + for (i = 0; i < size; i++) { + GstStructure *s = gst_caps_get_structure (caps, i); + const GValue *formats = gst_structure_get_value (s, "format"); + GValue new_formats = { 0, }; + gboolean has_format = FALSE; + + /* FIXME what to do if formats are missing? */ + if (formats) { + const GstVideoFormatInfo *info; + + if (GST_VALUE_HOLDS_LIST (formats)) { + guint list_size = gst_value_list_get_size (formats); + guint index; + + g_value_init (&new_formats, GST_TYPE_LIST); + + for (index = 0; index < list_size; index++) { + const GValue *list_item = gst_value_list_get_value (formats, index); + + info = + gst_video_format_get_info (gst_video_format_from_string + (g_value_get_string (list_item))); + if (!GST_VIDEO_FORMAT_INFO_HAS_ALPHA (info)) { + has_format = TRUE; + gst_value_list_append_value (&new_formats, list_item); + } + } + + } else if (G_VALUE_HOLDS_STRING (formats)) { + info = + gst_video_format_get_info (gst_video_format_from_string + (g_value_get_string (formats))); + if (!GST_VIDEO_FORMAT_INFO_HAS_ALPHA (info)) { + has_format = TRUE; + gst_value_init_and_copy (&new_formats, formats); + } + + } else { + g_assert_not_reached (); + GST_WARNING ("Unexpected type for video 'format' field: %s", + G_VALUE_TYPE_NAME (formats)); + } + + if (has_format) { + s = gst_structure_copy (s); + gst_structure_take_value (s, "format", &new_formats); + gst_caps_append_structure (result, s); + } + + } + } + + return result; +} + +static GstCaps * +gst_video_aggregator_pad_sink_getcaps (GstPad * pad, GstVideoAggregator * vagg, + GstCaps * filter) +{ + GstCaps *srccaps; + GstCaps *template_caps, *sink_template_caps; + GstCaps *returned_caps; + GstStructure *s; + gint i, n; + GstAggregator *agg = GST_AGGREGATOR (vagg); + GstPad *srcpad = GST_PAD (agg->srcpad); + gboolean has_alpha; + GstVideoInterlaceMode interlace_mode; + gboolean has_interlace_mode; + + template_caps = gst_pad_get_pad_template_caps (srcpad); + + GST_DEBUG_OBJECT (pad, "Get caps with filter: %" GST_PTR_FORMAT, filter); + + srccaps = gst_pad_peer_query_caps (srcpad, template_caps); + srccaps = gst_caps_make_writable (srccaps); + has_alpha = gst_video_aggregator_caps_has_alpha (srccaps); + + has_interlace_mode = + gst_video_aggregator_get_sinkpads_interlace_mode (vagg, NULL, + &interlace_mode); + + n = gst_caps_get_size (srccaps); + for (i = 0; i < n; i++) { + s = gst_caps_get_structure (srccaps, i); + gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, + "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); + + gst_structure_remove_fields (s, "colorimetry", "chroma-site", "format", + "pixel-aspect-ratio", NULL); + if (has_interlace_mode) + gst_structure_set (s, "interlace-mode", G_TYPE_STRING, + gst_video_interlace_mode_to_string (interlace_mode), NULL); + } + + if (filter) { + returned_caps = gst_caps_intersect (srccaps, filter); + gst_caps_unref (srccaps); + } else { + returned_caps = srccaps; + } + + sink_template_caps = gst_pad_get_pad_template_caps (pad); + if (!has_alpha) { + GstCaps *tmp = _get_non_alpha_caps (sink_template_caps); + gst_caps_unref (sink_template_caps); + sink_template_caps = tmp; + } + + { + GstCaps *intersect = gst_caps_intersect (returned_caps, sink_template_caps); + gst_caps_unref (returned_caps); + returned_caps = intersect; + } + + gst_caps_unref (template_caps); + gst_caps_unref (sink_template_caps); + + GST_DEBUG_OBJECT (pad, "Returning caps: %" GST_PTR_FORMAT, returned_caps); + + return returned_caps; +} + +static void +gst_video_aggregator_update_qos (GstVideoAggregator * vagg, gdouble proportion, + GstClockTimeDiff diff, GstClockTime timestamp) +{ + gboolean live; + + GST_DEBUG_OBJECT (vagg, + "Updating QoS: proportion %lf, diff %" GST_STIME_FORMAT ", timestamp %" + GST_TIME_FORMAT, proportion, GST_STIME_ARGS (diff), + GST_TIME_ARGS (timestamp)); + + live = + GST_CLOCK_TIME_IS_VALID (gst_aggregator_get_latency (GST_AGGREGATOR + (vagg))); + + GST_OBJECT_LOCK (vagg); + + vagg->priv->proportion = proportion; + if (G_LIKELY (timestamp != GST_CLOCK_TIME_NONE)) { + if (!live && G_UNLIKELY (diff > 0)) + vagg->priv->earliest_time = + timestamp + 2 * diff + gst_util_uint64_scale_int_round (GST_SECOND, + GST_VIDEO_INFO_FPS_D (&vagg->info), + GST_VIDEO_INFO_FPS_N (&vagg->info)); + else + vagg->priv->earliest_time = timestamp + diff; + } else { + vagg->priv->earliest_time = GST_CLOCK_TIME_NONE; + } + GST_OBJECT_UNLOCK (vagg); +} + +static void +gst_video_aggregator_reset_qos (GstVideoAggregator * vagg) +{ + gst_video_aggregator_update_qos (vagg, 0.5, 0, GST_CLOCK_TIME_NONE); + vagg->priv->qos_processed = vagg->priv->qos_dropped = 0; +} + +static void +gst_video_aggregator_read_qos (GstVideoAggregator * vagg, gdouble * proportion, + GstClockTime * time) +{ + GST_OBJECT_LOCK (vagg); + *proportion = vagg->priv->proportion; + *time = vagg->priv->earliest_time; + GST_OBJECT_UNLOCK (vagg); +} + +static void +gst_video_aggregator_reset (GstVideoAggregator * vagg) +{ + GstAggregator *agg = GST_AGGREGATOR (vagg); + GList *l; + + gst_video_info_init (&vagg->info); + vagg->priv->ts_offset = 0; + vagg->priv->nframes = 0; + vagg->priv->live = FALSE; + + GST_AGGREGATOR_PAD (agg->srcpad)->segment.position = -1; + + gst_video_aggregator_reset_qos (vagg); + + GST_OBJECT_LOCK (vagg); + for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) { + GstVideoAggregatorPad *p = l->data; + + gst_buffer_replace (&p->priv->buffer, NULL); + p->priv->start_time = -1; + p->priv->end_time = -1; + + gst_video_info_init (&p->info); + } + GST_OBJECT_UNLOCK (vagg); +} + +static GstFlowReturn +gst_video_aggregator_fill_queues (GstVideoAggregator * vagg, + GstClockTime output_start_running_time, + GstClockTime output_end_running_time) +{ + GstAggregator *agg = GST_AGGREGATOR (vagg); + GList *l; + gboolean eos = TRUE; + gboolean need_more_data = FALSE; + gboolean need_reconfigure = FALSE; + GstSegment *agg_segment = &GST_AGGREGATOR_PAD (agg->srcpad)->segment; + + /* get a set of buffers into pad->priv->buffer that are within output_start_running_time + * and output_end_running_time taking into account finished and unresponsive pads */ + + GST_OBJECT_LOCK (vagg); + for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) { + GstVideoAggregatorPad *pad = l->data; + GstSegment segment; + GstAggregatorPad *bpad; + GstBuffer *buf; + gboolean is_eos; + + bpad = GST_AGGREGATOR_PAD (pad); + GST_OBJECT_LOCK (bpad); + segment = bpad->segment; + GST_OBJECT_UNLOCK (bpad); + is_eos = gst_aggregator_pad_is_eos (bpad); + + if (!is_eos) + eos = FALSE; + buf = gst_aggregator_pad_peek_buffer (bpad); + if (buf) { + GstClockTime start_time, end_time; + + start_time = GST_BUFFER_TIMESTAMP (buf); + if (start_time == -1) { + gst_buffer_unref (buf); + GST_ERROR_OBJECT (pad, "Need timestamped buffers!"); + GST_OBJECT_UNLOCK (vagg); + return GST_FLOW_ERROR; + } + + /* FIXME: Make all this work with negative rates */ + end_time = GST_BUFFER_DURATION (buf); + + if (end_time == -1) { + start_time = MAX (start_time, segment.start); + start_time = + gst_segment_to_running_time (&segment, GST_FORMAT_TIME, start_time); + + if (start_time >= output_end_running_time) { + if (pad->priv->buffer) { + GST_DEBUG_OBJECT (pad, "buffer duration is -1, start_time >= " + "output_end_running_time. Keeping previous buffer"); + } else { + GST_DEBUG_OBJECT (pad, "buffer duration is -1, start_time >= " + "output_end_running_time. No previous buffer."); + } + gst_buffer_unref (buf); + continue; + } else if (start_time < output_start_running_time) { + GST_DEBUG_OBJECT (pad, "buffer duration is -1, start_time < " + "output_start_running_time. Discarding old buffer"); + gst_buffer_replace (&pad->priv->buffer, buf); + if (pad->priv->pending_vinfo.finfo) { + pad->info = pad->priv->pending_vinfo; + need_reconfigure = TRUE; + pad->priv->pending_vinfo.finfo = NULL; + } + gst_buffer_unref (buf); + gst_aggregator_pad_drop_buffer (bpad); + need_more_data = TRUE; + continue; + } + gst_buffer_unref (buf); + buf = gst_aggregator_pad_pop_buffer (bpad); + gst_buffer_replace (&pad->priv->buffer, buf); + if (pad->priv->pending_vinfo.finfo) { + pad->info = pad->priv->pending_vinfo; + need_reconfigure = TRUE; + pad->priv->pending_vinfo.finfo = NULL; + } + /* FIXME: Set start_time and end_time to something here? */ + gst_buffer_unref (buf); + GST_DEBUG_OBJECT (pad, "buffer duration is -1"); + continue; + } + + g_assert (start_time != -1 && end_time != -1); + end_time += start_time; /* convert from duration to position */ + + /* Check if it's inside the segment */ + if (start_time >= segment.stop || end_time < segment.start) { + GST_DEBUG_OBJECT (pad, + "Buffer outside the segment : segment: [%" GST_TIME_FORMAT " -- %" + GST_TIME_FORMAT "]" " Buffer [%" GST_TIME_FORMAT " -- %" + GST_TIME_FORMAT "]", GST_TIME_ARGS (segment.stop), + GST_TIME_ARGS (segment.start), GST_TIME_ARGS (start_time), + GST_TIME_ARGS (end_time)); + + gst_buffer_unref (buf); + gst_aggregator_pad_drop_buffer (bpad); + + need_more_data = TRUE; + continue; + } + + /* Clip to segment and convert to running time */ + start_time = MAX (start_time, segment.start); + if (segment.stop != -1) + end_time = MIN (end_time, segment.stop); + start_time = + gst_segment_to_running_time (&segment, GST_FORMAT_TIME, start_time); + end_time = + gst_segment_to_running_time (&segment, GST_FORMAT_TIME, end_time); + g_assert (start_time != -1 && end_time != -1); + + /* Convert to the output segment rate */ + if (ABS (agg_segment->rate) != 1.0) { + start_time *= ABS (agg_segment->rate); + end_time *= ABS (agg_segment->rate); + } + + GST_TRACE_OBJECT (pad, "dealing with buffer %p start %" GST_TIME_FORMAT + " end %" GST_TIME_FORMAT " out start %" GST_TIME_FORMAT + " out end %" GST_TIME_FORMAT, buf, GST_TIME_ARGS (start_time), + GST_TIME_ARGS (end_time), GST_TIME_ARGS (output_start_running_time), + GST_TIME_ARGS (output_end_running_time)); + + if (pad->priv->end_time != -1 && pad->priv->end_time > end_time) { + GST_DEBUG_OBJECT (pad, "Buffer from the past, dropping"); + gst_buffer_unref (buf); + gst_aggregator_pad_drop_buffer (bpad); + continue; + } + + if (end_time >= output_start_running_time + && start_time < output_end_running_time) { + GST_DEBUG_OBJECT (pad, + "Taking new buffer with start time %" GST_TIME_FORMAT, + GST_TIME_ARGS (start_time)); + gst_buffer_replace (&pad->priv->buffer, buf); + if (pad->priv->pending_vinfo.finfo) { + pad->info = pad->priv->pending_vinfo; + need_reconfigure = TRUE; + pad->priv->pending_vinfo.finfo = NULL; + } + pad->priv->start_time = start_time; + pad->priv->end_time = end_time; + + gst_buffer_unref (buf); + gst_aggregator_pad_drop_buffer (bpad); + eos = FALSE; + } else if (start_time >= output_end_running_time) { + GST_DEBUG_OBJECT (pad, "Keeping buffer until %" GST_TIME_FORMAT, + GST_TIME_ARGS (start_time)); + gst_buffer_unref (buf); + eos = FALSE; + } else { + gst_buffer_replace (&pad->priv->buffer, buf); + if (pad->priv->pending_vinfo.finfo) { + pad->info = pad->priv->pending_vinfo; + need_reconfigure = TRUE; + pad->priv->pending_vinfo.finfo = NULL; + } + pad->priv->start_time = start_time; + pad->priv->end_time = end_time; + GST_DEBUG_OBJECT (pad, + "replacing old buffer with a newer buffer, start %" GST_TIME_FORMAT + " out end %" GST_TIME_FORMAT, GST_TIME_ARGS (start_time), + GST_TIME_ARGS (output_end_running_time)); + gst_buffer_unref (buf); + gst_aggregator_pad_drop_buffer (bpad); + + need_more_data = TRUE; + continue; + } + } else { + if (is_eos && pad->priv->repeat_after_eos) { + eos = FALSE; + GST_DEBUG_OBJECT (pad, "ignoring EOS and re-using previous buffer"); + continue; + } + + if (pad->priv->end_time != -1) { + if (pad->priv->end_time <= output_start_running_time) { + pad->priv->start_time = pad->priv->end_time = -1; + if (!is_eos) { + GST_DEBUG ("I just need more data"); + need_more_data = TRUE; + } else { + gst_buffer_replace (&pad->priv->buffer, NULL); + } + } else if (is_eos) { + eos = FALSE; + } + } else if (is_eos) { + gst_buffer_replace (&pad->priv->buffer, NULL); + } + } + } + GST_OBJECT_UNLOCK (vagg); + + if (need_reconfigure) + gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (vagg)); + + if (need_more_data) + return GST_AGGREGATOR_FLOW_NEED_DATA; + if (eos) + return GST_FLOW_EOS; + + return GST_FLOW_OK; +} + +static gboolean +sync_pad_values (GstElement * vagg, GstPad * pad, gpointer user_data) +{ + GstVideoAggregatorPad *vpad = GST_VIDEO_AGGREGATOR_PAD (pad); + GstAggregatorPad *bpad = GST_AGGREGATOR_PAD_CAST (pad); + GstClockTime timestamp; + gint64 stream_time; + + if (vpad->priv->buffer == NULL) + return TRUE; + + timestamp = GST_BUFFER_TIMESTAMP (vpad->priv->buffer); + GST_OBJECT_LOCK (bpad); + stream_time = gst_segment_to_stream_time (&bpad->segment, GST_FORMAT_TIME, + timestamp); + GST_OBJECT_UNLOCK (bpad); + + /* sync object properties on stream time */ + if (GST_CLOCK_TIME_IS_VALID (stream_time)) + gst_object_sync_values (GST_OBJECT_CAST (pad), stream_time); + + return TRUE; +} + +static gboolean +prepare_frames (GstElement * agg, GstPad * pad, gpointer user_data) +{ + GstVideoAggregatorPad *vpad = GST_VIDEO_AGGREGATOR_PAD_CAST (pad); + GstVideoAggregatorPadClass *vaggpad_class = + GST_VIDEO_AGGREGATOR_PAD_GET_CLASS (pad); + + memset (&vpad->priv->prepared_frame, 0, sizeof (GstVideoFrame)); + + if (vpad->priv->buffer == NULL || !vaggpad_class->prepare_frame) + return TRUE; + + return vaggpad_class->prepare_frame (vpad, GST_VIDEO_AGGREGATOR_CAST (agg), + vpad->priv->buffer, &vpad->priv->prepared_frame); +} + +static gboolean +clean_pad (GstElement * agg, GstPad * pad, gpointer user_data) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR_CAST (agg); + GstVideoAggregatorPad *vpad = GST_VIDEO_AGGREGATOR_PAD_CAST (pad); + GstVideoAggregatorPadClass *vaggpad_class = + GST_VIDEO_AGGREGATOR_PAD_GET_CLASS (pad); + + if (vaggpad_class->clean_frame) + vaggpad_class->clean_frame (vpad, vagg, &vpad->priv->prepared_frame); + + memset (&vpad->priv->prepared_frame, 0, sizeof (GstVideoFrame)); + + return TRUE; +} + +static GstFlowReturn +gst_video_aggregator_do_aggregate (GstVideoAggregator * vagg, + GstClockTime output_start_time, GstClockTime output_end_time, + GstBuffer ** outbuf) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstElementClass *klass = GST_ELEMENT_GET_CLASS (vagg); + GstVideoAggregatorClass *vagg_klass = (GstVideoAggregatorClass *) klass; + + g_assert (vagg_klass->aggregate_frames != NULL); + g_assert (vagg_klass->create_output_buffer != NULL); + + if ((ret = vagg_klass->create_output_buffer (vagg, outbuf)) != GST_FLOW_OK) { + GST_WARNING_OBJECT (vagg, "Could not get an output buffer, reason: %s", + gst_flow_get_name (ret)); + return ret; + } + if (*outbuf == NULL) { + /* sub-class doesn't want to generate output right now */ + return GST_FLOW_OK; + } + + GST_BUFFER_TIMESTAMP (*outbuf) = output_start_time; + GST_BUFFER_DURATION (*outbuf) = output_end_time - output_start_time; + + /* Sync pad properties to the stream time */ + gst_element_foreach_sink_pad (GST_ELEMENT_CAST (vagg), sync_pad_values, NULL); + + /* Convert all the frames the subclass has before aggregating */ + gst_element_foreach_sink_pad (GST_ELEMENT_CAST (vagg), prepare_frames, NULL); + + ret = vagg_klass->aggregate_frames (vagg, *outbuf); + + gst_element_foreach_sink_pad (GST_ELEMENT_CAST (vagg), clean_pad, NULL); + + return ret; +} + +/* Perform qos calculations before processing the next frame. Returns TRUE if + * the frame should be processed, FALSE if the frame can be dropped entirely */ +static gint64 +gst_video_aggregator_do_qos (GstVideoAggregator * vagg, GstClockTime timestamp) +{ + GstAggregator *agg = GST_AGGREGATOR (vagg); + GstClockTime qostime, earliest_time; + gdouble proportion; + gint64 jitter; + + /* no timestamp, can't do QoS => process frame */ + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (timestamp))) { + GST_LOG_OBJECT (vagg, "invalid timestamp, can't do QoS, process frame"); + return -1; + } + + /* get latest QoS observation values */ + gst_video_aggregator_read_qos (vagg, &proportion, &earliest_time); + + /* skip qos if we have no observation (yet) => process frame */ + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (earliest_time))) { + GST_LOG_OBJECT (vagg, "no observation yet, process frame"); + return -1; + } + + /* qos is done on running time */ + qostime = + gst_segment_to_running_time (&GST_AGGREGATOR_PAD (agg->srcpad)->segment, + GST_FORMAT_TIME, timestamp); + + /* see how our next timestamp relates to the latest qos timestamp */ + GST_LOG_OBJECT (vagg, "qostime %" GST_TIME_FORMAT ", earliest %" + GST_TIME_FORMAT, GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time)); + + jitter = GST_CLOCK_DIFF (qostime, earliest_time); + if (qostime != GST_CLOCK_TIME_NONE && jitter > 0) { + GST_DEBUG_OBJECT (vagg, "we are late, drop frame"); + return jitter; + } + + GST_LOG_OBJECT (vagg, "process frame"); + return jitter; +} + +static void +gst_video_aggregator_advance_on_timeout (GstVideoAggregator * vagg) +{ + GstAggregator *agg = GST_AGGREGATOR (vagg); + guint64 frame_duration; + gint fps_d, fps_n; + GstSegment *agg_segment = &GST_AGGREGATOR_PAD (agg->srcpad)->segment; + + GST_OBJECT_LOCK (agg); + if (agg_segment->position == -1) { + if (agg_segment->rate > 0.0) + agg_segment->position = agg_segment->start; + else + agg_segment->position = agg_segment->stop; + } + + /* Advance position */ + fps_d = GST_VIDEO_INFO_FPS_D (&vagg->info) ? + GST_VIDEO_INFO_FPS_D (&vagg->info) : 1; + fps_n = GST_VIDEO_INFO_FPS_N (&vagg->info) ? + GST_VIDEO_INFO_FPS_N (&vagg->info) : 25; + /* Default to 25/1 if no "best fps" is known */ + frame_duration = gst_util_uint64_scale (GST_SECOND, fps_d, fps_n); + if (agg_segment->rate > 0.0) + agg_segment->position += frame_duration; + else if (agg_segment->position > frame_duration) + agg_segment->position -= frame_duration; + else + agg_segment->position = 0; + vagg->priv->nframes++; + GST_OBJECT_UNLOCK (agg); +} + +static GstFlowReturn +gst_video_aggregator_aggregate (GstAggregator * agg, gboolean timeout) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + GstClockTime output_start_time, output_end_time; + GstClockTime output_start_running_time, output_end_running_time; + GstBuffer *outbuf = NULL; + GstFlowReturn flow_ret; + gint64 jitter; + GstSegment *agg_segment = &GST_AGGREGATOR_PAD (agg->srcpad)->segment; + + GST_VIDEO_AGGREGATOR_LOCK (vagg); + + if (GST_VIDEO_INFO_FORMAT (&vagg->info) == GST_VIDEO_FORMAT_UNKNOWN) { + if (timeout) + gst_video_aggregator_advance_on_timeout (vagg); + flow_ret = GST_AGGREGATOR_FLOW_NEED_DATA; + goto unlock_and_return; + } + + output_start_time = agg_segment->position; + if (agg_segment->position == -1 || agg_segment->position < agg_segment->start) + output_start_time = agg_segment->start; + + if (vagg->priv->nframes == 0) { + vagg->priv->ts_offset = output_start_time; + GST_DEBUG_OBJECT (vagg, "New ts offset %" GST_TIME_FORMAT, + GST_TIME_ARGS (output_start_time)); + } + + if (GST_VIDEO_INFO_FPS_N (&vagg->info) == 0) { + output_end_time = -1; + } else { + output_end_time = + vagg->priv->ts_offset + + gst_util_uint64_scale_round (vagg->priv->nframes + 1, + GST_SECOND * GST_VIDEO_INFO_FPS_D (&vagg->info), + GST_VIDEO_INFO_FPS_N (&vagg->info)); + } + + if (agg_segment->stop != -1) + output_end_time = MIN (output_end_time, agg_segment->stop); + + output_start_running_time = + gst_segment_to_running_time (agg_segment, GST_FORMAT_TIME, + output_start_time); + output_end_running_time = + gst_segment_to_running_time (agg_segment, GST_FORMAT_TIME, + output_end_time); + + if (output_end_time == output_start_time) { + flow_ret = GST_FLOW_EOS; + } else { + flow_ret = + gst_video_aggregator_fill_queues (vagg, output_start_running_time, + output_end_running_time); + } + + if (flow_ret == GST_AGGREGATOR_FLOW_NEED_DATA && !timeout) { + GST_DEBUG_OBJECT (vagg, "Need more data for decisions"); + goto unlock_and_return; + } else if (flow_ret == GST_FLOW_EOS) { + GST_DEBUG_OBJECT (vagg, "All sinkpads are EOS -- forwarding"); + goto unlock_and_return; + } else if (flow_ret == GST_FLOW_ERROR) { + GST_WARNING_OBJECT (vagg, "Error collecting buffers"); + goto unlock_and_return; + } + + /* It is possible that gst_video_aggregator_fill_queues() marked the pad + * for reconfiguration. In this case we have to reconfigure before continuing + * because we have picked a new buffer with different caps than before from + * one one of the sink pads and continuing here may lead to a crash. + * https://bugzilla.gnome.org/show_bug.cgi?id=780682 + */ + if (gst_pad_needs_reconfigure (GST_AGGREGATOR_SRC_PAD (vagg))) { + GST_DEBUG_OBJECT (vagg, "Need reconfigure"); + flow_ret = GST_AGGREGATOR_FLOW_NEED_DATA; + goto unlock_and_return; + } + + GST_DEBUG_OBJECT (vagg, + "Producing buffer for %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT + ", running time start %" GST_TIME_FORMAT ", running time end %" + GST_TIME_FORMAT, GST_TIME_ARGS (output_start_time), + GST_TIME_ARGS (output_end_time), + GST_TIME_ARGS (output_start_running_time), + GST_TIME_ARGS (output_end_running_time)); + + jitter = gst_video_aggregator_do_qos (vagg, output_start_time); + if (jitter <= 0) { + flow_ret = gst_video_aggregator_do_aggregate (vagg, output_start_time, + output_end_time, &outbuf); + if (flow_ret != GST_FLOW_OK) + goto done; + vagg->priv->qos_processed++; + } else { + GstMessage *msg; + + vagg->priv->qos_dropped++; + + msg = + gst_message_new_qos (GST_OBJECT_CAST (vagg), vagg->priv->live, + output_start_running_time, gst_segment_to_stream_time (agg_segment, + GST_FORMAT_TIME, output_start_time), output_start_time, + output_end_time - output_start_time); + gst_message_set_qos_values (msg, jitter, vagg->priv->proportion, 1000000); + gst_message_set_qos_stats (msg, GST_FORMAT_BUFFERS, + vagg->priv->qos_processed, vagg->priv->qos_dropped); + gst_element_post_message (GST_ELEMENT_CAST (vagg), msg); + + flow_ret = GST_FLOW_OK; + } + + GST_VIDEO_AGGREGATOR_UNLOCK (vagg); + if (outbuf) { + GST_DEBUG_OBJECT (vagg, + "Pushing buffer with ts %" GST_TIME_FORMAT " and duration %" + GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf))); + + flow_ret = gst_aggregator_finish_buffer (agg, outbuf); + } + + GST_VIDEO_AGGREGATOR_LOCK (vagg); + vagg->priv->nframes++; + agg_segment->position = output_end_time; + GST_VIDEO_AGGREGATOR_UNLOCK (vagg); + + return flow_ret; + +done: + if (outbuf) + gst_buffer_unref (outbuf); +unlock_and_return: + GST_VIDEO_AGGREGATOR_UNLOCK (vagg); + return flow_ret; +} + +/* FIXME, the duration query should reflect how long you will produce + * data, that is the amount of stream time until you will emit EOS. + * + * For synchronized aggregating this is always the max of all the durations + * of upstream since we emit EOS when all of them finished. + * + * We don't do synchronized aggregating so this really depends on where the + * streams where punched in and what their relative offsets are against + * each other which we can get from the first timestamps we see. + * + * When we add a new stream (or remove a stream) the duration might + * also become invalid again and we need to post a new DURATION + * message to notify this fact to the parent. + * For now we take the max of all the upstream elements so the simple + * cases work at least somewhat. + */ +static gboolean +gst_video_aggregator_query_duration (GstVideoAggregator * vagg, + GstQuery * query) +{ + GValue item = { 0 }; + gint64 max; + gboolean res; + GstFormat format; + GstIterator *it; + gboolean done; + + /* parse format */ + gst_query_parse_duration (query, &format, NULL); + + max = -1; + res = TRUE; + done = FALSE; + + /* Take maximum of all durations */ + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (vagg)); + while (!done) { + switch (gst_iterator_next (it, &item)) { + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_OK: + { + GstPad *pad; + gint64 duration; + + pad = g_value_get_object (&item); + + /* ask sink peer for duration */ + res &= gst_pad_peer_query_duration (pad, format, &duration); + /* take max from all valid return values */ + if (res) { + /* valid unknown length, stop searching */ + if (duration == -1) { + max = duration; + done = TRUE; + } + /* else see if bigger than current max */ + else if (duration > max) + max = duration; + } + g_value_reset (&item); + break; + } + case GST_ITERATOR_RESYNC: + max = -1; + res = TRUE; + gst_iterator_resync (it); + break; + default: + res = FALSE; + done = TRUE; + break; + } + } + g_value_unset (&item); + gst_iterator_free (it); + + if (res) { + /* and store the max */ + GST_DEBUG_OBJECT (vagg, "Total duration in format %s: %" + GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max)); + gst_query_set_duration (query, format, max); + } + + return res; +} + +static gboolean +gst_video_aggregator_src_query (GstAggregator * agg, GstQuery * query) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + gboolean res = FALSE; + GstSegment *agg_segment = &GST_AGGREGATOR_PAD (agg->srcpad)->segment; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + { + GstFormat format; + + gst_query_parse_position (query, &format, NULL); + + switch (format) { + case GST_FORMAT_TIME: + gst_query_set_position (query, format, + gst_segment_to_stream_time (agg_segment, GST_FORMAT_TIME, + agg_segment->position)); + res = TRUE; + break; + default: + break; + } + break; + } + case GST_QUERY_DURATION: + res = gst_video_aggregator_query_duration (vagg, query); + break; + case GST_QUERY_LATENCY: + res = + GST_AGGREGATOR_CLASS (gst_video_aggregator_parent_class)->src_query + (agg, query); + + if (res) { + gst_query_parse_latency (query, &vagg->priv->live, NULL, NULL); + } + break; + default: + res = + GST_AGGREGATOR_CLASS (gst_video_aggregator_parent_class)->src_query + (agg, query); + break; + } + return res; +} + +static gboolean +gst_video_aggregator_src_event (GstAggregator * agg, GstEvent * event) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_QOS: + { + GstQOSType type; + GstClockTimeDiff diff; + GstClockTime timestamp; + gdouble proportion; + + gst_event_parse_qos (event, &type, &proportion, &diff, ×tamp); + gst_video_aggregator_update_qos (vagg, proportion, diff, timestamp); + break; + } + case GST_EVENT_SEEK: + { + GST_DEBUG_OBJECT (vagg, "Handling SEEK event"); + } + default: + break; + } + + return + GST_AGGREGATOR_CLASS (gst_video_aggregator_parent_class)->src_event (agg, + event); +} + +static GstFlowReturn +gst_video_aggregator_flush (GstAggregator * agg) +{ + GList *l; + gdouble abs_rate; + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + GstSegment *agg_segment = &GST_AGGREGATOR_PAD (agg->srcpad)->segment; + + GST_INFO_OBJECT (agg, "Flushing"); + GST_OBJECT_LOCK (vagg); + abs_rate = ABS (agg_segment->rate); + for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) { + GstVideoAggregatorPad *p = l->data; + + /* Convert to the output segment rate */ + if (ABS (agg_segment->rate) != abs_rate) { + if (ABS (agg_segment->rate) != 1.0 && p->priv->buffer) { + p->priv->start_time /= ABS (agg_segment->rate); + p->priv->end_time /= ABS (agg_segment->rate); + } + if (abs_rate != 1.0 && p->priv->buffer) { + p->priv->start_time *= abs_rate; + p->priv->end_time *= abs_rate; + } + } + } + GST_OBJECT_UNLOCK (vagg); + + agg_segment->position = -1; + vagg->priv->ts_offset = 0; + vagg->priv->nframes = 0; + + gst_video_aggregator_reset_qos (vagg); + return GST_FLOW_OK; +} + +static gboolean +gst_video_aggregator_sink_event (GstAggregator * agg, GstAggregatorPad * bpad, + GstEvent * event) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (bpad); + gboolean ret = TRUE; + + GST_DEBUG_OBJECT (pad, "Got %s event on pad %s:%s", + GST_EVENT_TYPE_NAME (event), GST_DEBUG_PAD_NAME (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + ret = + gst_video_aggregator_pad_sink_setcaps (GST_PAD (pad), + GST_OBJECT (vagg), caps); + gst_event_unref (event); + event = NULL; + break; + } + case GST_EVENT_SEGMENT:{ + GstSegment seg; + gst_event_copy_segment (event, &seg); + + g_assert (seg.format == GST_FORMAT_TIME); + gst_video_aggregator_reset_qos (vagg); + break; + } + default: + break; + } + + if (event != NULL) + return GST_AGGREGATOR_CLASS (gst_video_aggregator_parent_class)->sink_event + (agg, bpad, event); + + return ret; +} + +static gboolean +gst_video_aggregator_start (GstAggregator * agg) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + + gst_caps_replace (&vagg->priv->current_caps, NULL); + + return TRUE; +} + +static gboolean +gst_video_aggregator_stop (GstAggregator * agg) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + + gst_video_aggregator_reset (vagg); + + return TRUE; +} + +/* GstElement vmethods */ +static GstPad * +gst_video_aggregator_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * req_name, const GstCaps * caps) +{ + GstVideoAggregator *vagg; + GstVideoAggregatorPad *vaggpad; + + vagg = GST_VIDEO_AGGREGATOR (element); + + vaggpad = (GstVideoAggregatorPad *) + GST_ELEMENT_CLASS (gst_video_aggregator_parent_class)->request_new_pad + (element, templ, req_name, caps); + + if (vaggpad == NULL) + return NULL; + + GST_OBJECT_LOCK (vagg); + vaggpad->priv->zorder = GST_ELEMENT (vagg)->numsinkpads; + vaggpad->priv->start_time = -1; + vaggpad->priv->end_time = -1; + element->sinkpads = g_list_sort (element->sinkpads, + (GCompareFunc) pad_zorder_compare); + GST_OBJECT_UNLOCK (vagg); + + return GST_PAD (vaggpad); +} + +static void +gst_video_aggregator_release_pad (GstElement * element, GstPad * pad) +{ + GstVideoAggregator *vagg = NULL; + GstVideoAggregatorPad *vaggpad; + gboolean last_pad; + + vagg = GST_VIDEO_AGGREGATOR (element); + vaggpad = GST_VIDEO_AGGREGATOR_PAD (pad); + + GST_VIDEO_AGGREGATOR_LOCK (vagg); + + GST_OBJECT_LOCK (vagg); + last_pad = (GST_ELEMENT (vagg)->numsinkpads - 1 == 0); + GST_OBJECT_UNLOCK (vagg); + + if (last_pad) + gst_video_aggregator_reset (vagg); + + gst_buffer_replace (&vaggpad->priv->buffer, NULL); + + GST_ELEMENT_CLASS (gst_video_aggregator_parent_class)->release_pad + (GST_ELEMENT (vagg), pad); + + gst_pad_mark_reconfigure (GST_AGGREGATOR_SRC_PAD (vagg)); + + GST_VIDEO_AGGREGATOR_UNLOCK (vagg); + return; +} + +static gboolean +gst_video_aggregator_propose_allocation (GstAggregator * agg, + GstAggregatorPad * pad, GstQuery * decide_query, GstQuery * query) +{ + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); + + return TRUE; +} + +static gboolean +gst_video_aggregator_decide_allocation (GstAggregator * agg, GstQuery * query) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + GstAllocationParams params = { 0, 15, 0, 0 }; + guint i; + GstBufferPool *pool; + GstAllocator *allocator; + guint size, min, max; + gboolean update = FALSE; + GstStructure *config = NULL; + GstCaps *caps = NULL; + + if (gst_query_get_n_allocation_params (query) == 0) { + gst_query_add_allocation_param (query, NULL, ¶ms); + } else { + for (i = 0; i < gst_query_get_n_allocation_params (query); i++) { + GstAllocator *allocator; + + gst_query_parse_nth_allocation_param (query, i, &allocator, ¶ms); + params.align = MAX (params.align, 15); + gst_query_set_nth_allocation_param (query, i, allocator, ¶ms); + } + } + + gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms); + + if (gst_query_get_n_allocation_pools (query) > 0) { + gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); + + /* adjust size */ + size = MAX (size, vagg->info.size); + update = TRUE; + } else { + pool = NULL; + size = vagg->info.size; + min = max = 0; + update = FALSE; + } + + gst_query_parse_allocation (query, &caps, NULL); + + /* no downstream pool, make our own */ + if (pool == NULL) + pool = gst_video_buffer_pool_new (); + + config = gst_buffer_pool_get_config (pool); + + gst_buffer_pool_config_set_params (config, caps, size, min, max); + gst_buffer_pool_config_set_allocator (config, allocator, ¶ms); + if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) { + gst_buffer_pool_config_add_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_META); + } + + /* buffer pool may have to do some changes */ + if (!gst_buffer_pool_set_config (pool, config)) { + config = gst_buffer_pool_get_config (pool); + + /* If change are not acceptable, fallback to generic pool */ + if (!gst_buffer_pool_config_validate_params (config, caps, size, min, max)) { + GST_DEBUG_OBJECT (agg, "unsupported pool, making new pool"); + + gst_object_unref (pool); + pool = gst_video_buffer_pool_new (); + gst_buffer_pool_config_set_params (config, caps, size, min, max); + gst_buffer_pool_config_set_allocator (config, allocator, ¶ms); + + if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) { + gst_buffer_pool_config_add_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_META); + } + } + + if (!gst_buffer_pool_set_config (pool, config)) + goto config_failed; + } + + if (update) + gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max); + else + gst_query_add_allocation_pool (query, pool, size, min, max); + + if (pool) + gst_object_unref (pool); + if (allocator) + gst_object_unref (allocator); + + return TRUE; + +config_failed: + if (pool) + gst_object_unref (pool); + if (allocator) + gst_object_unref (allocator); + + GST_ELEMENT_ERROR (agg, RESOURCE, SETTINGS, + ("Failed to configure the buffer pool"), + ("Configuration is most likely invalid, please report this issue.")); + return FALSE; +} + +static GstFlowReturn +gst_video_aggregator_create_output_buffer (GstVideoAggregator * videoaggregator, + GstBuffer ** outbuf) +{ + GstAggregator *aggregator = GST_AGGREGATOR (videoaggregator); + GstBufferPool *pool; + GstFlowReturn ret = GST_FLOW_OK; + + pool = gst_aggregator_get_buffer_pool (aggregator); + + if (pool) { + if (!gst_buffer_pool_is_active (pool)) { + if (!gst_buffer_pool_set_active (pool, TRUE)) { + GST_ELEMENT_ERROR (videoaggregator, RESOURCE, SETTINGS, + ("failed to activate bufferpool"), + ("failed to activate bufferpool")); + return GST_FLOW_ERROR; + } + } + + ret = gst_buffer_pool_acquire_buffer (pool, outbuf, NULL); + gst_object_unref (pool); + } else { + guint outsize; + GstAllocator *allocator; + GstAllocationParams params; + + gst_aggregator_get_allocator (aggregator, &allocator, ¶ms); + + outsize = GST_VIDEO_INFO_SIZE (&videoaggregator->info); + *outbuf = gst_buffer_new_allocate (allocator, outsize, ¶ms); + + if (allocator) + gst_object_unref (allocator); + + if (*outbuf == NULL) { + GST_ELEMENT_ERROR (videoaggregator, RESOURCE, NO_SPACE_LEFT, + (NULL), ("Could not acquire buffer of size: %d", outsize)); + ret = GST_FLOW_ERROR; + } + } + return ret; +} + +static gboolean +gst_video_aggregator_pad_sink_acceptcaps (GstPad * pad, + GstVideoAggregator * vagg, GstCaps * caps) +{ + gboolean ret; + GstCaps *modified_caps; + GstCaps *accepted_caps; + GstCaps *template_caps; + gboolean had_current_caps = TRUE; + gint i, n; + GstStructure *s; + GstAggregator *agg = GST_AGGREGATOR (vagg); + + GST_DEBUG_OBJECT (pad, "%" GST_PTR_FORMAT, caps); + + accepted_caps = gst_pad_get_current_caps (GST_PAD (agg->srcpad)); + + template_caps = gst_pad_get_pad_template_caps (GST_PAD (agg->srcpad)); + + if (accepted_caps == NULL) { + accepted_caps = template_caps; + had_current_caps = FALSE; + } + + accepted_caps = gst_caps_make_writable (accepted_caps); + + GST_LOG_OBJECT (pad, "src caps %" GST_PTR_FORMAT, accepted_caps); + + n = gst_caps_get_size (accepted_caps); + for (i = 0; i < n; i++) { + s = gst_caps_get_structure (accepted_caps, i); + gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, G_MAXINT, + "height", GST_TYPE_INT_RANGE, 1, G_MAXINT, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL); + + gst_structure_remove_fields (s, "colorimetry", "chroma-site", "format", + "pixel-aspect-ratio", NULL); + } + + modified_caps = gst_caps_intersect (accepted_caps, template_caps); + + ret = gst_caps_can_intersect (caps, accepted_caps); + GST_DEBUG_OBJECT (pad, "%saccepted caps %" GST_PTR_FORMAT, + (ret ? "" : "not "), caps); + gst_caps_unref (accepted_caps); + gst_caps_unref (modified_caps); + if (had_current_caps) + gst_caps_unref (template_caps); + return ret; +} + +static gboolean +gst_video_aggregator_sink_query (GstAggregator * agg, GstAggregatorPad * bpad, + GstQuery * query) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + GstVideoAggregatorPad *pad = GST_VIDEO_AGGREGATOR_PAD (bpad); + gboolean ret = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CAPS: + { + GstCaps *filter, *caps; + + gst_query_parse_caps (query, &filter); + caps = + gst_video_aggregator_pad_sink_getcaps (GST_PAD (pad), vagg, filter); + gst_query_set_caps_result (query, caps); + gst_caps_unref (caps); + ret = TRUE; + break; + } + case GST_QUERY_ACCEPT_CAPS: + { + GstCaps *caps; + + gst_query_parse_accept_caps (query, &caps); + ret = + gst_video_aggregator_pad_sink_acceptcaps (GST_PAD (pad), vagg, caps); + gst_query_set_accept_caps_result (query, ret); + ret = TRUE; + break; + } + default: + ret = + GST_AGGREGATOR_CLASS (gst_video_aggregator_parent_class)->sink_query + (agg, bpad, query); + break; + } + return ret; +} + +/* GObject vmethods */ +static void +gst_video_aggregator_finalize (GObject * o) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (o); + + g_mutex_clear (&vagg->priv->lock); + + G_OBJECT_CLASS (gst_video_aggregator_parent_class)->finalize (o); +} + +static void +gst_video_aggregator_dispose (GObject * o) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (o); + + gst_caps_replace (&vagg->priv->current_caps, NULL); + + G_OBJECT_CLASS (gst_video_aggregator_parent_class)->dispose (o); +} + +static void +gst_video_aggregator_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_video_aggregator_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* GObject boilerplate */ +static void +gst_video_aggregator_class_init (GstVideoAggregatorClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + GstAggregatorClass *agg_class = (GstAggregatorClass *) klass; + + GST_DEBUG_CATEGORY_INIT (gst_video_aggregator_debug, "videoaggregator", 0, + "base video aggregator"); + + gst_video_aggregator_parent_class = g_type_class_peek_parent (klass); + + if (video_aggregator_private_offset != 0) + g_type_class_adjust_private_offset (klass, + &video_aggregator_private_offset); + + gobject_class->finalize = gst_video_aggregator_finalize; + gobject_class->dispose = gst_video_aggregator_dispose; + + gobject_class->get_property = gst_video_aggregator_get_property; + gobject_class->set_property = gst_video_aggregator_set_property; + + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_video_aggregator_request_new_pad); + gstelement_class->release_pad = + GST_DEBUG_FUNCPTR (gst_video_aggregator_release_pad); + + agg_class->start = gst_video_aggregator_start; + agg_class->stop = gst_video_aggregator_stop; + agg_class->sink_query = gst_video_aggregator_sink_query; + agg_class->sink_event = gst_video_aggregator_sink_event; + agg_class->flush = gst_video_aggregator_flush; + agg_class->aggregate = gst_video_aggregator_aggregate; + agg_class->src_event = gst_video_aggregator_src_event; + agg_class->src_query = gst_video_aggregator_src_query; + agg_class->get_next_time = gst_aggregator_simple_get_next_time; + agg_class->update_src_caps = gst_video_aggregator_default_update_src_caps; + agg_class->fixate_src_caps = gst_video_aggregator_default_fixate_src_caps; + agg_class->negotiated_src_caps = + gst_video_aggregator_default_negotiated_src_caps; + agg_class->decide_allocation = gst_video_aggregator_decide_allocation; + agg_class->propose_allocation = gst_video_aggregator_propose_allocation; + + klass->find_best_format = gst_video_aggregator_find_best_format; + klass->create_output_buffer = gst_video_aggregator_create_output_buffer; + klass->update_caps = gst_video_aggregator_default_update_caps; + + /* Register the pad class */ + g_type_class_ref (GST_TYPE_VIDEO_AGGREGATOR_PAD); +} + +static void +gst_video_aggregator_init (GstVideoAggregator * vagg, + GstVideoAggregatorClass * klass) +{ + vagg->priv = gst_video_aggregator_get_instance_private (vagg); + + vagg->priv->current_caps = NULL; + + g_mutex_init (&vagg->priv->lock); + + /* initialize variables */ + gst_video_aggregator_reset (vagg); +} diff --git a/gst-libs/gst/video/gstvideoaggregator.h b/gst-libs/gst/video/gstvideoaggregator.h new file mode 100644 index 0000000000..6b53ce41c1 --- /dev/null +++ b/gst-libs/gst/video/gstvideoaggregator.h @@ -0,0 +1,248 @@ +/* Generic video aggregator plugin + * Copyright (C) 2008 Wim Taymans + * Copyright (C) 2010 Sebastian Dröge + * + * 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. + */ + +#ifndef __GST_VIDEO_AGGREGATOR_H__ +#define __GST_VIDEO_AGGREGATOR_H__ + +#ifndef GST_USE_UNSTABLE_API +#warning "The Video library from gst-plugins-bad is unstable API and may change in future." +#warning "You can define GST_USE_UNSTABLE_API to avoid this warning." +#endif + +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct _GstVideoAggregator GstVideoAggregator; +typedef struct _GstVideoAggregatorClass GstVideoAggregatorClass; +typedef struct _GstVideoAggregatorPrivate GstVideoAggregatorPrivate; + +/************************* + * GstVideoAggregatorPad * + *************************/ + +#define GST_TYPE_VIDEO_AGGREGATOR_PAD (gst_video_aggregator_pad_get_type()) +#define GST_VIDEO_AGGREGATOR_PAD(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_AGGREGATOR_PAD, GstVideoAggregatorPad)) +#define GST_VIDEO_AGGREGATOR_PAD_CAST(obj) ((GstVideoAggregatorPad *)(obj)) +#define GST_VIDEO_AGGREGATOR_PAD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_AGGREGATOR_PAD, GstVideoAggregatorPadClass)) +#define GST_IS_VIDEO_AGGREGATOR_PAD(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_AGGREGATOR_PAD)) +#define GST_IS_VIDEO_AGGREGATOR_PAD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_AGGREGATOR_PAD)) +#define GST_VIDEO_AGGREGATOR_PAD_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_VIDEO_AGGREGATOR_PAD,GstVideoAggregatorPadClass)) + +typedef struct _GstVideoAggregatorPad GstVideoAggregatorPad; +typedef struct _GstVideoAggregatorPadClass GstVideoAggregatorPadClass; +typedef struct _GstVideoAggregatorPadPrivate GstVideoAggregatorPadPrivate; + +/** + * GstVideoAggregatorPad: + * @info: The #GstVideoInfo currently set on the pad + */ +struct _GstVideoAggregatorPad +{ + GstAggregatorPad parent; + + /*< public >*/ + /* read-only, with OBJECT_LOCK */ + GstVideoInfo info; + + /* < private > */ + GstVideoAggregatorPadPrivate *priv; + + gpointer _gst_reserved[GST_PADDING]; +}; + +/** + * GstVideoAggregatorPadClass: + * + * @update_conversion_info: Called when either the input or output formats + * have changed. + * @prepare_frame: Prepare the frame from the pad buffer and sets it to prepared_frame + * @clean_frame: clean the frame previously prepared in prepare_frame + */ +struct _GstVideoAggregatorPadClass +{ + GstAggregatorPadClass parent_class; + void (*update_conversion_info) (GstVideoAggregatorPad * pad); + + gboolean (*prepare_frame) (GstVideoAggregatorPad * pad, + GstVideoAggregator * videoaggregator, + GstBuffer * buffer, + GstVideoFrame * prepared_frame); + + void (*clean_frame) (GstVideoAggregatorPad * pad, + GstVideoAggregator * videoaggregator, + GstVideoFrame * prepared_frame); + + gpointer _gst_reserved[GST_PADDING_LARGE]; +}; + +GST_VIDEO_BAD_API +GType gst_video_aggregator_pad_get_type (void); + +GST_VIDEO_BAD_API +gboolean gst_video_aggregator_pad_has_current_buffer (GstVideoAggregatorPad *pad); + +GST_VIDEO_BAD_API +GstBuffer * gst_video_aggregator_pad_get_current_buffer (GstVideoAggregatorPad *pad); + +GST_VIDEO_BAD_API +GstVideoFrame * gst_video_aggregator_pad_get_prepared_frame (GstVideoAggregatorPad *pad); + +GST_VIDEO_BAD_API +void gst_video_aggregator_pad_set_needs_alpha (GstVideoAggregatorPad *pad, gboolean needs_alpha); + +/******************************** + * GstVideoAggregatorConvertPad * + *******************************/ + +#define GST_TYPE_VIDEO_AGGREGATOR_CONVERT_PAD (gst_video_aggregator_convert_pad_get_type()) +#define GST_VIDEO_AGGREGATOR_CONVERT_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_AGGREGATOR_CONVERT_PAD, GstVideoAggregatorConvertPad)) +#define GST_VIDEO_AGGREGATOR_CONVERT_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_AGGREGATOR_CONVERT_PAD, GstVideoAggregatorConvertPadClass)) +#define GST_VIDEO_AGGREGATOR_CONVERT_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_VIDEO_AGGREGATOR_CONVERT_PAD, GstVideoAggregatorConvertPadClass)) +#define GST_IS_VIDEO_AGGREGATOR_CONVERT_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_AGGREGATOR_CONVERT_PAD)) +#define GST_IS_VIDEO_AGGREGATOR_CONVERT_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_AGGREGATOR_CONVERT_PAD)) + +typedef struct _GstVideoAggregatorConvertPad GstVideoAggregatorConvertPad; +typedef struct _GstVideoAggregatorConvertPadClass GstVideoAggregatorConvertPadClass; +typedef struct _GstVideoAggregatorConvertPadPrivate GstVideoAggregatorConvertPadPrivate; + +/** + * GstVideoAggregatorConvertPad: + * + * An implementation of GstPad that can be used with #GstVideoAggregator. + * + * See #GstVideoAggregator for more details. + */ +struct _GstVideoAggregatorConvertPad +{ + /*< private >*/ + GstVideoAggregatorPad parent; + + GstVideoAggregatorConvertPadPrivate *priv; + + gpointer _gst_reserved[GST_PADDING]; +}; + +/** + * GstVideoAggregatorConvertPadClass: + * + */ +struct _GstVideoAggregatorConvertPadClass +{ + GstVideoAggregatorPadClass parent_class; + + void (*create_conversion_info) (GstVideoAggregatorConvertPad *pad, GstVideoAggregator *agg, GstVideoInfo *conversion_info); + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GST_VIDEO_BAD_API +GType gst_video_aggregator_convert_pad_get_type (void); + +GST_VIDEO_BAD_API +void gst_video_aggregator_convert_pad_update_conversion_info (GstVideoAggregatorConvertPad * pad); + +/********************** + * GstVideoAggregator * + *********************/ + +#define GST_TYPE_VIDEO_AGGREGATOR (gst_video_aggregator_get_type()) +#define GST_VIDEO_AGGREGATOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VIDEO_AGGREGATOR, GstVideoAggregator)) +#define GST_VIDEO_AGGREGATOR_CAST(obj) ((GstVideoAggregator *)(obj)) +#define GST_VIDEO_AGGREGATOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VIDEO_AGGREGATOR, GstVideoAggregatorClass)) +#define GST_IS_VIDEO_AGGREGATOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VIDEO_AGGREGATOR)) +#define GST_IS_VIDEO_AGGREGATOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VIDEO_AGGREGATOR)) +#define GST_VIDEO_AGGREGATOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_VIDEO_AGGREGATOR,GstVideoAggregatorClass)) + +/** + * GstVideoAggregator: + * @info: The #GstVideoInfo representing the currently set + * srcpad caps. + */ +struct _GstVideoAggregator +{ + GstAggregator aggregator; + + /*< public >*/ + /* Output caps */ + GstVideoInfo info; + + /* < private > */ + GstVideoAggregatorPrivate *priv; + gpointer _gst_reserved[GST_PADDING_LARGE]; +}; + +/** + * GstVideoAggregatorClass: + * @update_caps: Optional. + * Lets subclasses update the #GstCaps representing + * the src pad caps before usage. Return %NULL to indicate failure. + * @aggregate_frames: Lets subclasses aggregate frames that are ready. Subclasses + * should iterate the GstElement.sinkpads and use the already + * mapped #GstVideoFrame from gst_video_aggregator_pad_get_prepared_frame() + * or directly use the #GstBuffer from gst_video_aggregator_pad_get_current_buffer() + * if it needs to map the buffer in a special way. The result of the + * aggregation should land in @outbuffer. + * @create_output_buffer: Optional. + * Lets subclasses provide a #GstBuffer to be used as @outbuffer of + * the #aggregate_frames vmethod. + * @find_best_format: Optional. + * Lets subclasses decide of the best common format to use. + **/ +struct _GstVideoAggregatorClass +{ + /*< private >*/ + GstAggregatorClass parent_class; + + /*< public >*/ + GstCaps * (*update_caps) (GstVideoAggregator * videoaggregator, + GstCaps * caps); + GstFlowReturn (*aggregate_frames) (GstVideoAggregator * videoaggregator, + GstBuffer * outbuffer); + GstFlowReturn (*create_output_buffer) (GstVideoAggregator * videoaggregator, + GstBuffer ** outbuffer); + void (*find_best_format) (GstVideoAggregator * vagg, + GstCaps * downstream_caps, + GstVideoInfo * best_info, + gboolean * at_least_one_alpha); + + /* < private > */ + gpointer _gst_reserved[GST_PADDING_LARGE]; +}; + +GST_VIDEO_BAD_API +GType gst_video_aggregator_get_type (void); + +G_END_DECLS +#endif /* __GST_VIDEO_AGGREGATOR_H__ */ diff --git a/gst/compositor/.gitignore b/gst/compositor/.gitignore new file mode 100644 index 0000000000..131b2eef14 --- /dev/null +++ b/gst/compositor/.gitignore @@ -0,0 +1 @@ +compositororc.h diff --git a/gst/compositor/Makefile.am b/gst/compositor/Makefile.am new file mode 100644 index 0000000000..193d153d95 --- /dev/null +++ b/gst/compositor/Makefile.am @@ -0,0 +1,28 @@ +plugin_LTLIBRARIES = libgstcompositor.la + +ORC_SOURCE=compositororc + +include $(top_srcdir)/common/orc.mak + +libgstcompositor_la_SOURCES = \ + blend.c \ + compositor.c + + +nodist_libgstcompositor_la_SOURCES = $(ORC_NODIST_SOURCES) +libgstcompositor_la_CFLAGS = \ + -I$(top_srcdir)/gst-libs \ + -I$(top_builddir)/gst-libs \ + $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(ORC_CFLAGS) +libgstcompositor_la_LIBADD = \ + $(top_builddir)/gst-libs/gst/video/libgstbadvideo-$(GST_API_VERSION).la \ + $(GST_PLUGINS_BASE_LIBS) \ + -lgstvideo-@GST_API_VERSION@ \ + $(GST_BASE_LIBS) $(GST_LIBS) $(ORC_LIBS) $(LIBM) +libgstcompositor_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) + +# headers we need but don't want installed +noinst_HEADERS = \ + blend.h \ + compositor.h diff --git a/gst/compositor/blend.c b/gst/compositor/blend.c new file mode 100644 index 0000000000..c54d8988ee --- /dev/null +++ b/gst/compositor/blend.c @@ -0,0 +1,1116 @@ +/* + * Copyright (C) 2004 Wim Taymans + * Copyright (C) 2006 Mindfruit Bv. + * Author: Sjoerd Simons + * Author: Alex Ugarte + * Copyright (C) 2009 Alex Ugarte + * Copyright (C) 2009 Sebastian Dröge + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "blend.h" +#include "compositororc.h" + +#include + +#include + +GST_DEBUG_CATEGORY_STATIC (gst_compositor_blend_debug); +#define GST_CAT_DEFAULT gst_compositor_blend_debug + +/* Below are the implementations of everything */ + +/* A32 is for AYUV, ARGB and BGRA */ +#define BLEND_A32(name, method, LOOP) \ +static void \ +method##_ ##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \ + gdouble src_alpha, GstVideoFrame * destframe, GstCompositorBlendMode mode) \ +{ \ + guint s_alpha; \ + gint src_stride, dest_stride; \ + gint dest_width, dest_height; \ + guint8 *src, *dest; \ + gint src_width, src_height; \ + \ + src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \ + src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \ + src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \ + src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \ + dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \ + dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \ + dest_width = GST_VIDEO_FRAME_COMP_WIDTH (destframe, 0); \ + dest_height = GST_VIDEO_FRAME_COMP_HEIGHT (destframe, 0); \ + \ + s_alpha = CLAMP ((gint) (src_alpha * 255), 0, 255); \ + \ + /* If it's completely transparent... we just return */ \ + if (G_UNLIKELY (s_alpha == 0)) \ + return; \ + \ + /* adjust src pointers for negative sizes */ \ + if (xpos < 0) { \ + src += -xpos * 4; \ + src_width -= -xpos; \ + xpos = 0; \ + } \ + if (ypos < 0) { \ + src += -ypos * src_stride; \ + src_height -= -ypos; \ + ypos = 0; \ + } \ + /* adjust width/height if the src is bigger than dest */ \ + if (xpos + src_width > dest_width) { \ + src_width = dest_width - xpos; \ + } \ + if (ypos + src_height > dest_height) { \ + src_height = dest_height - ypos; \ + } \ + \ + if (src_height > 0 && src_width > 0) { \ + dest = dest + 4 * xpos + (ypos * dest_stride); \ + \ + LOOP (dest, src, src_height, src_width, src_stride, dest_stride, s_alpha, \ + mode); \ + } \ +} + +#define OVERLAY_A32_LOOP(name) \ +static inline void \ +_overlay_loop_##name (guint8 * dest, const guint8 * src, gint src_height, \ + gint src_width, gint src_stride, gint dest_stride, guint s_alpha, \ + GstCompositorBlendMode mode) \ +{ \ + s_alpha = MIN (255, s_alpha); \ + switch (mode) { \ + case COMPOSITOR_BLEND_MODE_SOURCE:\ + compositor_orc_source_##name (dest, dest_stride, src, src_stride, \ + s_alpha, src_width, src_height); \ + break;\ + case COMPOSITOR_BLEND_MODE_OVER:\ + compositor_orc_overlay_##name (dest, dest_stride, src, src_stride, \ + s_alpha, src_width, src_height); \ + break;\ + case COMPOSITOR_BLEND_MODE_ADD:\ + compositor_orc_overlay_##name##_addition (dest, dest_stride, src, src_stride, \ + s_alpha, src_width, src_height); \ + break;\ + }\ +} + +#define BLEND_A32_LOOP(name) \ +static inline void \ +_blend_loop_##name (guint8 * dest, const guint8 * src, gint src_height, \ + gint src_width, gint src_stride, gint dest_stride, guint s_alpha, \ + GstCompositorBlendMode mode) \ +{ \ + s_alpha = MIN (255, s_alpha); \ + switch (mode) { \ + case COMPOSITOR_BLEND_MODE_SOURCE:\ + compositor_orc_source_##name (dest, dest_stride, src, src_stride, \ + s_alpha, src_width, src_height); \ + break;\ + case COMPOSITOR_BLEND_MODE_OVER:\ + case COMPOSITOR_BLEND_MODE_ADD:\ + /* both modes are the same for opaque background */ \ + compositor_orc_blend_##name (dest, dest_stride, src, src_stride, \ + s_alpha, src_width, src_height); \ + break;\ + }\ +} + +OVERLAY_A32_LOOP (argb); +OVERLAY_A32_LOOP (bgra); +BLEND_A32_LOOP (argb); +BLEND_A32_LOOP (bgra); + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +BLEND_A32 (argb, blend, _blend_loop_argb); +BLEND_A32 (bgra, blend, _blend_loop_bgra); +BLEND_A32 (argb, overlay, _overlay_loop_argb); +BLEND_A32 (bgra, overlay, _overlay_loop_bgra); +#else +BLEND_A32 (argb, blend, _blend_loop_bgra); +BLEND_A32 (bgra, blend, _blend_loop_argb); +BLEND_A32 (argb, overlay, _overlay_loop_bgra); +BLEND_A32 (bgra, overlay, _overlay_loop_argb); +#endif + +#define A32_CHECKER_C(name, RGB, A, C1, C2, C3) \ +static void \ +fill_checker_##name##_c (GstVideoFrame * frame) \ +{ \ + gint i, j; \ + gint val; \ + static const gint tab[] = { 80, 160, 80, 160 }; \ + gint width, height; \ + guint8 *dest; \ + \ + dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \ + width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \ + height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \ + \ + if (!RGB) { \ + for (i = 0; i < height; i++) { \ + for (j = 0; j < width; j++) { \ + dest[A] = 0xff; \ + dest[C1] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \ + dest[C2] = 128; \ + dest[C3] = 128; \ + dest += 4; \ + } \ + } \ + } else { \ + for (i = 0; i < height; i++) { \ + for (j = 0; j < width; j++) { \ + val = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \ + dest[A] = 0xFF; \ + dest[C1] = val; \ + dest[C2] = val; \ + dest[C3] = val; \ + dest += 4; \ + } \ + } \ + } \ +} + +A32_CHECKER_C (argb, TRUE, 0, 1, 2, 3); +A32_CHECKER_C (bgra, TRUE, 3, 2, 1, 0); +A32_CHECKER_C (ayuv, FALSE, 0, 1, 2, 3); + +#define YUV_TO_R(Y,U,V) (CLAMP (1.164 * (Y - 16) + 1.596 * (V - 128), 0, 255)) +#define YUV_TO_G(Y,U,V) (CLAMP (1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128), 0, 255)) +#define YUV_TO_B(Y,U,V) (CLAMP (1.164 * (Y - 16) + 2.018 * (U - 128), 0, 255)) + +#define A32_COLOR(name, RGB, A, C1, C2, C3) \ +static void \ +fill_color_##name (GstVideoFrame * frame, gint Y, gint U, gint V) \ +{ \ + gint c1, c2, c3; \ + guint32 val; \ + gint width, height; \ + guint8 *dest; \ + \ + dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \ + width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \ + height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \ + \ + if (RGB) { \ + c1 = YUV_TO_R (Y, U, V); \ + c2 = YUV_TO_G (Y, U, V); \ + c3 = YUV_TO_B (Y, U, V); \ + } else { \ + c1 = Y; \ + c2 = U; \ + c3 = V; \ + } \ + val = GUINT32_FROM_BE ((0xff << A) | (c1 << C1) | (c2 << C2) | (c3 << C3)); \ + \ + compositor_orc_splat_u32 ((guint32 *) dest, val, height * width); \ +} + +A32_COLOR (argb, TRUE, 24, 16, 8, 0); +A32_COLOR (bgra, TRUE, 0, 8, 16, 24); +A32_COLOR (abgr, TRUE, 24, 0, 8, 16); +A32_COLOR (rgba, TRUE, 0, 24, 16, 8); +A32_COLOR (ayuv, FALSE, 24, 16, 8, 0); + +/* Y444, Y42B, I420, YV12, Y41B */ +#define PLANAR_YUV_BLEND(format_name,format_enum,x_round,y_round,MEMCPY,BLENDLOOP) \ +inline static void \ +_blend_##format_name (const guint8 * src, guint8 * dest, \ + gint src_stride, gint dest_stride, gint src_width, gint src_height, \ + gdouble src_alpha, GstCompositorBlendMode mode) \ +{ \ + gint i; \ + gint b_alpha; \ + \ + /* in source mode we just have to copy over things */ \ + if (mode == COMPOSITOR_BLEND_MODE_SOURCE) { \ + src_alpha = 1.0; \ + } \ + \ + /* If it's completely transparent... we just return */ \ + if (G_UNLIKELY (src_alpha == 0.0)) { \ + GST_INFO ("Fast copy (alpha == 0.0)"); \ + return; \ + } \ + \ + /* If it's completely opaque, we do a fast copy */ \ + if (G_UNLIKELY (src_alpha == 1.0)) { \ + GST_INFO ("Fast copy (alpha == 1.0)"); \ + for (i = 0; i < src_height; i++) { \ + MEMCPY (dest, src, src_width); \ + src += src_stride; \ + dest += dest_stride; \ + } \ + return; \ + } \ + \ + b_alpha = CLAMP ((gint) (src_alpha * 255), 0, 255); \ + \ + BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width, src_height);\ +} \ +\ +static void \ +blend_##format_name (GstVideoFrame * srcframe, gint xpos, gint ypos, \ + gdouble src_alpha, GstVideoFrame * destframe, GstCompositorBlendMode mode) \ +{ \ + const guint8 *b_src; \ + guint8 *b_dest; \ + gint b_src_width; \ + gint b_src_height; \ + gint xoffset = 0; \ + gint yoffset = 0; \ + gint src_comp_rowstride, dest_comp_rowstride; \ + gint src_comp_height; \ + gint src_comp_width; \ + gint comp_ypos, comp_xpos; \ + gint comp_yoffset, comp_xoffset; \ + gint dest_width, dest_height; \ + const GstVideoFormatInfo *info; \ + gint src_width, src_height; \ + \ + src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \ + src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \ + \ + info = srcframe->info.finfo; \ + dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \ + dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \ + \ + xpos = x_round (xpos); \ + ypos = y_round (ypos); \ + \ + b_src_width = src_width; \ + b_src_height = src_height; \ + \ + /* adjust src pointers for negative sizes */ \ + if (xpos < 0) { \ + xoffset = -xpos; \ + b_src_width -= -xpos; \ + xpos = 0; \ + } \ + if (ypos < 0) { \ + yoffset = -ypos; \ + b_src_height -= -ypos; \ + ypos = 0; \ + } \ + /* If x or y offset are larger then the source it's outside of the picture */ \ + if (xoffset >= src_width || yoffset >= src_height) { \ + return; \ + } \ + \ + /* adjust width/height if the src is bigger than dest */ \ + if (xpos + b_src_width > dest_width) { \ + b_src_width = dest_width - xpos; \ + } \ + if (ypos + b_src_height > dest_height) { \ + b_src_height = dest_height - ypos; \ + } \ + if (b_src_width <= 0 || b_src_height <= 0) { \ + return; \ + } \ + \ + /* First mix Y, then U, then V */ \ + b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 0); \ + b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 0); \ + src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \ + dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \ + src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 0, b_src_width); \ + src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, b_src_height); \ + comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xpos); \ + comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, ypos); \ + comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xoffset); \ + comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, yoffset); \ + _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \ + b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \ + src_comp_rowstride, \ + dest_comp_rowstride, src_comp_width, src_comp_height, \ + src_alpha, mode); \ + \ + b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 1); \ + b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 1); \ + src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 1); \ + dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 1); \ + src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 1, b_src_width); \ + src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, b_src_height); \ + comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xpos); \ + comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, ypos); \ + comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xoffset); \ + comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, yoffset); \ + _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \ + b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \ + src_comp_rowstride, \ + dest_comp_rowstride, src_comp_width, src_comp_height, \ + src_alpha, mode); \ + \ + b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 2); \ + b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 2); \ + src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 2); \ + dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 2); \ + src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 2, b_src_width); \ + src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 2, b_src_height); \ + comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 2, xpos); \ + comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 2, ypos); \ + comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 2, xoffset); \ + comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 2, yoffset); \ + _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \ + b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \ + src_comp_rowstride, \ + dest_comp_rowstride, src_comp_width, src_comp_height, \ + src_alpha, mode); \ +} + +#define PLANAR_YUV_FILL_CHECKER(format_name, format_enum, MEMSET) \ +static void \ +fill_checker_##format_name (GstVideoFrame * frame) \ +{ \ + gint i, j; \ + static const int tab[] = { 80, 160, 80, 160 }; \ + guint8 *p; \ + gint comp_width, comp_height; \ + gint rowstride; \ + \ + p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \ + comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \ + comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \ + rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \ + \ + for (i = 0; i < comp_height; i++) { \ + for (j = 0; j < comp_width; j++) { \ + *p++ = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \ + } \ + p += rowstride - comp_width; \ + } \ + \ + p = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \ + comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \ + comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \ + rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \ + \ + for (i = 0; i < comp_height; i++) { \ + MEMSET (p, 0x80, comp_width); \ + p += rowstride; \ + } \ + \ + p = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \ + comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 2); \ + comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 2); \ + rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); \ + \ + for (i = 0; i < comp_height; i++) { \ + MEMSET (p, 0x80, comp_width); \ + p += rowstride; \ + } \ +} + +#define PLANAR_YUV_FILL_COLOR(format_name,format_enum,MEMSET) \ +static void \ +fill_color_##format_name (GstVideoFrame * frame, \ + gint colY, gint colU, gint colV) \ +{ \ + guint8 *p; \ + gint comp_width, comp_height; \ + gint rowstride; \ + gint i; \ + \ + p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \ + comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \ + comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \ + rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \ + \ + for (i = 0; i < comp_height; i++) { \ + MEMSET (p, colY, comp_width); \ + p += rowstride; \ + } \ + \ + p = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \ + comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \ + comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \ + rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \ + \ + for (i = 0; i < comp_height; i++) { \ + MEMSET (p, colU, comp_width); \ + p += rowstride; \ + } \ + \ + p = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \ + comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 2); \ + comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 2); \ + rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); \ + \ + for (i = 0; i < comp_height; i++) { \ + MEMSET (p, colV, comp_width); \ + p += rowstride; \ + } \ +} + +#define GST_ROUND_UP_1(x) (x) + +PLANAR_YUV_BLEND (i420, GST_VIDEO_FORMAT_I420, GST_ROUND_UP_2, + GST_ROUND_UP_2, memcpy, compositor_orc_blend_u8); +PLANAR_YUV_FILL_CHECKER (i420, GST_VIDEO_FORMAT_I420, memset); +PLANAR_YUV_FILL_COLOR (i420, GST_VIDEO_FORMAT_I420, memset); +PLANAR_YUV_FILL_COLOR (yv12, GST_VIDEO_FORMAT_YV12, memset); +PLANAR_YUV_BLEND (y444, GST_VIDEO_FORMAT_Y444, GST_ROUND_UP_1, + GST_ROUND_UP_1, memcpy, compositor_orc_blend_u8); +PLANAR_YUV_FILL_CHECKER (y444, GST_VIDEO_FORMAT_Y444, memset); +PLANAR_YUV_FILL_COLOR (y444, GST_VIDEO_FORMAT_Y444, memset); +PLANAR_YUV_BLEND (y42b, GST_VIDEO_FORMAT_Y42B, GST_ROUND_UP_2, + GST_ROUND_UP_1, memcpy, compositor_orc_blend_u8); +PLANAR_YUV_FILL_CHECKER (y42b, GST_VIDEO_FORMAT_Y42B, memset); +PLANAR_YUV_FILL_COLOR (y42b, GST_VIDEO_FORMAT_Y42B, memset); +PLANAR_YUV_BLEND (y41b, GST_VIDEO_FORMAT_Y41B, GST_ROUND_UP_4, + GST_ROUND_UP_1, memcpy, compositor_orc_blend_u8); +PLANAR_YUV_FILL_CHECKER (y41b, GST_VIDEO_FORMAT_Y41B, memset); +PLANAR_YUV_FILL_COLOR (y41b, GST_VIDEO_FORMAT_Y41B, memset); + +/* NV12, NV21 */ +#define NV_YUV_BLEND(format_name,MEMCPY,BLENDLOOP) \ +inline static void \ +_blend_##format_name (const guint8 * src, guint8 * dest, \ + gint src_stride, gint dest_stride, gint src_width, gint src_height, \ + gdouble src_alpha, GstCompositorBlendMode mode) \ +{ \ + gint i; \ + gint b_alpha; \ + \ + /* in source mode we just have to copy over things */ \ + if (mode == COMPOSITOR_BLEND_MODE_SOURCE) { \ + src_alpha = 1.0; \ + } \ + \ + /* If it's completely transparent... we just return */ \ + if (G_UNLIKELY (src_alpha == 0.0)) { \ + GST_INFO ("Fast copy (alpha == 0.0)"); \ + return; \ + } \ + \ + /* If it's completely opaque, we do a fast copy */ \ + if (G_UNLIKELY (src_alpha == 1.0)) { \ + GST_INFO ("Fast copy (alpha == 1.0)"); \ + for (i = 0; i < src_height; i++) { \ + MEMCPY (dest, src, src_width); \ + src += src_stride; \ + dest += dest_stride; \ + } \ + return; \ + } \ + \ + b_alpha = CLAMP ((gint) (src_alpha * 255), 0, 255); \ + \ + BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width, src_height); \ +} \ +\ +static void \ +blend_##format_name (GstVideoFrame * srcframe, gint xpos, gint ypos, \ + gdouble src_alpha, GstVideoFrame * destframe, GstCompositorBlendMode mode) \ +{ \ + const guint8 *b_src; \ + guint8 *b_dest; \ + gint b_src_width; \ + gint b_src_height; \ + gint xoffset = 0; \ + gint yoffset = 0; \ + gint src_comp_rowstride, dest_comp_rowstride; \ + gint src_comp_height; \ + gint src_comp_width; \ + gint comp_ypos, comp_xpos; \ + gint comp_yoffset, comp_xoffset; \ + gint dest_width, dest_height; \ + const GstVideoFormatInfo *info; \ + gint src_width, src_height; \ + \ + src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \ + src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \ + \ + info = srcframe->info.finfo; \ + dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \ + dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \ + \ + xpos = GST_ROUND_UP_2 (xpos); \ + ypos = GST_ROUND_UP_2 (ypos); \ + \ + b_src_width = src_width; \ + b_src_height = src_height; \ + \ + /* adjust src pointers for negative sizes */ \ + if (xpos < 0) { \ + xoffset = -xpos; \ + b_src_width -= -xpos; \ + xpos = 0; \ + } \ + if (ypos < 0) { \ + yoffset += -ypos; \ + b_src_height -= -ypos; \ + ypos = 0; \ + } \ + /* If x or y offset are larger then the source it's outside of the picture */ \ + if (xoffset > src_width || yoffset > src_height) { \ + return; \ + } \ + \ + /* adjust width/height if the src is bigger than dest */ \ + if (xpos + src_width > dest_width) { \ + b_src_width = dest_width - xpos; \ + } \ + if (ypos + src_height > dest_height) { \ + b_src_height = dest_height - ypos; \ + } \ + if (b_src_width < 0 || b_src_height < 0) { \ + return; \ + } \ + \ + /* First mix Y, then UV */ \ + b_src = GST_VIDEO_FRAME_COMP_DATA (srcframe, 0); \ + b_dest = GST_VIDEO_FRAME_COMP_DATA (destframe, 0); \ + src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \ + dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \ + src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 0, b_src_width); \ + src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 0, b_src_height); \ + comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xpos); \ + comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, ypos); \ + comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 0, xoffset); \ + comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 0, yoffset); \ + _blend_##format_name (b_src + comp_xoffset + comp_yoffset * src_comp_rowstride, \ + b_dest + comp_xpos + comp_ypos * dest_comp_rowstride, \ + src_comp_rowstride, \ + dest_comp_rowstride, src_comp_width, src_comp_height, \ + src_alpha, mode); \ + \ + b_src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 1); \ + b_dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 1); \ + src_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 1); \ + dest_comp_rowstride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 1); \ + src_comp_width = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH(info, 1, b_src_width); \ + src_comp_height = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT(info, 1, b_src_height); \ + comp_xpos = (xpos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xpos); \ + comp_ypos = (ypos == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, ypos); \ + comp_xoffset = (xoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (info, 1, xoffset); \ + comp_yoffset = (yoffset == 0) ? 0 : GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (info, 1, yoffset); \ + _blend_##format_name (b_src + comp_xoffset * 2 + comp_yoffset * src_comp_rowstride, \ + b_dest + comp_xpos * 2 + comp_ypos * dest_comp_rowstride, \ + src_comp_rowstride, \ + dest_comp_rowstride, 2 * src_comp_width, src_comp_height, \ + src_alpha, mode); \ +} + +#define NV_YUV_FILL_CHECKER(format_name, MEMSET) \ +static void \ +fill_checker_##format_name (GstVideoFrame * frame) \ +{ \ + gint i, j; \ + static const int tab[] = { 80, 160, 80, 160 }; \ + guint8 *p; \ + gint comp_width, comp_height; \ + gint rowstride; \ + \ + p = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \ + comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \ + comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \ + rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \ + \ + for (i = 0; i < comp_height; i++) { \ + for (j = 0; j < comp_width; j++) { \ + *p++ = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \ + } \ + p += rowstride - comp_width; \ + } \ + \ + p = GST_VIDEO_FRAME_PLANE_DATA (frame, 1); \ + comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \ + comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \ + rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \ + \ + for (i = 0; i < comp_height; i++) { \ + MEMSET (p, 0x80, comp_width * 2); \ + p += rowstride; \ + } \ +} + +#define NV_YUV_FILL_COLOR(format_name,MEMSET) \ +static void \ +fill_color_##format_name (GstVideoFrame * frame, \ + gint colY, gint colU, gint colV) \ +{ \ + guint8 *y, *u, *v; \ + gint comp_width, comp_height; \ + gint rowstride; \ + gint i, j; \ + \ + y = GST_VIDEO_FRAME_COMP_DATA (frame, 0); \ + comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); \ + comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); \ + rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \ + \ + for (i = 0; i < comp_height; i++) { \ + MEMSET (y, colY, comp_width); \ + y += rowstride; \ + } \ + \ + u = GST_VIDEO_FRAME_COMP_DATA (frame, 1); \ + v = GST_VIDEO_FRAME_COMP_DATA (frame, 2); \ + comp_width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 1); \ + comp_height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); \ + rowstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); \ + \ + for (i = 0; i < comp_height; i++) { \ + for (j = 0; j < comp_width; j++) { \ + u[j*2] = colU; \ + v[j*2] = colV; \ + } \ + u += rowstride; \ + v += rowstride; \ + } \ +} + +NV_YUV_BLEND (nv12, memcpy, compositor_orc_blend_u8); +NV_YUV_FILL_CHECKER (nv12, memset); +NV_YUV_FILL_COLOR (nv12, memset); +NV_YUV_BLEND (nv21, memcpy, compositor_orc_blend_u8); +NV_YUV_FILL_CHECKER (nv21, memset); + +/* RGB, BGR, xRGB, xBGR, RGBx, BGRx */ + +#define RGB_BLEND(name, bpp, MEMCPY, BLENDLOOP) \ +static void \ +blend_##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \ + gdouble src_alpha, GstVideoFrame * destframe, GstCompositorBlendMode mode) \ +{ \ + gint b_alpha; \ + gint i; \ + gint src_stride, dest_stride; \ + gint dest_width, dest_height; \ + guint8 *dest, *src; \ + gint src_width, src_height; \ + \ + src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \ + src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \ + \ + src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \ + dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \ + \ + dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \ + dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \ + \ + src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \ + dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \ + \ + b_alpha = CLAMP ((gint) (src_alpha * 255), 0, 255); \ + \ + /* adjust src pointers for negative sizes */ \ + if (xpos < 0) { \ + src += -xpos * bpp; \ + src_width -= -xpos; \ + xpos = 0; \ + } \ + if (ypos < 0) { \ + src += -ypos * src_stride; \ + src_height -= -ypos; \ + ypos = 0; \ + } \ + /* adjust width/height if the src is bigger than dest */ \ + if (xpos + src_width > dest_width) { \ + src_width = dest_width - xpos; \ + } \ + if (ypos + src_height > dest_height) { \ + src_height = dest_height - ypos; \ + } \ + \ + dest = dest + bpp * xpos + (ypos * dest_stride); \ + \ + /* in source mode we just have to copy over things */ \ + if (mode == COMPOSITOR_BLEND_MODE_SOURCE) { \ + src_alpha = 1.0; \ + } \ + \ + /* If it's completely transparent... we just return */ \ + if (G_UNLIKELY (src_alpha == 0.0)) { \ + GST_INFO ("Fast copy (alpha == 0.0)"); \ + return; \ + } \ + \ + /* If it's completely opaque, we do a fast copy */ \ + if (G_UNLIKELY (src_alpha == 1.0)) { \ + GST_INFO ("Fast copy (alpha == 1.0)"); \ + for (i = 0; i < src_height; i++) { \ + MEMCPY (dest, src, bpp * src_width); \ + src += src_stride; \ + dest += dest_stride; \ + } \ + return; \ + } \ + \ + BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, src_width * bpp, src_height); \ +} + +#define RGB_FILL_CHECKER_C(name, bpp, r, g, b) \ +static void \ +fill_checker_##name##_c (GstVideoFrame * frame) \ +{ \ + gint i, j; \ + static const int tab[] = { 80, 160, 80, 160 }; \ + gint stride, dest_add, width, height; \ + guint8 *dest; \ + \ + width = GST_VIDEO_FRAME_WIDTH (frame); \ + height = GST_VIDEO_FRAME_HEIGHT (frame); \ + dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \ + stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \ + dest_add = stride - width * bpp; \ + \ + for (i = 0; i < height; i++) { \ + for (j = 0; j < width; j++) { \ + dest[r] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; /* red */ \ + dest[g] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; /* green */ \ + dest[b] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; /* blue */ \ + dest += bpp; \ + } \ + dest += dest_add; \ + } \ +} + +#define RGB_FILL_COLOR(name, bpp, MEMSET_RGB) \ +static void \ +fill_color_##name (GstVideoFrame * frame, \ + gint colY, gint colU, gint colV) \ +{ \ + gint red, green, blue; \ + gint i; \ + gint dest_stride; \ + gint width, height; \ + guint8 *dest; \ + \ + width = GST_VIDEO_FRAME_WIDTH (frame); \ + height = GST_VIDEO_FRAME_HEIGHT (frame); \ + dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \ + dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \ + \ + red = YUV_TO_R (colY, colU, colV); \ + green = YUV_TO_G (colY, colU, colV); \ + blue = YUV_TO_B (colY, colU, colV); \ + \ + for (i = 0; i < height; i++) { \ + MEMSET_RGB (dest, red, green, blue, width); \ + dest += dest_stride; \ + } \ +} + +#define MEMSET_RGB_C(name, r, g, b) \ +static inline void \ +_memset_##name##_c (guint8* dest, gint red, gint green, gint blue, gint width) { \ + gint j; \ + \ + for (j = 0; j < width; j++) { \ + dest[r] = red; \ + dest[g] = green; \ + dest[b] = blue; \ + dest += 3; \ + } \ +} + +#define MEMSET_XRGB(name, r, g, b) \ +static inline void \ +_memset_##name (guint8* dest, gint red, gint green, gint blue, gint width) { \ + guint32 val; \ + \ + val = GUINT32_FROM_BE ((red << r) | (green << g) | (blue << b)); \ + compositor_orc_splat_u32 ((guint32 *) dest, val, width); \ +} + +#define _orc_memcpy_u32(dest,src,len) compositor_orc_memcpy_u32((guint32 *) dest, (const guint32 *) src, len/4) + +RGB_BLEND (rgb, 3, memcpy, compositor_orc_blend_u8); +RGB_FILL_CHECKER_C (rgb, 3, 0, 1, 2); +MEMSET_RGB_C (rgb, 0, 1, 2); +RGB_FILL_COLOR (rgb_c, 3, _memset_rgb_c); + +MEMSET_RGB_C (bgr, 2, 1, 0); +RGB_FILL_COLOR (bgr_c, 3, _memset_bgr_c); + +RGB_BLEND (xrgb, 4, _orc_memcpy_u32, compositor_orc_blend_u8); +RGB_FILL_CHECKER_C (xrgb, 4, 1, 2, 3); +MEMSET_XRGB (xrgb, 24, 16, 0); +RGB_FILL_COLOR (xrgb, 4, _memset_xrgb); + +MEMSET_XRGB (xbgr, 0, 16, 24); +RGB_FILL_COLOR (xbgr, 4, _memset_xbgr); + +MEMSET_XRGB (rgbx, 24, 16, 8); +RGB_FILL_COLOR (rgbx, 4, _memset_rgbx); + +MEMSET_XRGB (bgrx, 8, 16, 24); +RGB_FILL_COLOR (bgrx, 4, _memset_bgrx); + +/* YUY2, YVYU, UYVY */ + +#define PACKED_422_BLEND(name, MEMCPY, BLENDLOOP) \ +static void \ +blend_##name (GstVideoFrame * srcframe, gint xpos, gint ypos, \ + gdouble src_alpha, GstVideoFrame * destframe, GstCompositorBlendMode mode) \ +{ \ + gint b_alpha; \ + gint i; \ + gint src_stride, dest_stride; \ + gint dest_width, dest_height; \ + guint8 *src, *dest; \ + gint src_width, src_height; \ + \ + src_width = GST_VIDEO_FRAME_WIDTH (srcframe); \ + src_height = GST_VIDEO_FRAME_HEIGHT (srcframe); \ + \ + dest_width = GST_VIDEO_FRAME_WIDTH (destframe); \ + dest_height = GST_VIDEO_FRAME_HEIGHT (destframe); \ + \ + src = GST_VIDEO_FRAME_PLANE_DATA (srcframe, 0); \ + dest = GST_VIDEO_FRAME_PLANE_DATA (destframe, 0); \ + \ + src_stride = GST_VIDEO_FRAME_COMP_STRIDE (srcframe, 0); \ + dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (destframe, 0); \ + \ + b_alpha = CLAMP ((gint) (src_alpha * 255), 0, 255); \ + \ + xpos = GST_ROUND_UP_2 (xpos); \ + \ + /* adjust src pointers for negative sizes */ \ + if (xpos < 0) { \ + src += -xpos * 2; \ + src_width -= -xpos; \ + xpos = 0; \ + } \ + if (ypos < 0) { \ + src += -ypos * src_stride; \ + src_height -= -ypos; \ + ypos = 0; \ + } \ + \ + /* adjust width/height if the src is bigger than dest */ \ + if (xpos + src_width > dest_width) { \ + src_width = dest_width - xpos; \ + } \ + if (ypos + src_height > dest_height) { \ + src_height = dest_height - ypos; \ + } \ + \ + dest = dest + 2 * xpos + (ypos * dest_stride); \ + \ + /* in source mode we just have to copy over things */ \ + if (mode == COMPOSITOR_BLEND_MODE_SOURCE) { \ + src_alpha = 1.0; \ + } \ + \ + /* If it's completely transparent... we just return */ \ + if (G_UNLIKELY (src_alpha == 0.0)) { \ + GST_INFO ("Fast copy (alpha == 0.0)"); \ + return; \ + } \ + \ + /* If it's completely opaque, we do a fast copy */ \ + if (G_UNLIKELY (src_alpha == 1.0)) { \ + GST_INFO ("Fast copy (alpha == 1.0)"); \ + for (i = 0; i < src_height; i++) { \ + MEMCPY (dest, src, 2 * src_width); \ + src += src_stride; \ + dest += dest_stride; \ + } \ + return; \ + } \ + \ + BLENDLOOP(dest, dest_stride, src, src_stride, b_alpha, 2 * src_width, src_height); \ +} + +#define PACKED_422_FILL_CHECKER_C(name, Y1, U, Y2, V) \ +static void \ +fill_checker_##name##_c (GstVideoFrame * frame) \ +{ \ + gint i, j; \ + static const int tab[] = { 80, 160, 80, 160 }; \ + gint dest_add; \ + gint width, height; \ + guint8 *dest; \ + \ + width = GST_VIDEO_FRAME_WIDTH (frame); \ + width = GST_ROUND_UP_2 (width); \ + height = GST_VIDEO_FRAME_HEIGHT (frame); \ + dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \ + dest_add = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0) - width * 2; \ + width /= 2; \ + \ + for (i = 0; i < height; i++) { \ + for (j = 0; j < width; j++) { \ + dest[Y1] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \ + dest[Y2] = tab[((i & 0x8) >> 3) + ((j & 0x8) >> 3)]; \ + dest[U] = 128; \ + dest[V] = 128; \ + dest += 4; \ + } \ + dest += dest_add; \ + } \ +} + +#define PACKED_422_FILL_COLOR(name, Y1, U, Y2, V) \ +static void \ +fill_color_##name (GstVideoFrame * frame, \ + gint colY, gint colU, gint colV) \ +{ \ + gint i; \ + gint dest_stride; \ + guint32 val; \ + gint width, height; \ + guint8 *dest; \ + \ + width = GST_VIDEO_FRAME_WIDTH (frame); \ + width = GST_ROUND_UP_2 (width); \ + height = GST_VIDEO_FRAME_HEIGHT (frame); \ + dest = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); \ + dest_stride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); \ + width /= 2; \ + \ + val = GUINT32_FROM_BE ((colY << Y1) | (colY << Y2) | (colU << U) | (colV << V)); \ + \ + for (i = 0; i < height; i++) { \ + compositor_orc_splat_u32 ((guint32 *) dest, val, width); \ + dest += dest_stride; \ + } \ +} + +PACKED_422_BLEND (yuy2, memcpy, compositor_orc_blend_u8); +PACKED_422_FILL_CHECKER_C (yuy2, 0, 1, 2, 3); +PACKED_422_FILL_CHECKER_C (uyvy, 1, 0, 3, 2); +PACKED_422_FILL_COLOR (yuy2, 24, 16, 8, 0); +PACKED_422_FILL_COLOR (yvyu, 24, 0, 8, 16); +PACKED_422_FILL_COLOR (uyvy, 16, 24, 0, 8); + +/* Init function */ +BlendFunction gst_compositor_blend_argb; +BlendFunction gst_compositor_blend_bgra; +BlendFunction gst_compositor_overlay_argb; +BlendFunction gst_compositor_overlay_bgra; +/* AYUV/ABGR is equal to ARGB, RGBA is equal to BGRA */ +BlendFunction gst_compositor_blend_y444; +BlendFunction gst_compositor_blend_y42b; +BlendFunction gst_compositor_blend_i420; +/* I420 is equal to YV12 */ +BlendFunction gst_compositor_blend_nv12; +BlendFunction gst_compositor_blend_nv21; +BlendFunction gst_compositor_blend_y41b; +BlendFunction gst_compositor_blend_rgb; +/* BGR is equal to RGB */ +BlendFunction gst_compositor_blend_rgbx; +/* BGRx, xRGB, xBGR are equal to RGBx */ +BlendFunction gst_compositor_blend_yuy2; +/* YVYU and UYVY are equal to YUY2 */ + +FillCheckerFunction gst_compositor_fill_checker_argb; +FillCheckerFunction gst_compositor_fill_checker_bgra; +/* ABGR is equal to ARGB, RGBA is equal to BGRA */ +FillCheckerFunction gst_compositor_fill_checker_ayuv; +FillCheckerFunction gst_compositor_fill_checker_y444; +FillCheckerFunction gst_compositor_fill_checker_y42b; +FillCheckerFunction gst_compositor_fill_checker_i420; +/* I420 is equal to YV12 */ +FillCheckerFunction gst_compositor_fill_checker_nv12; +FillCheckerFunction gst_compositor_fill_checker_nv21; +FillCheckerFunction gst_compositor_fill_checker_y41b; +FillCheckerFunction gst_compositor_fill_checker_rgb; +/* BGR is equal to RGB */ +FillCheckerFunction gst_compositor_fill_checker_xrgb; +/* BGRx, xRGB, xBGR are equal to RGBx */ +FillCheckerFunction gst_compositor_fill_checker_yuy2; +/* YVYU is equal to YUY2 */ +FillCheckerFunction gst_compositor_fill_checker_uyvy; + +FillColorFunction gst_compositor_fill_color_argb; +FillColorFunction gst_compositor_fill_color_bgra; +FillColorFunction gst_compositor_fill_color_abgr; +FillColorFunction gst_compositor_fill_color_rgba; +FillColorFunction gst_compositor_fill_color_ayuv; +FillColorFunction gst_compositor_fill_color_y444; +FillColorFunction gst_compositor_fill_color_y42b; +FillColorFunction gst_compositor_fill_color_i420; +FillColorFunction gst_compositor_fill_color_yv12; +FillColorFunction gst_compositor_fill_color_nv12; +/* NV21 is equal to NV12 */ +FillColorFunction gst_compositor_fill_color_y41b; +FillColorFunction gst_compositor_fill_color_rgb; +FillColorFunction gst_compositor_fill_color_bgr; +FillColorFunction gst_compositor_fill_color_xrgb; +FillColorFunction gst_compositor_fill_color_xbgr; +FillColorFunction gst_compositor_fill_color_rgbx; +FillColorFunction gst_compositor_fill_color_bgrx; +FillColorFunction gst_compositor_fill_color_yuy2; +FillColorFunction gst_compositor_fill_color_yvyu; +FillColorFunction gst_compositor_fill_color_uyvy; + +void +gst_compositor_init_blend (void) +{ + GST_DEBUG_CATEGORY_INIT (gst_compositor_blend_debug, "compositor_blend", 0, + "video compositor blending functions"); + + gst_compositor_blend_argb = GST_DEBUG_FUNCPTR (blend_argb); + gst_compositor_blend_bgra = GST_DEBUG_FUNCPTR (blend_bgra); + gst_compositor_overlay_argb = GST_DEBUG_FUNCPTR (overlay_argb); + gst_compositor_overlay_bgra = GST_DEBUG_FUNCPTR (overlay_bgra); + gst_compositor_blend_i420 = GST_DEBUG_FUNCPTR (blend_i420); + gst_compositor_blend_nv12 = GST_DEBUG_FUNCPTR (blend_nv12); + gst_compositor_blend_nv21 = GST_DEBUG_FUNCPTR (blend_nv21); + gst_compositor_blend_y444 = GST_DEBUG_FUNCPTR (blend_y444); + gst_compositor_blend_y42b = GST_DEBUG_FUNCPTR (blend_y42b); + gst_compositor_blend_y41b = GST_DEBUG_FUNCPTR (blend_y41b); + gst_compositor_blend_rgb = GST_DEBUG_FUNCPTR (blend_rgb); + gst_compositor_blend_xrgb = GST_DEBUG_FUNCPTR (blend_xrgb); + gst_compositor_blend_yuy2 = GST_DEBUG_FUNCPTR (blend_yuy2); + + gst_compositor_fill_checker_argb = GST_DEBUG_FUNCPTR (fill_checker_argb_c); + gst_compositor_fill_checker_bgra = GST_DEBUG_FUNCPTR (fill_checker_bgra_c); + gst_compositor_fill_checker_ayuv = GST_DEBUG_FUNCPTR (fill_checker_ayuv_c); + gst_compositor_fill_checker_i420 = GST_DEBUG_FUNCPTR (fill_checker_i420); + gst_compositor_fill_checker_nv12 = GST_DEBUG_FUNCPTR (fill_checker_nv12); + gst_compositor_fill_checker_nv21 = GST_DEBUG_FUNCPTR (fill_checker_nv21); + gst_compositor_fill_checker_y444 = GST_DEBUG_FUNCPTR (fill_checker_y444); + gst_compositor_fill_checker_y42b = GST_DEBUG_FUNCPTR (fill_checker_y42b); + gst_compositor_fill_checker_y41b = GST_DEBUG_FUNCPTR (fill_checker_y41b); + gst_compositor_fill_checker_rgb = GST_DEBUG_FUNCPTR (fill_checker_rgb_c); + gst_compositor_fill_checker_xrgb = GST_DEBUG_FUNCPTR (fill_checker_xrgb_c); + gst_compositor_fill_checker_yuy2 = GST_DEBUG_FUNCPTR (fill_checker_yuy2_c); + gst_compositor_fill_checker_uyvy = GST_DEBUG_FUNCPTR (fill_checker_uyvy_c); + + gst_compositor_fill_color_argb = GST_DEBUG_FUNCPTR (fill_color_argb); + gst_compositor_fill_color_bgra = GST_DEBUG_FUNCPTR (fill_color_bgra); + gst_compositor_fill_color_abgr = GST_DEBUG_FUNCPTR (fill_color_abgr); + gst_compositor_fill_color_rgba = GST_DEBUG_FUNCPTR (fill_color_rgba); + gst_compositor_fill_color_ayuv = GST_DEBUG_FUNCPTR (fill_color_ayuv); + gst_compositor_fill_color_i420 = GST_DEBUG_FUNCPTR (fill_color_i420); + gst_compositor_fill_color_yv12 = GST_DEBUG_FUNCPTR (fill_color_yv12); + gst_compositor_fill_color_nv12 = GST_DEBUG_FUNCPTR (fill_color_nv12); + gst_compositor_fill_color_y444 = GST_DEBUG_FUNCPTR (fill_color_y444); + gst_compositor_fill_color_y42b = GST_DEBUG_FUNCPTR (fill_color_y42b); + gst_compositor_fill_color_y41b = GST_DEBUG_FUNCPTR (fill_color_y41b); + gst_compositor_fill_color_rgb = GST_DEBUG_FUNCPTR (fill_color_rgb_c); + gst_compositor_fill_color_bgr = GST_DEBUG_FUNCPTR (fill_color_bgr_c); + gst_compositor_fill_color_xrgb = GST_DEBUG_FUNCPTR (fill_color_xrgb); + gst_compositor_fill_color_xbgr = GST_DEBUG_FUNCPTR (fill_color_xbgr); + gst_compositor_fill_color_rgbx = GST_DEBUG_FUNCPTR (fill_color_rgbx); + gst_compositor_fill_color_bgrx = GST_DEBUG_FUNCPTR (fill_color_bgrx); + gst_compositor_fill_color_yuy2 = GST_DEBUG_FUNCPTR (fill_color_yuy2); + gst_compositor_fill_color_yvyu = GST_DEBUG_FUNCPTR (fill_color_yvyu); + gst_compositor_fill_color_uyvy = GST_DEBUG_FUNCPTR (fill_color_uyvy); +} diff --git a/gst/compositor/blend.h b/gst/compositor/blend.h new file mode 100644 index 0000000000..710ec7dad0 --- /dev/null +++ b/gst/compositor/blend.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2009 Sebastian Dröge + * + * 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. + */ + +#ifndef __BLEND_H__ +#define __BLEND_H__ + +#include +#include + +/** + * GstCompositorBlendMode: + * @COMPOSITOR_BLEND_MODE_SOURCE: Copy source + * @COMPOSITOR_BLEND_MODE_OVER: Normal blending + * @COMPOSITOR_BLEND_MODE_ADD: Alphas are simply added, + * + * The different modes compositor can use for blending. + */ +typedef enum +{ + COMPOSITOR_BLEND_MODE_SOURCE, + COMPOSITOR_BLEND_MODE_OVER, + COMPOSITOR_BLEND_MODE_ADD, +} GstCompositorBlendMode; + +typedef void (*BlendFunction) (GstVideoFrame *srcframe, gint xpos, gint ypos, gdouble src_alpha, GstVideoFrame * destframe, + GstCompositorBlendMode mode); +typedef void (*FillCheckerFunction) (GstVideoFrame * frame); +typedef void (*FillColorFunction) (GstVideoFrame * frame, gint c1, gint c2, gint c3); + +extern BlendFunction gst_compositor_blend_argb; +extern BlendFunction gst_compositor_blend_bgra; +#define gst_compositor_blend_ayuv gst_compositor_blend_argb +#define gst_compositor_blend_abgr gst_compositor_blend_argb +#define gst_compositor_blend_rgba gst_compositor_blend_bgra + +extern BlendFunction gst_compositor_overlay_argb; +extern BlendFunction gst_compositor_overlay_bgra; +#define gst_compositor_overlay_ayuv gst_compositor_overlay_argb +#define gst_compositor_overlay_abgr gst_compositor_overlay_argb +#define gst_compositor_overlay_rgba gst_compositor_overlay_bgra +extern BlendFunction gst_compositor_blend_i420; +#define gst_compositor_blend_yv12 gst_compositor_blend_i420 +extern BlendFunction gst_compositor_blend_nv12; +extern BlendFunction gst_compositor_blend_nv21; +extern BlendFunction gst_compositor_blend_y41b; +extern BlendFunction gst_compositor_blend_y42b; +extern BlendFunction gst_compositor_blend_y444; +extern BlendFunction gst_compositor_blend_rgb; +#define gst_compositor_blend_bgr gst_compositor_blend_rgb +extern BlendFunction gst_compositor_blend_rgbx; +#define gst_compositor_blend_bgrx gst_compositor_blend_rgbx +#define gst_compositor_blend_xrgb gst_compositor_blend_rgbx +#define gst_compositor_blend_xbgr gst_compositor_blend_rgbx +extern BlendFunction gst_compositor_blend_yuy2; +#define gst_compositor_blend_uyvy gst_compositor_blend_yuy2; +#define gst_compositor_blend_yvyu gst_compositor_blend_yuy2; + +extern FillCheckerFunction gst_compositor_fill_checker_argb; +#define gst_compositor_fill_checker_abgr gst_compositor_fill_checker_argb +extern FillCheckerFunction gst_compositor_fill_checker_bgra; +#define gst_compositor_fill_checker_rgba gst_compositor_fill_checker_bgra +extern FillCheckerFunction gst_compositor_fill_checker_ayuv; +extern FillCheckerFunction gst_compositor_fill_checker_i420; +#define gst_compositor_fill_checker_yv12 gst_compositor_fill_checker_i420 +extern FillCheckerFunction gst_compositor_fill_checker_nv12; +extern FillCheckerFunction gst_compositor_fill_checker_nv21; +extern FillCheckerFunction gst_compositor_fill_checker_y41b; +extern FillCheckerFunction gst_compositor_fill_checker_y42b; +extern FillCheckerFunction gst_compositor_fill_checker_y444; +extern FillCheckerFunction gst_compositor_fill_checker_rgb; +#define gst_compositor_fill_checker_bgr gst_compositor_fill_checker_rgb +extern FillCheckerFunction gst_compositor_fill_checker_rgbx; +#define gst_compositor_fill_checker_bgrx gst_compositor_fill_checker_rgbx +#define gst_compositor_fill_checker_xrgb gst_compositor_fill_checker_rgbx +#define gst_compositor_fill_checker_xbgr gst_compositor_fill_checker_rgbx +extern FillCheckerFunction gst_compositor_fill_checker_yuy2; +#define gst_compositor_fill_checker_yvyu gst_compositor_fill_checker_yuy2; +extern FillCheckerFunction gst_compositor_fill_checker_uyvy; + +extern FillColorFunction gst_compositor_fill_color_argb; +extern FillColorFunction gst_compositor_fill_color_abgr; +extern FillColorFunction gst_compositor_fill_color_bgra; +extern FillColorFunction gst_compositor_fill_color_rgba; +extern FillColorFunction gst_compositor_fill_color_ayuv; +extern FillColorFunction gst_compositor_fill_color_i420; +extern FillColorFunction gst_compositor_fill_color_yv12; +extern FillColorFunction gst_compositor_fill_color_nv12; +#define gst_compositor_fill_color_nv21 gst_compositor_fill_color_nv12; +extern FillColorFunction gst_compositor_fill_color_y41b; +extern FillColorFunction gst_compositor_fill_color_y42b; +extern FillColorFunction gst_compositor_fill_color_y444; +extern FillColorFunction gst_compositor_fill_color_rgb; +extern FillColorFunction gst_compositor_fill_color_bgr; +extern FillColorFunction gst_compositor_fill_color_xrgb; +extern FillColorFunction gst_compositor_fill_color_xbgr; +extern FillColorFunction gst_compositor_fill_color_rgbx; +extern FillColorFunction gst_compositor_fill_color_bgrx; +extern FillColorFunction gst_compositor_fill_color_yuy2; +extern FillColorFunction gst_compositor_fill_color_yvyu; +extern FillColorFunction gst_compositor_fill_color_uyvy; + +void gst_compositor_init_blend (void); + +#endif /* __BLEND_H__ */ diff --git a/gst/compositor/blendorc.h b/gst/compositor/blendorc.h new file mode 100644 index 0000000000..6a39edc7f8 --- /dev/null +++ b/gst/compositor/blendorc.h @@ -0,0 +1,96 @@ + +/* autogenerated from blendorc.orc */ + +#ifndef _BLENDORC_H_ +#define _BLENDORC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifndef _ORC_INTEGER_TYPEDEFS_ +#define _ORC_INTEGER_TYPEDEFS_ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +typedef int8_t orc_int8; +typedef int16_t orc_int16; +typedef int32_t orc_int32; +typedef int64_t orc_int64; +typedef uint8_t orc_uint8; +typedef uint16_t orc_uint16; +typedef uint32_t orc_uint32; +typedef uint64_t orc_uint64; +#define ORC_UINT64_C(x) UINT64_C(x) +#elif defined(_MSC_VER) +typedef signed __int8 orc_int8; +typedef signed __int16 orc_int16; +typedef signed __int32 orc_int32; +typedef signed __int64 orc_int64; +typedef unsigned __int8 orc_uint8; +typedef unsigned __int16 orc_uint16; +typedef unsigned __int32 orc_uint32; +typedef unsigned __int64 orc_uint64; +#define ORC_UINT64_C(x) (x##Ui64) +#define inline __inline +#else +#include +typedef signed char orc_int8; +typedef short orc_int16; +typedef int orc_int32; +typedef unsigned char orc_uint8; +typedef unsigned short orc_uint16; +typedef unsigned int orc_uint32; +#if INT_MAX == LONG_MAX +typedef long long orc_int64; +typedef unsigned long long orc_uint64; +#define ORC_UINT64_C(x) (x##ULL) +#else +typedef long orc_int64; +typedef unsigned long orc_uint64; +#define ORC_UINT64_C(x) (x##UL) +#endif +#endif +typedef union { orc_int16 i; orc_int8 x2[2]; } orc_union16; +typedef union { orc_int32 i; float f; orc_int16 x2[2]; orc_int8 x4[4]; } orc_union32; +typedef union { orc_int64 i; double f; orc_int32 x2[2]; float x2f[2]; orc_int16 x4[4]; } orc_union64; +#endif +#ifndef ORC_RESTRICT +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define ORC_RESTRICT restrict +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define ORC_RESTRICT __restrict__ +#else +#define ORC_RESTRICT +#endif +#endif + +#ifndef ORC_INTERNAL +#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +#define ORC_INTERNAL __hidden +#elif defined (__GNUC__) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#else +#define ORC_INTERNAL +#endif +#endif + +void compositor_orc_splat_u32 (guint32 * ORC_RESTRICT d1, int p1, int n); +void compositor_orc_memcpy_u32 (guint32 * ORC_RESTRICT d1, const guint32 * ORC_RESTRICT s1, int n); +void compositor_orc_blend_u8 (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_blend_argb (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_blend_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_overlay_argb (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_overlay_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/gst/compositor/compositor.c b/gst/compositor/compositor.c new file mode 100644 index 0000000000..3f43efebb8 --- /dev/null +++ b/gst/compositor/compositor.c @@ -0,0 +1,1104 @@ +/* Video compositor plugin + * Copyright (C) 2004, 2008 Wim Taymans + * Copyright (C) 2010 Sebastian Dröge + * Copyright (C) 2014 Mathieu Duponchelle + * Copyright (C) 2014 Thibault Saunier + * + * 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. + */ + +/** + * SECTION:element-compositor + * @title: compositor + * + * Compositor can accept AYUV, ARGB and BGRA video streams. For each of the requested + * sink pads it will compare the incoming geometry and framerate to define the + * output parameters. Indeed output video frames will have the geometry of the + * biggest incoming video stream and the framerate of the fastest incoming one. + * + * Compositor will do colorspace conversion. + * + * Individual parameters for each input stream can be configured on the + * #GstCompositorPad: + * + * * "xpos": The x-coordinate position of the top-left corner of the picture (#gint) + * * "ypos": The y-coordinate position of the top-left corner of the picture (#gint) + * * "width": The width of the picture; the input will be scaled if necessary (#gint) + * * "height": The height of the picture; the input will be scaled if necessary (#gint) + * * "alpha": The transparency of the picture; between 0.0 and 1.0. The blending + * is a simple copy when fully-transparent (0.0) and fully-opaque (1.0). (#gdouble) + * * "zorder": The z-order position of the picture in the composition (#guint) + * + * ## Sample pipelines + * |[ + * gst-launch-1.0 \ + * videotestsrc pattern=1 ! \ + * video/x-raw,format=AYUV,framerate=\(fraction\)10/1,width=100,height=100 ! \ + * videobox border-alpha=0 top=-70 bottom=-70 right=-220 ! \ + * compositor name=comp sink_0::alpha=0.7 sink_1::alpha=0.5 ! \ + * videoconvert ! xvimagesink \ + * videotestsrc ! \ + * video/x-raw,format=AYUV,framerate=\(fraction\)5/1,width=320,height=240 ! comp. + * ]| A pipeline to demonstrate compositor used together with videobox. + * This should show a 320x240 pixels video test source with some transparency + * showing the background checker pattern. Another video test source with just + * the snow pattern of 100x100 pixels is overlaid on top of the first one on + * the left vertically centered with a small transparency showing the first + * video test source behind and the checker pattern under it. Note that the + * framerate of the output video is 10 frames per second. + * |[ + * gst-launch-1.0 videotestsrc pattern=1 ! \ + * video/x-raw, framerate=\(fraction\)10/1, width=100, height=100 ! \ + * compositor name=comp ! videoconvert ! ximagesink \ + * videotestsrc ! \ + * video/x-raw, framerate=\(fraction\)5/1, width=320, height=240 ! comp. + * ]| A pipeline to demostrate bgra comping. (This does not demonstrate alpha blending). + * |[ + * gst-launch-1.0 videotestsrc pattern=1 ! \ + * video/x-raw,format =I420, framerate=\(fraction\)10/1, width=100, height=100 ! \ + * compositor name=comp ! videoconvert ! ximagesink \ + * videotestsrc ! \ + * video/x-raw,format=I420, framerate=\(fraction\)5/1, width=320, height=240 ! comp. + * ]| A pipeline to test I420 + * |[ + * gst-launch-1.0 compositor name=comp sink_1::alpha=0.5 sink_1::xpos=50 sink_1::ypos=50 ! \ + * videoconvert ! ximagesink \ + * videotestsrc pattern=snow timestamp-offset=3000000000 ! \ + * "video/x-raw,format=AYUV,width=640,height=480,framerate=(fraction)30/1" ! \ + * timeoverlay ! queue2 ! comp. \ + * videotestsrc pattern=smpte ! \ + * "video/x-raw,format=AYUV,width=800,height=600,framerate=(fraction)10/1" ! \ + * timeoverlay ! queue2 ! comp. + * ]| A pipeline to demonstrate synchronized compositing (the second stream starts after 3 seconds) + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "compositor.h" + +#ifdef DISABLE_ORC +#define orc_memset memset +#else +#include +#endif + +GST_DEBUG_CATEGORY_STATIC (gst_compositor_debug); +#define GST_CAT_DEFAULT gst_compositor_debug + +#define FORMATS " { AYUV, BGRA, ARGB, RGBA, ABGR, Y444, Y42B, YUY2, UYVY, "\ + " YVYU, I420, YV12, NV12, NV21, Y41B, RGB, BGR, xRGB, xBGR, "\ + " RGBx, BGRx } " + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (FORMATS)) + ); + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%u", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (FORMATS)) + ); + +static void gst_compositor_child_proxy_init (gpointer g_iface, + gpointer iface_data); + +#define GST_TYPE_COMPOSITOR_OPERATOR (gst_compositor_operator_get_type()) +static GType +gst_compositor_operator_get_type (void) +{ + static GType compositor_operator_type = 0; + + static const GEnumValue compositor_operator[] = { + {COMPOSITOR_OPERATOR_SOURCE, "Source", "source"}, + {COMPOSITOR_OPERATOR_OVER, "Over", "over"}, + {COMPOSITOR_OPERATOR_ADD, "Add", "add"}, + {0, NULL, NULL}, + }; + + if (!compositor_operator_type) { + compositor_operator_type = + g_enum_register_static ("GstCompositorOperator", compositor_operator); + } + return compositor_operator_type; +} + +#define DEFAULT_PAD_XPOS 0 +#define DEFAULT_PAD_YPOS 0 +#define DEFAULT_PAD_WIDTH 0 +#define DEFAULT_PAD_HEIGHT 0 +#define DEFAULT_PAD_ALPHA 1.0 +#define DEFAULT_PAD_OPERATOR COMPOSITOR_OPERATOR_OVER +enum +{ + PROP_PAD_0, + PROP_PAD_XPOS, + PROP_PAD_YPOS, + PROP_PAD_WIDTH, + PROP_PAD_HEIGHT, + PROP_PAD_ALPHA, + PROP_PAD_OPERATOR, +}; + +G_DEFINE_TYPE (GstCompositorPad, gst_compositor_pad, + GST_TYPE_VIDEO_AGGREGATOR_CONVERT_PAD); + +static void +gst_compositor_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstCompositorPad *pad = GST_COMPOSITOR_PAD (object); + + switch (prop_id) { + case PROP_PAD_XPOS: + g_value_set_int (value, pad->xpos); + break; + case PROP_PAD_YPOS: + g_value_set_int (value, pad->ypos); + break; + case PROP_PAD_WIDTH: + g_value_set_int (value, pad->width); + break; + case PROP_PAD_HEIGHT: + g_value_set_int (value, pad->height); + break; + case PROP_PAD_ALPHA: + g_value_set_double (value, pad->alpha); + break; + case PROP_PAD_OPERATOR: + g_value_set_enum (value, pad->op); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_compositor_pad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstCompositorPad *pad = GST_COMPOSITOR_PAD (object); + + switch (prop_id) { + case PROP_PAD_XPOS: + pad->xpos = g_value_get_int (value); + break; + case PROP_PAD_YPOS: + pad->ypos = g_value_get_int (value); + break; + case PROP_PAD_WIDTH: + pad->width = g_value_get_int (value); + gst_video_aggregator_convert_pad_update_conversion_info + (GST_VIDEO_AGGREGATOR_CONVERT_PAD (pad)); + break; + case PROP_PAD_HEIGHT: + pad->height = g_value_get_int (value); + gst_video_aggregator_convert_pad_update_conversion_info + (GST_VIDEO_AGGREGATOR_CONVERT_PAD (pad)); + break; + case PROP_PAD_ALPHA: + pad->alpha = g_value_get_double (value); + break; + case PROP_PAD_OPERATOR: + pad->op = g_value_get_enum (value); + gst_video_aggregator_pad_set_needs_alpha (GST_VIDEO_AGGREGATOR_PAD (pad), + pad->op == COMPOSITOR_OPERATOR_ADD); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +_mixer_pad_get_output_size (GstCompositor * comp, + GstCompositorPad * comp_pad, gint out_par_n, gint out_par_d, gint * width, + gint * height) +{ + GstVideoAggregatorPad *vagg_pad = GST_VIDEO_AGGREGATOR_PAD (comp_pad); + gint pad_width, pad_height; + guint dar_n, dar_d; + + /* FIXME: Anything better we can do here? */ + if (!vagg_pad->info.finfo + || vagg_pad->info.finfo->format == GST_VIDEO_FORMAT_UNKNOWN) { + GST_DEBUG_OBJECT (comp_pad, "Have no caps yet"); + *width = 0; + *height = 0; + return; + } + + pad_width = + comp_pad->width <= + 0 ? GST_VIDEO_INFO_WIDTH (&vagg_pad->info) : comp_pad->width; + pad_height = + comp_pad->height <= + 0 ? GST_VIDEO_INFO_HEIGHT (&vagg_pad->info) : comp_pad->height; + + if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, pad_width, pad_height, + GST_VIDEO_INFO_PAR_N (&vagg_pad->info), + GST_VIDEO_INFO_PAR_D (&vagg_pad->info), out_par_n, out_par_d)) { + GST_WARNING_OBJECT (comp_pad, "Cannot calculate display aspect ratio"); + *width = *height = 0; + return; + } + GST_LOG_OBJECT (comp_pad, "scaling %ux%u by %u/%u (%u/%u / %u/%u)", pad_width, + pad_height, dar_n, dar_d, GST_VIDEO_INFO_PAR_N (&vagg_pad->info), + GST_VIDEO_INFO_PAR_D (&vagg_pad->info), out_par_n, out_par_d); + + if (pad_height % dar_n == 0) { + pad_width = gst_util_uint64_scale_int (pad_height, dar_n, dar_d); + } else if (pad_width % dar_d == 0) { + pad_height = gst_util_uint64_scale_int (pad_width, dar_d, dar_n); + } else { + pad_width = gst_util_uint64_scale_int (pad_height, dar_n, dar_d); + } + + *width = pad_width; + *height = pad_height; +} + +/* Test whether rectangle2 contains rectangle 1 (geometrically) */ +static gboolean +is_rectangle_contained (GstVideoRectangle rect1, GstVideoRectangle rect2) +{ + if ((rect2.x <= rect1.x) && (rect2.y <= rect1.y) && + ((rect2.x + rect2.w) >= (rect1.x + rect1.w)) && + ((rect2.y + rect2.h) >= (rect1.y + rect1.h))) + return TRUE; + return FALSE; +} + +static GstVideoRectangle +clamp_rectangle (gint x, gint y, gint w, gint h, gint outer_width, + gint outer_height) +{ + gint x2 = x + w; + gint y2 = y + h; + GstVideoRectangle clamped; + + /* Clamp the x/y coordinates of this frame to the output boundaries to cover + * the case where (say, with negative xpos/ypos or w/h greater than the output + * size) the non-obscured portion of the frame could be outside the bounds of + * the video itself and hence not visible at all */ + clamped.x = CLAMP (x, 0, outer_width); + clamped.y = CLAMP (y, 0, outer_height); + clamped.w = CLAMP (x2, 0, outer_width) - clamped.x; + clamped.h = CLAMP (y2, 0, outer_height) - clamped.y; + + return clamped; +} + +static gboolean +gst_compositor_pad_prepare_frame (GstVideoAggregatorPad * pad, + GstVideoAggregator * vagg, GstBuffer * buffer, + GstVideoFrame * prepared_frame) +{ + GstCompositor *comp = GST_COMPOSITOR (vagg); + GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad); + gint width, height; + gboolean frame_obscured = FALSE; + GList *l; + /* The rectangle representing this frame, clamped to the video's boundaries. + * Due to the clamping, this is different from the frame width/height above. */ + GstVideoRectangle frame_rect; + + /* There's three types of width/height here: + * 1. GST_VIDEO_FRAME_WIDTH/HEIGHT: + * The frame width/height (same as pad->info.height/width; + * see gst_video_frame_map()) + * 2. cpad->width/height: + * The optional pad property for scaling the frame (if zero, the video is + * left unscaled) + * 3. conversion_info.width/height: + * Equal to cpad->width/height if it's set, otherwise it's the pad + * width/height. See ->set_info() + * */ + + _mixer_pad_get_output_size (comp, cpad, GST_VIDEO_INFO_PAR_N (&vagg->info), + GST_VIDEO_INFO_PAR_D (&vagg->info), &width, &height); + + if (cpad->alpha == 0.0) { + GST_DEBUG_OBJECT (pad, "Pad has alpha 0.0, not converting frame"); + goto done; + } + + frame_rect = clamp_rectangle (cpad->xpos, cpad->ypos, width, height, + GST_VIDEO_INFO_WIDTH (&vagg->info), GST_VIDEO_INFO_HEIGHT (&vagg->info)); + + if (frame_rect.w == 0 || frame_rect.h == 0) { + GST_DEBUG_OBJECT (pad, "Resulting frame is zero-width or zero-height " + "(w: %i, h: %i), skipping", frame_rect.w, frame_rect.h); + goto done; + } + + GST_OBJECT_LOCK (vagg); + /* Check if this frame is obscured by a higher-zorder frame + * TODO: Also skip a frame if it's obscured by a combination of + * higher-zorder frames */ + l = g_list_find (GST_ELEMENT (vagg)->sinkpads, pad)->next; + for (; l; l = l->next) { + GstVideoRectangle frame2_rect; + GstVideoAggregatorPad *pad2 = l->data; + GstCompositorPad *cpad2 = GST_COMPOSITOR_PAD (pad2); + gint pad2_width, pad2_height; + + _mixer_pad_get_output_size (comp, cpad2, GST_VIDEO_INFO_PAR_N (&vagg->info), + GST_VIDEO_INFO_PAR_D (&vagg->info), &pad2_width, &pad2_height); + + /* We don't need to clamp the coords of the second rectangle */ + frame2_rect.x = cpad2->xpos; + frame2_rect.y = cpad2->ypos; + /* This is effectively what set_info and the above conversion + * code do to calculate the desired width/height */ + frame2_rect.w = pad2_width; + frame2_rect.h = pad2_height; + + /* Check if there's a buffer to be aggregated, ensure it can't have an alpha + * channel, then check opacity and frame boundaries */ + if (gst_video_aggregator_pad_has_current_buffer (pad2) + && cpad2->alpha == 1.0 && !GST_VIDEO_INFO_HAS_ALPHA (&pad2->info) + && is_rectangle_contained (frame_rect, frame2_rect)) { + frame_obscured = TRUE; + GST_DEBUG_OBJECT (pad, "%ix%i@(%i,%i) obscured by %s %ix%i@(%i,%i) " + "in output of size %ix%i; skipping frame", frame_rect.w, frame_rect.h, + frame_rect.x, frame_rect.y, GST_PAD_NAME (pad2), frame2_rect.w, + frame2_rect.h, frame2_rect.x, frame2_rect.y, + GST_VIDEO_INFO_WIDTH (&vagg->info), + GST_VIDEO_INFO_HEIGHT (&vagg->info)); + break; + } + } + GST_OBJECT_UNLOCK (vagg); + + if (frame_obscured) + goto done; + + return + GST_VIDEO_AGGREGATOR_PAD_CLASS + (gst_compositor_pad_parent_class)->prepare_frame (pad, vagg, buffer, + prepared_frame); + +done: + + return TRUE; +} + +static void +gst_compositor_pad_create_conversion_info (GstVideoAggregatorConvertPad * pad, + GstVideoAggregator * vagg, GstVideoInfo * conversion_info) +{ + GstCompositor *comp = GST_COMPOSITOR (vagg); + GstCompositorPad *cpad = GST_COMPOSITOR_PAD (pad); + gint width, height; + + GST_VIDEO_AGGREGATOR_CONVERT_PAD_CLASS + (gst_compositor_pad_parent_class)->create_conversion_info (pad, vagg, + conversion_info); + if (!conversion_info->finfo) + return; + + _mixer_pad_get_output_size (comp, cpad, GST_VIDEO_INFO_PAR_N (&vagg->info), + GST_VIDEO_INFO_PAR_D (&vagg->info), &width, &height); + + /* The only thing that can change here is the width + * and height, otherwise set_info would've been called */ + if (GST_VIDEO_INFO_WIDTH (conversion_info) != width || + GST_VIDEO_INFO_HEIGHT (conversion_info) != height) { + GstVideoInfo tmp_info; + + /* Initialize with the wanted video format and our original width and + * height as we don't want to rescale. Then copy over the wanted + * colorimetry, and chroma-site and our current pixel-aspect-ratio + * and other relevant fields. + */ + gst_video_info_set_format (&tmp_info, + GST_VIDEO_INFO_FORMAT (conversion_info), width, height); + tmp_info.chroma_site = conversion_info->chroma_site; + tmp_info.colorimetry = conversion_info->colorimetry; + tmp_info.par_n = conversion_info->par_n; + tmp_info.par_d = conversion_info->par_d; + tmp_info.fps_n = conversion_info->fps_n; + tmp_info.fps_d = conversion_info->fps_d; + tmp_info.flags = conversion_info->flags; + tmp_info.interlace_mode = conversion_info->interlace_mode; + + *conversion_info = tmp_info; + } +} + +static void +gst_compositor_pad_class_init (GstCompositorPadClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstVideoAggregatorPadClass *vaggpadclass = + (GstVideoAggregatorPadClass *) klass; + GstVideoAggregatorConvertPadClass *vaggcpadclass = + (GstVideoAggregatorConvertPadClass *) klass; + + gobject_class->set_property = gst_compositor_pad_set_property; + gobject_class->get_property = gst_compositor_pad_get_property; + + g_object_class_install_property (gobject_class, PROP_PAD_XPOS, + g_param_spec_int ("xpos", "X Position", "X Position of the picture", + G_MININT, G_MAXINT, DEFAULT_PAD_XPOS, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PAD_YPOS, + g_param_spec_int ("ypos", "Y Position", "Y Position of the picture", + G_MININT, G_MAXINT, DEFAULT_PAD_YPOS, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PAD_WIDTH, + g_param_spec_int ("width", "Width", "Width of the picture", + G_MININT, G_MAXINT, DEFAULT_PAD_WIDTH, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PAD_HEIGHT, + g_param_spec_int ("height", "Height", "Height of the picture", + G_MININT, G_MAXINT, DEFAULT_PAD_HEIGHT, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PAD_ALPHA, + g_param_spec_double ("alpha", "Alpha", "Alpha of the picture", 0.0, 1.0, + DEFAULT_PAD_ALPHA, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PAD_OPERATOR, + g_param_spec_enum ("operator", "Operator", + "Blending operator to use for blending this pad over the previous ones", + GST_TYPE_COMPOSITOR_OPERATOR, DEFAULT_PAD_OPERATOR, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + + vaggpadclass->prepare_frame = + GST_DEBUG_FUNCPTR (gst_compositor_pad_prepare_frame); + + vaggcpadclass->create_conversion_info = + GST_DEBUG_FUNCPTR (gst_compositor_pad_create_conversion_info); +} + +static void +gst_compositor_pad_init (GstCompositorPad * compo_pad) +{ + compo_pad->xpos = DEFAULT_PAD_XPOS; + compo_pad->ypos = DEFAULT_PAD_YPOS; + compo_pad->alpha = DEFAULT_PAD_ALPHA; + compo_pad->op = DEFAULT_PAD_OPERATOR; +} + + +/* GstCompositor */ +#define DEFAULT_BACKGROUND COMPOSITOR_BACKGROUND_CHECKER +enum +{ + PROP_0, + PROP_BACKGROUND, +}; + +#define GST_TYPE_COMPOSITOR_BACKGROUND (gst_compositor_background_get_type()) +static GType +gst_compositor_background_get_type (void) +{ + static GType compositor_background_type = 0; + + static const GEnumValue compositor_background[] = { + {COMPOSITOR_BACKGROUND_CHECKER, "Checker pattern", "checker"}, + {COMPOSITOR_BACKGROUND_BLACK, "Black", "black"}, + {COMPOSITOR_BACKGROUND_WHITE, "White", "white"}, + {COMPOSITOR_BACKGROUND_TRANSPARENT, + "Transparent Background to enable further compositing", "transparent"}, + {0, NULL, NULL}, + }; + + if (!compositor_background_type) { + compositor_background_type = + g_enum_register_static ("GstCompositorBackground", + compositor_background); + } + return compositor_background_type; +} + +static void +gst_compositor_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstCompositor *self = GST_COMPOSITOR (object); + + switch (prop_id) { + case PROP_BACKGROUND: + g_value_set_enum (value, self->background); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_compositor_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstCompositor *self = GST_COMPOSITOR (object); + + switch (prop_id) { + case PROP_BACKGROUND: + self->background = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +#define gst_compositor_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstCompositor, gst_compositor, + GST_TYPE_VIDEO_AGGREGATOR, G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, + gst_compositor_child_proxy_init)); + +static gboolean +set_functions (GstCompositor * self, GstVideoInfo * info) +{ + gboolean ret = FALSE; + + self->blend = NULL; + self->overlay = NULL; + self->fill_checker = NULL; + self->fill_color = NULL; + + switch (GST_VIDEO_INFO_FORMAT (info)) { + case GST_VIDEO_FORMAT_AYUV: + self->blend = gst_compositor_blend_ayuv; + self->overlay = gst_compositor_overlay_ayuv; + self->fill_checker = gst_compositor_fill_checker_ayuv; + self->fill_color = gst_compositor_fill_color_ayuv; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_ARGB: + self->blend = gst_compositor_blend_argb; + self->overlay = gst_compositor_overlay_argb; + self->fill_checker = gst_compositor_fill_checker_argb; + self->fill_color = gst_compositor_fill_color_argb; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_BGRA: + self->blend = gst_compositor_blend_bgra; + self->overlay = gst_compositor_overlay_bgra; + self->fill_checker = gst_compositor_fill_checker_bgra; + self->fill_color = gst_compositor_fill_color_bgra; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_ABGR: + self->blend = gst_compositor_blend_abgr; + self->overlay = gst_compositor_overlay_abgr; + self->fill_checker = gst_compositor_fill_checker_abgr; + self->fill_color = gst_compositor_fill_color_abgr; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_RGBA: + self->blend = gst_compositor_blend_rgba; + self->overlay = gst_compositor_overlay_rgba; + self->fill_checker = gst_compositor_fill_checker_rgba; + self->fill_color = gst_compositor_fill_color_rgba; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_Y444: + self->blend = gst_compositor_blend_y444; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_y444; + self->fill_color = gst_compositor_fill_color_y444; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_Y42B: + self->blend = gst_compositor_blend_y42b; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_y42b; + self->fill_color = gst_compositor_fill_color_y42b; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_YUY2: + self->blend = gst_compositor_blend_yuy2; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_yuy2; + self->fill_color = gst_compositor_fill_color_yuy2; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_UYVY: + self->blend = gst_compositor_blend_uyvy; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_uyvy; + self->fill_color = gst_compositor_fill_color_uyvy; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_YVYU: + self->blend = gst_compositor_blend_yvyu; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_yvyu; + self->fill_color = gst_compositor_fill_color_yvyu; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_I420: + self->blend = gst_compositor_blend_i420; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_i420; + self->fill_color = gst_compositor_fill_color_i420; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_YV12: + self->blend = gst_compositor_blend_yv12; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_yv12; + self->fill_color = gst_compositor_fill_color_yv12; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_NV12: + self->blend = gst_compositor_blend_nv12; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_nv12; + self->fill_color = gst_compositor_fill_color_nv12; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_NV21: + self->blend = gst_compositor_blend_nv21; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_nv21; + self->fill_color = gst_compositor_fill_color_nv21; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_Y41B: + self->blend = gst_compositor_blend_y41b; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_y41b; + self->fill_color = gst_compositor_fill_color_y41b; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_RGB: + self->blend = gst_compositor_blend_rgb; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_rgb; + self->fill_color = gst_compositor_fill_color_rgb; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_BGR: + self->blend = gst_compositor_blend_bgr; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_bgr; + self->fill_color = gst_compositor_fill_color_bgr; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_xRGB: + self->blend = gst_compositor_blend_xrgb; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_xrgb; + self->fill_color = gst_compositor_fill_color_xrgb; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_xBGR: + self->blend = gst_compositor_blend_xbgr; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_xbgr; + self->fill_color = gst_compositor_fill_color_xbgr; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_RGBx: + self->blend = gst_compositor_blend_rgbx; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_rgbx; + self->fill_color = gst_compositor_fill_color_rgbx; + ret = TRUE; + break; + case GST_VIDEO_FORMAT_BGRx: + self->blend = gst_compositor_blend_bgrx; + self->overlay = self->blend; + self->fill_checker = gst_compositor_fill_checker_bgrx; + self->fill_color = gst_compositor_fill_color_bgrx; + ret = TRUE; + break; + default: + break; + } + + return ret; +} + +static GstCaps * +_fixate_caps (GstAggregator * agg, GstCaps * caps) +{ + GstVideoAggregator *vagg = GST_VIDEO_AGGREGATOR (agg); + GList *l; + gint best_width = -1, best_height = -1; + gint best_fps_n = -1, best_fps_d = -1; + gint par_n, par_d; + gdouble best_fps = 0.; + GstCaps *ret = NULL; + GstStructure *s; + + ret = gst_caps_make_writable (caps); + + /* we need this to calculate how large to make the output frame */ + s = gst_caps_get_structure (ret, 0); + if (gst_structure_has_field (s, "pixel-aspect-ratio")) { + gst_structure_fixate_field_nearest_fraction (s, "pixel-aspect-ratio", 1, 1); + gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d); + } else { + par_n = par_d = 1; + } + + GST_OBJECT_LOCK (vagg); + for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) { + GstVideoAggregatorPad *vaggpad = l->data; + GstCompositorPad *compositor_pad = GST_COMPOSITOR_PAD (vaggpad); + gint this_width, this_height; + gint width, height; + gint fps_n, fps_d; + gdouble cur_fps; + + fps_n = GST_VIDEO_INFO_FPS_N (&vaggpad->info); + fps_d = GST_VIDEO_INFO_FPS_D (&vaggpad->info); + _mixer_pad_get_output_size (GST_COMPOSITOR (vagg), compositor_pad, par_n, + par_d, &width, &height); + + if (width == 0 || height == 0) + continue; + + this_width = width + MAX (compositor_pad->xpos, 0); + this_height = height + MAX (compositor_pad->ypos, 0); + + if (best_width < this_width) + best_width = this_width; + if (best_height < this_height) + best_height = this_height; + + if (fps_d == 0) + cur_fps = 0.0; + else + gst_util_fraction_to_double (fps_n, fps_d, &cur_fps); + + if (best_fps < cur_fps) { + best_fps = cur_fps; + best_fps_n = fps_n; + best_fps_d = fps_d; + } + } + GST_OBJECT_UNLOCK (vagg); + + if (best_fps_n <= 0 || best_fps_d <= 0 || best_fps == 0.0) { + best_fps_n = 25; + best_fps_d = 1; + best_fps = 25.0; + } + + gst_structure_fixate_field_nearest_int (s, "width", best_width); + gst_structure_fixate_field_nearest_int (s, "height", best_height); + gst_structure_fixate_field_nearest_fraction (s, "framerate", best_fps_n, + best_fps_d); + ret = gst_caps_fixate (ret); + + return ret; +} + +static gboolean +_negotiated_caps (GstAggregator * agg, GstCaps * caps) +{ + GstVideoInfo v_info; + + GST_DEBUG_OBJECT (agg, "Negotiated caps %" GST_PTR_FORMAT, caps); + + if (!gst_video_info_from_caps (&v_info, caps)) + return FALSE; + + if (!set_functions (GST_COMPOSITOR (agg), &v_info)) { + GST_ERROR_OBJECT (agg, "Failed to setup vfuncs"); + return FALSE; + } + + return GST_AGGREGATOR_CLASS (parent_class)->negotiated_src_caps (agg, caps); +} + +static GstFlowReturn +gst_compositor_aggregate_frames (GstVideoAggregator * vagg, GstBuffer * outbuf) +{ + GList *l; + GstCompositor *self = GST_COMPOSITOR (vagg); + BlendFunction composite; + GstVideoFrame out_frame, *outframe; + + if (!gst_video_frame_map (&out_frame, &vagg->info, outbuf, GST_MAP_WRITE)) { + GST_WARNING_OBJECT (vagg, "Could not map output buffer"); + return GST_FLOW_ERROR; + } + + outframe = &out_frame; + /* default to blending */ + composite = self->blend; + /* TODO: If the frames to be composited completely obscure the background, + * don't bother drawing the background at all. */ + switch (self->background) { + case COMPOSITOR_BACKGROUND_CHECKER: + self->fill_checker (outframe); + break; + case COMPOSITOR_BACKGROUND_BLACK: + self->fill_color (outframe, 16, 128, 128); + break; + case COMPOSITOR_BACKGROUND_WHITE: + self->fill_color (outframe, 240, 128, 128); + break; + case COMPOSITOR_BACKGROUND_TRANSPARENT: + { + guint i, plane, num_planes, height; + + num_planes = GST_VIDEO_FRAME_N_PLANES (outframe); + for (plane = 0; plane < num_planes; ++plane) { + guint8 *pdata; + gsize rowsize, plane_stride; + + pdata = GST_VIDEO_FRAME_PLANE_DATA (outframe, plane); + plane_stride = GST_VIDEO_FRAME_PLANE_STRIDE (outframe, plane); + rowsize = GST_VIDEO_FRAME_COMP_WIDTH (outframe, plane) + * GST_VIDEO_FRAME_COMP_PSTRIDE (outframe, plane); + height = GST_VIDEO_FRAME_COMP_HEIGHT (outframe, plane); + for (i = 0; i < height; ++i) { + memset (pdata, 0, rowsize); + pdata += plane_stride; + } + } + /* use overlay to keep background transparent */ + composite = self->overlay; + break; + } + } + + GST_OBJECT_LOCK (vagg); + for (l = GST_ELEMENT (vagg)->sinkpads; l; l = l->next) { + GstVideoAggregatorPad *pad = l->data; + GstCompositorPad *compo_pad = GST_COMPOSITOR_PAD (pad); + GstVideoFrame *prepared_frame = + gst_video_aggregator_pad_get_prepared_frame (pad); + GstCompositorBlendMode blend_mode = COMPOSITOR_BLEND_MODE_OVER; + + switch (compo_pad->op) { + case COMPOSITOR_OPERATOR_SOURCE: + blend_mode = COMPOSITOR_BLEND_MODE_SOURCE; + break; + case COMPOSITOR_OPERATOR_OVER: + blend_mode = COMPOSITOR_BLEND_MODE_OVER; + break; + case COMPOSITOR_OPERATOR_ADD: + blend_mode = COMPOSITOR_BLEND_MODE_ADD; + break; + default: + g_assert_not_reached (); + break; + } + + if (prepared_frame != NULL) { + composite (prepared_frame, + compo_pad->xpos, + compo_pad->ypos, compo_pad->alpha, outframe, blend_mode); + } + } + GST_OBJECT_UNLOCK (vagg); + + gst_video_frame_unmap (outframe); + + return GST_FLOW_OK; +} + +static GstPad * +gst_compositor_request_new_pad (GstElement * element, GstPadTemplate * templ, + const gchar * req_name, const GstCaps * caps) +{ + GstPad *newpad; + + newpad = (GstPad *) + GST_ELEMENT_CLASS (parent_class)->request_new_pad (element, + templ, req_name, caps); + + if (newpad == NULL) + goto could_not_create; + + gst_child_proxy_child_added (GST_CHILD_PROXY (element), G_OBJECT (newpad), + GST_OBJECT_NAME (newpad)); + + return newpad; + +could_not_create: + { + GST_DEBUG_OBJECT (element, "could not create/add pad"); + return NULL; + } +} + +static void +gst_compositor_release_pad (GstElement * element, GstPad * pad) +{ + GstCompositor *compositor; + + compositor = GST_COMPOSITOR (element); + + GST_DEBUG_OBJECT (compositor, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + gst_child_proxy_child_removed (GST_CHILD_PROXY (compositor), G_OBJECT (pad), + GST_OBJECT_NAME (pad)); + + GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad); +} + +static gboolean +_sink_query (GstAggregator * agg, GstAggregatorPad * bpad, GstQuery * query) +{ + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_ALLOCATION:{ + GstCaps *caps; + GstVideoInfo info; + GstBufferPool *pool; + guint size; + GstStructure *structure; + + gst_query_parse_allocation (query, &caps, NULL); + + if (caps == NULL) + return FALSE; + + if (!gst_video_info_from_caps (&info, caps)) + return FALSE; + + size = GST_VIDEO_INFO_SIZE (&info); + + pool = gst_video_buffer_pool_new (); + + structure = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set_params (structure, caps, size, 0, 0); + + if (!gst_buffer_pool_set_config (pool, structure)) { + gst_object_unref (pool); + return FALSE; + } + + gst_query_add_allocation_pool (query, pool, size, 0, 0); + gst_object_unref (pool); + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); + + return TRUE; + } + default: + return GST_AGGREGATOR_CLASS (parent_class)->sink_query (agg, bpad, query); + } +} + +/* GObject boilerplate */ +static void +gst_compositor_class_init (GstCompositorClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + GstVideoAggregatorClass *videoaggregator_class = + (GstVideoAggregatorClass *) klass; + GstAggregatorClass *agg_class = (GstAggregatorClass *) klass; + + gobject_class->get_property = gst_compositor_get_property; + gobject_class->set_property = gst_compositor_set_property; + + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_compositor_request_new_pad); + gstelement_class->release_pad = + GST_DEBUG_FUNCPTR (gst_compositor_release_pad); + agg_class->sink_query = _sink_query; + agg_class->fixate_src_caps = _fixate_caps; + agg_class->negotiated_src_caps = _negotiated_caps; + videoaggregator_class->aggregate_frames = gst_compositor_aggregate_frames; + + g_object_class_install_property (gobject_class, PROP_BACKGROUND, + g_param_spec_enum ("background", "Background", "Background type", + GST_TYPE_COMPOSITOR_BACKGROUND, + DEFAULT_BACKGROUND, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_static_pad_template_with_gtype (gstelement_class, + &src_factory, GST_TYPE_AGGREGATOR_PAD); + gst_element_class_add_static_pad_template_with_gtype (gstelement_class, + &sink_factory, GST_TYPE_COMPOSITOR_PAD); + + gst_element_class_set_static_metadata (gstelement_class, "Compositor", + "Filter/Editor/Video/Compositor", + "Composite multiple video streams", "Wim Taymans , " + "Sebastian Dröge "); +} + +static void +gst_compositor_init (GstCompositor * self) +{ + /* initialize variables */ + self->background = DEFAULT_BACKGROUND; +} + +/* GstChildProxy implementation */ +static GObject * +gst_compositor_child_proxy_get_child_by_index (GstChildProxy * child_proxy, + guint index) +{ + GstCompositor *compositor = GST_COMPOSITOR (child_proxy); + GObject *obj = NULL; + + GST_OBJECT_LOCK (compositor); + obj = g_list_nth_data (GST_ELEMENT_CAST (compositor)->sinkpads, index); + if (obj) + gst_object_ref (obj); + GST_OBJECT_UNLOCK (compositor); + + return obj; +} + +static guint +gst_compositor_child_proxy_get_children_count (GstChildProxy * child_proxy) +{ + guint count = 0; + GstCompositor *compositor = GST_COMPOSITOR (child_proxy); + + GST_OBJECT_LOCK (compositor); + count = GST_ELEMENT_CAST (compositor)->numsinkpads; + GST_OBJECT_UNLOCK (compositor); + GST_INFO_OBJECT (compositor, "Children Count: %d", count); + + return count; +} + +static void +gst_compositor_child_proxy_init (gpointer g_iface, gpointer iface_data) +{ + GstChildProxyInterface *iface = g_iface; + + iface->get_child_by_index = gst_compositor_child_proxy_get_child_by_index; + iface->get_children_count = gst_compositor_child_proxy_get_children_count; +} + +/* Element registration */ +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (gst_compositor_debug, "compositor", 0, "compositor"); + + gst_compositor_init_blend (); + + return gst_element_register (plugin, "compositor", GST_RANK_PRIMARY + 1, + GST_TYPE_COMPOSITOR); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + compositor, + "Compositor", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN) diff --git a/gst/compositor/compositor.h b/gst/compositor/compositor.h new file mode 100644 index 0000000000..5e8d90c82e --- /dev/null +++ b/gst/compositor/compositor.h @@ -0,0 +1,143 @@ +/* Generic video compositor plugin + * Copyright (C) 2008 Wim Taymans + * Copyright (C) 2010 Sebastian Dröge + * + * 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. + */ + +#ifndef __GST_COMPOSITOR_H__ +#define __GST_COMPOSITOR_H__ + +#include +#include +#include + +#include "blend.h" + +G_BEGIN_DECLS + +#define GST_TYPE_COMPOSITOR (gst_compositor_get_type()) +#define GST_COMPOSITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_COMPOSITOR, GstCompositor)) +#define GST_COMPOSITOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_COMPOSITOR, GstCompositorClass)) +#define GST_IS_COMPOSITOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_COMPOSITOR)) +#define GST_IS_COMPOSITOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_COMPOSITOR)) + +#define GST_TYPE_COMPOSITOR_PAD (gst_compositor_pad_get_type()) +#define GST_COMPOSITOR_PAD(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_COMPOSITOR_PAD, GstCompositorPad)) +#define GST_COMPOSITOR_PAD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_COMPOSITOR_PAD, GstCompositorPadClass)) +#define GST_IS_COMPOSITOR_PAD(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_COMPOSITOR_PAD)) +#define GST_IS_COMPOSITOR_PAD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_COMPOSITOR_PAD)) + +typedef struct _GstCompositor GstCompositor; +typedef struct _GstCompositorClass GstCompositorClass; + +typedef struct _GstCompositorPad GstCompositorPad; +typedef struct _GstCompositorPadClass GstCompositorPadClass; + +/** + * GstCompositorBackground: + * @COMPOSITOR_BACKGROUND_CHECKER: checker pattern background + * @COMPOSITOR_BACKGROUND_BLACK: solid color black background + * @COMPOSITOR_BACKGROUND_WHITE: solid color white background + * @COMPOSITOR_BACKGROUND_TRANSPARENT: background is left transparent and layers are composited using "A OVER B" composition rules. This is only applicable to AYUV and ARGB (and variants) as it preserves the alpha channel and allows for further mixing. + * + * The different backgrounds compositor can blend over. + */ +typedef enum +{ + COMPOSITOR_BACKGROUND_CHECKER, + COMPOSITOR_BACKGROUND_BLACK, + COMPOSITOR_BACKGROUND_WHITE, + COMPOSITOR_BACKGROUND_TRANSPARENT, +} GstCompositorBackground; + +/** + * GstCompositorOperator: + * @COMPOSITOR_OPERATOR_SOURCE: Copy the source over the destination, + * without the destination pixels. + * @COMPOSITOR_OPERATOR_OVER: Blend the source over the destination. + * @COMPOSITOR_OPERATOR_ADD: Similar to over but add the source and + * destination alpha. Requires output with alpha + * channel. + * + * The different blending operators that can be used by compositor. + * + * See https://www.cairographics.org/operators/ for some explanation and + * visualizations. + * + */ +typedef enum +{ + COMPOSITOR_OPERATOR_SOURCE, + COMPOSITOR_OPERATOR_OVER, + COMPOSITOR_OPERATOR_ADD, +} GstCompositorOperator; + +/** + * GstCompositor: + * + * The opaque #GstCompositor structure. + */ +struct _GstCompositor +{ + GstVideoAggregator videoaggregator; + GstCompositorBackground background; + + BlendFunction blend, overlay; + FillCheckerFunction fill_checker; + FillColorFunction fill_color; +}; + +struct _GstCompositorClass +{ + GstVideoAggregatorClass parent_class; +}; + +/** + * GstCompositorPad: + * + * The opaque #GstCompositorPad structure. + */ +struct _GstCompositorPad +{ + GstVideoAggregatorConvertPad parent; + + /* properties */ + gint xpos, ypos; + gint width, height; + gdouble alpha; + + GstCompositorOperator op; +}; + +struct _GstCompositorPadClass +{ + GstVideoAggregatorConvertPadClass parent_class; +}; + +GType gst_compositor_get_type (void); +GType gst_compositor_pad_get_type (void); + +G_END_DECLS +#endif /* __GST_COMPOSITOR_H__ */ diff --git a/gst/compositor/compositororc-dist.c b/gst/compositor/compositororc-dist.c new file mode 100644 index 0000000000..a931b4d6d5 --- /dev/null +++ b/gst/compositor/compositororc-dist.c @@ -0,0 +1,4401 @@ + +/* autogenerated from compositororc.orc */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include + +#ifndef _ORC_INTEGER_TYPEDEFS_ +#define _ORC_INTEGER_TYPEDEFS_ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +typedef int8_t orc_int8; +typedef int16_t orc_int16; +typedef int32_t orc_int32; +typedef int64_t orc_int64; +typedef uint8_t orc_uint8; +typedef uint16_t orc_uint16; +typedef uint32_t orc_uint32; +typedef uint64_t orc_uint64; +#define ORC_UINT64_C(x) UINT64_C(x) +#elif defined(_MSC_VER) +typedef signed __int8 orc_int8; +typedef signed __int16 orc_int16; +typedef signed __int32 orc_int32; +typedef signed __int64 orc_int64; +typedef unsigned __int8 orc_uint8; +typedef unsigned __int16 orc_uint16; +typedef unsigned __int32 orc_uint32; +typedef unsigned __int64 orc_uint64; +#define ORC_UINT64_C(x) (x##Ui64) +#define inline __inline +#else +#include +typedef signed char orc_int8; +typedef short orc_int16; +typedef int orc_int32; +typedef unsigned char orc_uint8; +typedef unsigned short orc_uint16; +typedef unsigned int orc_uint32; +#if INT_MAX == LONG_MAX +typedef long long orc_int64; +typedef unsigned long long orc_uint64; +#define ORC_UINT64_C(x) (x##ULL) +#else +typedef long orc_int64; +typedef unsigned long orc_uint64; +#define ORC_UINT64_C(x) (x##UL) +#endif +#endif +typedef union +{ + orc_int16 i; + orc_int8 x2[2]; +} orc_union16; +typedef union +{ + orc_int32 i; + float f; + orc_int16 x2[2]; + orc_int8 x4[4]; +} orc_union32; +typedef union +{ + orc_int64 i; + double f; + orc_int32 x2[2]; + float x2f[2]; + orc_int16 x4[4]; +} orc_union64; +#endif +#ifndef ORC_RESTRICT +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define ORC_RESTRICT restrict +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define ORC_RESTRICT __restrict__ +#else +#define ORC_RESTRICT +#endif +#endif + +#ifndef ORC_INTERNAL +#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +#define ORC_INTERNAL __hidden +#elif defined (__GNUC__) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#else +#define ORC_INTERNAL +#endif +#endif + + +#ifndef DISABLE_ORC +#include +#endif +void compositor_orc_splat_u32 (guint32 * ORC_RESTRICT d1, int p1, int n); +void compositor_orc_memcpy_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int n); +void compositor_orc_blend_u8 (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_blend_argb (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_source_argb (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_blend_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_source_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_overlay_argb (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_overlay_argb_addition (guint8 * ORC_RESTRICT d1, + int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, + int m); +void compositor_orc_overlay_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_overlay_bgra_addition (guint8 * ORC_RESTRICT d1, + int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, + int m); + + +/* begin Orc C target preamble */ +#define ORC_CLAMP(x,a,b) ((x)<(a) ? (a) : ((x)>(b) ? (b) : (x))) +#define ORC_ABS(a) ((a)<0 ? -(a) : (a)) +#define ORC_MIN(a,b) ((a)<(b) ? (a) : (b)) +#define ORC_MAX(a,b) ((a)>(b) ? (a) : (b)) +#define ORC_SB_MAX 127 +#define ORC_SB_MIN (-1-ORC_SB_MAX) +#define ORC_UB_MAX (orc_uint8) 255 +#define ORC_UB_MIN 0 +#define ORC_SW_MAX 32767 +#define ORC_SW_MIN (-1-ORC_SW_MAX) +#define ORC_UW_MAX (orc_uint16)65535 +#define ORC_UW_MIN 0 +#define ORC_SL_MAX 2147483647 +#define ORC_SL_MIN (-1-ORC_SL_MAX) +#define ORC_UL_MAX 4294967295U +#define ORC_UL_MIN 0 +#define ORC_CLAMP_SB(x) ORC_CLAMP(x,ORC_SB_MIN,ORC_SB_MAX) +#define ORC_CLAMP_UB(x) ORC_CLAMP(x,ORC_UB_MIN,ORC_UB_MAX) +#define ORC_CLAMP_SW(x) ORC_CLAMP(x,ORC_SW_MIN,ORC_SW_MAX) +#define ORC_CLAMP_UW(x) ORC_CLAMP(x,ORC_UW_MIN,ORC_UW_MAX) +#define ORC_CLAMP_SL(x) ORC_CLAMP(x,ORC_SL_MIN,ORC_SL_MAX) +#define ORC_CLAMP_UL(x) ORC_CLAMP(x,ORC_UL_MIN,ORC_UL_MAX) +#define ORC_SWAP_W(x) ((((x)&0xffU)<<8) | (((x)&0xff00U)>>8)) +#define ORC_SWAP_L(x) ((((x)&0xffU)<<24) | (((x)&0xff00U)<<8) | (((x)&0xff0000U)>>8) | (((x)&0xff000000U)>>24)) +#define ORC_SWAP_Q(x) ((((x)&ORC_UINT64_C(0xff))<<56) | (((x)&ORC_UINT64_C(0xff00))<<40) | (((x)&ORC_UINT64_C(0xff0000))<<24) | (((x)&ORC_UINT64_C(0xff000000))<<8) | (((x)&ORC_UINT64_C(0xff00000000))>>8) | (((x)&ORC_UINT64_C(0xff0000000000))>>24) | (((x)&ORC_UINT64_C(0xff000000000000))>>40) | (((x)&ORC_UINT64_C(0xff00000000000000))>>56)) +#define ORC_PTR_OFFSET(ptr,offset) ((void *)(((unsigned char *)(ptr)) + (offset))) +#define ORC_DENORMAL(x) ((x) & ((((x)&0x7f800000) == 0) ? 0xff800000 : 0xffffffff)) +#define ORC_ISNAN(x) ((((x)&0x7f800000) == 0x7f800000) && (((x)&0x007fffff) != 0)) +#define ORC_DENORMAL_DOUBLE(x) ((x) & ((((x)&ORC_UINT64_C(0x7ff0000000000000)) == 0) ? ORC_UINT64_C(0xfff0000000000000) : ORC_UINT64_C(0xffffffffffffffff))) +#define ORC_ISNAN_DOUBLE(x) ((((x)&ORC_UINT64_C(0x7ff0000000000000)) == ORC_UINT64_C(0x7ff0000000000000)) && (((x)&ORC_UINT64_C(0x000fffffffffffff)) != 0)) +#ifndef ORC_RESTRICT +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define ORC_RESTRICT restrict +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define ORC_RESTRICT __restrict__ +#else +#define ORC_RESTRICT +#endif +#endif +/* end Orc C target preamble */ + + + +/* compositor_orc_splat_u32 */ +#ifdef DISABLE_ORC +void +compositor_orc_splat_u32 (guint32 * ORC_RESTRICT d1, int p1, int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + orc_union32 var32; + orc_union32 var33; + + ptr0 = (orc_union32 *) d1; + + /* 0: loadpl */ + var32.i = p1; + + for (i = 0; i < n; i++) { + /* 1: copyl */ + var33.i = var32.i; + /* 2: storel */ + ptr0[i] = var33; + } + +} + +#else +static void +_backup_compositor_orc_splat_u32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + orc_union32 var32; + orc_union32 var33; + + ptr0 = (orc_union32 *) ex->arrays[0]; + + /* 0: loadpl */ + var32.i = ex->params[24]; + + for (i = 0; i < n; i++) { + /* 1: copyl */ + var33.i = var32.i; + /* 2: storel */ + ptr0[i] = var33; + } + +} + +void +compositor_orc_splat_u32 (guint32 * ORC_RESTRICT d1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 24, 99, 111, 109, 112, 111, 115, 105, 116, 111, 114, 95, 111, 114, + 99, 95, 115, 112, 108, 97, 116, 95, 117, 51, 50, 11, 4, 4, 16, 4, + 112, 0, 24, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_compositor_orc_splat_u32); +#else + p = orc_program_new (); + orc_program_set_name (p, "compositor_orc_splat_u32"); + orc_program_set_backup_function (p, _backup_compositor_orc_splat_u32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_parameter (p, 4, "p1"); + + orc_program_append_2 (p, "copyl", 0, ORC_VAR_D1, ORC_VAR_P1, ORC_VAR_D1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* compositor_orc_memcpy_u32 */ +#ifdef DISABLE_ORC +void +compositor_orc_memcpy_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr4[i]; + /* 1: copyl */ + var33.i = var32.i; + /* 2: storel */ + ptr0[i] = var33; + } + +} + +#else +static void +_backup_compositor_orc_memcpy_u32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr4[i]; + /* 1: copyl */ + var33.i = var32.i; + /* 2: storel */ + ptr0[i] = var33; + } + +} + +void +compositor_orc_memcpy_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 25, 99, 111, 109, 112, 111, 115, 105, 116, 111, 114, 95, 111, 114, + 99, 95, 109, 101, 109, 99, 112, 121, 95, 117, 51, 50, 11, 4, 4, 12, + 4, 4, 112, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_compositor_orc_memcpy_u32); +#else + p = orc_program_new (); + orc_program_set_name (p, "compositor_orc_memcpy_u32"); + orc_program_set_backup_function (p, _backup_compositor_orc_memcpy_u32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + + orc_program_append_2 (p, "copyl", 0, ORC_VAR_D1, ORC_VAR_S1, ORC_VAR_D1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* compositor_orc_blend_u8 */ +#ifdef DISABLE_ORC +void +compositor_orc_blend_u8 (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + int i; + int j; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var34; + orc_int8 var35; + orc_union16 var36; + orc_int8 var37; + orc_union16 var38; + orc_union16 var39; + orc_union16 var40; + orc_union16 var41; + orc_union16 var42; + orc_union16 var43; + orc_union16 var44; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (d1, d1_stride * j); + ptr4 = ORC_PTR_OFFSET (s1, s1_stride * j); + + /* 5: loadpw */ + var36.i = p1; + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr0[i]; + /* 1: convubw */ + var38.i = (orc_uint8) var34; + /* 2: loadb */ + var35 = ptr4[i]; + /* 3: convubw */ + var39.i = (orc_uint8) var35; + /* 4: subw */ + var40.i = var39.i - var38.i; + /* 6: mullw */ + var41.i = (var40.i * var36.i) & 0xffff; + /* 7: shlw */ + var42.i = ((orc_uint16) var38.i) << 8; + /* 8: addw */ + var43.i = var42.i + var41.i; + /* 9: shruw */ + var44.i = ((orc_uint16) var43.i) >> 8; + /* 10: convsuswb */ + var37 = ORC_CLAMP_UB (var44.i); + /* 11: storeb */ + ptr0[i] = var37; + } + } + +} + +#else +static void +_backup_compositor_orc_blend_u8 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int j; + int n = ex->n; + int m = ex->params[ORC_VAR_A1]; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var34; + orc_int8 var35; + orc_union16 var36; + orc_int8 var37; + orc_union16 var38; + orc_union16 var39; + orc_union16 var40; + orc_union16 var41; + orc_union16 var42; + orc_union16 var43; + orc_union16 var44; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (ex->arrays[0], ex->params[0] * j); + ptr4 = ORC_PTR_OFFSET (ex->arrays[4], ex->params[4] * j); + + /* 5: loadpw */ + var36.i = ex->params[24]; + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr0[i]; + /* 1: convubw */ + var38.i = (orc_uint8) var34; + /* 2: loadb */ + var35 = ptr4[i]; + /* 3: convubw */ + var39.i = (orc_uint8) var35; + /* 4: subw */ + var40.i = var39.i - var38.i; + /* 6: mullw */ + var41.i = (var40.i * var36.i) & 0xffff; + /* 7: shlw */ + var42.i = ((orc_uint16) var38.i) << 8; + /* 8: addw */ + var43.i = var42.i + var41.i; + /* 9: shruw */ + var44.i = ((orc_uint16) var43.i) >> 8; + /* 10: convsuswb */ + var37 = ORC_CLAMP_UB (var44.i); + /* 11: storeb */ + ptr0[i] = var37; + } + } + +} + +void +compositor_orc_blend_u8 (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 7, 9, 23, 99, 111, 109, 112, 111, 115, 105, 116, 111, 114, 95, 111, + 114, 99, 95, 98, 108, 101, 110, 100, 95, 117, 56, 11, 1, 1, 12, 1, + 1, 14, 1, 8, 0, 0, 0, 16, 2, 20, 2, 20, 2, 150, 32, 0, + 150, 33, 4, 98, 33, 33, 32, 89, 33, 33, 24, 93, 32, 32, 16, 70, + 33, 32, 33, 95, 33, 33, 16, 160, 0, 33, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_compositor_orc_blend_u8); +#else + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "compositor_orc_blend_u8"); + orc_program_set_backup_function (p, _backup_compositor_orc_blend_u8); + orc_program_add_destination (p, 1, "d1"); + orc_program_add_source (p, 1, "s1"); + orc_program_add_constant (p, 1, 0x00000008, "c1"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 2, "t1"); + orc_program_add_temporary (p, 2, "t2"); + + orc_program_append_2 (p, "convubw", 0, ORC_VAR_T1, ORC_VAR_D1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 0, ORC_VAR_T2, ORC_VAR_S1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "subw", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shlw", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_T2, + ORC_VAR_D1); + orc_program_append_2 (p, "shruw", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "convsuswb", 0, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1, ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ORC_EXECUTOR_M (ex) = m; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_D1] = d1_stride; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_S1] = s1_stride; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* compositor_orc_blend_argb */ +#ifdef DISABLE_ORC +void +compositor_orc_blend_argb (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + int i; + int j; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var39; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var40; +#else + orc_union32 var40; +#endif + orc_union32 var41; + orc_union16 var42; + orc_int8 var43; + orc_union32 var44; + orc_union64 var45; + orc_union64 var46; + orc_union64 var47; + orc_union64 var48; + orc_union32 var49; + orc_union64 var50; + orc_union64 var51; + orc_union64 var52; + orc_union64 var53; + orc_union64 var54; + orc_union32 var55; + orc_union32 var56; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (d1, d1_stride * j); + ptr4 = ORC_PTR_OFFSET (s1, s1_stride * j); + + /* 5: loadpw */ + var39.x4[0] = p1; + var39.x4[1] = p1; + var39.x4[2] = p1; + var39.x4[3] = p1; + /* 16: loadpl */ + var40.i = 0x000000ff; /* 255 or 1.25987e-321f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var41 = ptr4[i]; + /* 1: convlw */ + var42.i = var41.i; + /* 2: convwb */ + var43 = var42.i; + /* 3: splatbl */ + var44.i = + ((((orc_uint32) var43) & 0xff) << 24) | ((((orc_uint32) var43) & 0xff) + << 16) | ((((orc_uint32) var43) & 0xff) << 8) | (((orc_uint32) var43) + & 0xff); + /* 4: convubw */ + var45.x4[0] = (orc_uint8) var44.x4[0]; + var45.x4[1] = (orc_uint8) var44.x4[1]; + var45.x4[2] = (orc_uint8) var44.x4[2]; + var45.x4[3] = (orc_uint8) var44.x4[3]; + /* 6: mullw */ + var46.x4[0] = (var45.x4[0] * var39.x4[0]) & 0xffff; + var46.x4[1] = (var45.x4[1] * var39.x4[1]) & 0xffff; + var46.x4[2] = (var45.x4[2] * var39.x4[2]) & 0xffff; + var46.x4[3] = (var45.x4[3] * var39.x4[3]) & 0xffff; + /* 7: div255w */ + var47.x4[0] = + ((orc_uint16) (((orc_uint16) (var46.x4[0] + 128)) + + (((orc_uint16) (var46.x4[0] + 128)) >> 8))) >> 8; + var47.x4[1] = + ((orc_uint16) (((orc_uint16) (var46.x4[1] + 128)) + + (((orc_uint16) (var46.x4[1] + 128)) >> 8))) >> 8; + var47.x4[2] = + ((orc_uint16) (((orc_uint16) (var46.x4[2] + 128)) + + (((orc_uint16) (var46.x4[2] + 128)) >> 8))) >> 8; + var47.x4[3] = + ((orc_uint16) (((orc_uint16) (var46.x4[3] + 128)) + + (((orc_uint16) (var46.x4[3] + 128)) >> 8))) >> 8; + /* 8: convubw */ + var48.x4[0] = (orc_uint8) var41.x4[0]; + var48.x4[1] = (orc_uint8) var41.x4[1]; + var48.x4[2] = (orc_uint8) var41.x4[2]; + var48.x4[3] = (orc_uint8) var41.x4[3]; + /* 9: loadl */ + var49 = ptr0[i]; + /* 10: convubw */ + var50.x4[0] = (orc_uint8) var49.x4[0]; + var50.x4[1] = (orc_uint8) var49.x4[1]; + var50.x4[2] = (orc_uint8) var49.x4[2]; + var50.x4[3] = (orc_uint8) var49.x4[3]; + /* 11: subw */ + var51.x4[0] = var48.x4[0] - var50.x4[0]; + var51.x4[1] = var48.x4[1] - var50.x4[1]; + var51.x4[2] = var48.x4[2] - var50.x4[2]; + var51.x4[3] = var48.x4[3] - var50.x4[3]; + /* 12: mullw */ + var52.x4[0] = (var51.x4[0] * var47.x4[0]) & 0xffff; + var52.x4[1] = (var51.x4[1] * var47.x4[1]) & 0xffff; + var52.x4[2] = (var51.x4[2] * var47.x4[2]) & 0xffff; + var52.x4[3] = (var51.x4[3] * var47.x4[3]) & 0xffff; + /* 13: div255w */ + var53.x4[0] = + ((orc_uint16) (((orc_uint16) (var52.x4[0] + 128)) + + (((orc_uint16) (var52.x4[0] + 128)) >> 8))) >> 8; + var53.x4[1] = + ((orc_uint16) (((orc_uint16) (var52.x4[1] + 128)) + + (((orc_uint16) (var52.x4[1] + 128)) >> 8))) >> 8; + var53.x4[2] = + ((orc_uint16) (((orc_uint16) (var52.x4[2] + 128)) + + (((orc_uint16) (var52.x4[2] + 128)) >> 8))) >> 8; + var53.x4[3] = + ((orc_uint16) (((orc_uint16) (var52.x4[3] + 128)) + + (((orc_uint16) (var52.x4[3] + 128)) >> 8))) >> 8; + /* 14: addw */ + var54.x4[0] = var50.x4[0] + var53.x4[0]; + var54.x4[1] = var50.x4[1] + var53.x4[1]; + var54.x4[2] = var50.x4[2] + var53.x4[2]; + var54.x4[3] = var50.x4[3] + var53.x4[3]; + /* 15: convwb */ + var55.x4[0] = var54.x4[0]; + var55.x4[1] = var54.x4[1]; + var55.x4[2] = var54.x4[2]; + var55.x4[3] = var54.x4[3]; + /* 17: orl */ + var56.i = var55.i | var40.i; + /* 18: storel */ + ptr0[i] = var56; + } + } + +} + +#else +static void +_backup_compositor_orc_blend_argb (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int j; + int n = ex->n; + int m = ex->params[ORC_VAR_A1]; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var39; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var40; +#else + orc_union32 var40; +#endif + orc_union32 var41; + orc_union16 var42; + orc_int8 var43; + orc_union32 var44; + orc_union64 var45; + orc_union64 var46; + orc_union64 var47; + orc_union64 var48; + orc_union32 var49; + orc_union64 var50; + orc_union64 var51; + orc_union64 var52; + orc_union64 var53; + orc_union64 var54; + orc_union32 var55; + orc_union32 var56; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (ex->arrays[0], ex->params[0] * j); + ptr4 = ORC_PTR_OFFSET (ex->arrays[4], ex->params[4] * j); + + /* 5: loadpw */ + var39.x4[0] = ex->params[24]; + var39.x4[1] = ex->params[24]; + var39.x4[2] = ex->params[24]; + var39.x4[3] = ex->params[24]; + /* 16: loadpl */ + var40.i = 0x000000ff; /* 255 or 1.25987e-321f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var41 = ptr4[i]; + /* 1: convlw */ + var42.i = var41.i; + /* 2: convwb */ + var43 = var42.i; + /* 3: splatbl */ + var44.i = + ((((orc_uint32) var43) & 0xff) << 24) | ((((orc_uint32) var43) & 0xff) + << 16) | ((((orc_uint32) var43) & 0xff) << 8) | (((orc_uint32) var43) + & 0xff); + /* 4: convubw */ + var45.x4[0] = (orc_uint8) var44.x4[0]; + var45.x4[1] = (orc_uint8) var44.x4[1]; + var45.x4[2] = (orc_uint8) var44.x4[2]; + var45.x4[3] = (orc_uint8) var44.x4[3]; + /* 6: mullw */ + var46.x4[0] = (var45.x4[0] * var39.x4[0]) & 0xffff; + var46.x4[1] = (var45.x4[1] * var39.x4[1]) & 0xffff; + var46.x4[2] = (var45.x4[2] * var39.x4[2]) & 0xffff; + var46.x4[3] = (var45.x4[3] * var39.x4[3]) & 0xffff; + /* 7: div255w */ + var47.x4[0] = + ((orc_uint16) (((orc_uint16) (var46.x4[0] + 128)) + + (((orc_uint16) (var46.x4[0] + 128)) >> 8))) >> 8; + var47.x4[1] = + ((orc_uint16) (((orc_uint16) (var46.x4[1] + 128)) + + (((orc_uint16) (var46.x4[1] + 128)) >> 8))) >> 8; + var47.x4[2] = + ((orc_uint16) (((orc_uint16) (var46.x4[2] + 128)) + + (((orc_uint16) (var46.x4[2] + 128)) >> 8))) >> 8; + var47.x4[3] = + ((orc_uint16) (((orc_uint16) (var46.x4[3] + 128)) + + (((orc_uint16) (var46.x4[3] + 128)) >> 8))) >> 8; + /* 8: convubw */ + var48.x4[0] = (orc_uint8) var41.x4[0]; + var48.x4[1] = (orc_uint8) var41.x4[1]; + var48.x4[2] = (orc_uint8) var41.x4[2]; + var48.x4[3] = (orc_uint8) var41.x4[3]; + /* 9: loadl */ + var49 = ptr0[i]; + /* 10: convubw */ + var50.x4[0] = (orc_uint8) var49.x4[0]; + var50.x4[1] = (orc_uint8) var49.x4[1]; + var50.x4[2] = (orc_uint8) var49.x4[2]; + var50.x4[3] = (orc_uint8) var49.x4[3]; + /* 11: subw */ + var51.x4[0] = var48.x4[0] - var50.x4[0]; + var51.x4[1] = var48.x4[1] - var50.x4[1]; + var51.x4[2] = var48.x4[2] - var50.x4[2]; + var51.x4[3] = var48.x4[3] - var50.x4[3]; + /* 12: mullw */ + var52.x4[0] = (var51.x4[0] * var47.x4[0]) & 0xffff; + var52.x4[1] = (var51.x4[1] * var47.x4[1]) & 0xffff; + var52.x4[2] = (var51.x4[2] * var47.x4[2]) & 0xffff; + var52.x4[3] = (var51.x4[3] * var47.x4[3]) & 0xffff; + /* 13: div255w */ + var53.x4[0] = + ((orc_uint16) (((orc_uint16) (var52.x4[0] + 128)) + + (((orc_uint16) (var52.x4[0] + 128)) >> 8))) >> 8; + var53.x4[1] = + ((orc_uint16) (((orc_uint16) (var52.x4[1] + 128)) + + (((orc_uint16) (var52.x4[1] + 128)) >> 8))) >> 8; + var53.x4[2] = + ((orc_uint16) (((orc_uint16) (var52.x4[2] + 128)) + + (((orc_uint16) (var52.x4[2] + 128)) >> 8))) >> 8; + var53.x4[3] = + ((orc_uint16) (((orc_uint16) (var52.x4[3] + 128)) + + (((orc_uint16) (var52.x4[3] + 128)) >> 8))) >> 8; + /* 14: addw */ + var54.x4[0] = var50.x4[0] + var53.x4[0]; + var54.x4[1] = var50.x4[1] + var53.x4[1]; + var54.x4[2] = var50.x4[2] + var53.x4[2]; + var54.x4[3] = var50.x4[3] + var53.x4[3]; + /* 15: convwb */ + var55.x4[0] = var54.x4[0]; + var55.x4[1] = var54.x4[1]; + var55.x4[2] = var54.x4[2]; + var55.x4[3] = var54.x4[3]; + /* 17: orl */ + var56.i = var55.i | var40.i; + /* 18: storel */ + ptr0[i] = var56; + } + } + +} + +void +compositor_orc_blend_argb (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 7, 9, 25, 99, 111, 109, 112, 111, 115, 105, 116, 111, 114, 95, 111, + 114, 99, 95, 98, 108, 101, 110, 100, 95, 97, 114, 103, 98, 11, 4, 4, + 12, 4, 4, 14, 4, 255, 0, 0, 0, 16, 2, 20, 4, 20, 2, 20, + 1, 20, 4, 20, 8, 20, 8, 20, 8, 113, 32, 4, 163, 33, 32, 157, + 34, 33, 152, 35, 34, 21, 2, 150, 38, 35, 21, 2, 89, 38, 38, 24, + 21, 2, 80, 38, 38, 21, 2, 150, 37, 32, 113, 32, 0, 21, 2, 150, + 36, 32, 21, 2, 98, 37, 37, 36, 21, 2, 89, 37, 37, 38, 21, 2, + 80, 37, 37, 21, 2, 70, 36, 36, 37, 21, 2, 157, 32, 36, 123, 32, + 32, 16, 128, 0, 32, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_compositor_orc_blend_argb); +#else + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "compositor_orc_blend_argb"); + orc_program_set_backup_function (p, _backup_compositor_orc_blend_argb); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_constant (p, 4, 0x000000ff, "c1"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 4, "t1"); + orc_program_add_temporary (p, 2, "t2"); + orc_program_add_temporary (p, 1, "t3"); + orc_program_add_temporary (p, 4, "t4"); + orc_program_add_temporary (p, 8, "t5"); + orc_program_add_temporary (p, 8, "t6"); + orc_program_add_temporary (p, 8, "t7"); + + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T4, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T7, ORC_VAR_T4, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T6, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_D1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T5, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "subw", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_T5, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_T7, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 2, ORC_VAR_T5, ORC_VAR_T5, ORC_VAR_T6, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 2, ORC_VAR_T1, ORC_VAR_T5, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "orl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "storel", 0, ORC_VAR_D1, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ORC_EXECUTOR_M (ex) = m; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_D1] = d1_stride; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_S1] = s1_stride; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* compositor_orc_source_argb */ +#ifdef DISABLE_ORC +void +compositor_orc_source_argb (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + int i; + int j; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var38; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var39; +#else + orc_union32 var39; +#endif +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var40; +#else + orc_union32 var40; +#endif + orc_union32 var41; + orc_union32 var42; + orc_union16 var43; + orc_int8 var44; + orc_union32 var45; + orc_union64 var46; + orc_union64 var47; + orc_union64 var48; + orc_union32 var49; + orc_union32 var50; + orc_union32 var51; + orc_union32 var52; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (d1, d1_stride * j); + ptr4 = ORC_PTR_OFFSET (s1, s1_stride * j); + + /* 6: loadpw */ + var38.x4[0] = p1; + var38.x4[1] = p1; + var38.x4[2] = p1; + var38.x4[3] = p1; + /* 9: loadpl */ + var39.i = 0xffffff00; /* -256 or 2.122e-314f */ + /* 12: loadpl */ + var40.i = 0x000000ff; /* 255 or 1.25987e-321f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var41 = ptr4[i]; + /* 1: shrul */ + var42.i = ((orc_uint32) var41.i) >> 24; + /* 2: convlw */ + var43.i = var42.i; + /* 3: convwb */ + var44 = var43.i; + /* 4: splatbl */ + var45.i = + ((((orc_uint32) var44) & 0xff) << 24) | ((((orc_uint32) var44) & 0xff) + << 16) | ((((orc_uint32) var44) & 0xff) << 8) | (((orc_uint32) var44) + & 0xff); + /* 5: convubw */ + var46.x4[0] = (orc_uint8) var45.x4[0]; + var46.x4[1] = (orc_uint8) var45.x4[1]; + var46.x4[2] = (orc_uint8) var45.x4[2]; + var46.x4[3] = (orc_uint8) var45.x4[3]; + /* 7: mullw */ + var47.x4[0] = (var46.x4[0] * var38.x4[0]) & 0xffff; + var47.x4[1] = (var46.x4[1] * var38.x4[1]) & 0xffff; + var47.x4[2] = (var46.x4[2] * var38.x4[2]) & 0xffff; + var47.x4[3] = (var46.x4[3] * var38.x4[3]) & 0xffff; + /* 8: div255w */ + var48.x4[0] = + ((orc_uint16) (((orc_uint16) (var47.x4[0] + 128)) + + (((orc_uint16) (var47.x4[0] + 128)) >> 8))) >> 8; + var48.x4[1] = + ((orc_uint16) (((orc_uint16) (var47.x4[1] + 128)) + + (((orc_uint16) (var47.x4[1] + 128)) >> 8))) >> 8; + var48.x4[2] = + ((orc_uint16) (((orc_uint16) (var47.x4[2] + 128)) + + (((orc_uint16) (var47.x4[2] + 128)) >> 8))) >> 8; + var48.x4[3] = + ((orc_uint16) (((orc_uint16) (var47.x4[3] + 128)) + + (((orc_uint16) (var47.x4[3] + 128)) >> 8))) >> 8; + /* 10: andl */ + var49.i = var41.i & var39.i; + /* 11: convwb */ + var50.x4[0] = var48.x4[0]; + var50.x4[1] = var48.x4[1]; + var50.x4[2] = var48.x4[2]; + var50.x4[3] = var48.x4[3]; + /* 13: andl */ + var51.i = var50.i & var40.i; + /* 14: orl */ + var52.i = var49.i | var51.i; + /* 15: storel */ + ptr0[i] = var52; + } + } + +} + +#else +static void +_backup_compositor_orc_source_argb (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int j; + int n = ex->n; + int m = ex->params[ORC_VAR_A1]; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var38; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var39; +#else + orc_union32 var39; +#endif +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var40; +#else + orc_union32 var40; +#endif + orc_union32 var41; + orc_union32 var42; + orc_union16 var43; + orc_int8 var44; + orc_union32 var45; + orc_union64 var46; + orc_union64 var47; + orc_union64 var48; + orc_union32 var49; + orc_union32 var50; + orc_union32 var51; + orc_union32 var52; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (ex->arrays[0], ex->params[0] * j); + ptr4 = ORC_PTR_OFFSET (ex->arrays[4], ex->params[4] * j); + + /* 6: loadpw */ + var38.x4[0] = ex->params[24]; + var38.x4[1] = ex->params[24]; + var38.x4[2] = ex->params[24]; + var38.x4[3] = ex->params[24]; + /* 9: loadpl */ + var39.i = 0xffffff00; /* -256 or 2.122e-314f */ + /* 12: loadpl */ + var40.i = 0x000000ff; /* 255 or 1.25987e-321f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var41 = ptr4[i]; + /* 1: shrul */ + var42.i = ((orc_uint32) var41.i) >> 24; + /* 2: convlw */ + var43.i = var42.i; + /* 3: convwb */ + var44 = var43.i; + /* 4: splatbl */ + var45.i = + ((((orc_uint32) var44) & 0xff) << 24) | ((((orc_uint32) var44) & 0xff) + << 16) | ((((orc_uint32) var44) & 0xff) << 8) | (((orc_uint32) var44) + & 0xff); + /* 5: convubw */ + var46.x4[0] = (orc_uint8) var45.x4[0]; + var46.x4[1] = (orc_uint8) var45.x4[1]; + var46.x4[2] = (orc_uint8) var45.x4[2]; + var46.x4[3] = (orc_uint8) var45.x4[3]; + /* 7: mullw */ + var47.x4[0] = (var46.x4[0] * var38.x4[0]) & 0xffff; + var47.x4[1] = (var46.x4[1] * var38.x4[1]) & 0xffff; + var47.x4[2] = (var46.x4[2] * var38.x4[2]) & 0xffff; + var47.x4[3] = (var46.x4[3] * var38.x4[3]) & 0xffff; + /* 8: div255w */ + var48.x4[0] = + ((orc_uint16) (((orc_uint16) (var47.x4[0] + 128)) + + (((orc_uint16) (var47.x4[0] + 128)) >> 8))) >> 8; + var48.x4[1] = + ((orc_uint16) (((orc_uint16) (var47.x4[1] + 128)) + + (((orc_uint16) (var47.x4[1] + 128)) >> 8))) >> 8; + var48.x4[2] = + ((orc_uint16) (((orc_uint16) (var47.x4[2] + 128)) + + (((orc_uint16) (var47.x4[2] + 128)) >> 8))) >> 8; + var48.x4[3] = + ((orc_uint16) (((orc_uint16) (var47.x4[3] + 128)) + + (((orc_uint16) (var47.x4[3] + 128)) >> 8))) >> 8; + /* 10: andl */ + var49.i = var41.i & var39.i; + /* 11: convwb */ + var50.x4[0] = var48.x4[0]; + var50.x4[1] = var48.x4[1]; + var50.x4[2] = var48.x4[2]; + var50.x4[3] = var48.x4[3]; + /* 13: andl */ + var51.i = var50.i & var40.i; + /* 14: orl */ + var52.i = var49.i | var51.i; + /* 15: storel */ + ptr0[i] = var52; + } + } + +} + +void +compositor_orc_source_argb (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 7, 9, 26, 99, 111, 109, 112, 111, 115, 105, 116, 111, 114, 95, 111, + 114, 99, 95, 115, 111, 117, 114, 99, 101, 95, 97, 114, 103, 98, 11, 4, + 4, 12, 4, 4, 14, 4, 255, 0, 0, 0, 14, 4, 0, 255, 255, 255, + 14, 4, 24, 0, 0, 0, 16, 2, 20, 4, 20, 4, 20, 2, 20, 1, + 20, 4, 20, 8, 113, 32, 4, 126, 33, 32, 18, 163, 34, 33, 157, 35, + 34, 152, 36, 35, 21, 2, 150, 37, 36, 21, 2, 89, 37, 37, 24, 21, + 2, 80, 37, 37, 106, 32, 32, 17, 21, 2, 157, 33, 37, 106, 33, 33, + 16, 123, 32, 32, 33, 128, 0, 32, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_compositor_orc_source_argb); +#else + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "compositor_orc_source_argb"); + orc_program_set_backup_function (p, _backup_compositor_orc_source_argb); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_constant (p, 4, 0x000000ff, "c1"); + orc_program_add_constant (p, 4, 0xffffff00, "c2"); + orc_program_add_constant (p, 4, 0x00000018, "c3"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 4, "t1"); + orc_program_add_temporary (p, 4, "t2"); + orc_program_add_temporary (p, 2, "t3"); + orc_program_add_temporary (p, 1, "t4"); + orc_program_add_temporary (p, 4, "t5"); + orc_program_add_temporary (p, 8, "t6"); + + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrul", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_C3, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T4, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T5, ORC_VAR_T4, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T6, ORC_VAR_T5, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "andl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 2, ORC_VAR_T2, ORC_VAR_T6, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "andl", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "orl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_T2, + ORC_VAR_D1); + orc_program_append_2 (p, "storel", 0, ORC_VAR_D1, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ORC_EXECUTOR_M (ex) = m; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_D1] = d1_stride; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_S1] = s1_stride; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* compositor_orc_blend_bgra */ +#ifdef DISABLE_ORC +void +compositor_orc_blend_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + int i; + int j; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var40; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var41; +#else + orc_union32 var41; +#endif + orc_union32 var42; + orc_union32 var43; + orc_union16 var44; + orc_int8 var45; + orc_union32 var46; + orc_union64 var47; + orc_union64 var48; + orc_union64 var49; + orc_union64 var50; + orc_union32 var51; + orc_union64 var52; + orc_union64 var53; + orc_union64 var54; + orc_union64 var55; + orc_union64 var56; + orc_union32 var57; + orc_union32 var58; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (d1, d1_stride * j); + ptr4 = ORC_PTR_OFFSET (s1, s1_stride * j); + + /* 6: loadpw */ + var40.x4[0] = p1; + var40.x4[1] = p1; + var40.x4[2] = p1; + var40.x4[3] = p1; + /* 17: loadpl */ + var41.i = 0xff000000; /* -16777216 or 2.11371e-314f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var42 = ptr4[i]; + /* 1: shrul */ + var43.i = ((orc_uint32) var42.i) >> 24; + /* 2: convlw */ + var44.i = var43.i; + /* 3: convwb */ + var45 = var44.i; + /* 4: splatbl */ + var46.i = + ((((orc_uint32) var45) & 0xff) << 24) | ((((orc_uint32) var45) & 0xff) + << 16) | ((((orc_uint32) var45) & 0xff) << 8) | (((orc_uint32) var45) + & 0xff); + /* 5: convubw */ + var47.x4[0] = (orc_uint8) var46.x4[0]; + var47.x4[1] = (orc_uint8) var46.x4[1]; + var47.x4[2] = (orc_uint8) var46.x4[2]; + var47.x4[3] = (orc_uint8) var46.x4[3]; + /* 7: mullw */ + var48.x4[0] = (var47.x4[0] * var40.x4[0]) & 0xffff; + var48.x4[1] = (var47.x4[1] * var40.x4[1]) & 0xffff; + var48.x4[2] = (var47.x4[2] * var40.x4[2]) & 0xffff; + var48.x4[3] = (var47.x4[3] * var40.x4[3]) & 0xffff; + /* 8: div255w */ + var49.x4[0] = + ((orc_uint16) (((orc_uint16) (var48.x4[0] + 128)) + + (((orc_uint16) (var48.x4[0] + 128)) >> 8))) >> 8; + var49.x4[1] = + ((orc_uint16) (((orc_uint16) (var48.x4[1] + 128)) + + (((orc_uint16) (var48.x4[1] + 128)) >> 8))) >> 8; + var49.x4[2] = + ((orc_uint16) (((orc_uint16) (var48.x4[2] + 128)) + + (((orc_uint16) (var48.x4[2] + 128)) >> 8))) >> 8; + var49.x4[3] = + ((orc_uint16) (((orc_uint16) (var48.x4[3] + 128)) + + (((orc_uint16) (var48.x4[3] + 128)) >> 8))) >> 8; + /* 9: convubw */ + var50.x4[0] = (orc_uint8) var42.x4[0]; + var50.x4[1] = (orc_uint8) var42.x4[1]; + var50.x4[2] = (orc_uint8) var42.x4[2]; + var50.x4[3] = (orc_uint8) var42.x4[3]; + /* 10: loadl */ + var51 = ptr0[i]; + /* 11: convubw */ + var52.x4[0] = (orc_uint8) var51.x4[0]; + var52.x4[1] = (orc_uint8) var51.x4[1]; + var52.x4[2] = (orc_uint8) var51.x4[2]; + var52.x4[3] = (orc_uint8) var51.x4[3]; + /* 12: subw */ + var53.x4[0] = var50.x4[0] - var52.x4[0]; + var53.x4[1] = var50.x4[1] - var52.x4[1]; + var53.x4[2] = var50.x4[2] - var52.x4[2]; + var53.x4[3] = var50.x4[3] - var52.x4[3]; + /* 13: mullw */ + var54.x4[0] = (var53.x4[0] * var49.x4[0]) & 0xffff; + var54.x4[1] = (var53.x4[1] * var49.x4[1]) & 0xffff; + var54.x4[2] = (var53.x4[2] * var49.x4[2]) & 0xffff; + var54.x4[3] = (var53.x4[3] * var49.x4[3]) & 0xffff; + /* 14: div255w */ + var55.x4[0] = + ((orc_uint16) (((orc_uint16) (var54.x4[0] + 128)) + + (((orc_uint16) (var54.x4[0] + 128)) >> 8))) >> 8; + var55.x4[1] = + ((orc_uint16) (((orc_uint16) (var54.x4[1] + 128)) + + (((orc_uint16) (var54.x4[1] + 128)) >> 8))) >> 8; + var55.x4[2] = + ((orc_uint16) (((orc_uint16) (var54.x4[2] + 128)) + + (((orc_uint16) (var54.x4[2] + 128)) >> 8))) >> 8; + var55.x4[3] = + ((orc_uint16) (((orc_uint16) (var54.x4[3] + 128)) + + (((orc_uint16) (var54.x4[3] + 128)) >> 8))) >> 8; + /* 15: addw */ + var56.x4[0] = var52.x4[0] + var55.x4[0]; + var56.x4[1] = var52.x4[1] + var55.x4[1]; + var56.x4[2] = var52.x4[2] + var55.x4[2]; + var56.x4[3] = var52.x4[3] + var55.x4[3]; + /* 16: convwb */ + var57.x4[0] = var56.x4[0]; + var57.x4[1] = var56.x4[1]; + var57.x4[2] = var56.x4[2]; + var57.x4[3] = var56.x4[3]; + /* 18: orl */ + var58.i = var57.i | var41.i; + /* 19: storel */ + ptr0[i] = var58; + } + } + +} + +#else +static void +_backup_compositor_orc_blend_bgra (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int j; + int n = ex->n; + int m = ex->params[ORC_VAR_A1]; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var40; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var41; +#else + orc_union32 var41; +#endif + orc_union32 var42; + orc_union32 var43; + orc_union16 var44; + orc_int8 var45; + orc_union32 var46; + orc_union64 var47; + orc_union64 var48; + orc_union64 var49; + orc_union64 var50; + orc_union32 var51; + orc_union64 var52; + orc_union64 var53; + orc_union64 var54; + orc_union64 var55; + orc_union64 var56; + orc_union32 var57; + orc_union32 var58; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (ex->arrays[0], ex->params[0] * j); + ptr4 = ORC_PTR_OFFSET (ex->arrays[4], ex->params[4] * j); + + /* 6: loadpw */ + var40.x4[0] = ex->params[24]; + var40.x4[1] = ex->params[24]; + var40.x4[2] = ex->params[24]; + var40.x4[3] = ex->params[24]; + /* 17: loadpl */ + var41.i = 0xff000000; /* -16777216 or 2.11371e-314f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var42 = ptr4[i]; + /* 1: shrul */ + var43.i = ((orc_uint32) var42.i) >> 24; + /* 2: convlw */ + var44.i = var43.i; + /* 3: convwb */ + var45 = var44.i; + /* 4: splatbl */ + var46.i = + ((((orc_uint32) var45) & 0xff) << 24) | ((((orc_uint32) var45) & 0xff) + << 16) | ((((orc_uint32) var45) & 0xff) << 8) | (((orc_uint32) var45) + & 0xff); + /* 5: convubw */ + var47.x4[0] = (orc_uint8) var46.x4[0]; + var47.x4[1] = (orc_uint8) var46.x4[1]; + var47.x4[2] = (orc_uint8) var46.x4[2]; + var47.x4[3] = (orc_uint8) var46.x4[3]; + /* 7: mullw */ + var48.x4[0] = (var47.x4[0] * var40.x4[0]) & 0xffff; + var48.x4[1] = (var47.x4[1] * var40.x4[1]) & 0xffff; + var48.x4[2] = (var47.x4[2] * var40.x4[2]) & 0xffff; + var48.x4[3] = (var47.x4[3] * var40.x4[3]) & 0xffff; + /* 8: div255w */ + var49.x4[0] = + ((orc_uint16) (((orc_uint16) (var48.x4[0] + 128)) + + (((orc_uint16) (var48.x4[0] + 128)) >> 8))) >> 8; + var49.x4[1] = + ((orc_uint16) (((orc_uint16) (var48.x4[1] + 128)) + + (((orc_uint16) (var48.x4[1] + 128)) >> 8))) >> 8; + var49.x4[2] = + ((orc_uint16) (((orc_uint16) (var48.x4[2] + 128)) + + (((orc_uint16) (var48.x4[2] + 128)) >> 8))) >> 8; + var49.x4[3] = + ((orc_uint16) (((orc_uint16) (var48.x4[3] + 128)) + + (((orc_uint16) (var48.x4[3] + 128)) >> 8))) >> 8; + /* 9: convubw */ + var50.x4[0] = (orc_uint8) var42.x4[0]; + var50.x4[1] = (orc_uint8) var42.x4[1]; + var50.x4[2] = (orc_uint8) var42.x4[2]; + var50.x4[3] = (orc_uint8) var42.x4[3]; + /* 10: loadl */ + var51 = ptr0[i]; + /* 11: convubw */ + var52.x4[0] = (orc_uint8) var51.x4[0]; + var52.x4[1] = (orc_uint8) var51.x4[1]; + var52.x4[2] = (orc_uint8) var51.x4[2]; + var52.x4[3] = (orc_uint8) var51.x4[3]; + /* 12: subw */ + var53.x4[0] = var50.x4[0] - var52.x4[0]; + var53.x4[1] = var50.x4[1] - var52.x4[1]; + var53.x4[2] = var50.x4[2] - var52.x4[2]; + var53.x4[3] = var50.x4[3] - var52.x4[3]; + /* 13: mullw */ + var54.x4[0] = (var53.x4[0] * var49.x4[0]) & 0xffff; + var54.x4[1] = (var53.x4[1] * var49.x4[1]) & 0xffff; + var54.x4[2] = (var53.x4[2] * var49.x4[2]) & 0xffff; + var54.x4[3] = (var53.x4[3] * var49.x4[3]) & 0xffff; + /* 14: div255w */ + var55.x4[0] = + ((orc_uint16) (((orc_uint16) (var54.x4[0] + 128)) + + (((orc_uint16) (var54.x4[0] + 128)) >> 8))) >> 8; + var55.x4[1] = + ((orc_uint16) (((orc_uint16) (var54.x4[1] + 128)) + + (((orc_uint16) (var54.x4[1] + 128)) >> 8))) >> 8; + var55.x4[2] = + ((orc_uint16) (((orc_uint16) (var54.x4[2] + 128)) + + (((orc_uint16) (var54.x4[2] + 128)) >> 8))) >> 8; + var55.x4[3] = + ((orc_uint16) (((orc_uint16) (var54.x4[3] + 128)) + + (((orc_uint16) (var54.x4[3] + 128)) >> 8))) >> 8; + /* 15: addw */ + var56.x4[0] = var52.x4[0] + var55.x4[0]; + var56.x4[1] = var52.x4[1] + var55.x4[1]; + var56.x4[2] = var52.x4[2] + var55.x4[2]; + var56.x4[3] = var52.x4[3] + var55.x4[3]; + /* 16: convwb */ + var57.x4[0] = var56.x4[0]; + var57.x4[1] = var56.x4[1]; + var57.x4[2] = var56.x4[2]; + var57.x4[3] = var56.x4[3]; + /* 18: orl */ + var58.i = var57.i | var41.i; + /* 19: storel */ + ptr0[i] = var58; + } + } + +} + +void +compositor_orc_blend_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 7, 9, 25, 99, 111, 109, 112, 111, 115, 105, 116, 111, 114, 95, 111, + 114, 99, 95, 98, 108, 101, 110, 100, 95, 98, 103, 114, 97, 11, 4, 4, + 12, 4, 4, 14, 4, 0, 0, 0, 255, 14, 4, 24, 0, 0, 0, 16, + 2, 20, 4, 20, 4, 20, 2, 20, 1, 20, 4, 20, 8, 20, 8, 20, + 8, 113, 32, 4, 126, 33, 32, 17, 163, 34, 33, 157, 35, 34, 152, 36, + 35, 21, 2, 150, 39, 36, 21, 2, 89, 39, 39, 24, 21, 2, 80, 39, + 39, 21, 2, 150, 38, 32, 113, 32, 0, 21, 2, 150, 37, 32, 21, 2, + 98, 38, 38, 37, 21, 2, 89, 38, 38, 39, 21, 2, 80, 38, 38, 21, + 2, 70, 37, 37, 38, 21, 2, 157, 32, 37, 123, 32, 32, 16, 128, 0, + 32, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_compositor_orc_blend_bgra); +#else + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "compositor_orc_blend_bgra"); + orc_program_set_backup_function (p, _backup_compositor_orc_blend_bgra); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_constant (p, 4, 0xff000000, "c1"); + orc_program_add_constant (p, 4, 0x00000018, "c2"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 4, "t1"); + orc_program_add_temporary (p, 4, "t2"); + orc_program_add_temporary (p, 2, "t3"); + orc_program_add_temporary (p, 1, "t4"); + orc_program_add_temporary (p, 4, "t5"); + orc_program_add_temporary (p, 8, "t6"); + orc_program_add_temporary (p, 8, "t7"); + orc_program_add_temporary (p, 8, "t8"); + + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrul", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T4, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T5, ORC_VAR_T4, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T8, ORC_VAR_T5, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T8, ORC_VAR_T8, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T8, ORC_VAR_T8, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T7, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_D1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T6, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "subw", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_T6, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_T8, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_T7, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 2, ORC_VAR_T1, ORC_VAR_T6, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "orl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "storel", 0, ORC_VAR_D1, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ORC_EXECUTOR_M (ex) = m; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_D1] = d1_stride; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_S1] = s1_stride; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* compositor_orc_source_bgra */ +#ifdef DISABLE_ORC +void +compositor_orc_source_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + int i; + int j; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var38; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var39; +#else + orc_union32 var39; +#endif +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var40; +#else + orc_union32 var40; +#endif + orc_union32 var41; + orc_union32 var42; + orc_union16 var43; + orc_int8 var44; + orc_union32 var45; + orc_union64 var46; + orc_union64 var47; + orc_union64 var48; + orc_union32 var49; + orc_union32 var50; + orc_union32 var51; + orc_union32 var52; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (d1, d1_stride * j); + ptr4 = ORC_PTR_OFFSET (s1, s1_stride * j); + + /* 6: loadpw */ + var38.x4[0] = p1; + var38.x4[1] = p1; + var38.x4[2] = p1; + var38.x4[3] = p1; + /* 9: loadpl */ + var39.i = 0x00ffffff; /* 16777215 or 8.28905e-317f */ + /* 12: loadpl */ + var40.i = 0xff000000; /* -16777216 or 2.11371e-314f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var41 = ptr4[i]; + /* 1: shrul */ + var42.i = ((orc_uint32) var41.i) >> 24; + /* 2: convlw */ + var43.i = var42.i; + /* 3: convwb */ + var44 = var43.i; + /* 4: splatbl */ + var45.i = + ((((orc_uint32) var44) & 0xff) << 24) | ((((orc_uint32) var44) & 0xff) + << 16) | ((((orc_uint32) var44) & 0xff) << 8) | (((orc_uint32) var44) + & 0xff); + /* 5: convubw */ + var46.x4[0] = (orc_uint8) var45.x4[0]; + var46.x4[1] = (orc_uint8) var45.x4[1]; + var46.x4[2] = (orc_uint8) var45.x4[2]; + var46.x4[3] = (orc_uint8) var45.x4[3]; + /* 7: mullw */ + var47.x4[0] = (var46.x4[0] * var38.x4[0]) & 0xffff; + var47.x4[1] = (var46.x4[1] * var38.x4[1]) & 0xffff; + var47.x4[2] = (var46.x4[2] * var38.x4[2]) & 0xffff; + var47.x4[3] = (var46.x4[3] * var38.x4[3]) & 0xffff; + /* 8: div255w */ + var48.x4[0] = + ((orc_uint16) (((orc_uint16) (var47.x4[0] + 128)) + + (((orc_uint16) (var47.x4[0] + 128)) >> 8))) >> 8; + var48.x4[1] = + ((orc_uint16) (((orc_uint16) (var47.x4[1] + 128)) + + (((orc_uint16) (var47.x4[1] + 128)) >> 8))) >> 8; + var48.x4[2] = + ((orc_uint16) (((orc_uint16) (var47.x4[2] + 128)) + + (((orc_uint16) (var47.x4[2] + 128)) >> 8))) >> 8; + var48.x4[3] = + ((orc_uint16) (((orc_uint16) (var47.x4[3] + 128)) + + (((orc_uint16) (var47.x4[3] + 128)) >> 8))) >> 8; + /* 10: andl */ + var49.i = var41.i & var39.i; + /* 11: convwb */ + var50.x4[0] = var48.x4[0]; + var50.x4[1] = var48.x4[1]; + var50.x4[2] = var48.x4[2]; + var50.x4[3] = var48.x4[3]; + /* 13: andl */ + var51.i = var50.i & var40.i; + /* 14: orl */ + var52.i = var49.i | var51.i; + /* 15: storel */ + ptr0[i] = var52; + } + } + +} + +#else +static void +_backup_compositor_orc_source_bgra (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int j; + int n = ex->n; + int m = ex->params[ORC_VAR_A1]; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var38; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var39; +#else + orc_union32 var39; +#endif +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var40; +#else + orc_union32 var40; +#endif + orc_union32 var41; + orc_union32 var42; + orc_union16 var43; + orc_int8 var44; + orc_union32 var45; + orc_union64 var46; + orc_union64 var47; + orc_union64 var48; + orc_union32 var49; + orc_union32 var50; + orc_union32 var51; + orc_union32 var52; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (ex->arrays[0], ex->params[0] * j); + ptr4 = ORC_PTR_OFFSET (ex->arrays[4], ex->params[4] * j); + + /* 6: loadpw */ + var38.x4[0] = ex->params[24]; + var38.x4[1] = ex->params[24]; + var38.x4[2] = ex->params[24]; + var38.x4[3] = ex->params[24]; + /* 9: loadpl */ + var39.i = 0x00ffffff; /* 16777215 or 8.28905e-317f */ + /* 12: loadpl */ + var40.i = 0xff000000; /* -16777216 or 2.11371e-314f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var41 = ptr4[i]; + /* 1: shrul */ + var42.i = ((orc_uint32) var41.i) >> 24; + /* 2: convlw */ + var43.i = var42.i; + /* 3: convwb */ + var44 = var43.i; + /* 4: splatbl */ + var45.i = + ((((orc_uint32) var44) & 0xff) << 24) | ((((orc_uint32) var44) & 0xff) + << 16) | ((((orc_uint32) var44) & 0xff) << 8) | (((orc_uint32) var44) + & 0xff); + /* 5: convubw */ + var46.x4[0] = (orc_uint8) var45.x4[0]; + var46.x4[1] = (orc_uint8) var45.x4[1]; + var46.x4[2] = (orc_uint8) var45.x4[2]; + var46.x4[3] = (orc_uint8) var45.x4[3]; + /* 7: mullw */ + var47.x4[0] = (var46.x4[0] * var38.x4[0]) & 0xffff; + var47.x4[1] = (var46.x4[1] * var38.x4[1]) & 0xffff; + var47.x4[2] = (var46.x4[2] * var38.x4[2]) & 0xffff; + var47.x4[3] = (var46.x4[3] * var38.x4[3]) & 0xffff; + /* 8: div255w */ + var48.x4[0] = + ((orc_uint16) (((orc_uint16) (var47.x4[0] + 128)) + + (((orc_uint16) (var47.x4[0] + 128)) >> 8))) >> 8; + var48.x4[1] = + ((orc_uint16) (((orc_uint16) (var47.x4[1] + 128)) + + (((orc_uint16) (var47.x4[1] + 128)) >> 8))) >> 8; + var48.x4[2] = + ((orc_uint16) (((orc_uint16) (var47.x4[2] + 128)) + + (((orc_uint16) (var47.x4[2] + 128)) >> 8))) >> 8; + var48.x4[3] = + ((orc_uint16) (((orc_uint16) (var47.x4[3] + 128)) + + (((orc_uint16) (var47.x4[3] + 128)) >> 8))) >> 8; + /* 10: andl */ + var49.i = var41.i & var39.i; + /* 11: convwb */ + var50.x4[0] = var48.x4[0]; + var50.x4[1] = var48.x4[1]; + var50.x4[2] = var48.x4[2]; + var50.x4[3] = var48.x4[3]; + /* 13: andl */ + var51.i = var50.i & var40.i; + /* 14: orl */ + var52.i = var49.i | var51.i; + /* 15: storel */ + ptr0[i] = var52; + } + } + +} + +void +compositor_orc_source_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 7, 9, 26, 99, 111, 109, 112, 111, 115, 105, 116, 111, 114, 95, 111, + 114, 99, 95, 115, 111, 117, 114, 99, 101, 95, 98, 103, 114, 97, 11, 4, + 4, 12, 4, 4, 14, 4, 0, 0, 0, 255, 14, 4, 255, 255, 255, 0, + 14, 4, 24, 0, 0, 0, 16, 2, 20, 4, 20, 4, 20, 2, 20, 1, + 20, 4, 20, 8, 113, 32, 4, 126, 33, 32, 18, 163, 34, 33, 157, 35, + 34, 152, 36, 35, 21, 2, 150, 37, 36, 21, 2, 89, 37, 37, 24, 21, + 2, 80, 37, 37, 106, 32, 32, 17, 21, 2, 157, 33, 37, 106, 33, 33, + 16, 123, 32, 32, 33, 128, 0, 32, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_compositor_orc_source_bgra); +#else + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "compositor_orc_source_bgra"); + orc_program_set_backup_function (p, _backup_compositor_orc_source_bgra); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_constant (p, 4, 0xff000000, "c1"); + orc_program_add_constant (p, 4, 0x00ffffff, "c2"); + orc_program_add_constant (p, 4, 0x00000018, "c3"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 4, "t1"); + orc_program_add_temporary (p, 4, "t2"); + orc_program_add_temporary (p, 2, "t3"); + orc_program_add_temporary (p, 1, "t4"); + orc_program_add_temporary (p, 4, "t5"); + orc_program_add_temporary (p, 8, "t6"); + + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrul", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_C3, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T4, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T5, ORC_VAR_T4, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T6, ORC_VAR_T5, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "andl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 2, ORC_VAR_T2, ORC_VAR_T6, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "andl", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "orl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_T2, + ORC_VAR_D1); + orc_program_append_2 (p, "storel", 0, ORC_VAR_D1, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ORC_EXECUTOR_M (ex) = m; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_D1] = d1_stride; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_S1] = s1_stride; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* compositor_orc_overlay_argb */ +#ifdef DISABLE_ORC +void +compositor_orc_overlay_argb (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + int i; + int j; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var41; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var42; +#else + orc_union32 var42; +#endif +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var43; +#else + orc_union32 var43; +#endif + orc_union32 var44; + orc_union16 var45; + orc_int8 var46; + orc_union32 var47; + orc_union64 var48; + orc_union64 var49; + orc_union64 var50; + orc_union64 var51; + orc_union64 var52; + orc_union32 var53; + orc_union64 var54; + orc_union64 var55; + orc_union32 var56; + orc_union16 var57; + orc_int8 var58; + orc_union32 var59; + orc_union64 var60; + orc_union64 var61; + orc_union64 var62; + orc_union64 var63; + orc_union64 var64; + orc_union64 var65; + orc_union64 var66; + orc_union64 var67; + orc_union32 var68; + orc_union32 var69; + orc_union32 var70; + orc_union32 var71; + orc_union32 var72; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (d1, d1_stride * j); + ptr4 = ORC_PTR_OFFSET (s1, s1_stride * j); + + /* 5: loadpw */ + var41.x4[0] = p1; + var41.x4[1] = p1; + var41.x4[2] = p1; + var41.x4[3] = p1; + /* 10: loadpl */ + var53.i = 0xffffffff; /* -1 or 2.122e-314f */ + /* 26: loadpl */ + var42.i = 0xffffff00; /* -256 or 2.122e-314f */ + /* 29: loadpl */ + var43.i = 0x000000ff; /* 255 or 1.25987e-321f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var44 = ptr4[i]; + /* 1: convlw */ + var45.i = var44.i; + /* 2: convwb */ + var46 = var45.i; + /* 3: splatbl */ + var47.i = + ((((orc_uint32) var46) & 0xff) << 24) | ((((orc_uint32) var46) & 0xff) + << 16) | ((((orc_uint32) var46) & 0xff) << 8) | (((orc_uint32) var46) + & 0xff); + /* 4: convubw */ + var48.x4[0] = (orc_uint8) var47.x4[0]; + var48.x4[1] = (orc_uint8) var47.x4[1]; + var48.x4[2] = (orc_uint8) var47.x4[2]; + var48.x4[3] = (orc_uint8) var47.x4[3]; + /* 6: mullw */ + var49.x4[0] = (var48.x4[0] * var41.x4[0]) & 0xffff; + var49.x4[1] = (var48.x4[1] * var41.x4[1]) & 0xffff; + var49.x4[2] = (var48.x4[2] * var41.x4[2]) & 0xffff; + var49.x4[3] = (var48.x4[3] * var41.x4[3]) & 0xffff; + /* 7: div255w */ + var50.x4[0] = + ((orc_uint16) (((orc_uint16) (var49.x4[0] + 128)) + + (((orc_uint16) (var49.x4[0] + 128)) >> 8))) >> 8; + var50.x4[1] = + ((orc_uint16) (((orc_uint16) (var49.x4[1] + 128)) + + (((orc_uint16) (var49.x4[1] + 128)) >> 8))) >> 8; + var50.x4[2] = + ((orc_uint16) (((orc_uint16) (var49.x4[2] + 128)) + + (((orc_uint16) (var49.x4[2] + 128)) >> 8))) >> 8; + var50.x4[3] = + ((orc_uint16) (((orc_uint16) (var49.x4[3] + 128)) + + (((orc_uint16) (var49.x4[3] + 128)) >> 8))) >> 8; + /* 8: convubw */ + var51.x4[0] = (orc_uint8) var44.x4[0]; + var51.x4[1] = (orc_uint8) var44.x4[1]; + var51.x4[2] = (orc_uint8) var44.x4[2]; + var51.x4[3] = (orc_uint8) var44.x4[3]; + /* 9: mullw */ + var52.x4[0] = (var51.x4[0] * var50.x4[0]) & 0xffff; + var52.x4[1] = (var51.x4[1] * var50.x4[1]) & 0xffff; + var52.x4[2] = (var51.x4[2] * var50.x4[2]) & 0xffff; + var52.x4[3] = (var51.x4[3] * var50.x4[3]) & 0xffff; + /* 11: convubw */ + var54.x4[0] = (orc_uint8) var53.x4[0]; + var54.x4[1] = (orc_uint8) var53.x4[1]; + var54.x4[2] = (orc_uint8) var53.x4[2]; + var54.x4[3] = (orc_uint8) var53.x4[3]; + /* 12: subw */ + var55.x4[0] = var54.x4[0] - var50.x4[0]; + var55.x4[1] = var54.x4[1] - var50.x4[1]; + var55.x4[2] = var54.x4[2] - var50.x4[2]; + var55.x4[3] = var54.x4[3] - var50.x4[3]; + /* 13: loadl */ + var56 = ptr0[i]; + /* 14: convlw */ + var57.i = var56.i; + /* 15: convwb */ + var58 = var57.i; + /* 16: splatbl */ + var59.i = + ((((orc_uint32) var58) & 0xff) << 24) | ((((orc_uint32) var58) & 0xff) + << 16) | ((((orc_uint32) var58) & 0xff) << 8) | (((orc_uint32) var58) + & 0xff); + /* 17: convubw */ + var60.x4[0] = (orc_uint8) var59.x4[0]; + var60.x4[1] = (orc_uint8) var59.x4[1]; + var60.x4[2] = (orc_uint8) var59.x4[2]; + var60.x4[3] = (orc_uint8) var59.x4[3]; + /* 18: mullw */ + var61.x4[0] = (var60.x4[0] * var55.x4[0]) & 0xffff; + var61.x4[1] = (var60.x4[1] * var55.x4[1]) & 0xffff; + var61.x4[2] = (var60.x4[2] * var55.x4[2]) & 0xffff; + var61.x4[3] = (var60.x4[3] * var55.x4[3]) & 0xffff; + /* 19: div255w */ + var62.x4[0] = + ((orc_uint16) (((orc_uint16) (var61.x4[0] + 128)) + + (((orc_uint16) (var61.x4[0] + 128)) >> 8))) >> 8; + var62.x4[1] = + ((orc_uint16) (((orc_uint16) (var61.x4[1] + 128)) + + (((orc_uint16) (var61.x4[1] + 128)) >> 8))) >> 8; + var62.x4[2] = + ((orc_uint16) (((orc_uint16) (var61.x4[2] + 128)) + + (((orc_uint16) (var61.x4[2] + 128)) >> 8))) >> 8; + var62.x4[3] = + ((orc_uint16) (((orc_uint16) (var61.x4[3] + 128)) + + (((orc_uint16) (var61.x4[3] + 128)) >> 8))) >> 8; + /* 20: convubw */ + var63.x4[0] = (orc_uint8) var56.x4[0]; + var63.x4[1] = (orc_uint8) var56.x4[1]; + var63.x4[2] = (orc_uint8) var56.x4[2]; + var63.x4[3] = (orc_uint8) var56.x4[3]; + /* 21: mullw */ + var64.x4[0] = (var63.x4[0] * var62.x4[0]) & 0xffff; + var64.x4[1] = (var63.x4[1] * var62.x4[1]) & 0xffff; + var64.x4[2] = (var63.x4[2] * var62.x4[2]) & 0xffff; + var64.x4[3] = (var63.x4[3] * var62.x4[3]) & 0xffff; + /* 22: addw */ + var65.x4[0] = var64.x4[0] + var52.x4[0]; + var65.x4[1] = var64.x4[1] + var52.x4[1]; + var65.x4[2] = var64.x4[2] + var52.x4[2]; + var65.x4[3] = var64.x4[3] + var52.x4[3]; + /* 23: addw */ + var66.x4[0] = var62.x4[0] + var50.x4[0]; + var66.x4[1] = var62.x4[1] + var50.x4[1]; + var66.x4[2] = var62.x4[2] + var50.x4[2]; + var66.x4[3] = var62.x4[3] + var50.x4[3]; + /* 24: divluw */ + var67.x4[0] = + ((var66.x4[0] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var65.x4[0]) / + ((orc_uint16) var66.x4[0] & 0xff)); + var67.x4[1] = + ((var66.x4[1] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var65.x4[1]) / + ((orc_uint16) var66.x4[1] & 0xff)); + var67.x4[2] = + ((var66.x4[2] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var65.x4[2]) / + ((orc_uint16) var66.x4[2] & 0xff)); + var67.x4[3] = + ((var66.x4[3] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var65.x4[3]) / + ((orc_uint16) var66.x4[3] & 0xff)); + /* 25: convwb */ + var68.x4[0] = var67.x4[0]; + var68.x4[1] = var67.x4[1]; + var68.x4[2] = var67.x4[2]; + var68.x4[3] = var67.x4[3]; + /* 27: andl */ + var69.i = var68.i & var42.i; + /* 28: convwb */ + var70.x4[0] = var66.x4[0]; + var70.x4[1] = var66.x4[1]; + var70.x4[2] = var66.x4[2]; + var70.x4[3] = var66.x4[3]; + /* 30: andl */ + var71.i = var70.i & var43.i; + /* 31: orl */ + var72.i = var69.i | var71.i; + /* 32: storel */ + ptr0[i] = var72; + } + } + +} + +#else +static void +_backup_compositor_orc_overlay_argb (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int j; + int n = ex->n; + int m = ex->params[ORC_VAR_A1]; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var41; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var42; +#else + orc_union32 var42; +#endif +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var43; +#else + orc_union32 var43; +#endif + orc_union32 var44; + orc_union16 var45; + orc_int8 var46; + orc_union32 var47; + orc_union64 var48; + orc_union64 var49; + orc_union64 var50; + orc_union64 var51; + orc_union64 var52; + orc_union32 var53; + orc_union64 var54; + orc_union64 var55; + orc_union32 var56; + orc_union16 var57; + orc_int8 var58; + orc_union32 var59; + orc_union64 var60; + orc_union64 var61; + orc_union64 var62; + orc_union64 var63; + orc_union64 var64; + orc_union64 var65; + orc_union64 var66; + orc_union64 var67; + orc_union32 var68; + orc_union32 var69; + orc_union32 var70; + orc_union32 var71; + orc_union32 var72; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (ex->arrays[0], ex->params[0] * j); + ptr4 = ORC_PTR_OFFSET (ex->arrays[4], ex->params[4] * j); + + /* 5: loadpw */ + var41.x4[0] = ex->params[24]; + var41.x4[1] = ex->params[24]; + var41.x4[2] = ex->params[24]; + var41.x4[3] = ex->params[24]; + /* 10: loadpl */ + var53.i = 0xffffffff; /* -1 or 2.122e-314f */ + /* 26: loadpl */ + var42.i = 0xffffff00; /* -256 or 2.122e-314f */ + /* 29: loadpl */ + var43.i = 0x000000ff; /* 255 or 1.25987e-321f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var44 = ptr4[i]; + /* 1: convlw */ + var45.i = var44.i; + /* 2: convwb */ + var46 = var45.i; + /* 3: splatbl */ + var47.i = + ((((orc_uint32) var46) & 0xff) << 24) | ((((orc_uint32) var46) & 0xff) + << 16) | ((((orc_uint32) var46) & 0xff) << 8) | (((orc_uint32) var46) + & 0xff); + /* 4: convubw */ + var48.x4[0] = (orc_uint8) var47.x4[0]; + var48.x4[1] = (orc_uint8) var47.x4[1]; + var48.x4[2] = (orc_uint8) var47.x4[2]; + var48.x4[3] = (orc_uint8) var47.x4[3]; + /* 6: mullw */ + var49.x4[0] = (var48.x4[0] * var41.x4[0]) & 0xffff; + var49.x4[1] = (var48.x4[1] * var41.x4[1]) & 0xffff; + var49.x4[2] = (var48.x4[2] * var41.x4[2]) & 0xffff; + var49.x4[3] = (var48.x4[3] * var41.x4[3]) & 0xffff; + /* 7: div255w */ + var50.x4[0] = + ((orc_uint16) (((orc_uint16) (var49.x4[0] + 128)) + + (((orc_uint16) (var49.x4[0] + 128)) >> 8))) >> 8; + var50.x4[1] = + ((orc_uint16) (((orc_uint16) (var49.x4[1] + 128)) + + (((orc_uint16) (var49.x4[1] + 128)) >> 8))) >> 8; + var50.x4[2] = + ((orc_uint16) (((orc_uint16) (var49.x4[2] + 128)) + + (((orc_uint16) (var49.x4[2] + 128)) >> 8))) >> 8; + var50.x4[3] = + ((orc_uint16) (((orc_uint16) (var49.x4[3] + 128)) + + (((orc_uint16) (var49.x4[3] + 128)) >> 8))) >> 8; + /* 8: convubw */ + var51.x4[0] = (orc_uint8) var44.x4[0]; + var51.x4[1] = (orc_uint8) var44.x4[1]; + var51.x4[2] = (orc_uint8) var44.x4[2]; + var51.x4[3] = (orc_uint8) var44.x4[3]; + /* 9: mullw */ + var52.x4[0] = (var51.x4[0] * var50.x4[0]) & 0xffff; + var52.x4[1] = (var51.x4[1] * var50.x4[1]) & 0xffff; + var52.x4[2] = (var51.x4[2] * var50.x4[2]) & 0xffff; + var52.x4[3] = (var51.x4[3] * var50.x4[3]) & 0xffff; + /* 11: convubw */ + var54.x4[0] = (orc_uint8) var53.x4[0]; + var54.x4[1] = (orc_uint8) var53.x4[1]; + var54.x4[2] = (orc_uint8) var53.x4[2]; + var54.x4[3] = (orc_uint8) var53.x4[3]; + /* 12: subw */ + var55.x4[0] = var54.x4[0] - var50.x4[0]; + var55.x4[1] = var54.x4[1] - var50.x4[1]; + var55.x4[2] = var54.x4[2] - var50.x4[2]; + var55.x4[3] = var54.x4[3] - var50.x4[3]; + /* 13: loadl */ + var56 = ptr0[i]; + /* 14: convlw */ + var57.i = var56.i; + /* 15: convwb */ + var58 = var57.i; + /* 16: splatbl */ + var59.i = + ((((orc_uint32) var58) & 0xff) << 24) | ((((orc_uint32) var58) & 0xff) + << 16) | ((((orc_uint32) var58) & 0xff) << 8) | (((orc_uint32) var58) + & 0xff); + /* 17: convubw */ + var60.x4[0] = (orc_uint8) var59.x4[0]; + var60.x4[1] = (orc_uint8) var59.x4[1]; + var60.x4[2] = (orc_uint8) var59.x4[2]; + var60.x4[3] = (orc_uint8) var59.x4[3]; + /* 18: mullw */ + var61.x4[0] = (var60.x4[0] * var55.x4[0]) & 0xffff; + var61.x4[1] = (var60.x4[1] * var55.x4[1]) & 0xffff; + var61.x4[2] = (var60.x4[2] * var55.x4[2]) & 0xffff; + var61.x4[3] = (var60.x4[3] * var55.x4[3]) & 0xffff; + /* 19: div255w */ + var62.x4[0] = + ((orc_uint16) (((orc_uint16) (var61.x4[0] + 128)) + + (((orc_uint16) (var61.x4[0] + 128)) >> 8))) >> 8; + var62.x4[1] = + ((orc_uint16) (((orc_uint16) (var61.x4[1] + 128)) + + (((orc_uint16) (var61.x4[1] + 128)) >> 8))) >> 8; + var62.x4[2] = + ((orc_uint16) (((orc_uint16) (var61.x4[2] + 128)) + + (((orc_uint16) (var61.x4[2] + 128)) >> 8))) >> 8; + var62.x4[3] = + ((orc_uint16) (((orc_uint16) (var61.x4[3] + 128)) + + (((orc_uint16) (var61.x4[3] + 128)) >> 8))) >> 8; + /* 20: convubw */ + var63.x4[0] = (orc_uint8) var56.x4[0]; + var63.x4[1] = (orc_uint8) var56.x4[1]; + var63.x4[2] = (orc_uint8) var56.x4[2]; + var63.x4[3] = (orc_uint8) var56.x4[3]; + /* 21: mullw */ + var64.x4[0] = (var63.x4[0] * var62.x4[0]) & 0xffff; + var64.x4[1] = (var63.x4[1] * var62.x4[1]) & 0xffff; + var64.x4[2] = (var63.x4[2] * var62.x4[2]) & 0xffff; + var64.x4[3] = (var63.x4[3] * var62.x4[3]) & 0xffff; + /* 22: addw */ + var65.x4[0] = var64.x4[0] + var52.x4[0]; + var65.x4[1] = var64.x4[1] + var52.x4[1]; + var65.x4[2] = var64.x4[2] + var52.x4[2]; + var65.x4[3] = var64.x4[3] + var52.x4[3]; + /* 23: addw */ + var66.x4[0] = var62.x4[0] + var50.x4[0]; + var66.x4[1] = var62.x4[1] + var50.x4[1]; + var66.x4[2] = var62.x4[2] + var50.x4[2]; + var66.x4[3] = var62.x4[3] + var50.x4[3]; + /* 24: divluw */ + var67.x4[0] = + ((var66.x4[0] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var65.x4[0]) / + ((orc_uint16) var66.x4[0] & 0xff)); + var67.x4[1] = + ((var66.x4[1] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var65.x4[1]) / + ((orc_uint16) var66.x4[1] & 0xff)); + var67.x4[2] = + ((var66.x4[2] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var65.x4[2]) / + ((orc_uint16) var66.x4[2] & 0xff)); + var67.x4[3] = + ((var66.x4[3] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var65.x4[3]) / + ((orc_uint16) var66.x4[3] & 0xff)); + /* 25: convwb */ + var68.x4[0] = var67.x4[0]; + var68.x4[1] = var67.x4[1]; + var68.x4[2] = var67.x4[2]; + var68.x4[3] = var67.x4[3]; + /* 27: andl */ + var69.i = var68.i & var42.i; + /* 28: convwb */ + var70.x4[0] = var66.x4[0]; + var70.x4[1] = var66.x4[1]; + var70.x4[2] = var66.x4[2]; + var70.x4[3] = var66.x4[3]; + /* 30: andl */ + var71.i = var70.i & var43.i; + /* 31: orl */ + var72.i = var69.i | var71.i; + /* 32: storel */ + ptr0[i] = var72; + } + } + +} + +void +compositor_orc_overlay_argb (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 7, 9, 27, 99, 111, 109, 112, 111, 115, 105, 116, 111, 114, 95, 111, + 114, 99, 95, 111, 118, 101, 114, 108, 97, 121, 95, 97, 114, 103, 98, 11, + 4, 4, 12, 4, 4, 14, 4, 255, 255, 255, 255, 14, 4, 255, 0, 0, + 0, 14, 4, 0, 255, 255, 255, 16, 2, 20, 4, 20, 2, 20, 1, 20, + 8, 20, 8, 20, 8, 20, 4, 20, 8, 20, 8, 113, 32, 4, 163, 33, + 32, 157, 34, 33, 152, 38, 34, 21, 2, 150, 35, 38, 21, 2, 89, 35, + 35, 24, 21, 2, 80, 35, 35, 21, 2, 150, 40, 32, 21, 2, 89, 40, + 40, 35, 115, 38, 16, 21, 2, 150, 36, 38, 21, 2, 98, 36, 36, 35, + 113, 32, 0, 163, 33, 32, 157, 34, 33, 152, 38, 34, 21, 2, 150, 37, + 38, 21, 2, 89, 37, 37, 36, 21, 2, 80, 37, 37, 21, 2, 150, 39, + 32, 21, 2, 89, 39, 39, 37, 21, 2, 70, 39, 39, 40, 21, 2, 70, + 37, 37, 35, 21, 2, 81, 39, 39, 37, 21, 2, 157, 32, 39, 106, 32, + 32, 18, 21, 2, 157, 38, 37, 106, 38, 38, 17, 123, 32, 32, 38, 128, + 0, 32, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_compositor_orc_overlay_argb); +#else + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "compositor_orc_overlay_argb"); + orc_program_set_backup_function (p, _backup_compositor_orc_overlay_argb); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_constant (p, 4, 0xffffffff, "c1"); + orc_program_add_constant (p, 4, 0x000000ff, "c2"); + orc_program_add_constant (p, 4, 0xffffff00, "c3"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 4, "t1"); + orc_program_add_temporary (p, 2, "t2"); + orc_program_add_temporary (p, 1, "t3"); + orc_program_add_temporary (p, 8, "t4"); + orc_program_add_temporary (p, 8, "t5"); + orc_program_add_temporary (p, 8, "t6"); + orc_program_add_temporary (p, 4, "t7"); + orc_program_add_temporary (p, 8, "t8"); + orc_program_add_temporary (p, 8, "t9"); + + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T7, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T4, ORC_VAR_T7, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T4, ORC_VAR_T4, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T4, ORC_VAR_T4, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T9, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T9, ORC_VAR_T9, ORC_VAR_T4, + ORC_VAR_D1); + orc_program_append_2 (p, "loadpl", 0, ORC_VAR_T7, ORC_VAR_C1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T5, ORC_VAR_T7, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "subw", 2, ORC_VAR_T5, ORC_VAR_T5, ORC_VAR_T4, + ORC_VAR_D1); + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_D1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T7, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T6, ORC_VAR_T7, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_T5, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T8, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T8, ORC_VAR_T8, ORC_VAR_T6, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 2, ORC_VAR_T8, ORC_VAR_T8, ORC_VAR_T9, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_T4, + ORC_VAR_D1); + orc_program_append_2 (p, "divluw", 2, ORC_VAR_T8, ORC_VAR_T8, ORC_VAR_T6, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 2, ORC_VAR_T1, ORC_VAR_T8, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "andl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C3, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 2, ORC_VAR_T7, ORC_VAR_T6, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "andl", 0, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "orl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_T7, + ORC_VAR_D1); + orc_program_append_2 (p, "storel", 0, ORC_VAR_D1, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ORC_EXECUTOR_M (ex) = m; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_D1] = d1_stride; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_S1] = s1_stride; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* compositor_orc_overlay_argb_addition */ +#ifdef DISABLE_ORC +void +compositor_orc_overlay_argb_addition (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + int i; + int j; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var42; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var43; +#else + orc_union32 var43; +#endif +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var44; +#else + orc_union32 var44; +#endif + orc_union32 var45; + orc_union16 var46; + orc_int8 var47; + orc_union32 var48; + orc_union64 var49; + orc_union64 var50; + orc_union64 var51; + orc_union64 var52; + orc_union64 var53; + orc_union32 var54; + orc_union64 var55; + orc_union64 var56; + orc_union32 var57; + orc_union16 var58; + orc_int8 var59; + orc_union32 var60; + orc_union64 var61; + orc_union64 var62; + orc_union64 var63; + orc_union64 var64; + orc_union64 var65; + orc_union64 var66; + orc_union64 var67; + orc_union64 var68; + orc_union32 var69; + orc_union16 var70; + orc_int8 var71; + orc_union32 var72; + orc_union64 var73; + orc_union64 var74; + orc_union32 var75; + orc_union32 var76; + orc_union32 var77; + orc_union32 var78; + orc_union32 var79; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (d1, d1_stride * j); + ptr4 = ORC_PTR_OFFSET (s1, s1_stride * j); + + /* 5: loadpw */ + var42.x4[0] = p1; + var42.x4[1] = p1; + var42.x4[2] = p1; + var42.x4[3] = p1; + /* 10: loadpl */ + var54.i = 0xffffffff; /* -1 or 2.122e-314f */ + /* 32: loadpl */ + var43.i = 0xffffff00; /* -256 or 2.122e-314f */ + /* 35: loadpl */ + var44.i = 0x000000ff; /* 255 or 1.25987e-321f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var45 = ptr4[i]; + /* 1: convlw */ + var46.i = var45.i; + /* 2: convwb */ + var47 = var46.i; + /* 3: splatbl */ + var48.i = + ((((orc_uint32) var47) & 0xff) << 24) | ((((orc_uint32) var47) & 0xff) + << 16) | ((((orc_uint32) var47) & 0xff) << 8) | (((orc_uint32) var47) + & 0xff); + /* 4: convubw */ + var49.x4[0] = (orc_uint8) var48.x4[0]; + var49.x4[1] = (orc_uint8) var48.x4[1]; + var49.x4[2] = (orc_uint8) var48.x4[2]; + var49.x4[3] = (orc_uint8) var48.x4[3]; + /* 6: mullw */ + var50.x4[0] = (var49.x4[0] * var42.x4[0]) & 0xffff; + var50.x4[1] = (var49.x4[1] * var42.x4[1]) & 0xffff; + var50.x4[2] = (var49.x4[2] * var42.x4[2]) & 0xffff; + var50.x4[3] = (var49.x4[3] * var42.x4[3]) & 0xffff; + /* 7: div255w */ + var51.x4[0] = + ((orc_uint16) (((orc_uint16) (var50.x4[0] + 128)) + + (((orc_uint16) (var50.x4[0] + 128)) >> 8))) >> 8; + var51.x4[1] = + ((orc_uint16) (((orc_uint16) (var50.x4[1] + 128)) + + (((orc_uint16) (var50.x4[1] + 128)) >> 8))) >> 8; + var51.x4[2] = + ((orc_uint16) (((orc_uint16) (var50.x4[2] + 128)) + + (((orc_uint16) (var50.x4[2] + 128)) >> 8))) >> 8; + var51.x4[3] = + ((orc_uint16) (((orc_uint16) (var50.x4[3] + 128)) + + (((orc_uint16) (var50.x4[3] + 128)) >> 8))) >> 8; + /* 8: convubw */ + var52.x4[0] = (orc_uint8) var45.x4[0]; + var52.x4[1] = (orc_uint8) var45.x4[1]; + var52.x4[2] = (orc_uint8) var45.x4[2]; + var52.x4[3] = (orc_uint8) var45.x4[3]; + /* 9: mullw */ + var53.x4[0] = (var52.x4[0] * var51.x4[0]) & 0xffff; + var53.x4[1] = (var52.x4[1] * var51.x4[1]) & 0xffff; + var53.x4[2] = (var52.x4[2] * var51.x4[2]) & 0xffff; + var53.x4[3] = (var52.x4[3] * var51.x4[3]) & 0xffff; + /* 11: convubw */ + var55.x4[0] = (orc_uint8) var54.x4[0]; + var55.x4[1] = (orc_uint8) var54.x4[1]; + var55.x4[2] = (orc_uint8) var54.x4[2]; + var55.x4[3] = (orc_uint8) var54.x4[3]; + /* 12: subw */ + var56.x4[0] = var55.x4[0] - var51.x4[0]; + var56.x4[1] = var55.x4[1] - var51.x4[1]; + var56.x4[2] = var55.x4[2] - var51.x4[2]; + var56.x4[3] = var55.x4[3] - var51.x4[3]; + /* 13: loadl */ + var57 = ptr0[i]; + /* 14: convlw */ + var58.i = var57.i; + /* 15: convwb */ + var59 = var58.i; + /* 16: splatbl */ + var60.i = + ((((orc_uint32) var59) & 0xff) << 24) | ((((orc_uint32) var59) & 0xff) + << 16) | ((((orc_uint32) var59) & 0xff) << 8) | (((orc_uint32) var59) + & 0xff); + /* 17: convubw */ + var61.x4[0] = (orc_uint8) var60.x4[0]; + var61.x4[1] = (orc_uint8) var60.x4[1]; + var61.x4[2] = (orc_uint8) var60.x4[2]; + var61.x4[3] = (orc_uint8) var60.x4[3]; + /* 18: mullw */ + var62.x4[0] = (var61.x4[0] * var56.x4[0]) & 0xffff; + var62.x4[1] = (var61.x4[1] * var56.x4[1]) & 0xffff; + var62.x4[2] = (var61.x4[2] * var56.x4[2]) & 0xffff; + var62.x4[3] = (var61.x4[3] * var56.x4[3]) & 0xffff; + /* 19: div255w */ + var63.x4[0] = + ((orc_uint16) (((orc_uint16) (var62.x4[0] + 128)) + + (((orc_uint16) (var62.x4[0] + 128)) >> 8))) >> 8; + var63.x4[1] = + ((orc_uint16) (((orc_uint16) (var62.x4[1] + 128)) + + (((orc_uint16) (var62.x4[1] + 128)) >> 8))) >> 8; + var63.x4[2] = + ((orc_uint16) (((orc_uint16) (var62.x4[2] + 128)) + + (((orc_uint16) (var62.x4[2] + 128)) >> 8))) >> 8; + var63.x4[3] = + ((orc_uint16) (((orc_uint16) (var62.x4[3] + 128)) + + (((orc_uint16) (var62.x4[3] + 128)) >> 8))) >> 8; + /* 20: convubw */ + var64.x4[0] = (orc_uint8) var57.x4[0]; + var64.x4[1] = (orc_uint8) var57.x4[1]; + var64.x4[2] = (orc_uint8) var57.x4[2]; + var64.x4[3] = (orc_uint8) var57.x4[3]; + /* 21: mullw */ + var65.x4[0] = (var64.x4[0] * var63.x4[0]) & 0xffff; + var65.x4[1] = (var64.x4[1] * var63.x4[1]) & 0xffff; + var65.x4[2] = (var64.x4[2] * var63.x4[2]) & 0xffff; + var65.x4[3] = (var64.x4[3] * var63.x4[3]) & 0xffff; + /* 22: addw */ + var66.x4[0] = var65.x4[0] + var53.x4[0]; + var66.x4[1] = var65.x4[1] + var53.x4[1]; + var66.x4[2] = var65.x4[2] + var53.x4[2]; + var66.x4[3] = var65.x4[3] + var53.x4[3]; + /* 23: addw */ + var67.x4[0] = var63.x4[0] + var51.x4[0]; + var67.x4[1] = var63.x4[1] + var51.x4[1]; + var67.x4[2] = var63.x4[2] + var51.x4[2]; + var67.x4[3] = var63.x4[3] + var51.x4[3]; + /* 24: divluw */ + var68.x4[0] = + ((var67.x4[0] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var66.x4[0]) / + ((orc_uint16) var67.x4[0] & 0xff)); + var68.x4[1] = + ((var67.x4[1] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var66.x4[1]) / + ((orc_uint16) var67.x4[1] & 0xff)); + var68.x4[2] = + ((var67.x4[2] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var66.x4[2]) / + ((orc_uint16) var67.x4[2] & 0xff)); + var68.x4[3] = + ((var67.x4[3] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var66.x4[3]) / + ((orc_uint16) var67.x4[3] & 0xff)); + /* 25: loadl */ + var69 = ptr0[i]; + /* 26: convlw */ + var70.i = var69.i; + /* 27: convwb */ + var71 = var70.i; + /* 28: splatbl */ + var72.i = + ((((orc_uint32) var71) & 0xff) << 24) | ((((orc_uint32) var71) & 0xff) + << 16) | ((((orc_uint32) var71) & 0xff) << 8) | (((orc_uint32) var71) + & 0xff); + /* 29: convubw */ + var73.x4[0] = (orc_uint8) var72.x4[0]; + var73.x4[1] = (orc_uint8) var72.x4[1]; + var73.x4[2] = (orc_uint8) var72.x4[2]; + var73.x4[3] = (orc_uint8) var72.x4[3]; + /* 30: addw */ + var74.x4[0] = var73.x4[0] + var51.x4[0]; + var74.x4[1] = var73.x4[1] + var51.x4[1]; + var74.x4[2] = var73.x4[2] + var51.x4[2]; + var74.x4[3] = var73.x4[3] + var51.x4[3]; + /* 31: convwb */ + var75.x4[0] = var68.x4[0]; + var75.x4[1] = var68.x4[1]; + var75.x4[2] = var68.x4[2]; + var75.x4[3] = var68.x4[3]; + /* 33: andl */ + var76.i = var75.i & var43.i; + /* 34: convwb */ + var77.x4[0] = var74.x4[0]; + var77.x4[1] = var74.x4[1]; + var77.x4[2] = var74.x4[2]; + var77.x4[3] = var74.x4[3]; + /* 36: andl */ + var78.i = var77.i & var44.i; + /* 37: orl */ + var79.i = var76.i | var78.i; + /* 38: storel */ + ptr0[i] = var79; + } + } + +} + +#else +static void +_backup_compositor_orc_overlay_argb_addition (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int j; + int n = ex->n; + int m = ex->params[ORC_VAR_A1]; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var42; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var43; +#else + orc_union32 var43; +#endif +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var44; +#else + orc_union32 var44; +#endif + orc_union32 var45; + orc_union16 var46; + orc_int8 var47; + orc_union32 var48; + orc_union64 var49; + orc_union64 var50; + orc_union64 var51; + orc_union64 var52; + orc_union64 var53; + orc_union32 var54; + orc_union64 var55; + orc_union64 var56; + orc_union32 var57; + orc_union16 var58; + orc_int8 var59; + orc_union32 var60; + orc_union64 var61; + orc_union64 var62; + orc_union64 var63; + orc_union64 var64; + orc_union64 var65; + orc_union64 var66; + orc_union64 var67; + orc_union64 var68; + orc_union32 var69; + orc_union16 var70; + orc_int8 var71; + orc_union32 var72; + orc_union64 var73; + orc_union64 var74; + orc_union32 var75; + orc_union32 var76; + orc_union32 var77; + orc_union32 var78; + orc_union32 var79; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (ex->arrays[0], ex->params[0] * j); + ptr4 = ORC_PTR_OFFSET (ex->arrays[4], ex->params[4] * j); + + /* 5: loadpw */ + var42.x4[0] = ex->params[24]; + var42.x4[1] = ex->params[24]; + var42.x4[2] = ex->params[24]; + var42.x4[3] = ex->params[24]; + /* 10: loadpl */ + var54.i = 0xffffffff; /* -1 or 2.122e-314f */ + /* 32: loadpl */ + var43.i = 0xffffff00; /* -256 or 2.122e-314f */ + /* 35: loadpl */ + var44.i = 0x000000ff; /* 255 or 1.25987e-321f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var45 = ptr4[i]; + /* 1: convlw */ + var46.i = var45.i; + /* 2: convwb */ + var47 = var46.i; + /* 3: splatbl */ + var48.i = + ((((orc_uint32) var47) & 0xff) << 24) | ((((orc_uint32) var47) & 0xff) + << 16) | ((((orc_uint32) var47) & 0xff) << 8) | (((orc_uint32) var47) + & 0xff); + /* 4: convubw */ + var49.x4[0] = (orc_uint8) var48.x4[0]; + var49.x4[1] = (orc_uint8) var48.x4[1]; + var49.x4[2] = (orc_uint8) var48.x4[2]; + var49.x4[3] = (orc_uint8) var48.x4[3]; + /* 6: mullw */ + var50.x4[0] = (var49.x4[0] * var42.x4[0]) & 0xffff; + var50.x4[1] = (var49.x4[1] * var42.x4[1]) & 0xffff; + var50.x4[2] = (var49.x4[2] * var42.x4[2]) & 0xffff; + var50.x4[3] = (var49.x4[3] * var42.x4[3]) & 0xffff; + /* 7: div255w */ + var51.x4[0] = + ((orc_uint16) (((orc_uint16) (var50.x4[0] + 128)) + + (((orc_uint16) (var50.x4[0] + 128)) >> 8))) >> 8; + var51.x4[1] = + ((orc_uint16) (((orc_uint16) (var50.x4[1] + 128)) + + (((orc_uint16) (var50.x4[1] + 128)) >> 8))) >> 8; + var51.x4[2] = + ((orc_uint16) (((orc_uint16) (var50.x4[2] + 128)) + + (((orc_uint16) (var50.x4[2] + 128)) >> 8))) >> 8; + var51.x4[3] = + ((orc_uint16) (((orc_uint16) (var50.x4[3] + 128)) + + (((orc_uint16) (var50.x4[3] + 128)) >> 8))) >> 8; + /* 8: convubw */ + var52.x4[0] = (orc_uint8) var45.x4[0]; + var52.x4[1] = (orc_uint8) var45.x4[1]; + var52.x4[2] = (orc_uint8) var45.x4[2]; + var52.x4[3] = (orc_uint8) var45.x4[3]; + /* 9: mullw */ + var53.x4[0] = (var52.x4[0] * var51.x4[0]) & 0xffff; + var53.x4[1] = (var52.x4[1] * var51.x4[1]) & 0xffff; + var53.x4[2] = (var52.x4[2] * var51.x4[2]) & 0xffff; + var53.x4[3] = (var52.x4[3] * var51.x4[3]) & 0xffff; + /* 11: convubw */ + var55.x4[0] = (orc_uint8) var54.x4[0]; + var55.x4[1] = (orc_uint8) var54.x4[1]; + var55.x4[2] = (orc_uint8) var54.x4[2]; + var55.x4[3] = (orc_uint8) var54.x4[3]; + /* 12: subw */ + var56.x4[0] = var55.x4[0] - var51.x4[0]; + var56.x4[1] = var55.x4[1] - var51.x4[1]; + var56.x4[2] = var55.x4[2] - var51.x4[2]; + var56.x4[3] = var55.x4[3] - var51.x4[3]; + /* 13: loadl */ + var57 = ptr0[i]; + /* 14: convlw */ + var58.i = var57.i; + /* 15: convwb */ + var59 = var58.i; + /* 16: splatbl */ + var60.i = + ((((orc_uint32) var59) & 0xff) << 24) | ((((orc_uint32) var59) & 0xff) + << 16) | ((((orc_uint32) var59) & 0xff) << 8) | (((orc_uint32) var59) + & 0xff); + /* 17: convubw */ + var61.x4[0] = (orc_uint8) var60.x4[0]; + var61.x4[1] = (orc_uint8) var60.x4[1]; + var61.x4[2] = (orc_uint8) var60.x4[2]; + var61.x4[3] = (orc_uint8) var60.x4[3]; + /* 18: mullw */ + var62.x4[0] = (var61.x4[0] * var56.x4[0]) & 0xffff; + var62.x4[1] = (var61.x4[1] * var56.x4[1]) & 0xffff; + var62.x4[2] = (var61.x4[2] * var56.x4[2]) & 0xffff; + var62.x4[3] = (var61.x4[3] * var56.x4[3]) & 0xffff; + /* 19: div255w */ + var63.x4[0] = + ((orc_uint16) (((orc_uint16) (var62.x4[0] + 128)) + + (((orc_uint16) (var62.x4[0] + 128)) >> 8))) >> 8; + var63.x4[1] = + ((orc_uint16) (((orc_uint16) (var62.x4[1] + 128)) + + (((orc_uint16) (var62.x4[1] + 128)) >> 8))) >> 8; + var63.x4[2] = + ((orc_uint16) (((orc_uint16) (var62.x4[2] + 128)) + + (((orc_uint16) (var62.x4[2] + 128)) >> 8))) >> 8; + var63.x4[3] = + ((orc_uint16) (((orc_uint16) (var62.x4[3] + 128)) + + (((orc_uint16) (var62.x4[3] + 128)) >> 8))) >> 8; + /* 20: convubw */ + var64.x4[0] = (orc_uint8) var57.x4[0]; + var64.x4[1] = (orc_uint8) var57.x4[1]; + var64.x4[2] = (orc_uint8) var57.x4[2]; + var64.x4[3] = (orc_uint8) var57.x4[3]; + /* 21: mullw */ + var65.x4[0] = (var64.x4[0] * var63.x4[0]) & 0xffff; + var65.x4[1] = (var64.x4[1] * var63.x4[1]) & 0xffff; + var65.x4[2] = (var64.x4[2] * var63.x4[2]) & 0xffff; + var65.x4[3] = (var64.x4[3] * var63.x4[3]) & 0xffff; + /* 22: addw */ + var66.x4[0] = var65.x4[0] + var53.x4[0]; + var66.x4[1] = var65.x4[1] + var53.x4[1]; + var66.x4[2] = var65.x4[2] + var53.x4[2]; + var66.x4[3] = var65.x4[3] + var53.x4[3]; + /* 23: addw */ + var67.x4[0] = var63.x4[0] + var51.x4[0]; + var67.x4[1] = var63.x4[1] + var51.x4[1]; + var67.x4[2] = var63.x4[2] + var51.x4[2]; + var67.x4[3] = var63.x4[3] + var51.x4[3]; + /* 24: divluw */ + var68.x4[0] = + ((var67.x4[0] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var66.x4[0]) / + ((orc_uint16) var67.x4[0] & 0xff)); + var68.x4[1] = + ((var67.x4[1] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var66.x4[1]) / + ((orc_uint16) var67.x4[1] & 0xff)); + var68.x4[2] = + ((var67.x4[2] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var66.x4[2]) / + ((orc_uint16) var67.x4[2] & 0xff)); + var68.x4[3] = + ((var67.x4[3] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var66.x4[3]) / + ((orc_uint16) var67.x4[3] & 0xff)); + /* 25: loadl */ + var69 = ptr0[i]; + /* 26: convlw */ + var70.i = var69.i; + /* 27: convwb */ + var71 = var70.i; + /* 28: splatbl */ + var72.i = + ((((orc_uint32) var71) & 0xff) << 24) | ((((orc_uint32) var71) & 0xff) + << 16) | ((((orc_uint32) var71) & 0xff) << 8) | (((orc_uint32) var71) + & 0xff); + /* 29: convubw */ + var73.x4[0] = (orc_uint8) var72.x4[0]; + var73.x4[1] = (orc_uint8) var72.x4[1]; + var73.x4[2] = (orc_uint8) var72.x4[2]; + var73.x4[3] = (orc_uint8) var72.x4[3]; + /* 30: addw */ + var74.x4[0] = var73.x4[0] + var51.x4[0]; + var74.x4[1] = var73.x4[1] + var51.x4[1]; + var74.x4[2] = var73.x4[2] + var51.x4[2]; + var74.x4[3] = var73.x4[3] + var51.x4[3]; + /* 31: convwb */ + var75.x4[0] = var68.x4[0]; + var75.x4[1] = var68.x4[1]; + var75.x4[2] = var68.x4[2]; + var75.x4[3] = var68.x4[3]; + /* 33: andl */ + var76.i = var75.i & var43.i; + /* 34: convwb */ + var77.x4[0] = var74.x4[0]; + var77.x4[1] = var74.x4[1]; + var77.x4[2] = var74.x4[2]; + var77.x4[3] = var74.x4[3]; + /* 36: andl */ + var78.i = var77.i & var44.i; + /* 37: orl */ + var79.i = var76.i | var78.i; + /* 38: storel */ + ptr0[i] = var79; + } + } + +} + +void +compositor_orc_overlay_argb_addition (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 7, 9, 36, 99, 111, 109, 112, 111, 115, 105, 116, 111, 114, 95, 111, + 114, 99, 95, 111, 118, 101, 114, 108, 97, 121, 95, 97, 114, 103, 98, 95, + 97, 100, 100, 105, 116, 105, 111, 110, 11, 4, 4, 12, 4, 4, 14, 4, + 255, 255, 255, 255, 14, 4, 255, 0, 0, 0, 14, 4, 0, 255, 255, 255, + 16, 2, 20, 4, 20, 2, 20, 1, 20, 8, 20, 8, 20, 8, 20, 8, + 20, 4, 20, 8, 20, 8, 113, 32, 4, 163, 33, 32, 157, 34, 33, 152, + 39, 34, 21, 2, 150, 35, 39, 21, 2, 89, 35, 35, 24, 21, 2, 80, + 35, 35, 21, 2, 150, 41, 32, 21, 2, 89, 41, 41, 35, 115, 39, 16, + 21, 2, 150, 36, 39, 21, 2, 98, 36, 36, 35, 113, 32, 0, 163, 33, + 32, 157, 34, 33, 152, 39, 34, 21, 2, 150, 37, 39, 21, 2, 89, 37, + 37, 36, 21, 2, 80, 37, 37, 21, 2, 150, 40, 32, 21, 2, 89, 40, + 40, 37, 21, 2, 70, 40, 40, 41, 21, 2, 70, 37, 37, 35, 21, 2, + 81, 40, 40, 37, 113, 32, 0, 163, 33, 32, 157, 34, 33, 152, 39, 34, + 21, 2, 150, 38, 39, 21, 2, 70, 38, 38, 35, 21, 2, 157, 32, 40, + 106, 32, 32, 18, 21, 2, 157, 39, 38, 106, 39, 39, 17, 123, 32, 32, + 39, 128, 0, 32, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_compositor_orc_overlay_argb_addition); +#else + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "compositor_orc_overlay_argb_addition"); + orc_program_set_backup_function (p, + _backup_compositor_orc_overlay_argb_addition); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_constant (p, 4, 0xffffffff, "c1"); + orc_program_add_constant (p, 4, 0x000000ff, "c2"); + orc_program_add_constant (p, 4, 0xffffff00, "c3"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 4, "t1"); + orc_program_add_temporary (p, 2, "t2"); + orc_program_add_temporary (p, 1, "t3"); + orc_program_add_temporary (p, 8, "t4"); + orc_program_add_temporary (p, 8, "t5"); + orc_program_add_temporary (p, 8, "t6"); + orc_program_add_temporary (p, 8, "t7"); + orc_program_add_temporary (p, 4, "t8"); + orc_program_add_temporary (p, 8, "t9"); + orc_program_add_temporary (p, 8, "t10"); + + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T8, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T4, ORC_VAR_T8, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T4, ORC_VAR_T4, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T4, ORC_VAR_T4, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T10, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T10, ORC_VAR_T10, ORC_VAR_T4, + ORC_VAR_D1); + orc_program_append_2 (p, "loadpl", 0, ORC_VAR_T8, ORC_VAR_C1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T5, ORC_VAR_T8, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "subw", 2, ORC_VAR_T5, ORC_VAR_T5, ORC_VAR_T4, + ORC_VAR_D1); + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_D1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T8, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T6, ORC_VAR_T8, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_T5, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T9, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T9, ORC_VAR_T9, ORC_VAR_T6, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 2, ORC_VAR_T9, ORC_VAR_T9, ORC_VAR_T10, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_T4, + ORC_VAR_D1); + orc_program_append_2 (p, "divluw", 2, ORC_VAR_T9, ORC_VAR_T9, ORC_VAR_T6, + ORC_VAR_D1); + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_D1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T8, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T7, ORC_VAR_T8, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_T4, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 2, ORC_VAR_T1, ORC_VAR_T9, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "andl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C3, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 2, ORC_VAR_T8, ORC_VAR_T7, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "andl", 0, ORC_VAR_T8, ORC_VAR_T8, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "orl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_T8, + ORC_VAR_D1); + orc_program_append_2 (p, "storel", 0, ORC_VAR_D1, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ORC_EXECUTOR_M (ex) = m; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_D1] = d1_stride; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_S1] = s1_stride; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* compositor_orc_overlay_bgra */ +#ifdef DISABLE_ORC +void +compositor_orc_overlay_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + int i; + int j; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var42; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var43; +#else + orc_union32 var43; +#endif +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var44; +#else + orc_union32 var44; +#endif + orc_union32 var45; + orc_union32 var46; + orc_union16 var47; + orc_int8 var48; + orc_union32 var49; + orc_union64 var50; + orc_union64 var51; + orc_union64 var52; + orc_union64 var53; + orc_union64 var54; + orc_union32 var55; + orc_union64 var56; + orc_union64 var57; + orc_union32 var58; + orc_union32 var59; + orc_union16 var60; + orc_int8 var61; + orc_union32 var62; + orc_union64 var63; + orc_union64 var64; + orc_union64 var65; + orc_union64 var66; + orc_union64 var67; + orc_union64 var68; + orc_union64 var69; + orc_union64 var70; + orc_union32 var71; + orc_union32 var72; + orc_union32 var73; + orc_union32 var74; + orc_union32 var75; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (d1, d1_stride * j); + ptr4 = ORC_PTR_OFFSET (s1, s1_stride * j); + + /* 6: loadpw */ + var42.x4[0] = p1; + var42.x4[1] = p1; + var42.x4[2] = p1; + var42.x4[3] = p1; + /* 11: loadpl */ + var55.i = 0xffffffff; /* -1 or 2.122e-314f */ + /* 28: loadpl */ + var43.i = 0x00ffffff; /* 16777215 or 8.28905e-317f */ + /* 31: loadpl */ + var44.i = 0xff000000; /* -16777216 or 2.11371e-314f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var45 = ptr4[i]; + /* 1: shrul */ + var46.i = ((orc_uint32) var45.i) >> 24; + /* 2: convlw */ + var47.i = var46.i; + /* 3: convwb */ + var48 = var47.i; + /* 4: splatbl */ + var49.i = + ((((orc_uint32) var48) & 0xff) << 24) | ((((orc_uint32) var48) & 0xff) + << 16) | ((((orc_uint32) var48) & 0xff) << 8) | (((orc_uint32) var48) + & 0xff); + /* 5: convubw */ + var50.x4[0] = (orc_uint8) var49.x4[0]; + var50.x4[1] = (orc_uint8) var49.x4[1]; + var50.x4[2] = (orc_uint8) var49.x4[2]; + var50.x4[3] = (orc_uint8) var49.x4[3]; + /* 7: mullw */ + var51.x4[0] = (var50.x4[0] * var42.x4[0]) & 0xffff; + var51.x4[1] = (var50.x4[1] * var42.x4[1]) & 0xffff; + var51.x4[2] = (var50.x4[2] * var42.x4[2]) & 0xffff; + var51.x4[3] = (var50.x4[3] * var42.x4[3]) & 0xffff; + /* 8: div255w */ + var52.x4[0] = + ((orc_uint16) (((orc_uint16) (var51.x4[0] + 128)) + + (((orc_uint16) (var51.x4[0] + 128)) >> 8))) >> 8; + var52.x4[1] = + ((orc_uint16) (((orc_uint16) (var51.x4[1] + 128)) + + (((orc_uint16) (var51.x4[1] + 128)) >> 8))) >> 8; + var52.x4[2] = + ((orc_uint16) (((orc_uint16) (var51.x4[2] + 128)) + + (((orc_uint16) (var51.x4[2] + 128)) >> 8))) >> 8; + var52.x4[3] = + ((orc_uint16) (((orc_uint16) (var51.x4[3] + 128)) + + (((orc_uint16) (var51.x4[3] + 128)) >> 8))) >> 8; + /* 9: convubw */ + var53.x4[0] = (orc_uint8) var45.x4[0]; + var53.x4[1] = (orc_uint8) var45.x4[1]; + var53.x4[2] = (orc_uint8) var45.x4[2]; + var53.x4[3] = (orc_uint8) var45.x4[3]; + /* 10: mullw */ + var54.x4[0] = (var53.x4[0] * var52.x4[0]) & 0xffff; + var54.x4[1] = (var53.x4[1] * var52.x4[1]) & 0xffff; + var54.x4[2] = (var53.x4[2] * var52.x4[2]) & 0xffff; + var54.x4[3] = (var53.x4[3] * var52.x4[3]) & 0xffff; + /* 12: convubw */ + var56.x4[0] = (orc_uint8) var55.x4[0]; + var56.x4[1] = (orc_uint8) var55.x4[1]; + var56.x4[2] = (orc_uint8) var55.x4[2]; + var56.x4[3] = (orc_uint8) var55.x4[3]; + /* 13: subw */ + var57.x4[0] = var56.x4[0] - var52.x4[0]; + var57.x4[1] = var56.x4[1] - var52.x4[1]; + var57.x4[2] = var56.x4[2] - var52.x4[2]; + var57.x4[3] = var56.x4[3] - var52.x4[3]; + /* 14: loadl */ + var58 = ptr0[i]; + /* 15: shrul */ + var59.i = ((orc_uint32) var58.i) >> 24; + /* 16: convlw */ + var60.i = var59.i; + /* 17: convwb */ + var61 = var60.i; + /* 18: splatbl */ + var62.i = + ((((orc_uint32) var61) & 0xff) << 24) | ((((orc_uint32) var61) & 0xff) + << 16) | ((((orc_uint32) var61) & 0xff) << 8) | (((orc_uint32) var61) + & 0xff); + /* 19: convubw */ + var63.x4[0] = (orc_uint8) var62.x4[0]; + var63.x4[1] = (orc_uint8) var62.x4[1]; + var63.x4[2] = (orc_uint8) var62.x4[2]; + var63.x4[3] = (orc_uint8) var62.x4[3]; + /* 20: mullw */ + var64.x4[0] = (var63.x4[0] * var57.x4[0]) & 0xffff; + var64.x4[1] = (var63.x4[1] * var57.x4[1]) & 0xffff; + var64.x4[2] = (var63.x4[2] * var57.x4[2]) & 0xffff; + var64.x4[3] = (var63.x4[3] * var57.x4[3]) & 0xffff; + /* 21: div255w */ + var65.x4[0] = + ((orc_uint16) (((orc_uint16) (var64.x4[0] + 128)) + + (((orc_uint16) (var64.x4[0] + 128)) >> 8))) >> 8; + var65.x4[1] = + ((orc_uint16) (((orc_uint16) (var64.x4[1] + 128)) + + (((orc_uint16) (var64.x4[1] + 128)) >> 8))) >> 8; + var65.x4[2] = + ((orc_uint16) (((orc_uint16) (var64.x4[2] + 128)) + + (((orc_uint16) (var64.x4[2] + 128)) >> 8))) >> 8; + var65.x4[3] = + ((orc_uint16) (((orc_uint16) (var64.x4[3] + 128)) + + (((orc_uint16) (var64.x4[3] + 128)) >> 8))) >> 8; + /* 22: convubw */ + var66.x4[0] = (orc_uint8) var58.x4[0]; + var66.x4[1] = (orc_uint8) var58.x4[1]; + var66.x4[2] = (orc_uint8) var58.x4[2]; + var66.x4[3] = (orc_uint8) var58.x4[3]; + /* 23: mullw */ + var67.x4[0] = (var66.x4[0] * var65.x4[0]) & 0xffff; + var67.x4[1] = (var66.x4[1] * var65.x4[1]) & 0xffff; + var67.x4[2] = (var66.x4[2] * var65.x4[2]) & 0xffff; + var67.x4[3] = (var66.x4[3] * var65.x4[3]) & 0xffff; + /* 24: addw */ + var68.x4[0] = var67.x4[0] + var54.x4[0]; + var68.x4[1] = var67.x4[1] + var54.x4[1]; + var68.x4[2] = var67.x4[2] + var54.x4[2]; + var68.x4[3] = var67.x4[3] + var54.x4[3]; + /* 25: addw */ + var69.x4[0] = var65.x4[0] + var52.x4[0]; + var69.x4[1] = var65.x4[1] + var52.x4[1]; + var69.x4[2] = var65.x4[2] + var52.x4[2]; + var69.x4[3] = var65.x4[3] + var52.x4[3]; + /* 26: divluw */ + var70.x4[0] = + ((var69.x4[0] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var68.x4[0]) / + ((orc_uint16) var69.x4[0] & 0xff)); + var70.x4[1] = + ((var69.x4[1] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var68.x4[1]) / + ((orc_uint16) var69.x4[1] & 0xff)); + var70.x4[2] = + ((var69.x4[2] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var68.x4[2]) / + ((orc_uint16) var69.x4[2] & 0xff)); + var70.x4[3] = + ((var69.x4[3] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var68.x4[3]) / + ((orc_uint16) var69.x4[3] & 0xff)); + /* 27: convwb */ + var71.x4[0] = var70.x4[0]; + var71.x4[1] = var70.x4[1]; + var71.x4[2] = var70.x4[2]; + var71.x4[3] = var70.x4[3]; + /* 29: andl */ + var72.i = var71.i & var43.i; + /* 30: convwb */ + var73.x4[0] = var69.x4[0]; + var73.x4[1] = var69.x4[1]; + var73.x4[2] = var69.x4[2]; + var73.x4[3] = var69.x4[3]; + /* 32: andl */ + var74.i = var73.i & var44.i; + /* 33: orl */ + var75.i = var72.i | var74.i; + /* 34: storel */ + ptr0[i] = var75; + } + } + +} + +#else +static void +_backup_compositor_orc_overlay_bgra (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int j; + int n = ex->n; + int m = ex->params[ORC_VAR_A1]; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var42; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var43; +#else + orc_union32 var43; +#endif +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var44; +#else + orc_union32 var44; +#endif + orc_union32 var45; + orc_union32 var46; + orc_union16 var47; + orc_int8 var48; + orc_union32 var49; + orc_union64 var50; + orc_union64 var51; + orc_union64 var52; + orc_union64 var53; + orc_union64 var54; + orc_union32 var55; + orc_union64 var56; + orc_union64 var57; + orc_union32 var58; + orc_union32 var59; + orc_union16 var60; + orc_int8 var61; + orc_union32 var62; + orc_union64 var63; + orc_union64 var64; + orc_union64 var65; + orc_union64 var66; + orc_union64 var67; + orc_union64 var68; + orc_union64 var69; + orc_union64 var70; + orc_union32 var71; + orc_union32 var72; + orc_union32 var73; + orc_union32 var74; + orc_union32 var75; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (ex->arrays[0], ex->params[0] * j); + ptr4 = ORC_PTR_OFFSET (ex->arrays[4], ex->params[4] * j); + + /* 6: loadpw */ + var42.x4[0] = ex->params[24]; + var42.x4[1] = ex->params[24]; + var42.x4[2] = ex->params[24]; + var42.x4[3] = ex->params[24]; + /* 11: loadpl */ + var55.i = 0xffffffff; /* -1 or 2.122e-314f */ + /* 28: loadpl */ + var43.i = 0x00ffffff; /* 16777215 or 8.28905e-317f */ + /* 31: loadpl */ + var44.i = 0xff000000; /* -16777216 or 2.11371e-314f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var45 = ptr4[i]; + /* 1: shrul */ + var46.i = ((orc_uint32) var45.i) >> 24; + /* 2: convlw */ + var47.i = var46.i; + /* 3: convwb */ + var48 = var47.i; + /* 4: splatbl */ + var49.i = + ((((orc_uint32) var48) & 0xff) << 24) | ((((orc_uint32) var48) & 0xff) + << 16) | ((((orc_uint32) var48) & 0xff) << 8) | (((orc_uint32) var48) + & 0xff); + /* 5: convubw */ + var50.x4[0] = (orc_uint8) var49.x4[0]; + var50.x4[1] = (orc_uint8) var49.x4[1]; + var50.x4[2] = (orc_uint8) var49.x4[2]; + var50.x4[3] = (orc_uint8) var49.x4[3]; + /* 7: mullw */ + var51.x4[0] = (var50.x4[0] * var42.x4[0]) & 0xffff; + var51.x4[1] = (var50.x4[1] * var42.x4[1]) & 0xffff; + var51.x4[2] = (var50.x4[2] * var42.x4[2]) & 0xffff; + var51.x4[3] = (var50.x4[3] * var42.x4[3]) & 0xffff; + /* 8: div255w */ + var52.x4[0] = + ((orc_uint16) (((orc_uint16) (var51.x4[0] + 128)) + + (((orc_uint16) (var51.x4[0] + 128)) >> 8))) >> 8; + var52.x4[1] = + ((orc_uint16) (((orc_uint16) (var51.x4[1] + 128)) + + (((orc_uint16) (var51.x4[1] + 128)) >> 8))) >> 8; + var52.x4[2] = + ((orc_uint16) (((orc_uint16) (var51.x4[2] + 128)) + + (((orc_uint16) (var51.x4[2] + 128)) >> 8))) >> 8; + var52.x4[3] = + ((orc_uint16) (((orc_uint16) (var51.x4[3] + 128)) + + (((orc_uint16) (var51.x4[3] + 128)) >> 8))) >> 8; + /* 9: convubw */ + var53.x4[0] = (orc_uint8) var45.x4[0]; + var53.x4[1] = (orc_uint8) var45.x4[1]; + var53.x4[2] = (orc_uint8) var45.x4[2]; + var53.x4[3] = (orc_uint8) var45.x4[3]; + /* 10: mullw */ + var54.x4[0] = (var53.x4[0] * var52.x4[0]) & 0xffff; + var54.x4[1] = (var53.x4[1] * var52.x4[1]) & 0xffff; + var54.x4[2] = (var53.x4[2] * var52.x4[2]) & 0xffff; + var54.x4[3] = (var53.x4[3] * var52.x4[3]) & 0xffff; + /* 12: convubw */ + var56.x4[0] = (orc_uint8) var55.x4[0]; + var56.x4[1] = (orc_uint8) var55.x4[1]; + var56.x4[2] = (orc_uint8) var55.x4[2]; + var56.x4[3] = (orc_uint8) var55.x4[3]; + /* 13: subw */ + var57.x4[0] = var56.x4[0] - var52.x4[0]; + var57.x4[1] = var56.x4[1] - var52.x4[1]; + var57.x4[2] = var56.x4[2] - var52.x4[2]; + var57.x4[3] = var56.x4[3] - var52.x4[3]; + /* 14: loadl */ + var58 = ptr0[i]; + /* 15: shrul */ + var59.i = ((orc_uint32) var58.i) >> 24; + /* 16: convlw */ + var60.i = var59.i; + /* 17: convwb */ + var61 = var60.i; + /* 18: splatbl */ + var62.i = + ((((orc_uint32) var61) & 0xff) << 24) | ((((orc_uint32) var61) & 0xff) + << 16) | ((((orc_uint32) var61) & 0xff) << 8) | (((orc_uint32) var61) + & 0xff); + /* 19: convubw */ + var63.x4[0] = (orc_uint8) var62.x4[0]; + var63.x4[1] = (orc_uint8) var62.x4[1]; + var63.x4[2] = (orc_uint8) var62.x4[2]; + var63.x4[3] = (orc_uint8) var62.x4[3]; + /* 20: mullw */ + var64.x4[0] = (var63.x4[0] * var57.x4[0]) & 0xffff; + var64.x4[1] = (var63.x4[1] * var57.x4[1]) & 0xffff; + var64.x4[2] = (var63.x4[2] * var57.x4[2]) & 0xffff; + var64.x4[3] = (var63.x4[3] * var57.x4[3]) & 0xffff; + /* 21: div255w */ + var65.x4[0] = + ((orc_uint16) (((orc_uint16) (var64.x4[0] + 128)) + + (((orc_uint16) (var64.x4[0] + 128)) >> 8))) >> 8; + var65.x4[1] = + ((orc_uint16) (((orc_uint16) (var64.x4[1] + 128)) + + (((orc_uint16) (var64.x4[1] + 128)) >> 8))) >> 8; + var65.x4[2] = + ((orc_uint16) (((orc_uint16) (var64.x4[2] + 128)) + + (((orc_uint16) (var64.x4[2] + 128)) >> 8))) >> 8; + var65.x4[3] = + ((orc_uint16) (((orc_uint16) (var64.x4[3] + 128)) + + (((orc_uint16) (var64.x4[3] + 128)) >> 8))) >> 8; + /* 22: convubw */ + var66.x4[0] = (orc_uint8) var58.x4[0]; + var66.x4[1] = (orc_uint8) var58.x4[1]; + var66.x4[2] = (orc_uint8) var58.x4[2]; + var66.x4[3] = (orc_uint8) var58.x4[3]; + /* 23: mullw */ + var67.x4[0] = (var66.x4[0] * var65.x4[0]) & 0xffff; + var67.x4[1] = (var66.x4[1] * var65.x4[1]) & 0xffff; + var67.x4[2] = (var66.x4[2] * var65.x4[2]) & 0xffff; + var67.x4[3] = (var66.x4[3] * var65.x4[3]) & 0xffff; + /* 24: addw */ + var68.x4[0] = var67.x4[0] + var54.x4[0]; + var68.x4[1] = var67.x4[1] + var54.x4[1]; + var68.x4[2] = var67.x4[2] + var54.x4[2]; + var68.x4[3] = var67.x4[3] + var54.x4[3]; + /* 25: addw */ + var69.x4[0] = var65.x4[0] + var52.x4[0]; + var69.x4[1] = var65.x4[1] + var52.x4[1]; + var69.x4[2] = var65.x4[2] + var52.x4[2]; + var69.x4[3] = var65.x4[3] + var52.x4[3]; + /* 26: divluw */ + var70.x4[0] = + ((var69.x4[0] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var68.x4[0]) / + ((orc_uint16) var69.x4[0] & 0xff)); + var70.x4[1] = + ((var69.x4[1] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var68.x4[1]) / + ((orc_uint16) var69.x4[1] & 0xff)); + var70.x4[2] = + ((var69.x4[2] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var68.x4[2]) / + ((orc_uint16) var69.x4[2] & 0xff)); + var70.x4[3] = + ((var69.x4[3] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var68.x4[3]) / + ((orc_uint16) var69.x4[3] & 0xff)); + /* 27: convwb */ + var71.x4[0] = var70.x4[0]; + var71.x4[1] = var70.x4[1]; + var71.x4[2] = var70.x4[2]; + var71.x4[3] = var70.x4[3]; + /* 29: andl */ + var72.i = var71.i & var43.i; + /* 30: convwb */ + var73.x4[0] = var69.x4[0]; + var73.x4[1] = var69.x4[1]; + var73.x4[2] = var69.x4[2]; + var73.x4[3] = var69.x4[3]; + /* 32: andl */ + var74.i = var73.i & var44.i; + /* 33: orl */ + var75.i = var72.i | var74.i; + /* 34: storel */ + ptr0[i] = var75; + } + } + +} + +void +compositor_orc_overlay_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 7, 9, 27, 99, 111, 109, 112, 111, 115, 105, 116, 111, 114, 95, 111, + 114, 99, 95, 111, 118, 101, 114, 108, 97, 121, 95, 98, 103, 114, 97, 11, + 4, 4, 12, 4, 4, 14, 4, 255, 255, 255, 255, 14, 4, 0, 0, 0, + 255, 14, 4, 255, 255, 255, 0, 14, 4, 24, 0, 0, 0, 16, 2, 20, + 4, 20, 4, 20, 2, 20, 1, 20, 8, 20, 8, 20, 8, 20, 4, 20, + 8, 20, 8, 113, 32, 4, 126, 33, 32, 19, 163, 34, 33, 157, 35, 34, + 152, 39, 35, 21, 2, 150, 36, 39, 21, 2, 89, 36, 36, 24, 21, 2, + 80, 36, 36, 21, 2, 150, 41, 32, 21, 2, 89, 41, 41, 36, 115, 39, + 16, 21, 2, 150, 37, 39, 21, 2, 98, 37, 37, 36, 113, 32, 0, 126, + 33, 32, 19, 163, 34, 33, 157, 35, 34, 152, 39, 35, 21, 2, 150, 38, + 39, 21, 2, 89, 38, 38, 37, 21, 2, 80, 38, 38, 21, 2, 150, 40, + 32, 21, 2, 89, 40, 40, 38, 21, 2, 70, 40, 40, 41, 21, 2, 70, + 38, 38, 36, 21, 2, 81, 40, 40, 38, 21, 2, 157, 32, 40, 106, 32, + 32, 18, 21, 2, 157, 39, 38, 106, 39, 39, 17, 123, 32, 32, 39, 128, + 0, 32, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_compositor_orc_overlay_bgra); +#else + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "compositor_orc_overlay_bgra"); + orc_program_set_backup_function (p, _backup_compositor_orc_overlay_bgra); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_constant (p, 4, 0xffffffff, "c1"); + orc_program_add_constant (p, 4, 0xff000000, "c2"); + orc_program_add_constant (p, 4, 0x00ffffff, "c3"); + orc_program_add_constant (p, 4, 0x00000018, "c4"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 4, "t1"); + orc_program_add_temporary (p, 4, "t2"); + orc_program_add_temporary (p, 2, "t3"); + orc_program_add_temporary (p, 1, "t4"); + orc_program_add_temporary (p, 8, "t5"); + orc_program_add_temporary (p, 8, "t6"); + orc_program_add_temporary (p, 8, "t7"); + orc_program_add_temporary (p, 4, "t8"); + orc_program_add_temporary (p, 8, "t9"); + orc_program_add_temporary (p, 8, "t10"); + + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrul", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_C4, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T4, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T8, ORC_VAR_T4, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T5, ORC_VAR_T8, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T5, ORC_VAR_T5, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T5, ORC_VAR_T5, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T10, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T10, ORC_VAR_T10, ORC_VAR_T5, + ORC_VAR_D1); + orc_program_append_2 (p, "loadpl", 0, ORC_VAR_T8, ORC_VAR_C1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T6, ORC_VAR_T8, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "subw", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_T5, + ORC_VAR_D1); + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_D1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrul", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_C4, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T4, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T8, ORC_VAR_T4, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T7, ORC_VAR_T8, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_T6, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T9, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T9, ORC_VAR_T9, ORC_VAR_T7, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 2, ORC_VAR_T9, ORC_VAR_T9, ORC_VAR_T10, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_T5, + ORC_VAR_D1); + orc_program_append_2 (p, "divluw", 2, ORC_VAR_T9, ORC_VAR_T9, ORC_VAR_T7, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 2, ORC_VAR_T1, ORC_VAR_T9, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "andl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C3, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 2, ORC_VAR_T8, ORC_VAR_T7, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "andl", 0, ORC_VAR_T8, ORC_VAR_T8, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "orl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_T8, + ORC_VAR_D1); + orc_program_append_2 (p, "storel", 0, ORC_VAR_D1, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ORC_EXECUTOR_M (ex) = m; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_D1] = d1_stride; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_S1] = s1_stride; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* compositor_orc_overlay_bgra_addition */ +#ifdef DISABLE_ORC +void +compositor_orc_overlay_bgra_addition (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + int i; + int j; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var43; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var44; +#else + orc_union32 var44; +#endif +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var45; +#else + orc_union32 var45; +#endif + orc_union32 var46; + orc_union32 var47; + orc_union16 var48; + orc_int8 var49; + orc_union32 var50; + orc_union64 var51; + orc_union64 var52; + orc_union64 var53; + orc_union64 var54; + orc_union64 var55; + orc_union32 var56; + orc_union64 var57; + orc_union64 var58; + orc_union32 var59; + orc_union32 var60; + orc_union16 var61; + orc_int8 var62; + orc_union32 var63; + orc_union64 var64; + orc_union64 var65; + orc_union64 var66; + orc_union64 var67; + orc_union64 var68; + orc_union64 var69; + orc_union64 var70; + orc_union64 var71; + orc_union32 var72; + orc_union32 var73; + orc_union16 var74; + orc_int8 var75; + orc_union32 var76; + orc_union64 var77; + orc_union64 var78; + orc_union32 var79; + orc_union32 var80; + orc_union32 var81; + orc_union32 var82; + orc_union32 var83; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (d1, d1_stride * j); + ptr4 = ORC_PTR_OFFSET (s1, s1_stride * j); + + /* 6: loadpw */ + var43.x4[0] = p1; + var43.x4[1] = p1; + var43.x4[2] = p1; + var43.x4[3] = p1; + /* 11: loadpl */ + var56.i = 0xffffffff; /* -1 or 2.122e-314f */ + /* 35: loadpl */ + var44.i = 0x00ffffff; /* 16777215 or 8.28905e-317f */ + /* 38: loadpl */ + var45.i = 0xff000000; /* -16777216 or 2.11371e-314f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var46 = ptr4[i]; + /* 1: shrul */ + var47.i = ((orc_uint32) var46.i) >> 24; + /* 2: convlw */ + var48.i = var47.i; + /* 3: convwb */ + var49 = var48.i; + /* 4: splatbl */ + var50.i = + ((((orc_uint32) var49) & 0xff) << 24) | ((((orc_uint32) var49) & 0xff) + << 16) | ((((orc_uint32) var49) & 0xff) << 8) | (((orc_uint32) var49) + & 0xff); + /* 5: convubw */ + var51.x4[0] = (orc_uint8) var50.x4[0]; + var51.x4[1] = (orc_uint8) var50.x4[1]; + var51.x4[2] = (orc_uint8) var50.x4[2]; + var51.x4[3] = (orc_uint8) var50.x4[3]; + /* 7: mullw */ + var52.x4[0] = (var51.x4[0] * var43.x4[0]) & 0xffff; + var52.x4[1] = (var51.x4[1] * var43.x4[1]) & 0xffff; + var52.x4[2] = (var51.x4[2] * var43.x4[2]) & 0xffff; + var52.x4[3] = (var51.x4[3] * var43.x4[3]) & 0xffff; + /* 8: div255w */ + var53.x4[0] = + ((orc_uint16) (((orc_uint16) (var52.x4[0] + 128)) + + (((orc_uint16) (var52.x4[0] + 128)) >> 8))) >> 8; + var53.x4[1] = + ((orc_uint16) (((orc_uint16) (var52.x4[1] + 128)) + + (((orc_uint16) (var52.x4[1] + 128)) >> 8))) >> 8; + var53.x4[2] = + ((orc_uint16) (((orc_uint16) (var52.x4[2] + 128)) + + (((orc_uint16) (var52.x4[2] + 128)) >> 8))) >> 8; + var53.x4[3] = + ((orc_uint16) (((orc_uint16) (var52.x4[3] + 128)) + + (((orc_uint16) (var52.x4[3] + 128)) >> 8))) >> 8; + /* 9: convubw */ + var54.x4[0] = (orc_uint8) var46.x4[0]; + var54.x4[1] = (orc_uint8) var46.x4[1]; + var54.x4[2] = (orc_uint8) var46.x4[2]; + var54.x4[3] = (orc_uint8) var46.x4[3]; + /* 10: mullw */ + var55.x4[0] = (var54.x4[0] * var53.x4[0]) & 0xffff; + var55.x4[1] = (var54.x4[1] * var53.x4[1]) & 0xffff; + var55.x4[2] = (var54.x4[2] * var53.x4[2]) & 0xffff; + var55.x4[3] = (var54.x4[3] * var53.x4[3]) & 0xffff; + /* 12: convubw */ + var57.x4[0] = (orc_uint8) var56.x4[0]; + var57.x4[1] = (orc_uint8) var56.x4[1]; + var57.x4[2] = (orc_uint8) var56.x4[2]; + var57.x4[3] = (orc_uint8) var56.x4[3]; + /* 13: subw */ + var58.x4[0] = var57.x4[0] - var53.x4[0]; + var58.x4[1] = var57.x4[1] - var53.x4[1]; + var58.x4[2] = var57.x4[2] - var53.x4[2]; + var58.x4[3] = var57.x4[3] - var53.x4[3]; + /* 14: loadl */ + var59 = ptr0[i]; + /* 15: shrul */ + var60.i = ((orc_uint32) var59.i) >> 24; + /* 16: convlw */ + var61.i = var60.i; + /* 17: convwb */ + var62 = var61.i; + /* 18: splatbl */ + var63.i = + ((((orc_uint32) var62) & 0xff) << 24) | ((((orc_uint32) var62) & 0xff) + << 16) | ((((orc_uint32) var62) & 0xff) << 8) | (((orc_uint32) var62) + & 0xff); + /* 19: convubw */ + var64.x4[0] = (orc_uint8) var63.x4[0]; + var64.x4[1] = (orc_uint8) var63.x4[1]; + var64.x4[2] = (orc_uint8) var63.x4[2]; + var64.x4[3] = (orc_uint8) var63.x4[3]; + /* 20: mullw */ + var65.x4[0] = (var64.x4[0] * var58.x4[0]) & 0xffff; + var65.x4[1] = (var64.x4[1] * var58.x4[1]) & 0xffff; + var65.x4[2] = (var64.x4[2] * var58.x4[2]) & 0xffff; + var65.x4[3] = (var64.x4[3] * var58.x4[3]) & 0xffff; + /* 21: div255w */ + var66.x4[0] = + ((orc_uint16) (((orc_uint16) (var65.x4[0] + 128)) + + (((orc_uint16) (var65.x4[0] + 128)) >> 8))) >> 8; + var66.x4[1] = + ((orc_uint16) (((orc_uint16) (var65.x4[1] + 128)) + + (((orc_uint16) (var65.x4[1] + 128)) >> 8))) >> 8; + var66.x4[2] = + ((orc_uint16) (((orc_uint16) (var65.x4[2] + 128)) + + (((orc_uint16) (var65.x4[2] + 128)) >> 8))) >> 8; + var66.x4[3] = + ((orc_uint16) (((orc_uint16) (var65.x4[3] + 128)) + + (((orc_uint16) (var65.x4[3] + 128)) >> 8))) >> 8; + /* 22: convubw */ + var67.x4[0] = (orc_uint8) var59.x4[0]; + var67.x4[1] = (orc_uint8) var59.x4[1]; + var67.x4[2] = (orc_uint8) var59.x4[2]; + var67.x4[3] = (orc_uint8) var59.x4[3]; + /* 23: mullw */ + var68.x4[0] = (var67.x4[0] * var66.x4[0]) & 0xffff; + var68.x4[1] = (var67.x4[1] * var66.x4[1]) & 0xffff; + var68.x4[2] = (var67.x4[2] * var66.x4[2]) & 0xffff; + var68.x4[3] = (var67.x4[3] * var66.x4[3]) & 0xffff; + /* 24: addw */ + var69.x4[0] = var68.x4[0] + var55.x4[0]; + var69.x4[1] = var68.x4[1] + var55.x4[1]; + var69.x4[2] = var68.x4[2] + var55.x4[2]; + var69.x4[3] = var68.x4[3] + var55.x4[3]; + /* 25: addw */ + var70.x4[0] = var66.x4[0] + var53.x4[0]; + var70.x4[1] = var66.x4[1] + var53.x4[1]; + var70.x4[2] = var66.x4[2] + var53.x4[2]; + var70.x4[3] = var66.x4[3] + var53.x4[3]; + /* 26: divluw */ + var71.x4[0] = + ((var70.x4[0] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var69.x4[0]) / + ((orc_uint16) var70.x4[0] & 0xff)); + var71.x4[1] = + ((var70.x4[1] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var69.x4[1]) / + ((orc_uint16) var70.x4[1] & 0xff)); + var71.x4[2] = + ((var70.x4[2] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var69.x4[2]) / + ((orc_uint16) var70.x4[2] & 0xff)); + var71.x4[3] = + ((var70.x4[3] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var69.x4[3]) / + ((orc_uint16) var70.x4[3] & 0xff)); + /* 27: loadl */ + var72 = ptr0[i]; + /* 28: shrul */ + var73.i = ((orc_uint32) var72.i) >> 24; + /* 29: convlw */ + var74.i = var73.i; + /* 30: convwb */ + var75 = var74.i; + /* 31: splatbl */ + var76.i = + ((((orc_uint32) var75) & 0xff) << 24) | ((((orc_uint32) var75) & 0xff) + << 16) | ((((orc_uint32) var75) & 0xff) << 8) | (((orc_uint32) var75) + & 0xff); + /* 32: convubw */ + var77.x4[0] = (orc_uint8) var76.x4[0]; + var77.x4[1] = (orc_uint8) var76.x4[1]; + var77.x4[2] = (orc_uint8) var76.x4[2]; + var77.x4[3] = (orc_uint8) var76.x4[3]; + /* 33: addw */ + var78.x4[0] = var77.x4[0] + var53.x4[0]; + var78.x4[1] = var77.x4[1] + var53.x4[1]; + var78.x4[2] = var77.x4[2] + var53.x4[2]; + var78.x4[3] = var77.x4[3] + var53.x4[3]; + /* 34: convwb */ + var79.x4[0] = var71.x4[0]; + var79.x4[1] = var71.x4[1]; + var79.x4[2] = var71.x4[2]; + var79.x4[3] = var71.x4[3]; + /* 36: andl */ + var80.i = var79.i & var44.i; + /* 37: convwb */ + var81.x4[0] = var78.x4[0]; + var81.x4[1] = var78.x4[1]; + var81.x4[2] = var78.x4[2]; + var81.x4[3] = var78.x4[3]; + /* 39: andl */ + var82.i = var81.i & var45.i; + /* 40: orl */ + var83.i = var80.i | var82.i; + /* 41: storel */ + ptr0[i] = var83; + } + } + +} + +#else +static void +_backup_compositor_orc_overlay_bgra_addition (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int j; + int n = ex->n; + int m = ex->params[ORC_VAR_A1]; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union64 var43; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var44; +#else + orc_union32 var44; +#endif +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var45; +#else + orc_union32 var45; +#endif + orc_union32 var46; + orc_union32 var47; + orc_union16 var48; + orc_int8 var49; + orc_union32 var50; + orc_union64 var51; + orc_union64 var52; + orc_union64 var53; + orc_union64 var54; + orc_union64 var55; + orc_union32 var56; + orc_union64 var57; + orc_union64 var58; + orc_union32 var59; + orc_union32 var60; + orc_union16 var61; + orc_int8 var62; + orc_union32 var63; + orc_union64 var64; + orc_union64 var65; + orc_union64 var66; + orc_union64 var67; + orc_union64 var68; + orc_union64 var69; + orc_union64 var70; + orc_union64 var71; + orc_union32 var72; + orc_union32 var73; + orc_union16 var74; + orc_int8 var75; + orc_union32 var76; + orc_union64 var77; + orc_union64 var78; + orc_union32 var79; + orc_union32 var80; + orc_union32 var81; + orc_union32 var82; + orc_union32 var83; + + for (j = 0; j < m; j++) { + ptr0 = ORC_PTR_OFFSET (ex->arrays[0], ex->params[0] * j); + ptr4 = ORC_PTR_OFFSET (ex->arrays[4], ex->params[4] * j); + + /* 6: loadpw */ + var43.x4[0] = ex->params[24]; + var43.x4[1] = ex->params[24]; + var43.x4[2] = ex->params[24]; + var43.x4[3] = ex->params[24]; + /* 11: loadpl */ + var56.i = 0xffffffff; /* -1 or 2.122e-314f */ + /* 35: loadpl */ + var44.i = 0x00ffffff; /* 16777215 or 8.28905e-317f */ + /* 38: loadpl */ + var45.i = 0xff000000; /* -16777216 or 2.11371e-314f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var46 = ptr4[i]; + /* 1: shrul */ + var47.i = ((orc_uint32) var46.i) >> 24; + /* 2: convlw */ + var48.i = var47.i; + /* 3: convwb */ + var49 = var48.i; + /* 4: splatbl */ + var50.i = + ((((orc_uint32) var49) & 0xff) << 24) | ((((orc_uint32) var49) & 0xff) + << 16) | ((((orc_uint32) var49) & 0xff) << 8) | (((orc_uint32) var49) + & 0xff); + /* 5: convubw */ + var51.x4[0] = (orc_uint8) var50.x4[0]; + var51.x4[1] = (orc_uint8) var50.x4[1]; + var51.x4[2] = (orc_uint8) var50.x4[2]; + var51.x4[3] = (orc_uint8) var50.x4[3]; + /* 7: mullw */ + var52.x4[0] = (var51.x4[0] * var43.x4[0]) & 0xffff; + var52.x4[1] = (var51.x4[1] * var43.x4[1]) & 0xffff; + var52.x4[2] = (var51.x4[2] * var43.x4[2]) & 0xffff; + var52.x4[3] = (var51.x4[3] * var43.x4[3]) & 0xffff; + /* 8: div255w */ + var53.x4[0] = + ((orc_uint16) (((orc_uint16) (var52.x4[0] + 128)) + + (((orc_uint16) (var52.x4[0] + 128)) >> 8))) >> 8; + var53.x4[1] = + ((orc_uint16) (((orc_uint16) (var52.x4[1] + 128)) + + (((orc_uint16) (var52.x4[1] + 128)) >> 8))) >> 8; + var53.x4[2] = + ((orc_uint16) (((orc_uint16) (var52.x4[2] + 128)) + + (((orc_uint16) (var52.x4[2] + 128)) >> 8))) >> 8; + var53.x4[3] = + ((orc_uint16) (((orc_uint16) (var52.x4[3] + 128)) + + (((orc_uint16) (var52.x4[3] + 128)) >> 8))) >> 8; + /* 9: convubw */ + var54.x4[0] = (orc_uint8) var46.x4[0]; + var54.x4[1] = (orc_uint8) var46.x4[1]; + var54.x4[2] = (orc_uint8) var46.x4[2]; + var54.x4[3] = (orc_uint8) var46.x4[3]; + /* 10: mullw */ + var55.x4[0] = (var54.x4[0] * var53.x4[0]) & 0xffff; + var55.x4[1] = (var54.x4[1] * var53.x4[1]) & 0xffff; + var55.x4[2] = (var54.x4[2] * var53.x4[2]) & 0xffff; + var55.x4[3] = (var54.x4[3] * var53.x4[3]) & 0xffff; + /* 12: convubw */ + var57.x4[0] = (orc_uint8) var56.x4[0]; + var57.x4[1] = (orc_uint8) var56.x4[1]; + var57.x4[2] = (orc_uint8) var56.x4[2]; + var57.x4[3] = (orc_uint8) var56.x4[3]; + /* 13: subw */ + var58.x4[0] = var57.x4[0] - var53.x4[0]; + var58.x4[1] = var57.x4[1] - var53.x4[1]; + var58.x4[2] = var57.x4[2] - var53.x4[2]; + var58.x4[3] = var57.x4[3] - var53.x4[3]; + /* 14: loadl */ + var59 = ptr0[i]; + /* 15: shrul */ + var60.i = ((orc_uint32) var59.i) >> 24; + /* 16: convlw */ + var61.i = var60.i; + /* 17: convwb */ + var62 = var61.i; + /* 18: splatbl */ + var63.i = + ((((orc_uint32) var62) & 0xff) << 24) | ((((orc_uint32) var62) & 0xff) + << 16) | ((((orc_uint32) var62) & 0xff) << 8) | (((orc_uint32) var62) + & 0xff); + /* 19: convubw */ + var64.x4[0] = (orc_uint8) var63.x4[0]; + var64.x4[1] = (orc_uint8) var63.x4[1]; + var64.x4[2] = (orc_uint8) var63.x4[2]; + var64.x4[3] = (orc_uint8) var63.x4[3]; + /* 20: mullw */ + var65.x4[0] = (var64.x4[0] * var58.x4[0]) & 0xffff; + var65.x4[1] = (var64.x4[1] * var58.x4[1]) & 0xffff; + var65.x4[2] = (var64.x4[2] * var58.x4[2]) & 0xffff; + var65.x4[3] = (var64.x4[3] * var58.x4[3]) & 0xffff; + /* 21: div255w */ + var66.x4[0] = + ((orc_uint16) (((orc_uint16) (var65.x4[0] + 128)) + + (((orc_uint16) (var65.x4[0] + 128)) >> 8))) >> 8; + var66.x4[1] = + ((orc_uint16) (((orc_uint16) (var65.x4[1] + 128)) + + (((orc_uint16) (var65.x4[1] + 128)) >> 8))) >> 8; + var66.x4[2] = + ((orc_uint16) (((orc_uint16) (var65.x4[2] + 128)) + + (((orc_uint16) (var65.x4[2] + 128)) >> 8))) >> 8; + var66.x4[3] = + ((orc_uint16) (((orc_uint16) (var65.x4[3] + 128)) + + (((orc_uint16) (var65.x4[3] + 128)) >> 8))) >> 8; + /* 22: convubw */ + var67.x4[0] = (orc_uint8) var59.x4[0]; + var67.x4[1] = (orc_uint8) var59.x4[1]; + var67.x4[2] = (orc_uint8) var59.x4[2]; + var67.x4[3] = (orc_uint8) var59.x4[3]; + /* 23: mullw */ + var68.x4[0] = (var67.x4[0] * var66.x4[0]) & 0xffff; + var68.x4[1] = (var67.x4[1] * var66.x4[1]) & 0xffff; + var68.x4[2] = (var67.x4[2] * var66.x4[2]) & 0xffff; + var68.x4[3] = (var67.x4[3] * var66.x4[3]) & 0xffff; + /* 24: addw */ + var69.x4[0] = var68.x4[0] + var55.x4[0]; + var69.x4[1] = var68.x4[1] + var55.x4[1]; + var69.x4[2] = var68.x4[2] + var55.x4[2]; + var69.x4[3] = var68.x4[3] + var55.x4[3]; + /* 25: addw */ + var70.x4[0] = var66.x4[0] + var53.x4[0]; + var70.x4[1] = var66.x4[1] + var53.x4[1]; + var70.x4[2] = var66.x4[2] + var53.x4[2]; + var70.x4[3] = var66.x4[3] + var53.x4[3]; + /* 26: divluw */ + var71.x4[0] = + ((var70.x4[0] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var69.x4[0]) / + ((orc_uint16) var70.x4[0] & 0xff)); + var71.x4[1] = + ((var70.x4[1] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var69.x4[1]) / + ((orc_uint16) var70.x4[1] & 0xff)); + var71.x4[2] = + ((var70.x4[2] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var69.x4[2]) / + ((orc_uint16) var70.x4[2] & 0xff)); + var71.x4[3] = + ((var70.x4[3] & 0xff) == + 0) ? 255 : ORC_CLAMP_UB (((orc_uint16) var69.x4[3]) / + ((orc_uint16) var70.x4[3] & 0xff)); + /* 27: loadl */ + var72 = ptr0[i]; + /* 28: shrul */ + var73.i = ((orc_uint32) var72.i) >> 24; + /* 29: convlw */ + var74.i = var73.i; + /* 30: convwb */ + var75 = var74.i; + /* 31: splatbl */ + var76.i = + ((((orc_uint32) var75) & 0xff) << 24) | ((((orc_uint32) var75) & 0xff) + << 16) | ((((orc_uint32) var75) & 0xff) << 8) | (((orc_uint32) var75) + & 0xff); + /* 32: convubw */ + var77.x4[0] = (orc_uint8) var76.x4[0]; + var77.x4[1] = (orc_uint8) var76.x4[1]; + var77.x4[2] = (orc_uint8) var76.x4[2]; + var77.x4[3] = (orc_uint8) var76.x4[3]; + /* 33: addw */ + var78.x4[0] = var77.x4[0] + var53.x4[0]; + var78.x4[1] = var77.x4[1] + var53.x4[1]; + var78.x4[2] = var77.x4[2] + var53.x4[2]; + var78.x4[3] = var77.x4[3] + var53.x4[3]; + /* 34: convwb */ + var79.x4[0] = var71.x4[0]; + var79.x4[1] = var71.x4[1]; + var79.x4[2] = var71.x4[2]; + var79.x4[3] = var71.x4[3]; + /* 36: andl */ + var80.i = var79.i & var44.i; + /* 37: convwb */ + var81.x4[0] = var78.x4[0]; + var81.x4[1] = var78.x4[1]; + var81.x4[2] = var78.x4[2]; + var81.x4[3] = var78.x4[3]; + /* 39: andl */ + var82.i = var81.i & var45.i; + /* 40: orl */ + var83.i = var80.i | var82.i; + /* 41: storel */ + ptr0[i] = var83; + } + } + +} + +void +compositor_orc_overlay_bgra_addition (guint8 * ORC_RESTRICT d1, int d1_stride, + const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 7, 9, 36, 99, 111, 109, 112, 111, 115, 105, 116, 111, 114, 95, 111, + 114, 99, 95, 111, 118, 101, 114, 108, 97, 121, 95, 98, 103, 114, 97, 95, + 97, 100, 100, 105, 116, 105, 111, 110, 11, 4, 4, 12, 4, 4, 14, 4, + 255, 255, 255, 255, 14, 4, 0, 0, 0, 255, 14, 4, 255, 255, 255, 0, + 14, 4, 24, 0, 0, 0, 16, 2, 20, 4, 20, 4, 20, 2, 20, 1, + 20, 8, 20, 8, 20, 8, 20, 8, 20, 4, 20, 8, 20, 8, 113, 32, + 4, 126, 33, 32, 19, 163, 34, 33, 157, 35, 34, 152, 40, 35, 21, 2, + 150, 36, 40, 21, 2, 89, 36, 36, 24, 21, 2, 80, 36, 36, 21, 2, + 150, 42, 32, 21, 2, 89, 42, 42, 36, 115, 40, 16, 21, 2, 150, 37, + 40, 21, 2, 98, 37, 37, 36, 113, 32, 0, 126, 33, 32, 19, 163, 34, + 33, 157, 35, 34, 152, 40, 35, 21, 2, 150, 38, 40, 21, 2, 89, 38, + 38, 37, 21, 2, 80, 38, 38, 21, 2, 150, 41, 32, 21, 2, 89, 41, + 41, 38, 21, 2, 70, 41, 41, 42, 21, 2, 70, 38, 38, 36, 21, 2, + 81, 41, 41, 38, 113, 32, 0, 126, 33, 32, 19, 163, 34, 33, 157, 35, + 34, 152, 40, 35, 21, 2, 150, 39, 40, 21, 2, 70, 39, 39, 36, 21, + 2, 157, 32, 41, 106, 32, 32, 18, 21, 2, 157, 40, 39, 106, 40, 40, + 17, 123, 32, 32, 40, 128, 0, 32, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_compositor_orc_overlay_bgra_addition); +#else + p = orc_program_new (); + orc_program_set_2d (p); + orc_program_set_name (p, "compositor_orc_overlay_bgra_addition"); + orc_program_set_backup_function (p, + _backup_compositor_orc_overlay_bgra_addition); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_constant (p, 4, 0xffffffff, "c1"); + orc_program_add_constant (p, 4, 0xff000000, "c2"); + orc_program_add_constant (p, 4, 0x00ffffff, "c3"); + orc_program_add_constant (p, 4, 0x00000018, "c4"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 4, "t1"); + orc_program_add_temporary (p, 4, "t2"); + orc_program_add_temporary (p, 2, "t3"); + orc_program_add_temporary (p, 1, "t4"); + orc_program_add_temporary (p, 8, "t5"); + orc_program_add_temporary (p, 8, "t6"); + orc_program_add_temporary (p, 8, "t7"); + orc_program_add_temporary (p, 8, "t8"); + orc_program_add_temporary (p, 4, "t9"); + orc_program_add_temporary (p, 8, "t10"); + orc_program_add_temporary (p, 8, "t11"); + + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrul", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_C4, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T4, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T9, ORC_VAR_T4, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T5, ORC_VAR_T9, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T5, ORC_VAR_T5, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T5, ORC_VAR_T5, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T11, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T11, ORC_VAR_T11, ORC_VAR_T5, + ORC_VAR_D1); + orc_program_append_2 (p, "loadpl", 0, ORC_VAR_T9, ORC_VAR_C1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T6, ORC_VAR_T9, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "subw", 2, ORC_VAR_T6, ORC_VAR_T6, ORC_VAR_T5, + ORC_VAR_D1); + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_D1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrul", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_C4, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T4, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T9, ORC_VAR_T4, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T7, ORC_VAR_T9, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_T6, + ORC_VAR_D1); + orc_program_append_2 (p, "div255w", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T10, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "mullw", 2, ORC_VAR_T10, ORC_VAR_T10, ORC_VAR_T7, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 2, ORC_VAR_T10, ORC_VAR_T10, ORC_VAR_T11, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 2, ORC_VAR_T7, ORC_VAR_T7, ORC_VAR_T5, + ORC_VAR_D1); + orc_program_append_2 (p, "divluw", 2, ORC_VAR_T10, ORC_VAR_T10, + ORC_VAR_T7, ORC_VAR_D1); + orc_program_append_2 (p, "loadl", 0, ORC_VAR_T1, ORC_VAR_D1, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrul", 0, ORC_VAR_T2, ORC_VAR_T1, ORC_VAR_C4, + ORC_VAR_D1); + orc_program_append_2 (p, "convlw", 0, ORC_VAR_T3, ORC_VAR_T2, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 0, ORC_VAR_T4, ORC_VAR_T3, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "splatbl", 0, ORC_VAR_T9, ORC_VAR_T4, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "convubw", 2, ORC_VAR_T8, ORC_VAR_T9, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "addw", 2, ORC_VAR_T8, ORC_VAR_T8, ORC_VAR_T5, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 2, ORC_VAR_T1, ORC_VAR_T10, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "andl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C3, + ORC_VAR_D1); + orc_program_append_2 (p, "convwb", 2, ORC_VAR_T9, ORC_VAR_T8, ORC_VAR_D1, + ORC_VAR_D1); + orc_program_append_2 (p, "andl", 0, ORC_VAR_T9, ORC_VAR_T9, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "orl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_T9, + ORC_VAR_D1); + orc_program_append_2 (p, "storel", 0, ORC_VAR_D1, ORC_VAR_T1, ORC_VAR_D1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ORC_EXECUTOR_M (ex) = m; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_D1] = d1_stride; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_S1] = s1_stride; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif diff --git a/gst/compositor/compositororc-dist.h b/gst/compositor/compositororc-dist.h new file mode 100644 index 0000000000..75b6fb76a3 --- /dev/null +++ b/gst/compositor/compositororc-dist.h @@ -0,0 +1,100 @@ + +/* autogenerated from compositororc.orc */ + +#ifndef _COMPOSITORORC_H_ +#define _COMPOSITORORC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifndef _ORC_INTEGER_TYPEDEFS_ +#define _ORC_INTEGER_TYPEDEFS_ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +typedef int8_t orc_int8; +typedef int16_t orc_int16; +typedef int32_t orc_int32; +typedef int64_t orc_int64; +typedef uint8_t orc_uint8; +typedef uint16_t orc_uint16; +typedef uint32_t orc_uint32; +typedef uint64_t orc_uint64; +#define ORC_UINT64_C(x) UINT64_C(x) +#elif defined(_MSC_VER) +typedef signed __int8 orc_int8; +typedef signed __int16 orc_int16; +typedef signed __int32 orc_int32; +typedef signed __int64 orc_int64; +typedef unsigned __int8 orc_uint8; +typedef unsigned __int16 orc_uint16; +typedef unsigned __int32 orc_uint32; +typedef unsigned __int64 orc_uint64; +#define ORC_UINT64_C(x) (x##Ui64) +#define inline __inline +#else +#include +typedef signed char orc_int8; +typedef short orc_int16; +typedef int orc_int32; +typedef unsigned char orc_uint8; +typedef unsigned short orc_uint16; +typedef unsigned int orc_uint32; +#if INT_MAX == LONG_MAX +typedef long long orc_int64; +typedef unsigned long long orc_uint64; +#define ORC_UINT64_C(x) (x##ULL) +#else +typedef long orc_int64; +typedef unsigned long orc_uint64; +#define ORC_UINT64_C(x) (x##UL) +#endif +#endif +typedef union { orc_int16 i; orc_int8 x2[2]; } orc_union16; +typedef union { orc_int32 i; float f; orc_int16 x2[2]; orc_int8 x4[4]; } orc_union32; +typedef union { orc_int64 i; double f; orc_int32 x2[2]; float x2f[2]; orc_int16 x4[4]; } orc_union64; +#endif +#ifndef ORC_RESTRICT +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define ORC_RESTRICT restrict +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define ORC_RESTRICT __restrict__ +#else +#define ORC_RESTRICT +#endif +#endif + +#ifndef ORC_INTERNAL +#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +#define ORC_INTERNAL __hidden +#elif defined (__GNUC__) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#else +#define ORC_INTERNAL +#endif +#endif + +void compositor_orc_splat_u32 (guint32 * ORC_RESTRICT d1, int p1, int n); +void compositor_orc_memcpy_u32 (guint32 * ORC_RESTRICT d1, const guint32 * ORC_RESTRICT s1, int n); +void compositor_orc_blend_u8 (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_blend_argb (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_source_argb (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_blend_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_source_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_overlay_argb (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_overlay_argb_addition (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_overlay_bgra (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); +void compositor_orc_overlay_bgra_addition (guint8 * ORC_RESTRICT d1, int d1_stride, const guint8 * ORC_RESTRICT s1, int s1_stride, int p1, int n, int m); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/gst/compositor/compositororc.orc b/gst/compositor/compositororc.orc new file mode 100644 index 0000000000..fea01a473a --- /dev/null +++ b/gst/compositor/compositororc.orc @@ -0,0 +1,425 @@ +.function compositor_orc_splat_u32 +.dest 4 d1 guint32 +.param 4 p1 guint32 + +copyl d1, p1 + +.function compositor_orc_memcpy_u32 +.dest 4 d1 guint32 +.source 4 s1 guint32 + +copyl d1, s1 + +.function compositor_orc_blend_u8 +.flags 2d +.dest 1 d1 guint8 +.source 1 s1 guint8 +.param 2 p1 +.temp 2 t1 +.temp 2 t2 +.const 1 c1 8 + +convubw t1, d1 +convubw t2, s1 +subw t2, t2, t1 +mullw t2, t2, p1 +shlw t1, t1, c1 +addw t2, t1, t2 +shruw t2, t2, c1 +convsuswb d1, t2 + + +.function compositor_orc_blend_argb +.flags 2d +.dest 4 d guint8 +.source 4 s guint8 +.param 2 alpha +.temp 4 t +.temp 2 tw +.temp 1 tb +.temp 4 a +.temp 8 d_wide +.temp 8 s_wide +.temp 8 a_wide +.const 4 a_alpha 0x000000ff + +loadl t, s +convlw tw, t +convwb tb, tw +splatbl a, tb +x4 convubw a_wide, a +x4 mullw a_wide, a_wide, alpha +x4 div255w a_wide, a_wide +x4 convubw s_wide, t +loadl t, d +x4 convubw d_wide, t +x4 subw s_wide, s_wide, d_wide +x4 mullw s_wide, s_wide, a_wide + +x4 div255w s_wide, s_wide +x4 addw d_wide, d_wide, s_wide +x4 convwb t, d_wide +orl t, t, a_alpha +storel d, t + +.function compositor_orc_source_argb +.flags 2d +.dest 4 d guint8 +.source 4 s guint8 +.param 2 alpha +.temp 4 t +.temp 4 t2 +.temp 2 tw +.temp 1 tb +.temp 4 a +.temp 8 a_wide +.const 4 a_alpha 0x000000ff +.const 4 a_not_alpha 0xffffff00 + +loadl t, s +shrul t2, t, 24 +convlw tw, t2 +convwb tb, tw +splatbl a, tb +x4 convubw a_wide, a +x4 mullw a_wide, a_wide, alpha +x4 div255w a_wide, a_wide + +andl t, t, a_not_alpha +x4 convwb t2, a_wide +andl t2, t2, a_alpha +orl t, t, t2 + +storel d, t + +.function compositor_orc_blend_bgra +.flags 2d +.dest 4 d guint8 +.source 4 s guint8 +.param 2 alpha +.temp 4 t +.temp 4 t2 +.temp 2 tw +.temp 1 tb +.temp 4 a +.temp 8 d_wide +.temp 8 s_wide +.temp 8 a_wide +.const 4 a_alpha 0xff000000 + +loadl t, s +shrul t2, t, 24 +convlw tw, t2 +convwb tb, tw +splatbl a, tb +x4 convubw a_wide, a +x4 mullw a_wide, a_wide, alpha +x4 div255w a_wide, a_wide + +x4 convubw s_wide, t +loadl t, d +x4 convubw d_wide, t +x4 subw s_wide, s_wide, d_wide +x4 mullw s_wide, s_wide, a_wide +x4 div255w s_wide, s_wide + +x4 addw d_wide, d_wide, s_wide +x4 convwb t, d_wide +orl t, t, a_alpha +storel d, t + +.function compositor_orc_source_bgra +.flags 2d +.dest 4 d guint8 +.source 4 s guint8 +.param 2 alpha +.temp 4 t +.temp 4 t2 +.temp 2 tw +.temp 1 tb +.temp 4 a +.temp 8 a_wide +.const 4 a_alpha 0xff000000 +.const 4 a_not_alpha 0x00ffffff + +loadl t, s +shrul t2, t, 24 +convlw tw, t2 +convwb tb, tw +splatbl a, tb +x4 convubw a_wide, a +x4 mullw a_wide, a_wide, alpha +x4 div255w a_wide, a_wide + +andl t, t, a_not_alpha +x4 convwb t2, a_wide +andl t2, t2, a_alpha +orl t, t, t2 + +storel d, t + +.function compositor_orc_overlay_argb +.flags 2d +.dest 4 d guint8 +.source 4 s guint8 +.param 2 alpha +.temp 4 t +.temp 2 tw +.temp 1 tb +.temp 8 alpha_s +.temp 8 alpha_s_inv +.temp 8 alpha_d +.temp 4 a +.temp 8 d_wide +.temp 8 s_wide +.const 4 xfs 0xffffffff +.const 4 a_alpha 0x000000ff +.const 4 a_alpha_inv 0xffffff00 + +# calc source alpha as alpha_s = alpha_s * alpha / 255 +loadl t, s +convlw tw, t +convwb tb, tw +splatbl a, tb +x4 convubw alpha_s, a +x4 mullw alpha_s, alpha_s, alpha +x4 div255w alpha_s, alpha_s +x4 convubw s_wide, t +x4 mullw s_wide, s_wide, alpha_s + +# calc destination alpha as alpha_d = (255-alpha_s) * alpha_d / 255 +loadpl a, xfs +x4 convubw alpha_s_inv, a +x4 subw alpha_s_inv, alpha_s_inv, alpha_s +loadl t, d +convlw tw, t +convwb tb, tw +splatbl a, tb +x4 convubw alpha_d, a +x4 mullw alpha_d, alpha_d, alpha_s_inv +x4 div255w alpha_d, alpha_d +x4 convubw d_wide, t +x4 mullw d_wide, d_wide, alpha_d + +# calc final pixel as pix_d = pix_s*alpha_s + pix_d*alpha_d*(255-alpha_s)/255 +x4 addw d_wide, d_wide, s_wide + +# calc the final destination alpha_d = alpha_s + alpha_d * (255-alpha_s)/255 +x4 addw alpha_d, alpha_d, alpha_s + +# now normalize the pix_d by the final alpha to make it associative +x4 divluw, d_wide, d_wide, alpha_d + +# pack the new alpha into the correct spot +x4 convwb t, d_wide +andl t, t, a_alpha_inv +x4 convwb a, alpha_d +andl a, a, a_alpha +orl t, t, a +storel d, t + + +.function compositor_orc_overlay_argb_addition +.flags 2d +.dest 4 d guint8 +.source 4 s guint8 +.param 2 alpha +.temp 4 t +.temp 2 tw +.temp 1 tb +.temp 8 alpha_s +.temp 8 alpha_s_inv +.temp 8 alpha_factor +.temp 8 alpha_d +.temp 4 a +.temp 8 d_wide +.temp 8 s_wide +.const 4 xfs 0xffffffff +.const 4 a_alpha 0x000000ff +.const 4 a_alpha_inv 0xffffff00 + +# calc source alpha as alpha_s = alpha_s * alpha / 255 +loadl t, s +convlw tw, t +convwb tb, tw +splatbl a, tb +x4 convubw alpha_s, a +x4 mullw alpha_s, alpha_s, alpha +x4 div255w alpha_s, alpha_s +x4 convubw s_wide, t +x4 mullw s_wide, s_wide, alpha_s + +# calc destination alpha as alpha_factor = (255-alpha_s) * alpha_factor / factor +loadpl a, xfs +x4 convubw alpha_s_inv, a +x4 subw alpha_s_inv, alpha_s_inv, alpha_s +loadl t, d +convlw tw, t +convwb tb, tw +splatbl a, tb +x4 convubw alpha_factor, a +x4 mullw alpha_factor, alpha_factor, alpha_s_inv +x4 div255w alpha_factor, alpha_factor +x4 convubw d_wide, t +x4 mullw d_wide, d_wide, alpha_factor + +# calc final pixel as pix_d = pix_s*alpha_s + pix_d*alpha_factor*(255-alpha_s)/255 +x4 addw d_wide, d_wide, s_wide + +# calc the alpha factor alpha_factor = alpha_s + alpha_factor * (255-alpha_s)/255 +x4 addw alpha_factor, alpha_factor, alpha_s + +# now normalize the pix_d by the final alpha to make it associative +x4 divluw, d_wide, d_wide, alpha_factor + +# calc the final global alpha_d = alpha_d + (alpha_s * (alpha / 255)) +loadl t, d +convlw tw, t +convwb tb, tw +splatbl a, tb +x4 convubw alpha_d, a +x4 addw alpha_d, alpha_d, alpha_s + +# pack the new alpha into the correct spot +x4 convwb t, d_wide +andl t, t, a_alpha_inv +x4 convwb a, alpha_d +andl a, a, a_alpha +orl t, t, a +storel d, t + +.function compositor_orc_overlay_bgra +.flags 2d +.dest 4 d guint8 +.source 4 s guint8 +.param 2 alpha +.temp 4 t +.temp 4 t2 +.temp 2 tw +.temp 1 tb +.temp 8 alpha_s +.temp 8 alpha_s_inv +.temp 8 alpha_d +.temp 4 a +.temp 8 d_wide +.temp 8 s_wide +.const 4 xfs 0xffffffff +.const 4 a_alpha 0xff000000 +.const 4 a_alpha_inv 0x00ffffff + +# calc source alpha as alpha_s = alpha_s * alpha / 255 +loadl t, s +shrul t2, t, 24 +convlw tw, t2 +convwb tb, tw +splatbl a, tb +x4 convubw alpha_s, a +x4 mullw alpha_s, alpha_s, alpha +x4 div255w alpha_s, alpha_s +x4 convubw s_wide, t +x4 mullw s_wide, s_wide, alpha_s + +# calc destination alpha as alpha_d = (255-alpha_s) * alpha_d / 255 +loadpl a, xfs +x4 convubw alpha_s_inv, a +x4 subw alpha_s_inv, alpha_s_inv, alpha_s +loadl t, d +shrul t2, t, 24 +convlw tw, t2 +convwb tb, tw +splatbl a, tb +x4 convubw alpha_d, a +x4 mullw alpha_d, alpha_d, alpha_s_inv +x4 div255w alpha_d, alpha_d +x4 convubw d_wide, t +x4 mullw d_wide, d_wide, alpha_d + +# calc final pixel as pix_d = pix_s*alpha_s + pix_d*alpha_d*(255-alpha_s)/255 +x4 addw d_wide, d_wide, s_wide + +# calc the final destination alpha_d = alpha_s + alpha_d * (255-alpha_s)/255 +x4 addw alpha_d, alpha_d, alpha_s + +# now normalize the pix_d by the final alpha to make it associative +x4 divluw, d_wide, d_wide, alpha_d + +# pack the new alpha into the correct spot +x4 convwb t, d_wide +andl t, t, a_alpha_inv +x4 convwb a, alpha_d +andl a, a, a_alpha +orl t, t, a +storel d, t + +.function compositor_orc_overlay_bgra_addition +.flags 2d +.dest 4 d guint8 +.source 4 s guint8 +.param 2 alpha +.temp 4 t +.temp 4 t2 +.temp 2 tw +.temp 1 tb +.temp 8 alpha_s +.temp 8 alpha_s_inv +.temp 8 alpha_factor +.temp 8 alpha_d +.temp 4 a +.temp 8 d_wide +.temp 8 s_wide +.const 4 xfs 0xffffffff +.const 4 a_alpha 0xff000000 +.const 4 a_alpha_inv 0x00ffffff + +# calc source alpha as alpha_s = alpha_s * alpha / 255 +loadl t, s +shrul t2, t, 24 +convlw tw, t2 +convwb tb, tw +splatbl a, tb +x4 convubw alpha_s, a +x4 mullw alpha_s, alpha_s, alpha +x4 div255w alpha_s, alpha_s +x4 convubw s_wide, t +x4 mullw s_wide, s_wide, alpha_s + +# calc destination alpha as alpha_factor = (255-alpha_s) * alpha_factor / 255 +loadpl a, xfs +x4 convubw alpha_s_inv, a +x4 subw alpha_s_inv, alpha_s_inv, alpha_s +loadl t, d +shrul t2, t, 24 +convlw tw, t2 +convwb tb, tw +splatbl a, tb +x4 convubw alpha_factor, a +x4 mullw alpha_factor, alpha_factor, alpha_s_inv +x4 div255w alpha_factor, alpha_factor +x4 convubw d_wide, t +x4 mullw d_wide, d_wide, alpha_factor + +# calc final pixel as pix_d = pix_s*alpha_s + pix_d*alpha_factor*(255-alpha_s)/255 +x4 addw d_wide, d_wide, s_wide + +# calc the final destination alpha_factor = alpha_s + alpha_factor * (255-alpha_s)/255 +x4 addw alpha_factor, alpha_factor, alpha_s + +# now normalize the pix_d by the final alpha to make it associative +x4 divluw, d_wide, d_wide, alpha_factor + +# calc the final global alpha_d = alpha_d + (alpha_s * (alpha / 255)) +loadl t, d +shrul t2, t, 24 +convlw tw, t2 +convwb tb, tw +splatbl a, tb +x4 convubw alpha_d, a +x4 addw alpha_d, alpha_d, alpha_s + +# pack the new alpha into the correct spot +x4 convwb t, d_wide +andl t, t, a_alpha_inv +x4 convwb a, alpha_d +andl a, a, a_alpha +orl t, t, a +storel d, t diff --git a/gst/compositor/meson.build b/gst/compositor/meson.build new file mode 100644 index 0000000000..f379a31a07 --- /dev/null +++ b/gst/compositor/meson.build @@ -0,0 +1,33 @@ +compositor_sources = [ + 'blend.c', + 'compositor.c', +] + +orcsrc = 'compositororc' +if have_orcc + orc_h = custom_target(orcsrc + '.h', + input : orcsrc + '.orc', + output : orcsrc + '.h', + command : orcc_args + ['--header', '-o', '@OUTPUT@', '@INPUT@']) + orc_c = custom_target(orcsrc + '.c', + input : orcsrc + '.orc', + output : orcsrc + '.c', + command : orcc_args + ['--implementation', '-o', '@OUTPUT@', '@INPUT@']) +else + orc_h = configure_file(input : orcsrc + '-dist.h', + output : orcsrc + '.h', + copy : true) + orc_c = configure_file(input : orcsrc + '-dist.c', + output : orcsrc + '.c', + copy : true) +endif + +gstcompositor = library('gstcompositor', + compositor_sources, orc_c, orc_h, + c_args : gst_plugins_bad_args + ['-DGST_USE_UNSTABLE_API'], + include_directories : [configinc], + dependencies : [gstbadvideo_dep, gstvideo_dep, gstbase_dep, orc_dep, libm], + install : true, + install_dir : plugins_install_dir, +) +pkgconfig.generate(gstcompositor, install_dir : plugins_pkgconfig_install_dir) diff --git a/tests/check/elements/compositor.c b/tests/check/elements/compositor.c new file mode 100644 index 0000000000..34525e98ca --- /dev/null +++ b/tests/check/elements/compositor.c @@ -0,0 +1,2115 @@ +/* GStreamer + * + * unit test for compositor + * + * Copyright (C) <2005> Thomas Vander Stichele + * Copyright (C) <2013> Thibault Saunier + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_VALGRIND +# include +#endif + +#include + +#include +#include +#include +#include + +#define VIDEO_CAPS_STRING \ + "video/x-raw, " \ + "width = (int) 320, " \ + "height = (int) 240, " \ + "framerate = (fraction) 25/1 , " \ + "format = (string) I420" + +static GMainLoop *main_loop; + +static GstCaps * +_compositor_get_all_supported_caps (void) +{ + return gst_caps_from_string (GST_VIDEO_CAPS_MAKE + (" { AYUV, BGRA, ARGB, RGBA, ABGR, Y444, Y42B, YUY2, UYVY, " + " YVYU, I420, YV12, NV12, NV21, Y41B, RGB, BGR, xRGB, xBGR, " + " RGBx, BGRx } ")); +} + +static GstCaps * +_compositor_get_non_alpha_supported_caps (void) +{ + return gst_caps_from_string (GST_VIDEO_CAPS_MAKE + (" { Y444, Y42B, YUY2, UYVY, " + " YVYU, I420, YV12, NV12, NV21, Y41B, RGB, BGR, xRGB, xBGR, " + " RGBx, BGRx } ")); +} + +/* make sure downstream gets a CAPS event before buffers are sent */ +GST_START_TEST (test_caps) +{ + GstElement *pipeline, *src, *compositor, *sink; + GstStateChangeReturn state_res; + GstCaps *caps; + GstPad *pad; + + /* build pipeline */ + pipeline = gst_pipeline_new ("pipeline"); + + src = gst_element_factory_make ("videotestsrc", "src1"); + compositor = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (pipeline), src, compositor, sink, NULL); + + fail_unless (gst_element_link_many (src, compositor, sink, NULL)); + + /* prepare playing */ + state_res = gst_element_set_state (pipeline, GST_STATE_PAUSED); + fail_unless_equals_int (state_res, GST_STATE_CHANGE_ASYNC); + + /* wait for preroll */ + state_res = gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); + fail_unless_equals_int (state_res, GST_STATE_CHANGE_SUCCESS); + + /* check caps on fakesink */ + pad = gst_element_get_static_pad (sink, "sink"); + caps = gst_pad_get_current_caps (pad); + fail_unless (caps != NULL); + gst_caps_unref (caps); + gst_object_unref (pad); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_END_TEST; + +static void +message_received (GstBus * bus, GstMessage * message, GstPipeline * bin) +{ + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + switch (message->type) { + case GST_MESSAGE_EOS: + g_main_loop_quit (main_loop); + break; + case GST_MESSAGE_WARNING:{ + GError *gerror; + gchar *debug; + + gst_message_parse_warning (message, &gerror, &debug); + gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); + g_error_free (gerror); + g_free (debug); + break; + } + case GST_MESSAGE_ERROR:{ + GError *gerror; + gchar *debug; + + gst_message_parse_error (message, &gerror, &debug); + gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); + g_error_free (gerror); + g_free (debug); + g_main_loop_quit (main_loop); + break; + } + default: + break; + } +} + + +static GstFormat format = GST_FORMAT_UNDEFINED; +static gint64 position = -1; + +static void +test_event_message_received (GstBus * bus, GstMessage * message, + GstPipeline * bin) +{ + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + switch (message->type) { + case GST_MESSAGE_SEGMENT_DONE: + gst_message_parse_segment_done (message, &format, &position); + GST_INFO ("received segment_done : %" G_GINT64_FORMAT, position); + g_main_loop_quit (main_loop); + break; + default: + g_assert_not_reached (); + break; + } +} + + +GST_START_TEST (test_event) +{ + GstElement *bin, *src1, *src2, *compositor, *sink; + GstBus *bus; + GstEvent *seek_event; + GstStateChangeReturn state_res; + gboolean res; + GstPad *srcpad, *sinkpad; + GstStreamConsistency *chk_1, *chk_2, *chk_3; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("videotestsrc", "src1"); + src2 = gst_element_factory_make ("videotestsrc", "src2"); + compositor = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, src2, compositor, sink, NULL); + + res = gst_element_link (src1, compositor); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, compositor); + fail_unless (res == TRUE, NULL); + res = gst_element_link (compositor, sink); + fail_unless (res == TRUE, NULL); + + srcpad = gst_element_get_static_pad (compositor, "src"); + chk_3 = gst_consistency_checker_new (srcpad); + gst_object_unref (srcpad); + + /* create consistency checkers for the pads */ + srcpad = gst_element_get_static_pad (src1, "src"); + chk_1 = gst_consistency_checker_new (srcpad); + sinkpad = gst_pad_get_peer (srcpad); + gst_consistency_checker_add_pad (chk_3, sinkpad); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + srcpad = gst_element_get_static_pad (src2, "src"); + chk_2 = gst_consistency_checker_new (srcpad); + sinkpad = gst_pad_get_peer (srcpad); + gst_consistency_checker_add_pad (chk_3, sinkpad); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); + + format = GST_FORMAT_UNDEFINED; + position = -1; + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", + (GCallback) test_event_message_received, bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, seek_event); + fail_unless (res == TRUE, NULL); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + GST_INFO ("running main loop"); + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + ck_assert_int_eq (position, 2 * GST_SECOND); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_consistency_checker_free (chk_1); + gst_consistency_checker_free (chk_2); + gst_consistency_checker_free (chk_3); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +static GstBuffer * +create_video_buffer (GstCaps * caps, gint ts_in_seconds) +{ + GstVideoInfo info; + guint size; + GstBuffer *buf; + GstMapInfo mapinfo; + + fail_unless (gst_video_info_from_caps (&info, caps)); + + size = GST_VIDEO_INFO_WIDTH (&info) * GST_VIDEO_INFO_HEIGHT (&info); + + switch (GST_VIDEO_INFO_FORMAT (&info)) { + case GST_VIDEO_FORMAT_RGB: + size *= 3; + break; + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_ARGB: + size *= 4; + break; + case GST_VIDEO_FORMAT_I420: + size *= 2; + break; + default: + fail ("Unsupported test format"); + } + + buf = gst_buffer_new_and_alloc (size); + /* write something to avoid uninitialized error issues (valgrind) */ + gst_buffer_map (buf, &mapinfo, GST_MAP_WRITE); + memset (mapinfo.data, 0, mapinfo.size); + gst_buffer_unmap (buf, &mapinfo); + + GST_BUFFER_PTS (buf) = ts_in_seconds * GST_SECOND; + GST_BUFFER_DURATION (buf) = GST_SECOND; + return buf; +} + + +GST_START_TEST (test_caps_query) +{ + GstElement *compositor, *capsfilter, *sink; + GstElement *pipeline; + gboolean res; + GstStateChangeReturn state_res; + GstPad *sinkpad; + GstCaps *caps, *restriction_caps; + GstCaps *all_caps, *non_alpha_caps; + + /* initial setup */ + all_caps = _compositor_get_all_supported_caps (); + non_alpha_caps = _compositor_get_non_alpha_supported_caps (); + + compositor = gst_element_factory_make ("compositor", "compositor"); + capsfilter = gst_element_factory_make ("capsfilter", "out-cf"); + sink = gst_element_factory_make ("fakesink", "sink"); + pipeline = gst_pipeline_new ("test-pipeline"); + + gst_bin_add_many (GST_BIN (pipeline), compositor, capsfilter, sink, NULL); + res = gst_element_link (compositor, capsfilter); + fail_unless (res == TRUE, NULL); + res = gst_element_link (capsfilter, sink); + fail_unless (res == TRUE, NULL); + + sinkpad = gst_element_get_request_pad (compositor, "sink_%u"); + + state_res = gst_element_set_state (pipeline, GST_STATE_PLAYING); + fail_if (state_res == GST_STATE_CHANGE_FAILURE); + + /* try an unrestricted caps query, should return all formats */ + caps = gst_pad_query_caps (sinkpad, NULL); + fail_unless (gst_caps_is_equal (caps, all_caps)); + gst_caps_unref (caps); + + /* now restrict downstream to a single alpha format, it should still + * be able to convert anything else to it */ + restriction_caps = gst_caps_from_string ("video/x-raw, format=(string)AYUV"); + g_object_set (capsfilter, "caps", restriction_caps, NULL); + caps = gst_pad_query_caps (sinkpad, NULL); + fail_unless (gst_caps_is_equal (caps, all_caps)); + gst_caps_unref (caps); + gst_caps_unref (restriction_caps); + + /* now restrict downstream to a non-alpha format, it should + * be able to accept non-alpha formats */ + restriction_caps = gst_caps_from_string ("video/x-raw, format=(string)I420"); + g_object_set (capsfilter, "caps", restriction_caps, NULL); + caps = gst_pad_query_caps (sinkpad, NULL); + fail_unless (gst_caps_is_equal (caps, non_alpha_caps)); + gst_caps_unref (caps); + gst_caps_unref (restriction_caps); + + /* check that compositor proxies downstream interlace-mode */ + restriction_caps = + gst_caps_from_string ("video/x-raw, interlace-mode=(string)interleaved"); + g_object_set (capsfilter, "caps", restriction_caps, NULL); + caps = gst_pad_query_caps (sinkpad, NULL); + fail_unless (gst_caps_is_subset (caps, restriction_caps)); + gst_caps_unref (caps); + gst_caps_unref (restriction_caps); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_element_release_request_pad (compositor, sinkpad); + gst_object_unref (sinkpad); + gst_object_unref (pipeline); + gst_caps_unref (all_caps); + gst_caps_unref (non_alpha_caps); +} + +GST_END_TEST; + + +GST_START_TEST (test_caps_query_interlaced) +{ + GstElement *compositor, *sink; + GstElement *pipeline; + gboolean res; + GstStateChangeReturn state_res; + GstPad *sinkpad; + GstCaps *caps; + GstCaps *caps_mixed, *caps_progressive, *caps_interleaved; + GstEvent *caps_event; + GstQuery *drain; + + caps_interleaved = + gst_caps_from_string ("video/x-raw, interlace-mode=interleaved"); + caps_mixed = gst_caps_from_string ("video/x-raw, interlace-mode=mixed"); + caps_progressive = + gst_caps_from_string ("video/x-raw, interlace-mode=progressive"); + + /* initial setup */ + compositor = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("fakesink", "sink"); + pipeline = gst_pipeline_new ("test-pipeline"); + + gst_bin_add_many (GST_BIN (pipeline), compositor, sink, NULL); + res = gst_element_link (compositor, sink); + fail_unless (res == TRUE, NULL); + sinkpad = gst_element_get_request_pad (compositor, "sink_%u"); + + state_res = gst_element_set_state (pipeline, GST_STATE_PLAYING); + fail_if (state_res == GST_STATE_CHANGE_FAILURE); + + /* try an unrestricted caps query, should be compatible with all formats */ + caps = gst_pad_query_caps (sinkpad, NULL); + fail_unless (gst_caps_can_intersect (caps, caps_interleaved)); + fail_unless (gst_caps_can_intersect (caps, caps_progressive)); + fail_unless (gst_caps_can_intersect (caps, caps_mixed)); + gst_caps_unref (caps); + + /* now set caps on the pad, it should restrict the interlace-mode for + * future caps */ + caps = gst_caps_from_string ("video/x-raw, width=100, height=100, " + "format=RGB, framerate=1/1, interlace-mode=progressive"); + caps_event = gst_event_new_caps (caps); + gst_caps_unref (caps); + fail_unless (gst_pad_send_event (sinkpad, caps_event)); + + /* Send drain query to make sure this is processed */ + drain = gst_query_new_drain (); + gst_pad_query (sinkpad, drain); + gst_query_unref (drain); + + /* now recheck the interlace-mode */ + gst_object_unref (sinkpad); + sinkpad = gst_element_get_request_pad (compositor, "sink_%u"); + caps = gst_pad_query_caps (sinkpad, NULL); + fail_if (gst_caps_can_intersect (caps, caps_interleaved)); + fail_unless (gst_caps_can_intersect (caps, caps_progressive)); + fail_if (gst_caps_can_intersect (caps, caps_mixed)); + gst_object_unref (sinkpad); + gst_caps_unref (caps); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + gst_caps_unref (caps_interleaved); + gst_caps_unref (caps_mixed); + gst_caps_unref (caps_progressive); +} + +GST_END_TEST; + +static void +add_interlaced_mode_to_caps (GstCaps * caps, const gchar * mode) +{ + GstStructure *s; + gint i; + + for (i = 0; i < gst_caps_get_size (caps); i++) { + s = gst_caps_get_structure (caps, i); + gst_structure_set (s, "interlace-mode", G_TYPE_STRING, mode, NULL); + } +} + +#define MODE_ALL 1 +#define MODE_NON_ALPHA 2 + +static void +run_late_caps_query_test (GstCaps * input_caps, GstCaps * output_allowed_caps, + gint expected_caps_mode) +{ + GstElement *compositor, *capsfilter, *sink; + GstElement *pipeline; + gboolean res; + GstStateChangeReturn state_res; + GstPad *srcpad1, *srcpad2; + GstPad *sinkpad1, *sinkpad2; + GstSegment segment; + GstCaps *caps, *all_caps, *non_alpha_caps; + + all_caps = _compositor_get_all_supported_caps (); + non_alpha_caps = _compositor_get_non_alpha_supported_caps (); + + /* add progressive mode as it is what is used in the test, otherwise + * is_equal checks would fail */ + add_interlaced_mode_to_caps (all_caps, "progressive"); + add_interlaced_mode_to_caps (non_alpha_caps, "progressive"); + + compositor = gst_element_factory_make ("compositor", "compositor"); + capsfilter = gst_element_factory_make ("capsfilter", "out-cf"); + sink = gst_element_factory_make ("fakesink", "sink"); + pipeline = gst_pipeline_new ("test-pipeline"); + + gst_bin_add_many (GST_BIN (pipeline), compositor, capsfilter, sink, NULL); + res = gst_element_link (compositor, capsfilter); + fail_unless (res == TRUE, NULL); + res = gst_element_link (capsfilter, sink); + fail_unless (res == TRUE, NULL); + + sinkpad1 = gst_element_get_request_pad (compositor, "sink_%u"); + srcpad1 = gst_pad_new ("src1", GST_PAD_SRC); + fail_unless (gst_pad_link (srcpad1, sinkpad1) == GST_PAD_LINK_OK); + gst_pad_set_active (srcpad1, TRUE); + + state_res = gst_element_set_state (pipeline, GST_STATE_PLAYING); + fail_if (state_res == GST_STATE_CHANGE_FAILURE); + + if (output_allowed_caps) + g_object_set (capsfilter, "caps", output_allowed_caps, NULL); + + gst_segment_init (&segment, GST_FORMAT_TIME); + fail_unless (gst_pad_push_event (srcpad1, + gst_event_new_stream_start ("test-1"))); + fail_unless (gst_pad_push_event (srcpad1, gst_event_new_caps (input_caps))); + fail_unless (gst_pad_push_event (srcpad1, gst_event_new_segment (&segment))); + fail_unless (gst_pad_push (srcpad1, + create_video_buffer (input_caps, 0)) == GST_FLOW_OK); + fail_unless (gst_pad_push (srcpad1, + create_video_buffer (input_caps, 1)) == GST_FLOW_OK); + + /* now comes the second pad */ + sinkpad2 = gst_element_get_request_pad (compositor, "sink_%u"); + srcpad2 = gst_pad_new ("src2", GST_PAD_SRC); + fail_unless (gst_pad_link (srcpad2, sinkpad2) == GST_PAD_LINK_OK); + gst_pad_set_active (srcpad2, TRUE); + fail_unless (gst_pad_push_event (srcpad2, + gst_event_new_stream_start ("test-2"))); + + caps = gst_pad_peer_query_caps (srcpad2, NULL); + fail_unless (gst_caps_is_equal (caps, + expected_caps_mode == MODE_ALL ? all_caps : non_alpha_caps)); + gst_caps_unref (caps); + + gst_pad_set_active (srcpad1, FALSE); + gst_pad_set_active (srcpad2, FALSE); + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_element_release_request_pad (compositor, sinkpad1); + gst_element_release_request_pad (compositor, sinkpad2); + gst_object_unref (sinkpad1); + gst_object_unref (sinkpad2); + gst_object_unref (pipeline); + gst_object_unref (srcpad1); + gst_object_unref (srcpad2); + gst_caps_unref (all_caps); + gst_caps_unref (non_alpha_caps); +} + +GST_START_TEST (test_late_caps_query) +{ + GstCaps *rgb_caps; + GstCaps *non_alpha_caps; + + rgb_caps = gst_caps_from_string ("video/x-raw, format=(string)RGB, " + "width=(int)100, height=(int)100, framerate=(fraction)1/1"); + non_alpha_caps = gst_caps_from_string ("video/x-raw, format=(string)RGB"); + + /* check that a 2nd pad that is added late to compositor will be able to + * negotiate to formats that depend only on downstream caps and not on what + * the other pads have already negotiated */ + run_late_caps_query_test (rgb_caps, NULL, MODE_ALL); + run_late_caps_query_test (rgb_caps, non_alpha_caps, MODE_NON_ALPHA); + + gst_caps_unref (non_alpha_caps); + gst_caps_unref (rgb_caps); +} + +GST_END_TEST; + +static void +run_late_caps_set_test (GstCaps * first_caps, GstCaps * expected_query_caps, + GstCaps * second_caps, gboolean accept_caps) +{ + GstElement *capsfilter_1; + GstElement *compositor; + GstElement *pipeline; + GstStateChangeReturn state_res; + GstPad *sinkpad_2; + GstCaps *caps; + GstBus *bus; + GstMessage *msg; + + pipeline = + gst_parse_launch ("videotestsrc num-buffers=10 ! capsfilter name=cf1 !" + " compositor name=c ! fakesink sync=true", NULL); + fail_unless (pipeline != NULL); + + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + + compositor = gst_bin_get_by_name (GST_BIN (pipeline), "c"); + capsfilter_1 = gst_bin_get_by_name (GST_BIN (pipeline), "cf1"); + + g_object_set (capsfilter_1, "caps", first_caps, NULL); + + state_res = gst_element_set_state (pipeline, GST_STATE_PAUSED); + fail_if (state_res == GST_STATE_CHANGE_FAILURE); + + /* wait for pipeline to get to paused */ + msg = + gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, + GST_MESSAGE_ASYNC_DONE); + fail_unless (msg != NULL); + fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ASYNC_DONE); + gst_message_unref (msg); + + /* try to set the second caps */ + sinkpad_2 = gst_element_get_request_pad (compositor, "sink_%u"); + caps = gst_pad_query_caps (sinkpad_2, NULL); + fail_unless (gst_caps_is_subset (expected_query_caps, caps)); + gst_caps_unref (caps); + caps = gst_pad_query_caps (sinkpad_2, second_caps); + fail_unless (gst_caps_is_empty (caps) != accept_caps); + gst_caps_unref (caps); + gst_object_unref (sinkpad_2); + + gst_object_unref (bus); + gst_object_unref (compositor); + gst_object_unref (capsfilter_1); + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_START_TEST (test_late_caps_different_interlaced) +{ + GstCaps *non_interlaced_caps; + GstCaps *interlaced_caps; + + non_interlaced_caps = + gst_caps_from_string ("video/x-raw, interlace-mode=progressive, " + "format=RGB, width=100, height=100, framerate=1/1"); + interlaced_caps = + gst_caps_from_string ("video/x-raw, interlace-mode=interleaved, " + "format=RGB, width=100, height=100, framerate=1/1"); + + run_late_caps_set_test (non_interlaced_caps, non_interlaced_caps, + interlaced_caps, FALSE); + + gst_caps_unref (non_interlaced_caps); + gst_caps_unref (interlaced_caps); +} + +GST_END_TEST; + +static guint play_count = 0; +static GstEvent *play_seek_event = NULL; + +static void +test_play_twice_message_received (GstBus * bus, GstMessage * message, + GstPipeline * bin) +{ + gboolean res; + GstStateChangeReturn state_res; + + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + switch (message->type) { + case GST_MESSAGE_SEGMENT_DONE: + play_count++; + if (play_count == 1) { + state_res = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_READY); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* prepare playing again */ + state_res = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (GST_ELEMENT (bin), + gst_event_ref (play_seek_event)); + fail_unless (res == TRUE, NULL); + + state_res = + gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + } else { + g_main_loop_quit (main_loop); + } + break; + default: + g_assert_not_reached (); + break; + } +} + + +GST_START_TEST (test_play_twice) +{ + GstElement *bin, *src1, *src2, *compositor, *sink; + GstBus *bus; + gboolean res; + GstStateChangeReturn state_res; + GstPad *srcpad; + GstStreamConsistency *consist; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("videotestsrc", "src1"); + src2 = gst_element_factory_make ("videotestsrc", "src2"); + compositor = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, src2, compositor, sink, NULL); + + res = gst_element_link (src1, compositor); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, compositor); + fail_unless (res == TRUE, NULL); + res = gst_element_link (compositor, sink); + fail_unless (res == TRUE, NULL); + + srcpad = gst_element_get_static_pad (compositor, "src"); + consist = gst_consistency_checker_new (srcpad); + gst_object_unref (srcpad); + + play_seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); + + play_count = 0; + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", + (GCallback) test_play_twice_message_received, bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, gst_event_ref (play_seek_event)); + fail_unless (res == TRUE, NULL); + + GST_INFO ("seeked"); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + ck_assert_int_eq (play_count, 2); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_consistency_checker_free (consist); + gst_event_unref (play_seek_event); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +GST_START_TEST (test_play_twice_then_add_and_play_again) +{ + GstElement *bin, *src1, *src2, *src3, *compositor, *sink; + GstBus *bus; + gboolean res; + GstStateChangeReturn state_res; + gint i; + GstPad *srcpad; + GstStreamConsistency *consist; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("videotestsrc", "src1"); + src2 = gst_element_factory_make ("videotestsrc", "src2"); + compositor = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, src2, compositor, sink, NULL); + + srcpad = gst_element_get_static_pad (compositor, "src"); + consist = gst_consistency_checker_new (srcpad); + gst_object_unref (srcpad); + + res = gst_element_link (src1, compositor); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, compositor); + fail_unless (res == TRUE, NULL); + res = gst_element_link (compositor, sink); + fail_unless (res == TRUE, NULL); + + play_seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", + (GCallback) test_play_twice_message_received, bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + /* run it twice */ + for (i = 0; i < 2; i++) { + play_count = 0; + + GST_INFO ("starting test-loop %d", i); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, gst_event_ref (play_seek_event)); + fail_unless (res == TRUE, NULL); + + GST_INFO ("seeked"); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_READY); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + ck_assert_int_eq (play_count, 2); + + /* plug another source */ + if (i == 0) { + src3 = gst_element_factory_make ("videotestsrc", "src3"); + gst_bin_add (GST_BIN (bin), src3); + + res = gst_element_link (src3, compositor); + fail_unless (res == TRUE, NULL); + } + + gst_consistency_checker_reset (consist); + } + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_event_unref (play_seek_event); + gst_consistency_checker_free (consist); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +/* check if adding pads work as expected */ +GST_START_TEST (test_add_pad) +{ + GstElement *bin, *src1, *src2, *compositor, *sink; + GstBus *bus; + GstPad *srcpad; + gboolean res; + GstStateChangeReturn state_res; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("videotestsrc", "src1"); + g_object_set (src1, "num-buffers", 4, NULL); + src2 = gst_element_factory_make ("videotestsrc", "src2"); + /* one buffer less, we connect with 1 buffer of delay */ + g_object_set (src2, "num-buffers", 3, NULL); + compositor = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, compositor, sink, NULL); + + res = gst_element_link (src1, compositor); + fail_unless (res == TRUE, NULL); + res = gst_element_link (compositor, sink); + fail_unless (res == TRUE, NULL); + + srcpad = gst_element_get_static_pad (compositor, "src"); + gst_object_unref (srcpad); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", (GCallback) message_received, + bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* add other element */ + gst_bin_add_many (GST_BIN (bin), src2, NULL); + + /* now link the second element */ + res = gst_element_link (src2, compositor); + fail_unless (res == TRUE, NULL); + + /* set to PAUSED as well */ + state_res = gst_element_set_state (src2, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* now play all */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +/* check if removing pads work as expected */ +GST_START_TEST (test_remove_pad) +{ + GstElement *bin, *src, *compositor, *sink; + GstBus *bus; + GstPad *pad, *srcpad; + gboolean res; + GstStateChangeReturn state_res; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src = gst_element_factory_make ("videotestsrc", "src"); + g_object_set (src, "num-buffers", 4, NULL); + compositor = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src, compositor, sink, NULL); + + res = gst_element_link (src, compositor); + fail_unless (res == TRUE, NULL); + res = gst_element_link (compositor, sink); + fail_unless (res == TRUE, NULL); + + /* create an unconnected sinkpad in compositor */ + pad = gst_element_get_request_pad (compositor, "sink_%u"); + fail_if (pad == NULL, NULL); + + srcpad = gst_element_get_static_pad (compositor, "src"); + gst_object_unref (srcpad); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", (GCallback) message_received, + bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing, this will not preroll as compositor is waiting + * on the unconnected sinkpad. */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion for one second, will return ASYNC */ + state_res = gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, GST_SECOND); + ck_assert_int_eq (state_res, GST_STATE_CHANGE_ASYNC); + + /* get rid of the pad now, compositor should stop waiting on it and + * continue the preroll */ + gst_element_release_request_pad (compositor, pad); + gst_object_unref (pad); + + /* wait for completion, should work now */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* now play all */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_bus_remove_signal_watch (bus); + gst_object_unref (G_OBJECT (bus)); + gst_object_unref (G_OBJECT (bin)); +} + +GST_END_TEST; + + +static GstBuffer *handoff_buffer = NULL; + +static gboolean +_quit (GMainLoop * ml) +{ + g_main_loop_quit (ml); + + return G_SOURCE_REMOVE; +} + +static void +handoff_buffer_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad, + gpointer user_data) +{ + GST_DEBUG ("got buffer %p", buffer); + gst_buffer_replace (&handoff_buffer, buffer); + + if (main_loop) + g_idle_add ((GSourceFunc) _quit, main_loop); +} + +/* check if clipping works as expected */ +GST_START_TEST (test_clip) +{ + GstSegment segment; + GstElement *bin, *compositor, *sink; + GstBus *bus; + GstPad *sinkpad; + gboolean res; + GstStateChangeReturn state_res; + GstFlowReturn ret; + GstEvent *event; + GstBuffer *buffer; + GstCaps *caps; + GMainLoop *local_mainloop; + + GST_INFO ("preparing test"); + + local_mainloop = g_main_loop_new (NULL, FALSE); + main_loop = NULL; + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + /* just an compositor and a fakesink */ + compositor = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("fakesink", "sink"); + g_object_set (sink, "signal-handoffs", TRUE, NULL); + g_signal_connect (sink, "handoff", (GCallback) handoff_buffer_cb, NULL); + gst_bin_add_many (GST_BIN (bin), compositor, sink, NULL); + + res = gst_element_link (compositor, sink); + fail_unless (res == TRUE, NULL); + + /* set to playing */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* create an unconnected sinkpad in compositor, should also automatically activate + * the pad */ + sinkpad = gst_element_get_request_pad (compositor, "sink_%u"); + fail_if (sinkpad == NULL, NULL); + + gst_pad_send_event (sinkpad, gst_event_new_stream_start ("test")); + + caps = gst_caps_from_string (VIDEO_CAPS_STRING); + + gst_pad_set_caps (sinkpad, caps); + gst_caps_unref (caps); + + /* send segment to compositor */ + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = GST_SECOND; + segment.stop = 2 * GST_SECOND; + segment.time = 0; + event = gst_event_new_segment (&segment); + gst_pad_send_event (sinkpad, event); + + /* should be clipped and ok */ + buffer = gst_buffer_new_and_alloc (115200); + GST_BUFFER_TIMESTAMP (buffer) = 0; + GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + fail_unless (handoff_buffer == NULL); + + /* should be partially clipped */ + buffer = gst_buffer_new_and_alloc (115200); + GST_BUFFER_TIMESTAMP (buffer) = 900 * GST_MSECOND; + GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p", buffer); + + main_loop = local_mainloop; + ret = gst_pad_chain (sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + g_main_loop_run (main_loop); + gst_buffer_replace (&handoff_buffer, NULL); + + /* should not be clipped */ + buffer = gst_buffer_new_and_alloc (115200); + GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (sinkpad, buffer); + g_main_loop_run (main_loop); + main_loop = NULL; + ck_assert_int_eq (ret, GST_FLOW_OK); + fail_unless (handoff_buffer != NULL); + gst_buffer_replace (&handoff_buffer, NULL); + + /* should be clipped and ok */ + buffer = gst_buffer_new_and_alloc (115200); + GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + fail_unless (handoff_buffer == NULL); + + gst_object_unref (sinkpad); + gst_element_set_state (bin, GST_STATE_NULL); + g_main_loop_unref (local_mainloop); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +GST_START_TEST (test_duration_is_max) +{ + GstElement *bin, *src[3], *compositor, *sink; + GstStateChangeReturn state_res; + GstFormat format = GST_FORMAT_TIME; + gboolean res; + gint64 duration; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + + /* 3 sources, an compositor and a fakesink */ + src[0] = gst_element_factory_make ("videotestsrc", NULL); + src[1] = gst_element_factory_make ("videotestsrc", NULL); + src[2] = gst_element_factory_make ("videotestsrc", NULL); + compositor = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src[0], src[1], src[2], compositor, sink, + NULL); + + gst_element_link (src[0], compositor); + gst_element_link (src[1], compositor); + gst_element_link (src[2], compositor); + gst_element_link (compositor, sink); + + /* irks, duration is reset on basesrc */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + /* set durations on src */ + GST_BASE_SRC (src[0])->segment.duration = 1000; + GST_BASE_SRC (src[1])->segment.duration = 3000; + GST_BASE_SRC (src[2])->segment.duration = 2000; + + /* set to playing */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + res = gst_element_query_duration (GST_ELEMENT (bin), format, &duration); + fail_unless (res, NULL); + + ck_assert_int_eq (duration, 3000); + + gst_element_set_state (bin, GST_STATE_NULL); + gst_object_unref (bin); +} + +GST_END_TEST; + +GST_START_TEST (test_duration_unknown_overrides) +{ + GstElement *bin, *src[3], *compositor, *sink; + GstStateChangeReturn state_res; + GstFormat format = GST_FORMAT_TIME; + gboolean res; + gint64 duration; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + + /* 3 sources, an compositor and a fakesink */ + src[0] = gst_element_factory_make ("videotestsrc", NULL); + src[1] = gst_element_factory_make ("videotestsrc", NULL); + src[2] = gst_element_factory_make ("videotestsrc", NULL); + compositor = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src[0], src[1], src[2], compositor, sink, + NULL); + + gst_element_link (src[0], compositor); + gst_element_link (src[1], compositor); + gst_element_link (src[2], compositor); + gst_element_link (compositor, sink); + + /* irks, duration is reset on basesrc */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + /* set durations on src */ + GST_BASE_SRC (src[0])->segment.duration = GST_CLOCK_TIME_NONE; + GST_BASE_SRC (src[1])->segment.duration = 3000; + GST_BASE_SRC (src[2])->segment.duration = 2000; + + /* set to playing */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + res = gst_element_query_duration (GST_ELEMENT (bin), format, &duration); + fail_unless (res, NULL); + + ck_assert_int_eq (duration, GST_CLOCK_TIME_NONE); + + gst_element_set_state (bin, GST_STATE_NULL); + gst_object_unref (bin); +} + +GST_END_TEST; + + +static gboolean looped = FALSE; + +static void +loop_segment_done (GstBus * bus, GstMessage * message, GstElement * bin) +{ + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + if (looped) { + g_main_loop_quit (main_loop); + } else { + GstEvent *seek_event; + gboolean res; + + seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 1 * GST_SECOND); + + res = gst_element_send_event (bin, seek_event); + fail_unless (res == TRUE, NULL); + looped = TRUE; + } +} + +GST_START_TEST (test_loop) +{ + GstElement *bin, *src1, *src2, *compositor, *sink; + GstBus *bus; + GstEvent *seek_event; + GstStateChangeReturn state_res; + gboolean res; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("videotestsrc", "src1"); + src2 = gst_element_factory_make ("videotestsrc", "src2"); + compositor = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, src2, compositor, sink, NULL); + + res = gst_element_link (src1, compositor); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, compositor); + fail_unless (res == TRUE, NULL); + res = gst_element_link (compositor, sink); + fail_unless (res == TRUE, NULL); + + seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, GST_SEEK_TYPE_SET, + (GstClockTime) 2 * GST_SECOND); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", + (GCallback) loop_segment_done, bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, seek_event); + fail_unless (res == TRUE, NULL); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + GST_INFO ("running main loop"); + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +GST_START_TEST (test_flush_start_flush_stop) +{ + GstPadTemplate *sink_template; + GstPad *sinkpad1, *sinkpad2, *compositor_src; + GstElement *compositor; + + GST_INFO ("preparing test"); + + /* build pipeline */ + compositor = gst_element_factory_make ("compositor", "compositor"); + + sink_template = + gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (compositor), + "sink_%u"); + fail_unless (GST_IS_PAD_TEMPLATE (sink_template)); + sinkpad1 = gst_element_request_pad (compositor, sink_template, NULL, NULL); + sinkpad2 = gst_element_request_pad (compositor, sink_template, NULL, NULL); + gst_object_unref (sinkpad2); + + gst_element_set_state (compositor, GST_STATE_PLAYING); + fail_unless (gst_element_get_state (compositor, NULL, NULL, + GST_CLOCK_TIME_NONE) == GST_STATE_CHANGE_SUCCESS); + + compositor_src = gst_element_get_static_pad (compositor, "src"); + fail_if (GST_PAD_IS_FLUSHING (compositor_src)); + gst_pad_send_event (sinkpad1, gst_event_new_flush_start ()); + fail_if (GST_PAD_IS_FLUSHING (compositor_src)); + fail_unless (GST_PAD_IS_FLUSHING (sinkpad1)); + gst_pad_send_event (sinkpad1, gst_event_new_flush_stop (TRUE)); + fail_if (GST_PAD_IS_FLUSHING (compositor_src)); + fail_if (GST_PAD_IS_FLUSHING (sinkpad1)); + gst_object_unref (compositor_src); + + /* cleanup */ + gst_element_set_state (compositor, GST_STATE_NULL); + gst_object_unref (sinkpad1); + gst_object_unref (compositor); +} + +GST_END_TEST; + +GST_START_TEST (test_segment_base_handling) +{ + GstElement *pipeline, *sink, *mix, *src1, *src2; + GstPad *srcpad, *sinkpad; + GstClockTime end_time; + GstSample *last_sample = NULL; + GstSample *sample; + GstBuffer *buf; + GstCaps *caps; + + caps = gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT, 16, + "height", G_TYPE_INT, 16, "framerate", GST_TYPE_FRACTION, 30, 1, NULL); + + /* each source generates 5 seconds of data, src2 shifted by 5 seconds */ + pipeline = gst_pipeline_new ("pipeline"); + mix = gst_element_factory_make ("compositor", "compositor"); + sink = gst_element_factory_make ("appsink", "sink"); + g_object_set (sink, "caps", caps, "sync", FALSE, NULL); + gst_caps_unref (caps); + src1 = gst_element_factory_make ("videotestsrc", "src1"); + g_object_set (src1, "num-buffers", 30 * 5, "pattern", 2, NULL); + src2 = gst_element_factory_make ("videotestsrc", "src2"); + g_object_set (src2, "num-buffers", 30 * 5, "pattern", 2, NULL); + gst_bin_add_many (GST_BIN (pipeline), src1, src2, mix, sink, NULL); + fail_unless (gst_element_link (mix, sink)); + + srcpad = gst_element_get_static_pad (src1, "src"); + sinkpad = gst_element_get_request_pad (mix, "sink_1"); + fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + srcpad = gst_element_get_static_pad (src2, "src"); + sinkpad = gst_element_get_request_pad (mix, "sink_2"); + fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK); + gst_pad_set_offset (sinkpad, 5 * GST_SECOND); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + do { + g_signal_emit_by_name (sink, "pull-sample", &sample); + if (sample == NULL) + break; + if (last_sample) + gst_sample_unref (last_sample); + last_sample = sample; + } while (TRUE); + + buf = gst_sample_get_buffer (last_sample); + end_time = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf); + fail_unless_equals_int64 (end_time, 10 * GST_SECOND); + gst_sample_unref (last_sample); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_END_TEST; + +static gboolean buffer_mapped; +static gboolean (*default_map) (GstVideoMeta * meta, guint plane, + GstMapInfo * info, gpointer * data, gint * stride, GstMapFlags flags); + +static gboolean +test_obscured_new_videometa_map (GstVideoMeta * meta, guint plane, + GstMapInfo * info, gpointer * data, gint * stride, GstMapFlags flags) +{ + buffer_mapped = TRUE; + return default_map (meta, plane, info, data, stride, flags); +} + +static GstPadProbeReturn +test_obscured_pad_probe_cb (GstPad * srcpad, GstPadProbeInfo * info, + gpointer user_data) +{ + GstBuffer *obuf, *nbuf; + GstVideoMeta *meta; + + GST_DEBUG ("pad probe called"); + /* We need to deep-copy the buffer here because videotestsrc reuses buffers + * and hence the GstVideoMap associated with the buffers, and that causes a + * segfault inside videotestsrc when it tries to reuse the buffer */ + obuf = GST_PAD_PROBE_INFO_BUFFER (info); + nbuf = gst_buffer_new (); + gst_buffer_copy_into (nbuf, obuf, GST_BUFFER_COPY_ALL | GST_BUFFER_COPY_DEEP, + 0, -1); + meta = gst_buffer_get_video_meta (nbuf); + /* Override the default map() function to set also buffer_mapped */ + default_map = meta->map; + meta->map = test_obscured_new_videometa_map; + /* Replace the buffer that's going downstream */ + GST_PAD_PROBE_INFO_DATA (info) = nbuf; + gst_buffer_unref (obuf); + + return GST_PAD_PROBE_PASS; +} + +static void +_test_obscured (const gchar * caps_str, gint xpos0, gint ypos0, gint width0, + gint height0, gdouble alpha0, gint xpos1, gint ypos1, gint width1, + gint height1, gdouble alpha1, gint out_width, gint out_height) +{ + GstElement *pipeline, *sink, *mix, *src0, *cfilter0, *src1, *cfilter1; + GstElement *out_cfilter; + GstPad *srcpad, *sinkpad; + GstSample *last_sample = NULL; + GstSample *sample; + GstCaps *caps; + + GST_INFO ("preparing test"); + + pipeline = gst_pipeline_new ("pipeline"); + src0 = gst_element_factory_make ("videotestsrc", "src0"); + g_object_set (src0, "num-buffers", 5, NULL); + cfilter0 = gst_element_factory_make ("capsfilter", "capsfilter0"); + caps = gst_caps_from_string (caps_str); + g_object_set (cfilter0, "caps", caps, NULL); + gst_caps_unref (caps); + + src1 = gst_element_factory_make ("videotestsrc", "src1"); + g_object_set (src1, "num-buffers", 5, NULL); + cfilter1 = gst_element_factory_make ("capsfilter", "capsfilter1"); + caps = gst_caps_from_string (caps_str); + g_object_set (cfilter1, "caps", caps, NULL); + gst_caps_unref (caps); + + mix = gst_element_factory_make ("compositor", "compositor"); + out_cfilter = gst_element_factory_make ("capsfilter", "out_capsfilter"); + caps = gst_caps_from_string (caps_str); + if (out_width > 0) + gst_caps_set_simple (caps, "width", G_TYPE_INT, out_width, NULL); + if (out_height > 0) + gst_caps_set_simple (caps, "height", G_TYPE_INT, out_height, NULL); + g_object_set (out_cfilter, "caps", caps, NULL); + gst_caps_unref (caps); + sink = gst_element_factory_make ("appsink", "sink"); + + gst_bin_add_many (GST_BIN (pipeline), src0, cfilter0, src1, cfilter1, mix, + out_cfilter, sink, NULL); + fail_unless (gst_element_link (src0, cfilter0)); + fail_unless (gst_element_link (src1, cfilter1)); + fail_unless (gst_element_link (mix, out_cfilter)); + fail_unless (gst_element_link (out_cfilter, sink)); + + srcpad = gst_element_get_static_pad (cfilter0, "src"); + sinkpad = gst_element_get_request_pad (mix, "sink_0"); + g_object_set (sinkpad, "xpos", xpos0, "ypos", ypos0, "width", width0, + "height", height0, "alpha", alpha0, NULL); + fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK); + gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_BUFFER, + test_obscured_pad_probe_cb, NULL, NULL); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + srcpad = gst_element_get_static_pad (cfilter1, "src"); + sinkpad = gst_element_get_request_pad (mix, "sink_1"); + g_object_set (sinkpad, "xpos", xpos1, "ypos", ypos1, "width", width1, + "height", height1, "alpha", alpha1, NULL); + fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + GST_INFO ("sample prepared"); + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + do { + GST_DEBUG ("sample pulling"); + g_signal_emit_by_name (sink, "pull-sample", &sample); + if (sample == NULL) + break; + if (last_sample) + gst_sample_unref (last_sample); + last_sample = sample; + GST_DEBUG ("sample pulled"); + } while (TRUE); + gst_sample_unref (last_sample); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_START_TEST (test_obscured_skipped) +{ + gint xpos0, xpos1; + gint ypos0, ypos1; + gint width0, width1; + gint height0, height1; + gint out_width, out_height; + gdouble alpha0, alpha1; + const gchar *caps_str; + + caps_str = "video/x-raw"; + buffer_mapped = FALSE; + /* Set else to compositor defaults */ + alpha0 = alpha1 = 1.0; + xpos0 = xpos1 = ypos0 = ypos1 = 0; + width0 = width1 = height0 = height1 = 0; + out_width = out_height = 0; + + GST_INFO ("testing defaults"); + /* With everything at defaults, sink_1 will obscure sink_0, so buffers from + * sink_0 will never get mapped by compositor. To verify, run with + * GST_DEBUG=compositor:6 and look for "Obscured by" messages */ + _test_obscured (caps_str, xpos0, ypos0, width0, height0, alpha0, xpos1, ypos1, + width1, height1, alpha1, out_width, out_height); + fail_unless (buffer_mapped == FALSE); + buffer_mapped = FALSE; + + caps_str = "video/x-raw,format=ARGB"; + GST_INFO ("testing video with alpha channel"); + _test_obscured (caps_str, xpos0, ypos0, width0, height0, alpha0, xpos1, ypos1, + width1, height1, alpha1, out_width, out_height); + fail_unless (buffer_mapped == TRUE); + caps_str = "video/x-raw"; + buffer_mapped = FALSE; + + alpha1 = 0.0; + GST_INFO ("testing alpha1 = %.2g", alpha1); + _test_obscured (caps_str, xpos0, ypos0, width0, height0, alpha0, xpos1, ypos1, + width1, height1, alpha1, out_width, out_height); + fail_unless (buffer_mapped == TRUE); + alpha1 = 1.0; + buffer_mapped = FALSE; + + /* Test 0.1, ..., 0.9 */ + for (alpha1 = 1; alpha1 < 10; alpha1 += 1) { + GST_INFO ("testing alpha1 = %.2g", alpha1 / 10); + _test_obscured (caps_str, xpos0, ypos0, width0, height0, alpha0, xpos1, + ypos1, width1, height1, alpha1 / 10, out_width, out_height); + fail_unless (buffer_mapped == TRUE); + } + alpha1 = 1.0; + buffer_mapped = FALSE; + + width1 = height1 = 10; + GST_INFO ("testing smaller sink_1"); + _test_obscured (caps_str, xpos0, ypos0, width0, height0, alpha0, xpos1, ypos1, + width1, height1, alpha1, out_width, out_height); + fail_unless (buffer_mapped == TRUE); + width1 = height1 = 0; + buffer_mapped = FALSE; + + width0 = height0 = width1 = height1 = 10; + GST_INFO ("testing smaller sink_1 and sink0 (same sizes)"); + _test_obscured (caps_str, xpos0, ypos0, width0, height0, alpha0, xpos1, ypos1, + width1, height1, alpha1, out_width, out_height); + fail_unless (buffer_mapped == FALSE); + width0 = height0 = width1 = height1 = 0; + buffer_mapped = FALSE; + + width0 = height0 = 20; + width1 = height1 = 10; + GST_INFO ("testing smaller sink_1 and sink0 (sink_0 > sink_1)"); + _test_obscured (caps_str, xpos0, ypos0, width0, height0, alpha0, xpos1, ypos1, + width1, height1, alpha1, out_width, out_height); + fail_unless (buffer_mapped == TRUE); + width0 = height0 = width1 = height1 = 0; + buffer_mapped = FALSE; + + width0 = height0 = 10; + width1 = height1 = 20; + GST_INFO ("testing smaller sink_1 and sink0 (sink_0 < sink_1)"); + _test_obscured (caps_str, xpos0, ypos0, width0, height0, alpha0, xpos1, ypos1, + width1, height1, alpha1, out_width, out_height); + fail_unless (buffer_mapped == FALSE); + width0 = height0 = width1 = height1 = 0; + buffer_mapped = FALSE; + + xpos0 = ypos0 = 10; + xpos1 = ypos1 = 20; + GST_INFO ("testing offset"); + _test_obscured (caps_str, xpos0, ypos0, width0, height0, alpha0, xpos1, ypos1, + width1, height1, alpha1, out_width, out_height); + fail_unless (buffer_mapped == TRUE); + xpos0 = ypos0 = xpos1 = ypos1 = 0; + buffer_mapped = FALSE; + + xpos1 = ypos1 = 0; + xpos0 = ypos0 = width0 = height0 = width1 = height1 = 10; + out_width = out_height = 20; + GST_INFO ("testing bug 754107"); + _test_obscured (caps_str, xpos0, ypos0, width0, height0, alpha0, xpos1, ypos1, + width1, height1, alpha1, out_width, out_height); + fail_unless (buffer_mapped == TRUE); + xpos0 = ypos0 = xpos1 = ypos1 = width0 = height0 = width1 = height1 = 0; + out_width = out_height = 0; + buffer_mapped = FALSE; + + xpos1 = -1; + xpos0 = ypos0 = width0 = height0 = width1 = height1 = 10; + out_width = out_height = 20; + GST_INFO ("testing bug 754576"); + _test_obscured (caps_str, xpos0, ypos0, width0, height0, alpha0, xpos1, ypos1, + width1, height1, alpha1, out_width, out_height); + fail_unless (buffer_mapped == TRUE); + xpos0 = xpos1 = ypos1 = width0 = height0 = width1 = height1 = 0; + out_width = out_height = 0; + buffer_mapped = FALSE; + + xpos0 = ypos0 = 10000; + out_width = 320; + out_height = 240; + GST_INFO ("testing sink_0 outside the frame"); + _test_obscured (caps_str, xpos0, ypos0, width0, height0, alpha0, xpos1, ypos1, + width1, height1, alpha1, out_width, out_height); + fail_unless (buffer_mapped == FALSE); + xpos0 = ypos0 = out_width = out_height = 0; + buffer_mapped = FALSE; +} + +GST_END_TEST; + +static void +_pipeline_eos (GstBus * bus, GstMessage * message, GstPipeline * bin) +{ + GST_INFO ("pipeline EOS"); + g_main_loop_quit (main_loop); +} + +static GstFlowReturn +_buffer_recvd (GstElement * appsink, gint * buffers_recvd) +{ + GstSample *sample; + + g_signal_emit_by_name (appsink, "pull-sample", &sample); + ck_assert_msg (sample != NULL, "NULL sample received!"); + + (*buffers_recvd)++; + GST_INFO ("buffer recvd"); + gst_sample_unref (sample); + + if (*buffers_recvd > 5) + g_main_loop_quit (main_loop); + + return GST_FLOW_OK; +} + +GST_START_TEST (test_repeat_after_eos) +{ + gboolean res; + gint buffers_recvd; + GstPadLinkReturn link_res; + GstStateChangeReturn state_res; + GstElement *bin, *src, *compositor, *appsink; + GstPad *srcpad, *sinkpad; + GstBus *bus; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src = gst_element_factory_make ("videotestsrc", NULL); + g_object_set (src, "num-buffers", 5, NULL); + compositor = gst_element_factory_make ("compositor", NULL); + appsink = gst_element_factory_make ("appsink", NULL); + g_object_set (appsink, "emit-signals", TRUE, NULL); + gst_bin_add_many (GST_BIN (bin), src, compositor, appsink, NULL); + + res = gst_element_link (compositor, appsink); + ck_assert_msg (res == TRUE, "Could not link compositor with appsink"); + srcpad = gst_element_get_static_pad (src, "src"); + sinkpad = gst_element_get_request_pad (compositor, "sink_%u"); + /* When "repeat-after-eos" is set, compositor will keep sending the last buffer even + * after EOS, so we will receive more buffers than we sent. */ + g_object_set (sinkpad, "repeat-after-eos", TRUE, NULL); + link_res = gst_pad_link (srcpad, sinkpad); + ck_assert_msg (GST_PAD_LINK_SUCCESSFUL (link_res), "videotestsrc -> " + "compositor pad link failed: %i", link_res); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + GST_INFO ("pipeline built, connecting signals"); + + buffers_recvd = 0; + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_msg (state_res != GST_STATE_CHANGE_FAILURE, "Pipeline didn't play"); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::error", G_CALLBACK (message_received), bin); + g_signal_connect (bus, "message::warning", G_CALLBACK (message_received), + bin); + g_signal_connect (bus, "message::eos", G_CALLBACK (_pipeline_eos), bin); + g_signal_connect (appsink, "new-sample", G_CALLBACK (_buffer_recvd), + &buffers_recvd); + + GST_INFO ("starting test"); + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + ck_assert_msg (buffers_recvd > 5, "Did not receive more buffers" + " than were sent"); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +/* Test that the GST_ELEMENT(vagg)->sinkpads GList is always sorted by zorder */ +GST_START_TEST (test_pad_z_order) +{ + GstElement *compositor; + GstPad *sinkpad1, *sinkpad2, *sinkpad3; + guint zorder1, zorder2; + GList *sinkpads; + + GST_INFO ("preparing test"); + + compositor = gst_element_factory_make ("compositor", NULL); + sinkpad1 = gst_element_get_request_pad (compositor, "sink_%u"); + sinkpad2 = gst_element_get_request_pad (compositor, "sink_%u"); + + /* Pads requested later have a higher z-order than earlier ones by default */ + g_object_get (sinkpad1, "zorder", &zorder1, NULL); + g_object_get (sinkpad2, "zorder", &zorder2, NULL); + ck_assert_int_gt (zorder2, zorder1); + sinkpads = GST_ELEMENT (compositor)->sinkpads; + ck_assert_ptr_eq (sinkpads->data, sinkpad1); + ck_assert_ptr_eq (sinkpads->next->data, sinkpad2); + + /* Make sinkpad1's zorder the largest, which should re-sort the sinkpads */ + g_object_set (sinkpad1, "zorder", zorder2 + 1, NULL); + sinkpads = GST_ELEMENT (compositor)->sinkpads; + ck_assert_ptr_eq (sinkpads->data, sinkpad2); + ck_assert_ptr_eq (sinkpads->next->data, sinkpad1); + + /* Get a new pad, which should be the highest pad now */ + sinkpad3 = gst_element_get_request_pad (compositor, "sink_%u"); + sinkpads = GST_ELEMENT (compositor)->sinkpads; + ck_assert_ptr_eq (sinkpads->data, sinkpad2); + ck_assert_ptr_eq (sinkpads->next->data, sinkpad1); + ck_assert_ptr_eq (sinkpads->next->next->data, sinkpad3); + + /* cleanup */ + gst_object_unref (compositor); + gst_object_unref (sinkpad1); + gst_object_unref (sinkpad2); + gst_object_unref (sinkpad3); +} + +GST_END_TEST; + +/* + * Test that the pad numbering assigned by aggregator behaves as follows: + * 1. If a pad number is requested, it must be assigned if it is available + * 2. When numbering automatically, the largest available pad number is used + * 3. Pad names must be unique + */ +GST_START_TEST (test_pad_numbering) +{ + GstElement *mixer; + GstPad *sinkpad1, *sinkpad2, *sinkpad3, *sinkpad4; + + GST_INFO ("preparing test"); + + mixer = gst_element_factory_make ("compositor", NULL); + sinkpad1 = gst_element_get_request_pad (mixer, "sink_%u"); + sinkpad2 = gst_element_get_request_pad (mixer, "sink_7"); + sinkpad3 = gst_element_get_request_pad (mixer, "sink_1"); + sinkpad4 = gst_element_get_request_pad (mixer, "sink_%u"); + + ck_assert_str_eq (GST_PAD_NAME (sinkpad1), "sink_0"); + ck_assert_str_eq (GST_PAD_NAME (sinkpad2), "sink_7"); + ck_assert_str_eq (GST_PAD_NAME (sinkpad3), "sink_1"); + ck_assert_str_eq (GST_PAD_NAME (sinkpad4), "sink_8"); + + /* cleanup */ + gst_object_unref (mixer); + gst_object_unref (sinkpad1); + gst_object_unref (sinkpad2); + gst_object_unref (sinkpad3); + gst_object_unref (sinkpad4); +} + +GST_END_TEST; + +typedef struct +{ + gint buffers_sent; + GstClockTime first_pts; + gboolean first; + gboolean drop; +} TestStartTimeSelectionData; + +static GstPadProbeReturn +drop_buffer_cb (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) +{ + TestStartTimeSelectionData *data = user_data; + + if (data->drop) { + data->buffers_sent = data->buffers_sent + 1; + if (data->buffers_sent < 4) + return GST_PAD_PROBE_DROP; + } + + data->first_pts = GST_BUFFER_PTS (info->data); + + return GST_PAD_PROBE_REMOVE; +} + +static GstFlowReturn +first_buffer_received_cb (GstElement * appsink, gpointer user_data) +{ + TestStartTimeSelectionData *data = user_data; + GstSample *sample; + GstBuffer *buffer; + + g_signal_emit_by_name (appsink, "pull-sample", &sample); + ck_assert_msg (sample != NULL, "NULL sample received!"); + + buffer = gst_sample_get_buffer (sample); + if (!data->first) { + fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), 0); + } else { + fail_unless_equals_uint64 (GST_BUFFER_PTS (buffer), data->first_pts); + } + + gst_sample_unref (sample); + + g_main_loop_quit (main_loop); + + return GST_FLOW_EOS; +} + +static void +run_test_start_time (gboolean first, gboolean drop, gboolean unlinked) +{ + gboolean res; + GstPadLinkReturn link_res; + GstStateChangeReturn state_res; + GstElement *bin, *src, *compositor, *appsink; + GstPad *srcpad, *sinkpad; + GstBus *bus; + TestStartTimeSelectionData data = { 0, GST_CLOCK_TIME_NONE, first, drop }; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src = gst_element_factory_make ("videotestsrc", NULL); + + srcpad = gst_element_get_static_pad (src, "src"); + gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_BUFFER, drop_buffer_cb, &data, + NULL); + gst_object_unref (srcpad); + + g_object_set (src, "is-live", TRUE, NULL); + compositor = gst_element_factory_make ("compositor", NULL); + g_object_set (compositor, "start-time-selection", (first ? 1 : 0), NULL); + appsink = gst_element_factory_make ("appsink", NULL); + g_object_set (appsink, "emit-signals", TRUE, NULL); + gst_bin_add_many (GST_BIN (bin), src, compositor, appsink, NULL); + + res = gst_element_link (compositor, appsink); + ck_assert_msg (res == TRUE, "Could not link compositor with appsink"); + srcpad = gst_element_get_static_pad (src, "src"); + sinkpad = gst_element_get_request_pad (compositor, "sink_%u"); + link_res = gst_pad_link (srcpad, sinkpad); + ck_assert_msg (GST_PAD_LINK_SUCCESSFUL (link_res), "videotestsrc -> " + "compositor pad link failed: %i", link_res); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + if (unlinked) { + sinkpad = gst_element_get_request_pad (compositor, "sink_%u"); + gst_object_unref (sinkpad); + } + + GST_INFO ("pipeline built, connecting signals"); + + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_msg (state_res != GST_STATE_CHANGE_FAILURE, "Pipeline didn't play"); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::error", G_CALLBACK (message_received), bin); + g_signal_connect (bus, "message::warning", G_CALLBACK (message_received), + bin); + g_signal_connect (bus, "message::eos", G_CALLBACK (_pipeline_eos), bin); + g_signal_connect (appsink, "new-sample", + G_CALLBACK (first_buffer_received_cb), &data); + + GST_INFO ("starting test"); + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_START_TEST (test_start_time_zero_live_drop_0) +{ + run_test_start_time (FALSE, FALSE, FALSE); +} + +GST_END_TEST; + +GST_START_TEST (test_start_time_zero_live_drop_3) +{ + run_test_start_time (FALSE, TRUE, FALSE); +} + +GST_END_TEST; + +GST_START_TEST (test_start_time_zero_live_drop_3_unlinked_1) +{ + run_test_start_time (FALSE, TRUE, TRUE); +} + +GST_END_TEST; + +GST_START_TEST (test_start_time_first_live_drop_0) +{ + run_test_start_time (TRUE, FALSE, FALSE); +} + +GST_END_TEST; + +GST_START_TEST (test_start_time_first_live_drop_3) +{ + run_test_start_time (TRUE, TRUE, FALSE); +} + +GST_END_TEST; + +GST_START_TEST (test_start_time_first_live_drop_3_unlinked_1) +{ + run_test_start_time (TRUE, TRUE, TRUE); +} + +GST_END_TEST; + +static Suite * +compositor_suite (void) +{ + Suite *s = suite_create ("compositor"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_caps); + tcase_add_test (tc_chain, test_event); + tcase_add_test (tc_chain, test_caps_query); + tcase_add_test (tc_chain, test_caps_query_interlaced); + tcase_add_test (tc_chain, test_late_caps_query); + tcase_add_test (tc_chain, test_late_caps_different_interlaced); + tcase_add_test (tc_chain, test_play_twice); + tcase_add_test (tc_chain, test_play_twice_then_add_and_play_again); + tcase_add_test (tc_chain, test_add_pad); + tcase_add_test (tc_chain, test_remove_pad); + tcase_add_test (tc_chain, test_clip); + tcase_add_test (tc_chain, test_duration_is_max); + tcase_add_test (tc_chain, test_duration_unknown_overrides); + tcase_add_test (tc_chain, test_loop); + tcase_add_test (tc_chain, test_flush_start_flush_stop); + tcase_add_test (tc_chain, test_segment_base_handling); + tcase_add_test (tc_chain, test_obscured_skipped); + tcase_add_test (tc_chain, test_repeat_after_eos); + tcase_add_test (tc_chain, test_pad_z_order); + tcase_add_test (tc_chain, test_pad_numbering); + tcase_add_test (tc_chain, test_start_time_zero_live_drop_0); + tcase_add_test (tc_chain, test_start_time_zero_live_drop_3); + tcase_add_test (tc_chain, test_start_time_zero_live_drop_3_unlinked_1); + tcase_add_test (tc_chain, test_start_time_first_live_drop_0); + tcase_add_test (tc_chain, test_start_time_first_live_drop_3); + tcase_add_test (tc_chain, test_start_time_first_live_drop_3_unlinked_1); + + return s; +} + +GST_CHECK_MAIN (compositor); diff --git a/tests/examples/compositor/crossfade.c b/tests/examples/compositor/crossfade.c new file mode 100644 index 0000000000..503503ca9d --- /dev/null +++ b/tests/examples/compositor/crossfade.c @@ -0,0 +1,133 @@ +/* + * GStreamer + * Copyright (C) 2017 Thibault Saunier + * + * 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. + */ + +/** + * Simple crossfade example using the compositor element. + * + * Takes two video files and crossfades them for 10 seconds and returns. + */ + +#include +#include +#include +#include + +typedef struct +{ + GstElement *compositor; + guint z_order; +} VideoInfo; + +static gchar * +ensure_uri (const gchar * location) +{ + if (gst_uri_is_valid (location)) + return g_strdup (location); + else + return gst_filename_to_uri (location, NULL); +} + +static void +_pad_added_cb (GstElement * decodebin, GstPad * pad, VideoInfo * info) +{ + GstPad *sinkpad = + gst_element_get_request_pad (GST_ELEMENT (info->compositor), "sink_%u"); + GstControlSource *control_source; + gboolean is_last = info->z_order == 1; + + control_source = gst_interpolation_control_source_new (); + + gst_util_set_object_arg (G_OBJECT (sinkpad), "operator", + info->z_order == 0 ? "source" : "add"); + gst_object_add_control_binding (GST_OBJECT (sinkpad), + gst_direct_control_binding_new_absolute (GST_OBJECT (sinkpad), "alpha", + control_source)); + + g_object_set (control_source, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL); + + gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE + (control_source), 0, is_last ? 0.0 : 1.0); + gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE + (control_source), 10 * GST_SECOND, is_last ? 1.0 : 0.0); + g_object_set (sinkpad, "zorder", info->z_order, NULL); + + gst_pad_link (pad, sinkpad); + + g_free (info); +} + +int +main (int argc, char *argv[]) +{ + gint i; + GstMessage *message; + GstElement *compositor, *sink, *pipeline; + GstBus *bus; + + if (argc != 3) { + g_error ("Need to provide 2 input videos"); + return -1; + } + + gst_init (&argc, &argv); + pipeline = gst_element_factory_make ("pipeline", NULL); + compositor = gst_element_factory_make ("compositor", NULL); + sink = + gst_parse_bin_from_description ("videoconvert ! autovideosink", TRUE, + NULL); + + gst_util_set_object_arg (G_OBJECT (compositor), "background", "black"); + + gst_bin_add_many (GST_BIN (pipeline), compositor, sink, NULL); + g_assert (gst_element_link (compositor, sink)); + + for (i = 1; i < 3; i++) { + gchar *uri = ensure_uri (argv[i]); + VideoInfo *info = g_malloc0 (sizeof (VideoInfo)); + GstElement *uridecodebin = gst_element_factory_make ("uridecodebin", NULL); + + g_object_set (uridecodebin, "uri", uri, "expose-all-streams", FALSE, + "caps", gst_caps_from_string ("video/x-raw(ANY)"), NULL); + + info->compositor = compositor; + info->z_order = i - 1; + g_signal_connect (uridecodebin, "pad-added", (GCallback) _pad_added_cb, + info); + + gst_bin_add (GST_BIN (pipeline), uridecodebin); + } + + bus = gst_element_get_bus (pipeline); + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + message = + gst_bus_timed_pop_filtered (bus, 11 * GST_SECOND, + GST_MESSAGE_EOS | GST_MESSAGE_ERROR); + GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline), + GST_DEBUG_GRAPH_SHOW_ALL, "go"); + if (message) + gst_print ("%" GST_PTR_FORMAT "\n", message); + else + gst_print ("Timeout\n"); + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + return 0; +}